Jonathan Hart

Initial implementation of GossipIntentStore.

Create/update operations are implemented and working in 2-node cluster.
No remove operations yet.

Change-Id: Ief68c9d5c3bb483823b6f92d29fa83df0ab7b58f
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +package org.onosproject.net.intent;
17 +
18 +import org.onosproject.store.Timestamp;
19 +
20 +/**
21 + * Logical clock service that issues per-intent timestamps.
22 + */
23 +public interface IntentClockService {
24 +
25 + /**
26 + * Returns a new timestamp for the specified intent.
27 + *
28 + * @param intentId identifier for the intent.
29 + * @return timestamp
30 + */
31 + public Timestamp getTimestamp(IntentId intentId);
32 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +package org.onosproject.store.intent.impl;
17 +
18 +import com.google.common.collect.ImmutableList;
19 +import com.google.common.collect.Lists;
20 +import com.google.common.collect.Sets;
21 +import org.apache.felix.scr.annotations.Activate;
22 +import org.apache.felix.scr.annotations.Component;
23 +import org.apache.felix.scr.annotations.Deactivate;
24 +import org.apache.felix.scr.annotations.Reference;
25 +import org.apache.felix.scr.annotations.ReferenceCardinality;
26 +import org.apache.felix.scr.annotations.Service;
27 +import org.onlab.util.KryoNamespace;
28 +import org.onosproject.cluster.ClusterService;
29 +import org.onosproject.cluster.NodeId;
30 +import org.onosproject.net.intent.BatchWrite;
31 +import org.onosproject.net.intent.Intent;
32 +import org.onosproject.net.intent.IntentClockService;
33 +import org.onosproject.net.intent.IntentEvent;
34 +import org.onosproject.net.intent.IntentId;
35 +import org.onosproject.net.intent.IntentState;
36 +import org.onosproject.net.intent.IntentStore;
37 +import org.onosproject.net.intent.IntentStoreDelegate;
38 +import org.onosproject.store.AbstractStore;
39 +import org.onosproject.store.Timestamp;
40 +import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
41 +import org.onosproject.store.cluster.messaging.ClusterMessage;
42 +import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
43 +import org.onosproject.store.cluster.messaging.MessageSubject;
44 +import org.onosproject.store.impl.Timestamped;
45 +import org.onosproject.store.serializers.KryoSerializer;
46 +import org.onosproject.store.serializers.impl.DistributedStoreSerializers;
47 +import org.slf4j.Logger;
48 +
49 +import java.io.IOException;
50 +import java.util.ArrayList;
51 +import java.util.List;
52 +import java.util.Set;
53 +import java.util.concurrent.ConcurrentHashMap;
54 +import java.util.concurrent.ConcurrentMap;
55 +import java.util.concurrent.ExecutorService;
56 +import java.util.concurrent.Executors;
57 +import java.util.concurrent.ScheduledExecutorService;
58 +import java.util.concurrent.TimeUnit;
59 +
60 +import static com.google.common.base.Preconditions.checkArgument;
61 +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
62 +import static org.onlab.util.Tools.minPriority;
63 +import static org.onlab.util.Tools.namedThreads;
64 +import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
65 +import static org.onosproject.store.intent.impl.GossipIntentStoreMessageSubjects.INTENT_ANTI_ENTROPY_ADVERTISEMENT;
66 +import static org.onosproject.store.intent.impl.GossipIntentStoreMessageSubjects.INTENT_SET_INSTALLABLES_MSG;
67 +import static org.onosproject.store.intent.impl.GossipIntentStoreMessageSubjects.INTENT_UPDATED_MSG;
68 +import static org.slf4j.LoggerFactory.getLogger;
69 +
70 +/**
71 + * Manages inventory of Intents in a distributed data store that uses optimistic
72 + * replication and gossip based techniques.
73 + */
74 +@Component(immediate = true, enabled = false)
75 +@Service
76 +public class GossipIntentStore
77 + extends AbstractStore<IntentEvent, IntentStoreDelegate>
78 + implements IntentStore {
79 +
80 + private final Logger log = getLogger(getClass());
81 +
82 + private final ConcurrentMap<IntentId, Intent> intents =
83 + new ConcurrentHashMap<>();
84 +
85 + private final ConcurrentMap<IntentId, Timestamped<IntentState>> intentStates
86 + = new ConcurrentHashMap<>();
87 +
88 + private final Set<IntentId> withdrawRequestedIntents
89 + = Sets.newConcurrentHashSet();
90 +
91 + private ConcurrentMap<IntentId, Timestamped<List<Intent>>> installables
92 + = new ConcurrentHashMap<>();
93 +
94 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 + protected IntentClockService intentClockService;
96 +
97 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 + protected ClusterCommunicationService clusterCommunicator;
99 +
100 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 + protected ClusterService clusterService;
102 +
103 + private static final KryoSerializer SERIALIZER = new KryoSerializer() {
104 + @Override
105 + protected void setupKryoPool() {
106 + serializerPool = KryoNamespace.newBuilder()
107 + .register(DistributedStoreSerializers.STORE_COMMON)
108 + .nextId(DistributedStoreSerializers.STORE_CUSTOM_BEGIN)
109 + .register(InternalIntentEvent.class)
110 + .register(InternalSetInstallablesEvent.class)
111 + //.register(InternalIntentAntiEntropyEvent.class)
112 + //.register(IntentAntiEntropyAdvertisement.class)
113 + .build();
114 + }
115 + };
116 +
117 + private ExecutorService executor;
118 +
119 + private ScheduledExecutorService backgroundExecutor;
120 +
121 + // TODO: Make these anti-entropy params configurable
122 + private long initialDelaySec = 5;
123 + private long periodSec = 5;
124 +
125 + @Activate
126 + public void activate() {
127 + clusterCommunicator.addSubscriber(INTENT_UPDATED_MSG,
128 + new InternalIntentCreateOrUpdateEventListener());
129 + clusterCommunicator.addSubscriber(INTENT_SET_INSTALLABLES_MSG,
130 + new InternalIntentSetInstallablesListener());
131 + clusterCommunicator.addSubscriber(
132 + INTENT_ANTI_ENTROPY_ADVERTISEMENT,
133 + new InternalIntentAntiEntropyAdvertisementListener());
134 +
135 + executor = Executors.newCachedThreadPool(namedThreads("intent-fg-%d"));
136 +
137 + backgroundExecutor =
138 + newSingleThreadScheduledExecutor(minPriority(namedThreads("intent-bg-%d")));
139 +
140 + // start anti-entropy thread
141 + //backgroundExecutor.scheduleAtFixedRate(new SendAdvertisementTask(),
142 + //initialDelaySec, periodSec, TimeUnit.SECONDS);
143 +
144 + log.info("Started");
145 + }
146 +
147 + @Deactivate
148 + public void deactivate() {
149 + executor.shutdownNow();
150 + backgroundExecutor.shutdownNow();
151 + try {
152 + if (!backgroundExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
153 + log.error("Timeout during executor shutdown");
154 + }
155 + } catch (InterruptedException e) {
156 + log.error("Error during executor shutdown", e);
157 + }
158 +
159 + intents.clear();
160 +
161 + log.info("Stopped");
162 + }
163 +
164 + @Override
165 + public long getIntentCount() {
166 + return intents.size();
167 + }
168 +
169 + @Override
170 + public Iterable<Intent> getIntents() {
171 + // TODO don't actually need to copy intents, they are immutable
172 + return ImmutableList.copyOf(intents.values());
173 + }
174 +
175 + @Override
176 + public Intent getIntent(IntentId intentId) {
177 + return intents.get(intentId);
178 + }
179 +
180 + @Override
181 + public IntentState getIntentState(IntentId intentId) {
182 + Timestamped<IntentState> state = intentStates.get(intentId);
183 + if (state != null) {
184 + return state.value();
185 + }
186 + return null;
187 + }
188 +
189 + @Override
190 + public void setState(Intent intent, IntentState newState) {
191 + // TODO implement
192 + }
193 +
194 + private IntentEvent setStateInternal(IntentId intentId, IntentState newState, Timestamp timestamp) {
195 + switch (newState) {
196 + case WITHDRAW_REQ:
197 + withdrawRequestedIntents.add(intentId);
198 + break;
199 + case INSTALL_REQ:
200 + case COMPILING:
201 + case INSTALLING:
202 + case INSTALLED:
203 + case RECOMPILING:
204 + case WITHDRAWING:
205 + case WITHDRAWN:
206 + case FAILED:
207 + synchronized (intentStates) {
208 + Timestamped<IntentState> existing = intentStates.get(intentId);
209 + if (existing == null || !existing.isNewer(timestamp)) {
210 + intentStates.put(intentId, new Timestamped<>(newState, timestamp));
211 + }
212 + }
213 + break;
214 + default:
215 + log.warn("Unknown intent state {}", newState);
216 + break;
217 + }
218 +
219 + try {
220 + // TODO make sure it's OK if the intent is null
221 + return IntentEvent.getEvent(newState, intents.get(intentId));
222 + } catch (IllegalArgumentException e) {
223 + // Transient states can't be used for events, so don't send one
224 + return null;
225 + }
226 + }
227 +
228 + @Override
229 + public void setInstallableIntents(IntentId intentId,
230 + List<Intent> installableIntents) {
231 + // TODO implement
232 + }
233 +
234 + private void setInstallableIntentsInternal(IntentId intentId,
235 + List<Intent> installableIntents,
236 + Timestamp timestamp) {
237 + synchronized (installables) {
238 + Timestamped<List<Intent>> existing = installables.get(intentId);
239 + if (existing == null || !existing.isNewer(timestamp)) {
240 + installables.put(intentId,
241 + new Timestamped<>(installableIntents, timestamp));
242 + }
243 + }
244 + }
245 +
246 + @Override
247 + public List<Intent> getInstallableIntents(IntentId intentId) {
248 + Timestamped<List<Intent>> tInstallables = installables.get(intentId);
249 + if (tInstallables != null) {
250 + return tInstallables.value();
251 + }
252 + return null;
253 + }
254 +
255 + @Override
256 + public void removeInstalledIntents(IntentId intentId) {
257 + // TODO implement
258 + }
259 +
260 + @Override
261 + public List<BatchWrite.Operation> batchWrite(BatchWrite batch) {
262 +
263 + List<IntentEvent> events = Lists.newArrayList();
264 + List<BatchWrite.Operation> failed = new ArrayList<>();
265 +
266 + for (BatchWrite.Operation op : batch.operations()) {
267 + switch (op.type()) {
268 + case CREATE_INTENT:
269 + checkArgument(op.args().size() == 1,
270 + "CREATE_INTENT takes 1 argument. %s", op);
271 + Intent intent = op.arg(0);
272 +
273 + events.add(createIntentInternal(intent));
274 + notifyPeers(new InternalIntentEvent(
275 + intent.id(), intent, INSTALL_REQ, null));
276 +
277 + break;
278 + case REMOVE_INTENT:
279 + checkArgument(op.args().size() == 1,
280 + "REMOVE_INTENT takes 1 argument. %s", op);
281 + IntentId intentId = (IntentId) op.arg(0);
282 + // TODO implement
283 +
284 + break;
285 + case SET_STATE:
286 + checkArgument(op.args().size() == 2,
287 + "SET_STATE takes 2 arguments. %s", op);
288 + intent = op.arg(0);
289 + IntentState newState = op.arg(1);
290 +
291 + Timestamp timestamp = intentClockService.getTimestamp(
292 + intent.id());
293 + IntentEvent externalEvent = setStateInternal(intent.id(), newState, timestamp);
294 + events.add(externalEvent);
295 + notifyPeers(new InternalIntentEvent(intent.id(), null, newState, timestamp));
296 +
297 + break;
298 + case SET_INSTALLABLE:
299 + checkArgument(op.args().size() == 2,
300 + "SET_INSTALLABLE takes 2 arguments. %s", op);
301 + intentId = op.arg(0);
302 + List<Intent> installableIntents = op.arg(1);
303 +
304 + Timestamp timestamp1 = intentClockService.getTimestamp(intentId);
305 + setInstallableIntentsInternal(
306 + intentId, installableIntents, timestamp1);
307 +
308 + notifyPeers(new InternalSetInstallablesEvent(intentId, installableIntents, timestamp1));
309 +
310 + break;
311 + case REMOVE_INSTALLED:
312 + checkArgument(op.args().size() == 1,
313 + "REMOVE_INSTALLED takes 1 argument. %s", op);
314 + intentId = op.arg(0);
315 + // TODO implement
316 + break;
317 + default:
318 + log.warn("Unknown Operation encountered: {}", op);
319 + failed.add(op);
320 + break;
321 + }
322 + }
323 +
324 + notifyDelegate(events);
325 + return failed;
326 + }
327 +
328 + private IntentEvent createIntentInternal(Intent intent) {
329 + Intent oldValue = intents.putIfAbsent(intent.id(), intent);
330 + if (oldValue == null) {
331 + return IntentEvent.getEvent(INSTALL_REQ, intent);
332 + }
333 +
334 + log.warn("Intent ID {} already in store, throwing new update away",
335 + intent.id());
336 + return null;
337 + }
338 +
339 + private void notifyPeers(InternalIntentEvent event) {
340 + try {
341 + broadcastMessage(INTENT_UPDATED_MSG, event);
342 + } catch (IOException e) {
343 + // TODO this won't happen; remove from API
344 + log.debug("IOException broadcasting update", e);
345 + }
346 + }
347 +
348 + private void notifyPeers(InternalSetInstallablesEvent event) {
349 + try {
350 + broadcastMessage(INTENT_SET_INSTALLABLES_MSG, event);
351 + } catch (IOException e) {
352 + // TODO this won't happen; remove from API
353 + log.debug("IOException broadcasting update", e);
354 + }
355 + }
356 +
357 + private void broadcastMessage(MessageSubject subject, Object event) throws
358 + IOException {
359 + ClusterMessage message = new ClusterMessage(
360 + clusterService.getLocalNode().id(),
361 + subject,
362 + SERIALIZER.encode(event));
363 + clusterCommunicator.broadcast(message);
364 + }
365 +
366 + private void unicastMessage(NodeId peer,
367 + MessageSubject subject,
368 + Object event) throws IOException {
369 + ClusterMessage message = new ClusterMessage(
370 + clusterService.getLocalNode().id(),
371 + subject,
372 + SERIALIZER.encode(event));
373 + clusterCommunicator.unicast(message, peer);
374 + }
375 +
376 + private void notifyDelegateIfNotNull(IntentEvent event) {
377 + if (event != null) {
378 + notifyDelegate(event);
379 + }
380 + }
381 +
382 + private final class InternalIntentCreateOrUpdateEventListener
383 + implements ClusterMessageHandler {
384 + @Override
385 + public void handle(ClusterMessage message) {
386 +
387 + log.debug("Received intent update event from peer: {}", message.sender());
388 + InternalIntentEvent event = SERIALIZER.decode(message.payload());
389 +
390 + IntentId intentId = event.intentId();
391 + Intent intent = event.intent();
392 + IntentState state = event.state();
393 + Timestamp timestamp = event.timestamp();
394 +
395 + executor.submit(() -> {
396 + try {
397 + switch (state) {
398 + case INSTALL_REQ:
399 + notifyDelegateIfNotNull(createIntentInternal(intent));
400 + break;
401 + default:
402 + notifyDelegateIfNotNull(setStateInternal(intentId, state, timestamp));
403 + break;
404 + }
405 + } catch (Exception e) {
406 + log.warn("Exception thrown handling intent create or update", e);
407 + }
408 + });
409 + }
410 + }
411 +
412 + private final class InternalIntentSetInstallablesListener
413 + implements ClusterMessageHandler {
414 + @Override
415 + public void handle(ClusterMessage message) {
416 + log.debug("Received intent set installables event from peer: {}", message.sender());
417 + InternalSetInstallablesEvent event = SERIALIZER.decode(message.payload());
418 +
419 + IntentId intentId = event.intentId();
420 + List<Intent> installables = event.installables();
421 + Timestamp timestamp = event.timestamp();
422 +
423 + executor.submit(() -> {
424 + try {
425 + setInstallableIntentsInternal(intentId, installables, timestamp);
426 + } catch (Exception e) {
427 + log.warn("Exception thrown handling intent set installables", e);
428 + }
429 + });
430 + }
431 + }
432 +
433 + private final class InternalIntentAntiEntropyAdvertisementListener
434 + implements ClusterMessageHandler {
435 +
436 + @Override
437 + public void handle(ClusterMessage message) {
438 + log.trace("Received intent Anti-Entropy advertisement from peer: {}", message.sender());
439 + // TODO implement
440 + //IntentAntiEntropyAdvertisement advertisement = SERIALIZER.decode(message.payload());
441 + backgroundExecutor.submit(() -> {
442 + try {
443 + log.debug("something");
444 + //handleAntiEntropyAdvertisement(advertisement);
445 + } catch (Exception e) {
446 + log.warn("Exception thrown handling intent advertisements", e);
447 + }
448 + });
449 + }
450 + }
451 +}
452 +
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +package org.onosproject.store.intent.impl;
17 +
18 +import org.onosproject.store.cluster.messaging.MessageSubject;
19 +
20 +/**
21 + * Message subjects for internal gossip intent store node-to-node messages.
22 + */
23 +public final class GossipIntentStoreMessageSubjects {
24 + private GossipIntentStoreMessageSubjects() {}
25 +
26 + public static final MessageSubject INTENT_UPDATED_MSG
27 + = new MessageSubject("peer-intent-updated");
28 + public static final MessageSubject INTENT_SET_INSTALLABLES_MSG
29 + = new MessageSubject("peer-intent-set-installables");
30 + public static final MessageSubject INTENT_ANTI_ENTROPY_ADVERTISEMENT
31 + = new MessageSubject("intent-anti-entropy-advertisement");
32 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +package org.onosproject.store.intent.impl;
17 +
18 +import org.apache.felix.scr.annotations.Activate;
19 +import org.apache.felix.scr.annotations.Component;
20 +import org.apache.felix.scr.annotations.Deactivate;
21 +import org.apache.felix.scr.annotations.Service;
22 +import org.onosproject.net.intent.IntentClockService;
23 +import org.onosproject.net.intent.IntentId;
24 +import org.onosproject.store.Timestamp;
25 +import org.onosproject.store.impl.WallClockTimestamp;
26 +import org.slf4j.Logger;
27 +
28 +import static org.slf4j.LoggerFactory.getLogger;
29 +
30 +/**
31 + * IntentClockService that issues timestamps based on local wallclock time.
32 + */
33 +@Component(immediate = true)
34 +@Service
35 +public class IntentClockManager implements IntentClockService {
36 +
37 + private final Logger log = getLogger(getClass());
38 +
39 + @Activate
40 + public void activate() {
41 + log.info("Started");
42 + }
43 +
44 + @Deactivate
45 + public void deactivate() {
46 + log.info("Stopped");
47 + }
48 +
49 + @Override
50 + public Timestamp getTimestamp(IntentId intentId) {
51 + return new WallClockTimestamp();
52 + }
53 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +package org.onosproject.store.intent.impl;
17 +
18 +import org.onosproject.net.intent.Intent;
19 +import org.onosproject.net.intent.IntentId;
20 +import org.onosproject.net.intent.IntentState;
21 +import org.onosproject.store.Timestamp;
22 +
23 +/**
24 + * Information published by GossipIntentStore to notify peers of an intent
25 + * creation or state update event.
26 + */
27 +public class InternalIntentEvent {
28 +
29 + private final IntentId intentId;
30 + private final Intent intent;
31 + private final IntentState state;
32 + private final Timestamp timestamp;
33 +
34 + public InternalIntentEvent(IntentId intentId, Intent intent, IntentState state,
35 + Timestamp timestamp) {
36 + this.intentId = intentId;
37 + this.intent = intent;
38 + this.state = state;
39 + this.timestamp = timestamp;
40 + }
41 +
42 + public IntentId intentId() {
43 + return intentId;
44 + }
45 +
46 + public Intent intent() {
47 + return intent;
48 + }
49 +
50 + public IntentState state() {
51 + return state;
52 + }
53 +
54 + public Timestamp timestamp() {
55 + return timestamp;
56 + }
57 +
58 + // Needed for serialization.
59 + @SuppressWarnings("unused")
60 + private InternalIntentEvent() {
61 + intentId = null;
62 + intent = null;
63 + state = null;
64 + timestamp = null;
65 + }
66 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
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 +package org.onosproject.store.intent.impl;
17 +
18 +import org.onosproject.net.intent.Intent;
19 +import org.onosproject.net.intent.IntentId;
20 +import org.onosproject.store.Timestamp;
21 +
22 +import java.util.List;
23 +
24 +/**
25 + * Information published by GossipIntentStore to notify peers of an intent
26 + * set installables event.
27 + */
28 +public class InternalSetInstallablesEvent {
29 +
30 + private final IntentId intentId;
31 + private final List<Intent> installables;
32 + private final Timestamp timestamp;
33 +
34 + public InternalSetInstallablesEvent(IntentId intentId,
35 + List<Intent> installables,
36 + Timestamp timestamp) {
37 + this.intentId = intentId;
38 + this.installables = installables;
39 + this.timestamp = timestamp;
40 + }
41 +
42 + public IntentId intentId() {
43 + return intentId;
44 + }
45 +
46 + public List<Intent> installables() {
47 + return installables;
48 + }
49 +
50 + public Timestamp timestamp() {
51 + return timestamp;
52 + }
53 +
54 + // Needed for serialization.
55 + @SuppressWarnings("unused")
56 + private InternalSetInstallablesEvent() {
57 + intentId = null;
58 + installables = null;
59 + timestamp = null;
60 + }
61 +}