HONEYCOMB-106 - Support for generic cache management
[honeycomb.git] / infra / translate-utils / src / main / java / io / fd / honeycomb / v3po / translate / util / read / KeepaliveReaderWrapper.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package io.fd.honeycomb.v3po.translate.util.read;
18
19 import com.google.common.base.Optional;
20 import com.google.common.base.Preconditions;
21 import io.fd.honeycomb.v3po.translate.MappingContext;
22 import io.fd.honeycomb.v3po.translate.ModificationCache;
23 import io.fd.honeycomb.v3po.translate.read.ReadContext;
24 import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
25 import io.fd.honeycomb.v3po.translate.read.Reader;
26 import java.io.Closeable;
27 import java.util.concurrent.ScheduledExecutorService;
28 import java.util.concurrent.ScheduledFuture;
29 import java.util.concurrent.TimeUnit;
30 import javax.annotation.Nonnegative;
31 import javax.annotation.Nonnull;
32 import org.opendaylight.yangtools.concepts.Builder;
33 import org.opendaylight.yangtools.yang.binding.DataObject;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * Reader wrapper that periodically invokes a read to determine whether reads are still fully functional.
40  * In case a specific error occurs, Keep-alive failure listener gets notified.
41  */
42 public final class KeepaliveReaderWrapper<D extends DataObject, B extends Builder<D>> implements Reader<D, B>, Runnable, Closeable {
43
44     private static final Logger LOG = LoggerFactory.getLogger(KeepaliveReaderWrapper.class);
45
46     private static final NoopReadContext CTX = new NoopReadContext();
47
48     private final Reader<D, B> delegate;
49     private final Class<? extends Exception> exceptionType;
50     private final KeepaliveFailureListener failureListener;
51     private final ScheduledFuture<?> scheduledFuture;
52
53     /**
54      * Create new Keepalive wrapper
55      *
56      * @param delegate underlying reader performing actual reads
57      * @param executor scheduled executor service to schedule keepalive calls
58      * @param exception type of exception used to differentiate keepalive exception from other exceptions
59      * @param delayInSeconds number of seconds to wait between keepalive calls
60      * @param failureListener listener to be called whenever a keepalive failure is detected
61      */
62     public KeepaliveReaderWrapper(@Nonnull final Reader<D, B> delegate,
63                                   @Nonnull final ScheduledExecutorService executor,
64                                   @Nonnull final Class<? extends Exception> exception,
65                                   @Nonnegative final int delayInSeconds,
66                                   @Nonnull final KeepaliveFailureListener failureListener) {
67         this.delegate = delegate;
68         this.exceptionType = exception;
69         this.failureListener = failureListener;
70         Preconditions.checkArgument(delayInSeconds > 0, "Delay cannot be < 0");
71         LOG.debug("Starting keep-alive execution on top of: {} with delay of: {} seconds", delegate, delayInSeconds);
72         scheduledFuture = executor.scheduleWithFixedDelay(this, delayInSeconds, delayInSeconds, TimeUnit.SECONDS);
73     }
74
75     @Nonnull
76     public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier id,
77                                                @Nonnull final ReadContext ctx) throws ReadFailedException {
78         return delegate.read(id, ctx);
79     }
80
81     public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
82                                       @Nonnull final B builder,
83                                       @Nonnull final ReadContext ctx) throws ReadFailedException {
84         delegate.readCurrentAttributes(id, builder, ctx);
85     }
86
87     @Nonnull
88     public B getBuilder(final InstanceIdentifier<D> id) {
89         return delegate.getBuilder(id);
90     }
91
92     public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder,
93                       @Nonnull final D readValue) {
94         delegate.merge(parentBuilder, readValue);
95     }
96
97     @Nonnull
98     @Override
99     public InstanceIdentifier<D> getManagedDataObjectType() {
100         return delegate.getManagedDataObjectType();
101     }
102
103     @Override
104     public void run() {
105         LOG.trace("Invoking keepalive");
106         try {
107             final Optional<? extends DataObject> read = read(delegate.getManagedDataObjectType(), CTX);
108             LOG.debug("Keepalive executed successfully with data: {}", read);
109         } catch (Exception e) {
110             if (exceptionType.isAssignableFrom(e.getClass())) {
111                 LOG.warn("Keepalive failed. Notifying listener", e);
112                 failureListener.onKeepaliveFailure();
113             }
114             LOG.warn("Keepalive failed unexpectedly", e);
115             throw new IllegalArgumentException("Unexpected failure during keep-alive execution", e);
116         }
117     }
118
119     @Override
120     public void close() {
121         // Do not interrupt, it's not our executor
122         scheduledFuture.cancel(false);
123     }
124
125     /**
126      * Listener that gets called whenever keepalive fails as expected
127      */
128     public interface KeepaliveFailureListener {
129
130         void onKeepaliveFailure();
131     }
132
133     private static final class NoopMappingContext implements MappingContext {
134         @Override
135         public <T extends DataObject> Optional<T> read(@Nonnull final InstanceIdentifier<T> currentId) {
136             return Optional.absent();
137         }
138
139         @Override
140         public void delete(final InstanceIdentifier<?> path) {}
141
142         @Override
143         public <T extends DataObject> void merge(final InstanceIdentifier<T> path, final T data) {}
144
145         @Override
146         public <T extends DataObject> void put(final InstanceIdentifier<T> path, final T data) {}
147
148         @Override
149         public void close() {}
150     }
151
152     private static class NoopReadContext implements ReadContext {
153
154         private final ModificationCache modificationCache = new ModificationCache();
155
156         @Nonnull
157         @Override
158         public ModificationCache getModificationCache() {
159             return modificationCache;
160         }
161
162         @Nonnull
163         @Override
164         public MappingContext getMappingContext() {
165             return new NoopMappingContext();
166         }
167
168         @Override
169         public void close() {
170
171         }
172     }
173 }