blob: 8a31de1c180fe743f65bfe53c0c148cac7c553fc [file] [log] [blame]
alshabib10580802015-02-18 18:30:33 -08001/*
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 */
16package org.onosproject.store.group.impl;
17
Jonathan Hart6ec029a2015-03-24 17:12:35 -070018import com.google.common.collect.FluentIterable;
19import com.google.common.collect.Iterables;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070020
alshabib10580802015-02-18 18:30:33 -080021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070024import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabib10580802015-02-18 18:30:33 -080026import org.apache.felix.scr.annotations.Service;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070027import org.onlab.util.KryoNamespace;
alshabib10580802015-02-18 18:30:33 -080028import org.onlab.util.NewConcurrentHashMap;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070029import org.onosproject.cluster.ClusterService;
30import org.onosproject.core.DefaultApplicationId;
alshabib10580802015-02-18 18:30:33 -080031import org.onosproject.core.DefaultGroupId;
32import org.onosproject.core.GroupId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070033import org.onosproject.mastership.MastershipService;
alshabib10580802015-02-18 18:30:33 -080034import org.onosproject.net.DeviceId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070035import org.onosproject.net.MastershipRole;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.FlowRule;
39import org.onosproject.net.flow.instructions.Instructions;
40import org.onosproject.net.flow.instructions.L0ModificationInstruction;
41import org.onosproject.net.flow.instructions.L2ModificationInstruction;
42import org.onosproject.net.flow.instructions.L3ModificationInstruction;
alshabib10580802015-02-18 18:30:33 -080043import org.onosproject.net.group.DefaultGroup;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070044import org.onosproject.net.group.DefaultGroupBucket;
alshabib10580802015-02-18 18:30:33 -080045import org.onosproject.net.group.DefaultGroupDescription;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070046import org.onosproject.net.group.DefaultGroupKey;
alshabib10580802015-02-18 18:30:33 -080047import org.onosproject.net.group.Group;
48import org.onosproject.net.group.Group.GroupState;
49import org.onosproject.net.group.GroupBucket;
50import org.onosproject.net.group.GroupBuckets;
51import org.onosproject.net.group.GroupDescription;
52import org.onosproject.net.group.GroupEvent;
53import org.onosproject.net.group.GroupEvent.Type;
54import org.onosproject.net.group.GroupKey;
55import org.onosproject.net.group.GroupOperation;
56import org.onosproject.net.group.GroupStore;
57import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070058import org.onosproject.net.group.StoredGroupBucketEntry;
alshabib10580802015-02-18 18:30:33 -080059import org.onosproject.net.group.StoredGroupEntry;
60import org.onosproject.store.AbstractStore;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070061import org.onosproject.store.Timestamp;
62import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
63import org.onosproject.store.cluster.messaging.ClusterMessage;
64import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070065import org.onosproject.store.impl.MultiValuedTimestamp;
66import org.onosproject.store.serializers.KryoNamespaces;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070067import org.onosproject.store.service.ClockService;
68import org.onosproject.store.service.EventuallyConsistentMap;
69import org.onosproject.store.service.EventuallyConsistentMapBuilder;
70import org.onosproject.store.service.EventuallyConsistentMapEvent;
71import org.onosproject.store.service.EventuallyConsistentMapListener;
72import org.onosproject.store.service.StorageService;
alshabib10580802015-02-18 18:30:33 -080073import org.slf4j.Logger;
74
Jonathan Hart6ec029a2015-03-24 17:12:35 -070075import java.net.URI;
76import java.util.ArrayList;
77import java.util.HashMap;
78import java.util.List;
79import java.util.Objects;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070080import java.util.Optional;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070081import java.util.concurrent.ConcurrentHashMap;
82import java.util.concurrent.ConcurrentMap;
83import java.util.concurrent.ExecutorService;
84import java.util.concurrent.Executors;
85import java.util.concurrent.atomic.AtomicInteger;
86import java.util.concurrent.atomic.AtomicLong;
87import java.util.stream.Collectors;
88
89import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
90import static org.onlab.util.Tools.groupedThreads;
91import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -080092
93/**
94 * Manages inventory of group entries using trivial in-memory implementation.
95 */
96@Component(immediate = true)
97@Service
98public class DistributedGroupStore
99 extends AbstractStore<GroupEvent, GroupStoreDelegate>
100 implements GroupStore {
101
102 private final Logger log = getLogger(getClass());
103
104 private final int dummyId = 0xffffffff;
105 private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
106
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected ClusterCommunicationService clusterCommunicator;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected ClusterService clusterService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700114 protected StorageService storageService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700117 protected MastershipService mastershipService;
118
119 // Per device group table with (device id + app cookie) as key
120 private EventuallyConsistentMap<GroupStoreKeyMapKey,
121 StoredGroupEntry> groupStoreEntriesByKey = null;
122 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700123 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
124 groupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700125 private EventuallyConsistentMap<GroupStoreKeyMapKey,
126 StoredGroupEntry> auditPendingReqQueue = null;
alshabib10580802015-02-18 18:30:33 -0800127 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
128 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700129 private ExecutorService messageHandlingExecutor;
130 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
alshabib10580802015-02-18 18:30:33 -0800131
132 private final HashMap<DeviceId, Boolean> deviceAuditStatus =
133 new HashMap<DeviceId, Boolean>();
134
135 private final AtomicInteger groupIdGen = new AtomicInteger();
136
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700137 private KryoNamespace.Builder kryoBuilder = null;
138
alshabib10580802015-02-18 18:30:33 -0800139 @Activate
140 public void activate() {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700141 kryoBuilder = new KryoNamespace.Builder()
142 .register(DefaultGroup.class,
143 DefaultGroupBucket.class,
144 DefaultGroupDescription.class,
145 DefaultGroupKey.class,
146 GroupDescription.Type.class,
147 Group.GroupState.class,
148 GroupBuckets.class,
149 DefaultGroupId.class,
150 GroupStoreMessage.class,
151 GroupStoreMessage.Type.class,
152 UpdateType.class,
153 GroupStoreMessageSubjects.class,
154 MultiValuedTimestamp.class,
155 GroupStoreKeyMapKey.class,
156 GroupStoreIdMapKey.class,
157 GroupStoreMapKey.class
158 )
159 .register(URI.class)
160 .register(DeviceId.class)
161 .register(PortNumber.class)
162 .register(DefaultApplicationId.class)
163 .register(DefaultTrafficTreatment.class,
164 Instructions.DropInstruction.class,
165 Instructions.OutputInstruction.class,
166 Instructions.GroupInstruction.class,
167 Instructions.TableTypeTransition.class,
168 FlowRule.Type.class,
169 L0ModificationInstruction.class,
170 L0ModificationInstruction.L0SubType.class,
171 L0ModificationInstruction.ModLambdaInstruction.class,
172 L2ModificationInstruction.class,
173 L2ModificationInstruction.L2SubType.class,
174 L2ModificationInstruction.ModEtherInstruction.class,
175 L2ModificationInstruction.PushHeaderInstructions.class,
176 L2ModificationInstruction.ModVlanIdInstruction.class,
177 L2ModificationInstruction.ModVlanPcpInstruction.class,
178 L2ModificationInstruction.ModMplsLabelInstruction.class,
179 L2ModificationInstruction.ModMplsTtlInstruction.class,
180 L3ModificationInstruction.class,
181 L3ModificationInstruction.L3SubType.class,
182 L3ModificationInstruction.ModIPInstruction.class,
183 L3ModificationInstruction.ModIPv6FlowLabelInstruction.class,
184 L3ModificationInstruction.ModTtlInstruction.class,
185 org.onlab.packet.MplsLabel.class
186 )
187 .register(org.onosproject.cluster.NodeId.class)
188 .register(KryoNamespaces.BASIC)
189 .register(KryoNamespaces.MISC);
190
191 messageHandlingExecutor = Executors.
192 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
193 groupedThreads("onos/store/group",
194 "message-handlers"));
195 clusterCommunicator.
196 addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
197 new ClusterGroupMsgHandler(),
198 messageHandlingExecutor);
199
200 log.debug("Creating EC map groupstorekeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700201 EventuallyConsistentMapBuilder<GroupStoreKeyMapKey, StoredGroupEntry>
202 keyMapBuilder = storageService.eventuallyConsistentMapBuilder();
203
204 groupStoreEntriesByKey = keyMapBuilder
205 .withName("groupstorekeymap")
206 .withSerializer(kryoBuilder)
207 .withClockService(new GroupStoreLogicalClockManager<>())
208 .build();
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700209 groupStoreEntriesByKey.addListener(new GroupStoreKeyMapListener());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700210 log.trace("Current size {}", groupStoreEntriesByKey.size());
211
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700212 log.debug("Creating EC map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700213 EventuallyConsistentMapBuilder<GroupStoreKeyMapKey, StoredGroupEntry>
214 auditMapBuilder = storageService.eventuallyConsistentMapBuilder();
215
216 auditPendingReqQueue = auditMapBuilder
217 .withName("pendinggroupkeymap")
218 .withSerializer(kryoBuilder)
219 .withClockService(new GroupStoreLogicalClockManager<>())
220 .build();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700221 log.trace("Current size {}", auditPendingReqQueue.size());
222
alshabib10580802015-02-18 18:30:33 -0800223 log.info("Started");
224 }
225
226 @Deactivate
227 public void deactivate() {
Thomas Vachuska152f9fd2015-04-02 16:28:13 -0700228 groupStoreEntriesByKey.destroy();
Thomas Vachuska152f9fd2015-04-02 16:28:13 -0700229 auditPendingReqQueue.destroy();
alshabib10580802015-02-18 18:30:33 -0800230 log.info("Stopped");
231 }
232
alshabib10580802015-02-18 18:30:33 -0800233 private static NewConcurrentHashMap<GroupId, Group>
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700234 lazyEmptyExtraneousGroupIdTable() {
alshabib10580802015-02-18 18:30:33 -0800235 return NewConcurrentHashMap.<GroupId, Group>ifNeeded();
236 }
237
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700238 private static NewConcurrentHashMap<GroupId, StoredGroupEntry>
239 lazyEmptyGroupIdTable() {
240 return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
241 }
242
alshabib10580802015-02-18 18:30:33 -0800243 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700244 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800245 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700246 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800247 */
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700248 private EventuallyConsistentMap<GroupStoreKeyMapKey, StoredGroupEntry>
249 getGroupStoreKeyMap() {
250 return groupStoreEntriesByKey;
alshabib10580802015-02-18 18:30:33 -0800251 }
252
253 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700254 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800255 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700256 * @param deviceId identifier of the device
257 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800258 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700259 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
260 return createIfAbsentUnchecked(groupEntriesById,
261 deviceId, lazyEmptyGroupIdTable());
alshabib10580802015-02-18 18:30:33 -0800262 }
263
264 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700265 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800266 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700267 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800268 */
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700269 private EventuallyConsistentMap<GroupStoreKeyMapKey, StoredGroupEntry>
270 getPendingGroupKeyTable() {
271 return auditPendingReqQueue;
alshabib10580802015-02-18 18:30:33 -0800272 }
273
274 /**
275 * Returns the extraneous group id table for specified device.
276 *
277 * @param deviceId identifier of the device
278 * @return Map representing group key table of given device.
279 */
280 private ConcurrentMap<GroupId, Group>
281 getExtraneousGroupIdTable(DeviceId deviceId) {
282 return createIfAbsentUnchecked(extraneousGroupEntriesById,
283 deviceId,
284 lazyEmptyExtraneousGroupIdTable());
285 }
286
287 /**
288 * Returns the number of groups for the specified device in the store.
289 *
290 * @return number of groups for the specified device
291 */
292 @Override
293 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700294 return (getGroups(deviceId) != null) ?
295 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800296 }
297
298 /**
299 * Returns the groups associated with a device.
300 *
301 * @param deviceId the device ID
302 *
303 * @return the group entries
304 */
305 @Override
306 public Iterable<Group> getGroups(DeviceId deviceId) {
307 // flatten and make iterator unmodifiable
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700308 log.trace("getGroups: for device {} total number of groups {}",
309 deviceId, getGroupStoreKeyMap().values().size());
310 return FluentIterable.from(getGroupStoreKeyMap().values())
311 .filter(input -> input.deviceId().equals(deviceId))
312 .transform(input -> input);
alshabib10580802015-02-18 18:30:33 -0800313 }
314
315 /**
316 * Returns the stored group entry.
317 *
318 * @param deviceId the device ID
319 * @param appCookie the group key
320 *
321 * @return a group associated with the key
322 */
323 @Override
324 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700325 return getStoredGroupEntry(deviceId, appCookie);
326 }
327
328 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
329 GroupKey appCookie) {
330 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
331 appCookie));
332 }
333
334 @Override
335 public Group getGroup(DeviceId deviceId, GroupId groupId) {
336 return getStoredGroupEntry(deviceId, groupId);
337 }
338
339 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
340 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700341 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800342 }
343
344 private int getFreeGroupIdValue(DeviceId deviceId) {
345 int freeId = groupIdGen.incrementAndGet();
346
347 while (true) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700348 Group existing = getGroup(deviceId, new DefaultGroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800349 if (existing == null) {
350 existing = (
351 extraneousGroupEntriesById.get(deviceId) != null) ?
352 extraneousGroupEntriesById.get(deviceId).
353 get(new DefaultGroupId(freeId)) :
354 null;
355 }
356 if (existing != null) {
357 freeId = groupIdGen.incrementAndGet();
358 } else {
359 break;
360 }
361 }
362 return freeId;
363 }
364
365 /**
366 * Stores a new group entry using the information from group description.
367 *
368 * @param groupDesc group description to be used to create group entry
369 */
370 @Override
371 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700372 log.trace("In storeGroupDescription");
alshabib10580802015-02-18 18:30:33 -0800373 // Check if a group is existing with the same key
374 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700375 log.warn("Group already exists with the same key {}",
376 groupDesc.appCookie());
alshabib10580802015-02-18 18:30:33 -0800377 return;
378 }
379
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700380 // Check if group to be created by a remote instance
381 if (mastershipService.getLocalRole(
382 groupDesc.deviceId()) != MastershipRole.MASTER) {
383 log.debug("Device {} local role is not MASTER",
384 groupDesc.deviceId());
385 GroupStoreMessage groupOp = GroupStoreMessage.
386 createGroupAddRequestMsg(groupDesc.deviceId(),
387 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700388
389 if (!clusterCommunicator.unicast(groupOp,
390 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
391 m -> kryoBuilder.build().serialize(m),
392 mastershipService.getMasterFor(groupDesc.deviceId()))) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700393 log.warn("Failed to send request to master: {} to {}",
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700394 groupOp,
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700395 mastershipService.getMasterFor(groupDesc.deviceId()));
396 //TODO: Send Group operation failure event
397 }
398 log.debug("Sent Group operation request for device {} "
399 + "to remote MASTER {}",
400 groupDesc.deviceId(),
401 mastershipService.getMasterFor(groupDesc.deviceId()));
alshabib10580802015-02-18 18:30:33 -0800402 return;
403 }
404
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700405 log.debug("Store group for device {} is getting handled locally",
406 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800407 storeGroupDescriptionInternal(groupDesc);
408 }
409
410 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
411 // Check if a group is existing with the same key
412 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
413 return;
414 }
415
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700416 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
417 // Device group audit has not completed yet
418 // Add this group description to pending group key table
419 // Create a group entry object with Dummy Group ID
420 log.debug("storeGroupDescriptionInternal: Device {} AUDIT "
421 + "pending...Queuing Group ADD request",
422 groupDesc.deviceId());
423 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
424 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
425 EventuallyConsistentMap<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
426 getPendingGroupKeyTable();
427 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
428 groupDesc.appCookie()),
429 group);
430 return;
431 }
432
Saurav Das100e3b82015-04-30 11:12:10 -0700433 GroupId id = null;
434 if (groupDesc.givenGroupId() == null) {
435 // Get a new group identifier
436 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
437 } else {
438 id = new DefaultGroupId(groupDesc.givenGroupId());
439 }
alshabib10580802015-02-18 18:30:33 -0800440 // Create a group entry object
441 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700442 // Insert the newly created group entry into key and id maps
443 getGroupStoreKeyMap().
444 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
445 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700446 // Ensure it also inserted into group id based table to
447 // avoid any chances of duplication in group id generation
448 getGroupIdTable(groupDesc.deviceId()).
449 put(id, group);
alshabib10580802015-02-18 18:30:33 -0800450 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
451 group));
452 }
453
454 /**
455 * Updates the existing group entry with the information
456 * from group description.
457 *
458 * @param deviceId the device ID
459 * @param oldAppCookie the current group key
460 * @param type update type
461 * @param newBuckets group buckets for updates
462 * @param newAppCookie optional new group key
463 */
464 @Override
465 public void updateGroupDescription(DeviceId deviceId,
466 GroupKey oldAppCookie,
467 UpdateType type,
468 GroupBuckets newBuckets,
469 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700470 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700471 if (mastershipService.getMasterFor(deviceId) != null &&
472 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700473 GroupStoreMessage groupOp = GroupStoreMessage.
474 createGroupUpdateRequestMsg(deviceId,
475 oldAppCookie,
476 type,
477 newBuckets,
478 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700479
480 if (!clusterCommunicator.unicast(groupOp,
481 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
482 m -> kryoBuilder.build().serialize(m),
483 mastershipService.getMasterFor(deviceId))) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700484 log.warn("Failed to send request to master: {} to {}",
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700485 groupOp,
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700486 mastershipService.getMasterFor(deviceId));
487 //TODO: Send Group operation failure event
488 }
489 return;
490 }
491 updateGroupDescriptionInternal(deviceId,
492 oldAppCookie,
493 type,
494 newBuckets,
495 newAppCookie);
496 }
497
498 private void updateGroupDescriptionInternal(DeviceId deviceId,
499 GroupKey oldAppCookie,
500 UpdateType type,
501 GroupBuckets newBuckets,
502 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800503 // Check if a group is existing with the provided key
504 Group oldGroup = getGroup(deviceId, oldAppCookie);
505 if (oldGroup == null) {
506 return;
507 }
508
509 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
510 type,
511 newBuckets);
512 if (newBucketList != null) {
513 // Create a new group object from the old group
514 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
515 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
516 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
517 oldGroup.deviceId(),
518 oldGroup.type(),
519 updatedBuckets,
520 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700521 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800522 oldGroup.appId());
523 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
524 updatedGroupDesc);
525 newGroup.setState(GroupState.PENDING_UPDATE);
526 newGroup.setLife(oldGroup.life());
527 newGroup.setPackets(oldGroup.packets());
528 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700529 //Update the group entry in groupkey based map.
530 //Update to groupid based map will happen in the
531 //groupkey based map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700532 getGroupStoreKeyMap().
533 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
534 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800535 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
536 }
537 }
538
539 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
540 UpdateType type,
541 GroupBuckets buckets) {
542 GroupBuckets oldBuckets = oldGroup.buckets();
543 List<GroupBucket> newBucketList = new ArrayList<GroupBucket>(
544 oldBuckets.buckets());
545 boolean groupDescUpdated = false;
546
547 if (type == UpdateType.ADD) {
548 // Check if the any of the new buckets are part of
549 // the old bucket list
550 for (GroupBucket addBucket:buckets.buckets()) {
551 if (!newBucketList.contains(addBucket)) {
552 newBucketList.add(addBucket);
553 groupDescUpdated = true;
554 }
555 }
556 } else if (type == UpdateType.REMOVE) {
557 // Check if the to be removed buckets are part of the
558 // old bucket list
559 for (GroupBucket removeBucket:buckets.buckets()) {
560 if (newBucketList.contains(removeBucket)) {
561 newBucketList.remove(removeBucket);
562 groupDescUpdated = true;
563 }
564 }
565 }
566
567 if (groupDescUpdated) {
568 return newBucketList;
569 } else {
570 return null;
571 }
572 }
573
574 /**
575 * Triggers deleting the existing group entry.
576 *
577 * @param deviceId the device ID
578 * @param appCookie the group key
579 */
580 @Override
581 public void deleteGroupDescription(DeviceId deviceId,
582 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700583 // Check if group to be deleted by a remote instance
584 if (mastershipService.
585 getLocalRole(deviceId) != MastershipRole.MASTER) {
586 GroupStoreMessage groupOp = GroupStoreMessage.
587 createGroupDeleteRequestMsg(deviceId,
588 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700589
590 if (!clusterCommunicator.unicast(groupOp,
591 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
592 m -> kryoBuilder.build().serialize(m),
593 mastershipService.getMasterFor(deviceId))) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700594 log.warn("Failed to send request to master: {} to {}",
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700595 groupOp,
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700596 mastershipService.getMasterFor(deviceId));
597 //TODO: Send Group operation failure event
598 }
599 return;
600 }
601 deleteGroupDescriptionInternal(deviceId, appCookie);
602 }
603
604 private void deleteGroupDescriptionInternal(DeviceId deviceId,
605 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800606 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700607 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800608 if (existing == null) {
609 return;
610 }
611
612 synchronized (existing) {
613 existing.setState(GroupState.PENDING_DELETE);
614 }
615 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
616 }
617
618 /**
619 * Stores a new group entry, or updates an existing entry.
620 *
621 * @param group group entry
622 */
623 @Override
624 public void addOrUpdateGroupEntry(Group group) {
625 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700626 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
627 group.id());
alshabib10580802015-02-18 18:30:33 -0800628 GroupEvent event = null;
629
630 if (existing != null) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700631 log.trace("addOrUpdateGroupEntry: updating group "
632 + "entry {} in device {}",
633 group.id(),
634 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800635 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700636 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700637 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700638 existing.buckets().buckets()
639 .stream()
640 .filter((existingBucket)->(existingBucket.equals(bucket)))
641 .findFirst();
642 if (matchingBucket.isPresent()) {
643 ((StoredGroupBucketEntry) matchingBucket.
644 get()).setPackets(bucket.packets());
645 ((StoredGroupBucketEntry) matchingBucket.
646 get()).setBytes(bucket.bytes());
647 } else {
648 log.warn("addOrUpdateGroupEntry: No matching "
649 + "buckets to update stats");
650 }
651 }
alshabib10580802015-02-18 18:30:33 -0800652 existing.setLife(group.life());
653 existing.setPackets(group.packets());
654 existing.setBytes(group.bytes());
655 if (existing.state() == GroupState.PENDING_ADD) {
656 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700657 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800658 event = new GroupEvent(Type.GROUP_ADDED, existing);
659 } else {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700660 existing.setState(GroupState.ADDED);
661 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800662 event = new GroupEvent(Type.GROUP_UPDATED, existing);
663 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700664 //Re-PUT map entries to trigger map update events
665 getGroupStoreKeyMap().
666 put(new GroupStoreKeyMapKey(existing.deviceId(),
667 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800668 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700669 } else {
670 log.warn("addOrUpdateGroupEntry: Group update "
671 + "happening for a non-existing entry in the map");
alshabib10580802015-02-18 18:30:33 -0800672 }
673
674 if (event != null) {
675 notifyDelegate(event);
676 }
677 }
678
679 /**
680 * Removes the group entry from store.
681 *
682 * @param group group entry
683 */
684 @Override
685 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700686 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
687 group.id());
alshabib10580802015-02-18 18:30:33 -0800688
689 if (existing != null) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700690 log.trace("removeGroupEntry: removing group "
691 + "entry {} in device {}",
692 group.id(),
693 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700694 //Removal from groupid based map will happen in the
695 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700696 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
697 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800698 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
699 }
700 }
701
702 @Override
703 public void deviceInitialAuditCompleted(DeviceId deviceId,
704 boolean completed) {
705 synchronized (deviceAuditStatus) {
706 if (completed) {
707 log.debug("deviceInitialAuditCompleted: AUDIT "
708 + "completed for device {}", deviceId);
709 deviceAuditStatus.put(deviceId, true);
710 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700711 List<StoredGroupEntry> pendingGroupRequests =
712 getPendingGroupKeyTable().values()
713 .stream()
714 .filter(g-> g.deviceId().equals(deviceId))
715 .collect(Collectors.toList());
716 log.trace("deviceInitialAuditCompleted: processing "
717 + "pending group add requests for device {} and "
718 + "number of pending requests {}",
719 deviceId,
720 pendingGroupRequests.size());
721 for (Group group:pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -0800722 GroupDescription tmp = new DefaultGroupDescription(
723 group.deviceId(),
724 group.type(),
725 group.buckets(),
726 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700727 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800728 group.appId());
729 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700730 getPendingGroupKeyTable().
731 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800732 }
alshabib10580802015-02-18 18:30:33 -0800733 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700734 Boolean audited = deviceAuditStatus.get(deviceId);
735 if (audited != null && audited) {
alshabib10580802015-02-18 18:30:33 -0800736 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
737 + "status for device {}", deviceId);
738 deviceAuditStatus.put(deviceId, false);
739 }
740 }
741 }
742 }
743
744 @Override
745 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
746 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700747 Boolean audited = deviceAuditStatus.get(deviceId);
748 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -0800749 }
750 }
751
752 @Override
753 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
754
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700755 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
756 operation.groupId());
alshabib10580802015-02-18 18:30:33 -0800757
758 if (existing == null) {
759 log.warn("No group entry with ID {} found ", operation.groupId());
760 return;
761 }
762
763 switch (operation.opType()) {
764 case ADD:
765 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
766 break;
767 case MODIFY:
768 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
769 break;
770 case DELETE:
771 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
772 break;
773 default:
774 log.warn("Unknown group operation type {}", operation.opType());
775 }
776
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700777 //Removal from groupid based map will happen in the
778 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700779 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
780 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800781 }
782
783 @Override
784 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700785 log.trace("addOrUpdateExtraneousGroupEntry: add/update extraneous "
786 + "group entry {} in device {}",
787 group.id(),
788 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800789 ConcurrentMap<GroupId, Group> extraneousIdTable =
790 getExtraneousGroupIdTable(group.deviceId());
791 extraneousIdTable.put(group.id(), group);
792 // Check the reference counter
793 if (group.referenceCount() == 0) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700794 log.trace("addOrUpdateExtraneousGroupEntry: Flow reference "
795 + "counter is zero and triggering remove",
796 group.id(),
797 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800798 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
799 }
800 }
801
802 @Override
803 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700804 log.trace("removeExtraneousGroupEntry: remove extraneous "
805 + "group entry {} of device {} from store",
806 group.id(),
807 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800808 ConcurrentMap<GroupId, Group> extraneousIdTable =
809 getExtraneousGroupIdTable(group.deviceId());
810 extraneousIdTable.remove(group.id());
811 }
812
813 @Override
814 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
815 // flatten and make iterator unmodifiable
816 return FluentIterable.from(
817 getExtraneousGroupIdTable(deviceId).values());
818 }
819
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700820 /**
821 * ClockService that generates wallclock based timestamps.
822 */
823 private class GroupStoreLogicalClockManager<T, U>
824 implements ClockService<T, U> {
alshabib10580802015-02-18 18:30:33 -0800825
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700826 private final AtomicLong sequenceNumber = new AtomicLong(0);
827
828 @Override
829 public Timestamp getTimestamp(T t1, U u1) {
830 return new MultiValuedTimestamp<>(System.currentTimeMillis(),
831 sequenceNumber.getAndIncrement());
832 }
833 }
834
835 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700836 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700837 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700838 private class GroupStoreKeyMapListener implements
839 EventuallyConsistentMapListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700840
841 @Override
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700842 public void event(EventuallyConsistentMapEvent<GroupStoreKeyMapKey,
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700843 StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700844 GroupEvent groupEvent = null;
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700845 StoredGroupEntry group = mapEvent.value();
846 log.trace("GroupStoreKeyMapListener: received groupid map event {}",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700847 mapEvent.type());
848 if (mapEvent.type() == EventuallyConsistentMapEvent.Type.PUT) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700849 log.trace("GroupStoreKeyMapListener: Received PUT event");
850 // Update the group ID table
851 getGroupIdTable(group.deviceId()).put(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700852 if (mapEvent.value().state() == Group.GroupState.ADDED) {
853 if (mapEvent.value().isGroupStateAddedFirstTime()) {
854 groupEvent = new GroupEvent(Type.GROUP_ADDED,
855 mapEvent.value());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700856 log.trace("GroupStoreKeyMapListener: Received first time "
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700857 + "GROUP_ADDED state update");
858 } else {
859 groupEvent = new GroupEvent(Type.GROUP_UPDATED,
860 mapEvent.value());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700861 log.trace("GroupStoreKeyMapListener: Received following "
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700862 + "GROUP_ADDED state update");
863 }
864 }
865 } else if (mapEvent.type() == EventuallyConsistentMapEvent.Type.REMOVE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700866 log.trace("GroupStoreKeyMapListener: Received REMOVE event");
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700867 groupEvent = new GroupEvent(Type.GROUP_REMOVED, mapEvent.value());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700868 // Remove the entry from the group ID table
869 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700870 }
871
872 if (groupEvent != null) {
873 notifyDelegate(groupEvent);
874 }
875 }
876 }
877 /**
878 * Message handler to receive messages from group subsystems of
879 * other cluster members.
880 */
881 private final class ClusterGroupMsgHandler
882 implements ClusterMessageHandler {
883 @Override
884 public void handle(ClusterMessage message) {
885 log.trace("ClusterGroupMsgHandler: received remote group message");
886 if (message.subject() ==
887 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST) {
888 GroupStoreMessage groupOp = kryoBuilder.
889 build().deserialize(message.payload());
890 log.trace("received remote group operation request");
891 if (!(mastershipService.
892 getLocalRole(groupOp.deviceId()) !=
893 MastershipRole.MASTER)) {
894 log.warn("ClusterGroupMsgHandler: This node is not "
895 + "MASTER for device {}", groupOp.deviceId());
896 return;
897 }
898 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
899 log.trace("processing remote group "
900 + "add operation request");
901 storeGroupDescriptionInternal(groupOp.groupDesc());
902 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
903 log.trace("processing remote group "
904 + "update operation request");
905 updateGroupDescriptionInternal(groupOp.deviceId(),
906 groupOp.appCookie(),
907 groupOp.updateType(),
908 groupOp.updateBuckets(),
909 groupOp.newAppCookie());
910 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
911 log.trace("processing remote group "
912 + "delete operation request");
913 deleteGroupDescriptionInternal(groupOp.deviceId(),
914 groupOp.appCookie());
915 }
916 }
917 }
918 }
919
920 /**
921 * Flattened map key to be used to store group entries.
922 */
923 private class GroupStoreMapKey {
924 private final DeviceId deviceId;
925
926 public GroupStoreMapKey(DeviceId deviceId) {
927 this.deviceId = deviceId;
928 }
929
930 @Override
931 public boolean equals(Object o) {
932 if (this == o) {
933 return true;
934 }
935 if (!(o instanceof GroupStoreMapKey)) {
936 return false;
937 }
938 GroupStoreMapKey that = (GroupStoreMapKey) o;
939 return this.deviceId.equals(that.deviceId);
940 }
941
942 @Override
943 public int hashCode() {
944 int result = 17;
945
946 result = 31 * result + Objects.hash(this.deviceId);
947
948 return result;
949 }
950 }
951
952 private class GroupStoreKeyMapKey extends GroupStoreMapKey {
953 private final GroupKey appCookie;
954 public GroupStoreKeyMapKey(DeviceId deviceId,
955 GroupKey appCookie) {
956 super(deviceId);
957 this.appCookie = appCookie;
958 }
959
960 @Override
961 public boolean equals(Object o) {
962 if (this == o) {
963 return true;
964 }
965 if (!(o instanceof GroupStoreKeyMapKey)) {
966 return false;
967 }
968 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
969 return (super.equals(that) &&
970 this.appCookie.equals(that.appCookie));
971 }
972
973 @Override
974 public int hashCode() {
975 int result = 17;
976
977 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
978
979 return result;
980 }
981 }
982
983 private class GroupStoreIdMapKey extends GroupStoreMapKey {
984 private final GroupId groupId;
985 public GroupStoreIdMapKey(DeviceId deviceId,
986 GroupId groupId) {
987 super(deviceId);
988 this.groupId = groupId;
989 }
990
991 @Override
992 public boolean equals(Object o) {
993 if (this == o) {
994 return true;
995 }
996 if (!(o instanceof GroupStoreIdMapKey)) {
997 return false;
998 }
999 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1000 return (super.equals(that) &&
1001 this.groupId.equals(that.groupId));
1002 }
1003
1004 @Override
1005 public int hashCode() {
1006 int result = 17;
1007
1008 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1009
1010 return result;
1011 }
1012 }
alshabib10580802015-02-18 18:30:33 -08001013}