blob: 270557e4cd981d8a0acbd462d04c4d5a11a179dd [file] [log] [blame]
tom8cc5aa72014-09-19 15:14:43 -07001package org.onlab.onos.store.device.impl;
2
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -07003import static com.google.common.base.Preconditions.checkArgument;
4import static com.google.common.base.Preconditions.checkNotNull;
5import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
6import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_MASTERSHIP_CHANGED;
7import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_REMOVED;
8import static org.onlab.onos.net.device.DeviceEvent.Type.PORT_ADDED;
9import static org.onlab.onos.net.device.DeviceEvent.Type.PORT_REMOVED;
10import static org.onlab.onos.net.device.DeviceEvent.Type.PORT_UPDATED;
11import static org.slf4j.LoggerFactory.getLogger;
tom8cc5aa72014-09-19 15:14:43 -070012
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070013import java.net.URI;
tom8cc5aa72014-09-19 15:14:43 -070014import java.util.ArrayList;
15import java.util.Collections;
16import java.util.HashMap;
17import java.util.HashSet;
18import java.util.Iterator;
19import java.util.List;
20import java.util.Map;
21import java.util.Objects;
22import java.util.Set;
tom8cc5aa72014-09-19 15:14:43 -070023
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.apache.felix.scr.annotations.Service;
30import org.onlab.onos.net.DefaultDevice;
31import org.onlab.onos.net.DefaultPort;
32import org.onlab.onos.net.Device;
33import org.onlab.onos.net.DeviceId;
34import org.onlab.onos.net.Element;
35import org.onlab.onos.net.MastershipRole;
36import org.onlab.onos.net.Port;
37import org.onlab.onos.net.PortNumber;
38import org.onlab.onos.net.device.DeviceDescription;
39import org.onlab.onos.net.device.DeviceEvent;
40import org.onlab.onos.net.device.DeviceStore;
41import org.onlab.onos.net.device.PortDescription;
42import org.onlab.onos.net.provider.ProviderId;
tomdc66b382014-09-22 17:05:47 -070043import org.onlab.onos.store.StoreService;
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070044import org.onlab.util.KryoPool;
45import org.slf4j.Logger;
46
47import com.google.common.base.Optional;
48import com.google.common.cache.CacheBuilder;
49import com.google.common.cache.CacheLoader;
50import com.google.common.cache.LoadingCache;
51import com.google.common.collect.ImmutableList;
52import com.google.common.collect.ImmutableSet;
53import com.google.common.collect.ImmutableSet.Builder;
54import com.hazelcast.core.EntryAdapter;
55import com.hazelcast.core.EntryEvent;
56import com.hazelcast.core.HazelcastInstance;
57import com.hazelcast.core.IMap;
58import com.hazelcast.core.ISet;
59import com.hazelcast.core.MapEvent;
60
61import de.javakaffee.kryoserializers.URISerializer;
62
tom8cc5aa72014-09-19 15:14:43 -070063
64/**
65 * Manages inventory of infrastructure devices using Hazelcast-backed map.
66 */
67@Component(immediate = true)
68@Service
69public class DistributedDeviceStore implements DeviceStore {
70
71 private final Logger log = getLogger(getClass());
72
73 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
74
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070075 // FIXME Slice out types used in common to separate pool/namespace.
76 private static final KryoPool POOL = KryoPool.newBuilder()
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070077 .register(
78 ArrayList.class,
Yuta HIGUCHIc7052012014-09-22 19:11:00 -070079 HashMap.class,
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070080
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070081 Device.Type.class,
82
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070083 DefaultDevice.class,
84 MastershipRole.class,
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070085 Port.class,
86 Element.class
87 )
Yuta HIGUCHIc7052012014-09-22 19:11:00 -070088 .register(URI.class, new URISerializer())
89 .register(ProviderId.class, new ProviderIdSerializer())
90 .register(DeviceId.class, new DeviceIdSerializer())
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070091 .register(PortNumber.class, new PortNumberSerializer())
92 .register(DefaultPort.class, new DefaultPortSerializer())
93 .build()
94 .populate(10);
95
96 // private IMap<DeviceId, DefaultDevice> cache;
97 private IMap<byte[], byte[]> rawDevices;
98 private LoadingCache<DeviceId, Optional<DefaultDevice>> devices;
99
100 // private IMap<DeviceId, MastershipRole> roles;
101 private IMap<byte[], byte[]> rawRoles;
102 private LoadingCache<DeviceId, Optional<MastershipRole>> roles;
103
104 // private ISet<DeviceId> availableDevices;
105 private ISet<byte[]> availableDevices;
106
107 // TODO DevicePorts is very inefficient consider restructuring.
108 // private IMap<DeviceId, Map<PortNumber, Port>> devicePorts;
109 private IMap<byte[], byte[]> rawDevicePorts;
110 private LoadingCache<DeviceId, Optional<Map<PortNumber, Port>>> devicePorts;
111
112 // FIXME change to protected once we remove DistributedDeviceManagerTest.
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tomdc66b382014-09-22 17:05:47 -0700114 protected StoreService storeService;
115
Yuta HIGUCHIc7052012014-09-22 19:11:00 -0700116 protected HazelcastInstance theInstance;
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700117
tom8cc5aa72014-09-19 15:14:43 -0700118
119 @Activate
120 public void activate() {
121 log.info("Started");
tomdc66b382014-09-22 17:05:47 -0700122 theInstance = storeService.getHazelcastInstance();
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700123
124 // IMap event handler needs value
125 final boolean includeValue = true;
126
127 // TODO decide on Map name scheme to avoid collision
128 rawDevices = theInstance.getMap("devices");
129 devices = new AbsentInvalidatingLoadingCache<DeviceId, DefaultDevice>(
130 CacheBuilder.newBuilder()
131 .build(new OptionalCacheLoader<DeviceId, DefaultDevice>(rawDevices)));
132 // refresh/populate cache based on notification from other instance
133 rawDevices.addEntryListener(
134 new RemoteEventHandler<DeviceId, DefaultDevice>(devices),
135 includeValue);
136
137 rawRoles = theInstance.getMap("roles");
138 roles = new AbsentInvalidatingLoadingCache<DeviceId, MastershipRole>(
139 CacheBuilder.newBuilder()
140 .build(new OptionalCacheLoader<DeviceId, MastershipRole>(rawRoles)));
141 // refresh/populate cache based on notification from other instance
142 rawRoles.addEntryListener(
143 new RemoteEventHandler<DeviceId, MastershipRole>(roles),
144 includeValue);
145
146 // TODO cache avai
147 availableDevices = theInstance.getSet("availableDevices");
148
149 rawDevicePorts = theInstance.getMap("devicePorts");
150 devicePorts = new AbsentInvalidatingLoadingCache<DeviceId, Map<PortNumber, Port>>(
151 CacheBuilder.newBuilder()
152 .build(new OptionalCacheLoader<DeviceId, Map<PortNumber, Port>>(rawDevicePorts)));
153 // refresh/populate cache based on notification from other instance
154 rawDevicePorts.addEntryListener(
155 new RemoteEventHandler<DeviceId, Map<PortNumber, Port>>(devicePorts),
156 includeValue);
157
tom8cc5aa72014-09-19 15:14:43 -0700158 }
159
160 @Deactivate
161 public void deactivate() {
162 log.info("Stopped");
163 }
164
165 @Override
166 public int getDeviceCount() {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700167 // TODO IMap size or cache size?
168 return rawDevices.size();
tom8cc5aa72014-09-19 15:14:43 -0700169 }
170
171 @Override
172 public Iterable<Device> getDevices() {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700173// TODO Revisit if we ever need to do this.
174// log.info("{}:{}", rawMap.size(), cache.size());
175// if (rawMap.size() != cache.size()) {
176// for (Entry<byte[], byte[]> e : rawMap.entrySet()) {
177// final DeviceId key = deserialize(e.getKey());
178// final DefaultDevice val = deserialize(e.getValue());
179// cache.put(key, val);
180// }
181// }
182
183 // TODO builder v.s. copyOf. Guava semms to be using copyOf?
184 Builder<Device> builder = ImmutableSet.<Device>builder();
185 for (Optional<DefaultDevice> e : devices.asMap().values()) {
186 if (e.isPresent()) {
187 builder.add(e.get());
188 }
189 }
190 return builder.build();
tom8cc5aa72014-09-19 15:14:43 -0700191 }
192
193 @Override
194 public Device getDevice(DeviceId deviceId) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700195 // TODO revisit if ignoring exception is safe.
196 return devices.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700197 }
198
199 @Override
200 public DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
201 DeviceDescription deviceDescription) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700202 DefaultDevice device = devices.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700203 if (device == null) {
204 return createDevice(providerId, deviceId, deviceDescription);
205 }
206 return updateDevice(providerId, device, deviceDescription);
207 }
208
209 // Creates the device and returns the appropriate event if necessary.
210 private DeviceEvent createDevice(ProviderId providerId, DeviceId deviceId,
211 DeviceDescription desc) {
212 DefaultDevice device = new DefaultDevice(providerId, deviceId, desc.type(),
213 desc.manufacturer(),
214 desc.hwVersion(), desc.swVersion(),
215 desc.serialNumber());
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700216
tom8cc5aa72014-09-19 15:14:43 -0700217 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700218 final byte[] deviceIdBytes = serialize(deviceId);
219 rawDevices.put(deviceIdBytes, serialize(device));
220 devices.put(deviceId, Optional.of(device));
221
222 availableDevices.add(deviceIdBytes);
tom8cc5aa72014-09-19 15:14:43 -0700223
224 // For now claim the device as a master automatically.
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700225 rawRoles.put(deviceIdBytes, serialize(MastershipRole.MASTER));
226 roles.put(deviceId, Optional.of(MastershipRole.MASTER));
tom8cc5aa72014-09-19 15:14:43 -0700227 }
228 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device, null);
229 }
230
231 // Updates the device and returns the appropriate event if necessary.
232 private DeviceEvent updateDevice(ProviderId providerId, DefaultDevice device,
233 DeviceDescription desc) {
234 // We allow only certain attributes to trigger update
235 if (!Objects.equals(device.hwVersion(), desc.hwVersion()) ||
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700236 !Objects.equals(device.swVersion(), desc.swVersion())) {
237
tom8cc5aa72014-09-19 15:14:43 -0700238 DefaultDevice updated = new DefaultDevice(providerId, device.id(),
239 desc.type(),
240 desc.manufacturer(),
241 desc.hwVersion(),
242 desc.swVersion(),
243 desc.serialNumber());
244 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700245 devices.put(device.id(), Optional.of(updated));
246 availableDevices.add(serialize(device.id()));
tom8cc5aa72014-09-19 15:14:43 -0700247 }
248 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, device, null);
249 }
250
251 // Otherwise merely attempt to change availability
252 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700253 boolean added = availableDevices.add(serialize(device.id()));
tom8cc5aa72014-09-19 15:14:43 -0700254 return !added ? null :
255 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
256 }
257 }
258
259 @Override
260 public DeviceEvent markOffline(DeviceId deviceId) {
261 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700262 Device device = devices.getUnchecked(deviceId).orNull();
263 boolean removed = device != null && availableDevices.remove(serialize(deviceId));
tom8cc5aa72014-09-19 15:14:43 -0700264 return !removed ? null :
265 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
266 }
267 }
268
269 @Override
270 public List<DeviceEvent> updatePorts(DeviceId deviceId,
271 List<PortDescription> portDescriptions) {
272 List<DeviceEvent> events = new ArrayList<>();
273 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700274 Device device = devices.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700275 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
276 Map<PortNumber, Port> ports = getPortMap(deviceId);
277
278 // Add new ports
279 Set<PortNumber> processed = new HashSet<>();
280 for (PortDescription portDescription : portDescriptions) {
281 Port port = ports.get(portDescription.portNumber());
282 events.add(port == null ?
283 createPort(device, portDescription, ports) :
284 updatePort(device, port, portDescription, ports));
285 processed.add(portDescription.portNumber());
286 }
287
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700288 updatePortMap(deviceId, ports);
289
tom8cc5aa72014-09-19 15:14:43 -0700290 events.addAll(pruneOldPorts(device, ports, processed));
291 }
292 return events;
293 }
294
295 // Creates a new port based on the port description adds it to the map and
296 // Returns corresponding event.
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700297 //@GuardedBy("this")
tom8cc5aa72014-09-19 15:14:43 -0700298 private DeviceEvent createPort(Device device, PortDescription portDescription,
299 Map<PortNumber, Port> ports) {
300 DefaultPort port = new DefaultPort(device, portDescription.portNumber(),
301 portDescription.isEnabled());
302 ports.put(port.number(), port);
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700303 updatePortMap(device.id(), ports);
tom8cc5aa72014-09-19 15:14:43 -0700304 return new DeviceEvent(PORT_ADDED, device, port);
305 }
306
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700307 // Checks if the specified port requires update and if so, it replaces the
tom8cc5aa72014-09-19 15:14:43 -0700308 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700309 //@GuardedBy("this")
tom8cc5aa72014-09-19 15:14:43 -0700310 private DeviceEvent updatePort(Device device, Port port,
311 PortDescription portDescription,
312 Map<PortNumber, Port> ports) {
313 if (port.isEnabled() != portDescription.isEnabled()) {
314 DefaultPort updatedPort =
315 new DefaultPort(device, portDescription.portNumber(),
316 portDescription.isEnabled());
317 ports.put(port.number(), updatedPort);
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700318 updatePortMap(device.id(), ports);
tom8cc5aa72014-09-19 15:14:43 -0700319 return new DeviceEvent(PORT_UPDATED, device, port);
320 }
321 return null;
322 }
323
324 // Prunes the specified list of ports based on which ports are in the
325 // processed list and returns list of corresponding events.
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700326 //@GuardedBy("this")
tom8cc5aa72014-09-19 15:14:43 -0700327 private List<DeviceEvent> pruneOldPorts(Device device,
328 Map<PortNumber, Port> ports,
329 Set<PortNumber> processed) {
330 List<DeviceEvent> events = new ArrayList<>();
331 Iterator<PortNumber> iterator = ports.keySet().iterator();
332 while (iterator.hasNext()) {
333 PortNumber portNumber = iterator.next();
334 if (!processed.contains(portNumber)) {
335 events.add(new DeviceEvent(PORT_REMOVED, device,
336 ports.get(portNumber)));
337 iterator.remove();
338 }
339 }
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700340 if (!events.isEmpty()) {
341 updatePortMap(device.id(), ports);
342 }
tom8cc5aa72014-09-19 15:14:43 -0700343 return events;
344 }
345
346 // Gets the map of ports for the specified device; if one does not already
347 // exist, it creates and registers a new one.
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700348 // WARN: returned value is a copy, changes made to the Map
349 // needs to be written back using updatePortMap
350 //@GuardedBy("this")
tom8cc5aa72014-09-19 15:14:43 -0700351 private Map<PortNumber, Port> getPortMap(DeviceId deviceId) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700352 Map<PortNumber, Port> ports = devicePorts.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700353 if (ports == null) {
354 ports = new HashMap<>();
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700355 // this probably is waste of time in most cases.
356 updatePortMap(deviceId, ports);
tom8cc5aa72014-09-19 15:14:43 -0700357 }
358 return ports;
359 }
360
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700361 //@GuardedBy("this")
362 private void updatePortMap(DeviceId deviceId, Map<PortNumber, Port> ports) {
363 rawDevicePorts.put(serialize(deviceId), serialize(ports));
364 devicePorts.put(deviceId, Optional.of(ports));
365 }
366
tom8cc5aa72014-09-19 15:14:43 -0700367 @Override
368 public DeviceEvent updatePortStatus(DeviceId deviceId,
369 PortDescription portDescription) {
370 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700371 Device device = devices.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700372 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
373 Map<PortNumber, Port> ports = getPortMap(deviceId);
374 Port port = ports.get(portDescription.portNumber());
375 return updatePort(device, port, portDescription, ports);
376 }
377 }
378
379 @Override
380 public List<Port> getPorts(DeviceId deviceId) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700381 Map<PortNumber, Port> ports = devicePorts.getUnchecked(deviceId).orNull();
382 return ports == null ? Collections.<Port>emptyList() : ImmutableList.copyOf(ports.values());
tom8cc5aa72014-09-19 15:14:43 -0700383 }
384
385 @Override
386 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700387 Map<PortNumber, Port> ports = devicePorts.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700388 return ports == null ? null : ports.get(portNumber);
389 }
390
391 @Override
392 public boolean isAvailable(DeviceId deviceId) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700393 return availableDevices.contains(serialize(deviceId));
tom8cc5aa72014-09-19 15:14:43 -0700394 }
395
396 @Override
397 public MastershipRole getRole(DeviceId deviceId) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700398 MastershipRole role = roles.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700399 return role != null ? role : MastershipRole.NONE;
400 }
401
402 @Override
403 public DeviceEvent setRole(DeviceId deviceId, MastershipRole role) {
404 synchronized (this) {
405 Device device = getDevice(deviceId);
406 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700407 MastershipRole oldRole = deserialize(
408 rawRoles.put(serialize(deviceId), serialize(role)));
409 roles.put(deviceId, Optional.of(role));
tom8cc5aa72014-09-19 15:14:43 -0700410 return oldRole == role ? null :
411 new DeviceEvent(DEVICE_MASTERSHIP_CHANGED, device, null);
412 }
413 }
414
415 @Override
416 public DeviceEvent removeDevice(DeviceId deviceId) {
417 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700418 byte[] deviceIdBytes = serialize(deviceId);
419 rawRoles.remove(deviceIdBytes);
420 roles.invalidate(deviceId);
421
422 // TODO conditional remove?
423 Device device = deserialize(rawDevices.remove(deviceIdBytes));
424 devices.invalidate(deviceId);
tom8cc5aa72014-09-19 15:14:43 -0700425 return device == null ? null :
426 new DeviceEvent(DEVICE_REMOVED, device, null);
427 }
428 }
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700429
430 // TODO cache serialized DeviceID if we suffer from serialization cost
431
432 private static byte[] serialize(final Object obj) {
433 return POOL.serialize(obj);
434 }
435
436 private static <T> T deserialize(final byte[] bytes) {
437 if (bytes == null) {
438 return null;
439 }
440 return POOL.deserialize(bytes);
441 }
442
Yuta HIGUCHIc7052012014-09-22 19:11:00 -0700443 /**
444 * An IMap EntryListener, which reflects each remote event to cache.
445 *
446 * @param <K> IMap key type after deserialization
447 * @param <V> IMap value type after deserialization
448 */
449 public static final class RemoteEventHandler<K, V> extends
450 EntryAdapter<byte[], byte[]> {
451
452 private LoadingCache<K, Optional<V>> cache;
453
454 /**
455 * Constructor.
456 *
457 * @param cache cache to update
458 */
459 public RemoteEventHandler(
460 LoadingCache<K, Optional<V>> cache) {
461 this.cache = checkNotNull(cache);
462 }
463
464 @Override
465 public void mapCleared(MapEvent event) {
466 cache.invalidateAll();
467 }
468
469 @Override
470 public void entryUpdated(EntryEvent<byte[], byte[]> event) {
471 cache.put(POOL.<K>deserialize(event.getKey()),
472 Optional.of(POOL.<V>deserialize(
473 event.getValue())));
474 }
475
476 @Override
477 public void entryRemoved(EntryEvent<byte[], byte[]> event) {
478 cache.invalidate(POOL.<DeviceId>deserialize(event.getKey()));
479 }
480
481 @Override
482 public void entryAdded(EntryEvent<byte[], byte[]> event) {
483 entryUpdated(event);
484 }
485 }
486
487 /**
488 * CacheLoader to wrap Map value with Optional,
489 * to handle negative hit on underlying IMap.
490 *
491 * @param <K> IMap key type after deserialization
492 * @param <V> IMap value type after deserialization
493 */
494 public static final class OptionalCacheLoader<K, V> extends
495 CacheLoader<K, Optional<V>> {
496
497 private IMap<byte[], byte[]> rawMap;
498
499 /**
500 * Constructor.
501 *
502 * @param rawMap underlying IMap
503 */
504 public OptionalCacheLoader(IMap<byte[], byte[]> rawMap) {
505 this.rawMap = checkNotNull(rawMap);
506 }
507
508 @Override
509 public Optional<V> load(K key) throws Exception {
510 byte[] keyBytes = serialize(key);
511 byte[] valBytes = rawMap.get(keyBytes);
512 if (valBytes == null) {
513 return Optional.absent();
514 }
515 V dev = deserialize(valBytes);
516 return Optional.of(dev);
517 }
518 }
tom8cc5aa72014-09-19 15:14:43 -0700519}