blob: a4eb027fbfe0168b4b681033ef019149545570e5 [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 com.google.common.base.Optional;
4import com.google.common.cache.CacheBuilder;
5import com.google.common.cache.CacheLoader;
6import com.google.common.cache.LoadingCache;
7import com.google.common.collect.ImmutableList;
8import com.google.common.collect.ImmutableSet;
9import com.google.common.collect.ImmutableSet.Builder;
10import com.hazelcast.core.EntryAdapter;
11import com.hazelcast.core.EntryEvent;
12import com.hazelcast.core.HazelcastInstance;
13import com.hazelcast.core.IMap;
14import com.hazelcast.core.ISet;
15import com.hazelcast.core.MapEvent;
tom0872a172014-09-23 11:24:26 -070016import org.apache.felix.scr.annotations.Activate;
17import org.apache.felix.scr.annotations.Component;
18import org.apache.felix.scr.annotations.Deactivate;
19import org.apache.felix.scr.annotations.Reference;
20import org.apache.felix.scr.annotations.ReferenceCardinality;
21import org.apache.felix.scr.annotations.Service;
22import org.onlab.onos.net.DefaultDevice;
23import org.onlab.onos.net.DefaultPort;
24import org.onlab.onos.net.Device;
25import org.onlab.onos.net.DeviceId;
26import org.onlab.onos.net.MastershipRole;
27import org.onlab.onos.net.Port;
28import org.onlab.onos.net.PortNumber;
29import org.onlab.onos.net.device.DeviceDescription;
30import org.onlab.onos.net.device.DeviceEvent;
31import org.onlab.onos.net.device.DeviceStore;
32import org.onlab.onos.net.device.PortDescription;
33import org.onlab.onos.net.provider.ProviderId;
34import org.onlab.onos.store.StoreService;
35import org.onlab.onos.store.impl.AbsentInvalidatingLoadingCache;
36import org.slf4j.Logger;
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070037
tom0872a172014-09-23 11:24:26 -070038import java.util.ArrayList;
39import java.util.Collections;
40import java.util.HashMap;
41import java.util.HashSet;
42import java.util.Iterator;
43import java.util.List;
44import java.util.Map;
45import java.util.Objects;
46import java.util.Set;
47
48import static com.google.common.base.Preconditions.checkArgument;
49import static com.google.common.base.Preconditions.checkNotNull;
50import static org.onlab.onos.net.device.DeviceEvent.Type.*;
51import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070052
tom8cc5aa72014-09-19 15:14:43 -070053
54/**
55 * Manages inventory of infrastructure devices using Hazelcast-backed map.
56 */
57@Component(immediate = true)
58@Service
59public class DistributedDeviceStore implements DeviceStore {
60
61 private final Logger log = getLogger(getClass());
62
63 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
64
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070065 // private IMap<DeviceId, DefaultDevice> cache;
66 private IMap<byte[], byte[]> rawDevices;
67 private LoadingCache<DeviceId, Optional<DefaultDevice>> devices;
68
69 // private IMap<DeviceId, MastershipRole> roles;
70 private IMap<byte[], byte[]> rawRoles;
71 private LoadingCache<DeviceId, Optional<MastershipRole>> roles;
72
73 // private ISet<DeviceId> availableDevices;
74 private ISet<byte[]> availableDevices;
75
76 // TODO DevicePorts is very inefficient consider restructuring.
77 // private IMap<DeviceId, Map<PortNumber, Port>> devicePorts;
78 private IMap<byte[], byte[]> rawDevicePorts;
79 private LoadingCache<DeviceId, Optional<Map<PortNumber, Port>>> devicePorts;
80
81 // FIXME change to protected once we remove DistributedDeviceManagerTest.
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tomdc66b382014-09-22 17:05:47 -070083 protected StoreService storeService;
84
Yuta HIGUCHIc7052012014-09-22 19:11:00 -070085 protected HazelcastInstance theInstance;
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070086
tom8cc5aa72014-09-19 15:14:43 -070087
88 @Activate
89 public void activate() {
90 log.info("Started");
tomdc66b382014-09-22 17:05:47 -070091 theInstance = storeService.getHazelcastInstance();
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070092
93 // IMap event handler needs value
94 final boolean includeValue = true;
95
96 // TODO decide on Map name scheme to avoid collision
97 rawDevices = theInstance.getMap("devices");
tom0872a172014-09-23 11:24:26 -070098 devices = new AbsentInvalidatingLoadingCache<>(
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -070099 CacheBuilder.newBuilder()
tom0872a172014-09-23 11:24:26 -0700100 .build(new OptionalCacheLoader<DeviceId, DefaultDevice>(rawDevices)));
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700101 // refresh/populate cache based on notification from other instance
102 rawDevices.addEntryListener(
tom0872a172014-09-23 11:24:26 -0700103 new RemoteEventHandler<>(devices),
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700104 includeValue);
105
106 rawRoles = theInstance.getMap("roles");
tom0872a172014-09-23 11:24:26 -0700107 roles = new AbsentInvalidatingLoadingCache<>(
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700108 CacheBuilder.newBuilder()
tom0872a172014-09-23 11:24:26 -0700109 .build(new OptionalCacheLoader<DeviceId, MastershipRole>(rawRoles)));
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700110 // refresh/populate cache based on notification from other instance
111 rawRoles.addEntryListener(
tom0872a172014-09-23 11:24:26 -0700112 new RemoteEventHandler<>(roles),
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700113 includeValue);
114
115 // TODO cache avai
116 availableDevices = theInstance.getSet("availableDevices");
117
118 rawDevicePorts = theInstance.getMap("devicePorts");
tom0872a172014-09-23 11:24:26 -0700119 devicePorts = new AbsentInvalidatingLoadingCache<>(
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700120 CacheBuilder.newBuilder()
tom0872a172014-09-23 11:24:26 -0700121 .build(new OptionalCacheLoader<DeviceId, Map<PortNumber, Port>>(rawDevicePorts)));
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700122 // refresh/populate cache based on notification from other instance
123 rawDevicePorts.addEntryListener(
tom0872a172014-09-23 11:24:26 -0700124 new RemoteEventHandler<>(devicePorts),
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700125 includeValue);
126
tom8cc5aa72014-09-19 15:14:43 -0700127 }
128
129 @Deactivate
130 public void deactivate() {
131 log.info("Stopped");
132 }
133
134 @Override
135 public int getDeviceCount() {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700136 // TODO IMap size or cache size?
137 return rawDevices.size();
tom8cc5aa72014-09-19 15:14:43 -0700138 }
139
140 @Override
141 public Iterable<Device> getDevices() {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700142// TODO Revisit if we ever need to do this.
143// log.info("{}:{}", rawMap.size(), cache.size());
144// if (rawMap.size() != cache.size()) {
145// for (Entry<byte[], byte[]> e : rawMap.entrySet()) {
146// final DeviceId key = deserialize(e.getKey());
147// final DefaultDevice val = deserialize(e.getValue());
148// cache.put(key, val);
149// }
150// }
151
152 // TODO builder v.s. copyOf. Guava semms to be using copyOf?
tom0872a172014-09-23 11:24:26 -0700153 Builder<Device> builder = ImmutableSet.builder();
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700154 for (Optional<DefaultDevice> e : devices.asMap().values()) {
155 if (e.isPresent()) {
156 builder.add(e.get());
157 }
158 }
159 return builder.build();
tom8cc5aa72014-09-19 15:14:43 -0700160 }
161
162 @Override
163 public Device getDevice(DeviceId deviceId) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700164 // TODO revisit if ignoring exception is safe.
165 return devices.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700166 }
167
168 @Override
169 public DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
tom0872a172014-09-23 11:24:26 -0700170 DeviceDescription deviceDescription) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700171 DefaultDevice device = devices.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700172 if (device == null) {
173 return createDevice(providerId, deviceId, deviceDescription);
174 }
175 return updateDevice(providerId, device, deviceDescription);
176 }
177
178 // Creates the device and returns the appropriate event if necessary.
179 private DeviceEvent createDevice(ProviderId providerId, DeviceId deviceId,
180 DeviceDescription desc) {
181 DefaultDevice device = new DefaultDevice(providerId, deviceId, desc.type(),
182 desc.manufacturer(),
183 desc.hwVersion(), desc.swVersion(),
184 desc.serialNumber());
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700185
tom8cc5aa72014-09-19 15:14:43 -0700186 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700187 final byte[] deviceIdBytes = serialize(deviceId);
188 rawDevices.put(deviceIdBytes, serialize(device));
189 devices.put(deviceId, Optional.of(device));
190
191 availableDevices.add(deviceIdBytes);
tom8cc5aa72014-09-19 15:14:43 -0700192
193 // For now claim the device as a master automatically.
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700194 rawRoles.put(deviceIdBytes, serialize(MastershipRole.MASTER));
tom0872a172014-09-23 11:24:26 -0700195 roles.put(deviceId, Optional.of(MastershipRole.MASTER));
tom8cc5aa72014-09-19 15:14:43 -0700196 }
197 return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device, null);
198 }
199
200 // Updates the device and returns the appropriate event if necessary.
201 private DeviceEvent updateDevice(ProviderId providerId, DefaultDevice device,
202 DeviceDescription desc) {
203 // We allow only certain attributes to trigger update
204 if (!Objects.equals(device.hwVersion(), desc.hwVersion()) ||
tom0872a172014-09-23 11:24:26 -0700205 !Objects.equals(device.swVersion(), desc.swVersion())) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700206
tom8cc5aa72014-09-19 15:14:43 -0700207 DefaultDevice updated = new DefaultDevice(providerId, device.id(),
208 desc.type(),
209 desc.manufacturer(),
210 desc.hwVersion(),
211 desc.swVersion(),
212 desc.serialNumber());
213 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700214 devices.put(device.id(), Optional.of(updated));
215 availableDevices.add(serialize(device.id()));
tom8cc5aa72014-09-19 15:14:43 -0700216 }
217 return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, device, null);
218 }
219
220 // Otherwise merely attempt to change availability
221 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700222 boolean added = availableDevices.add(serialize(device.id()));
tom8cc5aa72014-09-19 15:14:43 -0700223 return !added ? null :
224 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
225 }
226 }
227
228 @Override
229 public DeviceEvent markOffline(DeviceId deviceId) {
230 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700231 Device device = devices.getUnchecked(deviceId).orNull();
232 boolean removed = device != null && availableDevices.remove(serialize(deviceId));
tom8cc5aa72014-09-19 15:14:43 -0700233 return !removed ? null :
234 new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
235 }
236 }
237
238 @Override
239 public List<DeviceEvent> updatePorts(DeviceId deviceId,
tom0872a172014-09-23 11:24:26 -0700240 List<PortDescription> portDescriptions) {
tom8cc5aa72014-09-19 15:14:43 -0700241 List<DeviceEvent> events = new ArrayList<>();
242 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700243 Device device = devices.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700244 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
245 Map<PortNumber, Port> ports = getPortMap(deviceId);
246
247 // Add new ports
248 Set<PortNumber> processed = new HashSet<>();
249 for (PortDescription portDescription : portDescriptions) {
250 Port port = ports.get(portDescription.portNumber());
251 events.add(port == null ?
252 createPort(device, portDescription, ports) :
253 updatePort(device, port, portDescription, ports));
254 processed.add(portDescription.portNumber());
255 }
256
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700257 updatePortMap(deviceId, ports);
258
tom8cc5aa72014-09-19 15:14:43 -0700259 events.addAll(pruneOldPorts(device, ports, processed));
260 }
261 return events;
262 }
263
264 // Creates a new port based on the port description adds it to the map and
265 // Returns corresponding event.
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700266 //@GuardedBy("this")
tom8cc5aa72014-09-19 15:14:43 -0700267 private DeviceEvent createPort(Device device, PortDescription portDescription,
268 Map<PortNumber, Port> ports) {
269 DefaultPort port = new DefaultPort(device, portDescription.portNumber(),
270 portDescription.isEnabled());
271 ports.put(port.number(), port);
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700272 updatePortMap(device.id(), ports);
tom8cc5aa72014-09-19 15:14:43 -0700273 return new DeviceEvent(PORT_ADDED, device, port);
274 }
275
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700276 // Checks if the specified port requires update and if so, it replaces the
tom8cc5aa72014-09-19 15:14:43 -0700277 // existing entry in the map and returns corresponding event.
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700278 //@GuardedBy("this")
tom8cc5aa72014-09-19 15:14:43 -0700279 private DeviceEvent updatePort(Device device, Port port,
280 PortDescription portDescription,
281 Map<PortNumber, Port> ports) {
282 if (port.isEnabled() != portDescription.isEnabled()) {
283 DefaultPort updatedPort =
284 new DefaultPort(device, portDescription.portNumber(),
285 portDescription.isEnabled());
286 ports.put(port.number(), updatedPort);
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700287 updatePortMap(device.id(), ports);
tom8cc5aa72014-09-19 15:14:43 -0700288 return new DeviceEvent(PORT_UPDATED, device, port);
289 }
290 return null;
291 }
292
293 // Prunes the specified list of ports based on which ports are in the
294 // processed list and returns list of corresponding events.
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700295 //@GuardedBy("this")
tom8cc5aa72014-09-19 15:14:43 -0700296 private List<DeviceEvent> pruneOldPorts(Device device,
297 Map<PortNumber, Port> ports,
298 Set<PortNumber> processed) {
299 List<DeviceEvent> events = new ArrayList<>();
300 Iterator<PortNumber> iterator = ports.keySet().iterator();
301 while (iterator.hasNext()) {
302 PortNumber portNumber = iterator.next();
303 if (!processed.contains(portNumber)) {
304 events.add(new DeviceEvent(PORT_REMOVED, device,
305 ports.get(portNumber)));
306 iterator.remove();
307 }
308 }
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700309 if (!events.isEmpty()) {
310 updatePortMap(device.id(), ports);
311 }
tom8cc5aa72014-09-19 15:14:43 -0700312 return events;
313 }
314
315 // Gets the map of ports for the specified device; if one does not already
316 // exist, it creates and registers a new one.
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700317 // WARN: returned value is a copy, changes made to the Map
318 // needs to be written back using updatePortMap
319 //@GuardedBy("this")
tom8cc5aa72014-09-19 15:14:43 -0700320 private Map<PortNumber, Port> getPortMap(DeviceId deviceId) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700321 Map<PortNumber, Port> ports = devicePorts.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700322 if (ports == null) {
323 ports = new HashMap<>();
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700324 // this probably is waste of time in most cases.
325 updatePortMap(deviceId, ports);
tom8cc5aa72014-09-19 15:14:43 -0700326 }
327 return ports;
328 }
329
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700330 //@GuardedBy("this")
331 private void updatePortMap(DeviceId deviceId, Map<PortNumber, Port> ports) {
332 rawDevicePorts.put(serialize(deviceId), serialize(ports));
333 devicePorts.put(deviceId, Optional.of(ports));
334 }
335
tom8cc5aa72014-09-19 15:14:43 -0700336 @Override
337 public DeviceEvent updatePortStatus(DeviceId deviceId,
tom0872a172014-09-23 11:24:26 -0700338 PortDescription portDescription) {
tom8cc5aa72014-09-19 15:14:43 -0700339 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700340 Device device = devices.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700341 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
342 Map<PortNumber, Port> ports = getPortMap(deviceId);
343 Port port = ports.get(portDescription.portNumber());
344 return updatePort(device, port, portDescription, ports);
345 }
346 }
347
348 @Override
349 public List<Port> getPorts(DeviceId deviceId) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700350 Map<PortNumber, Port> ports = devicePorts.getUnchecked(deviceId).orNull();
351 return ports == null ? Collections.<Port>emptyList() : ImmutableList.copyOf(ports.values());
tom8cc5aa72014-09-19 15:14:43 -0700352 }
353
354 @Override
355 public Port getPort(DeviceId deviceId, PortNumber portNumber) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700356 Map<PortNumber, Port> ports = devicePorts.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700357 return ports == null ? null : ports.get(portNumber);
358 }
359
360 @Override
361 public boolean isAvailable(DeviceId deviceId) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700362 return availableDevices.contains(serialize(deviceId));
tom8cc5aa72014-09-19 15:14:43 -0700363 }
364
365 @Override
366 public MastershipRole getRole(DeviceId deviceId) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700367 MastershipRole role = roles.getUnchecked(deviceId).orNull();
tom8cc5aa72014-09-19 15:14:43 -0700368 return role != null ? role : MastershipRole.NONE;
369 }
370
371 @Override
372 public DeviceEvent setRole(DeviceId deviceId, MastershipRole role) {
373 synchronized (this) {
374 Device device = getDevice(deviceId);
375 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700376 MastershipRole oldRole = deserialize(
377 rawRoles.put(serialize(deviceId), serialize(role)));
378 roles.put(deviceId, Optional.of(role));
tom8cc5aa72014-09-19 15:14:43 -0700379 return oldRole == role ? null :
380 new DeviceEvent(DEVICE_MASTERSHIP_CHANGED, device, null);
381 }
382 }
383
384 @Override
385 public DeviceEvent removeDevice(DeviceId deviceId) {
386 synchronized (this) {
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700387 byte[] deviceIdBytes = serialize(deviceId);
388 rawRoles.remove(deviceIdBytes);
389 roles.invalidate(deviceId);
390
391 // TODO conditional remove?
392 Device device = deserialize(rawDevices.remove(deviceIdBytes));
393 devices.invalidate(deviceId);
tom8cc5aa72014-09-19 15:14:43 -0700394 return device == null ? null :
395 new DeviceEvent(DEVICE_REMOVED, device, null);
396 }
397 }
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700398
399 // TODO cache serialized DeviceID if we suffer from serialization cost
tom0872a172014-09-23 11:24:26 -0700400 private byte[] serialize(final Object obj) {
401 return storeService.serialize(obj);
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700402 }
403
tom0872a172014-09-23 11:24:26 -0700404 private <T> T deserialize(final byte[] bytes) {
405 return storeService.deserialize(bytes);
Yuta HIGUCHIb3b2ac42014-09-21 23:37:11 -0700406 }
407
Yuta HIGUCHIc7052012014-09-22 19:11:00 -0700408 /**
409 * An IMap EntryListener, which reflects each remote event to cache.
410 *
411 * @param <K> IMap key type after deserialization
412 * @param <V> IMap value type after deserialization
413 */
tom0872a172014-09-23 11:24:26 -0700414 public final class RemoteEventHandler<K, V> extends
Yuta HIGUCHIc7052012014-09-22 19:11:00 -0700415 EntryAdapter<byte[], byte[]> {
416
417 private LoadingCache<K, Optional<V>> cache;
418
419 /**
420 * Constructor.
421 *
422 * @param cache cache to update
423 */
424 public RemoteEventHandler(
425 LoadingCache<K, Optional<V>> cache) {
426 this.cache = checkNotNull(cache);
427 }
428
429 @Override
430 public void mapCleared(MapEvent event) {
431 cache.invalidateAll();
432 }
433
434 @Override
435 public void entryUpdated(EntryEvent<byte[], byte[]> event) {
tom0872a172014-09-23 11:24:26 -0700436 cache.put(storeService.<K>deserialize(event.getKey()),
437 Optional.of(storeService.<V>deserialize(event.getValue())));
Yuta HIGUCHIc7052012014-09-22 19:11:00 -0700438 }
439
440 @Override
441 public void entryRemoved(EntryEvent<byte[], byte[]> event) {
tom0872a172014-09-23 11:24:26 -0700442 cache.invalidate(storeService.<DeviceId>deserialize(event.getKey()));
Yuta HIGUCHIc7052012014-09-22 19:11:00 -0700443 }
444
445 @Override
446 public void entryAdded(EntryEvent<byte[], byte[]> event) {
447 entryUpdated(event);
448 }
449 }
450
451 /**
452 * CacheLoader to wrap Map value with Optional,
453 * to handle negative hit on underlying IMap.
454 *
455 * @param <K> IMap key type after deserialization
456 * @param <V> IMap value type after deserialization
457 */
tom0872a172014-09-23 11:24:26 -0700458 public final class OptionalCacheLoader<K, V> extends
Yuta HIGUCHIc7052012014-09-22 19:11:00 -0700459 CacheLoader<K, Optional<V>> {
460
461 private IMap<byte[], byte[]> rawMap;
462
463 /**
464 * Constructor.
465 *
466 * @param rawMap underlying IMap
467 */
468 public OptionalCacheLoader(IMap<byte[], byte[]> rawMap) {
469 this.rawMap = checkNotNull(rawMap);
470 }
471
472 @Override
473 public Optional<V> load(K key) throws Exception {
tom0872a172014-09-23 11:24:26 -0700474 byte[] keyBytes = storeService.serialize(key);
Yuta HIGUCHIc7052012014-09-22 19:11:00 -0700475 byte[] valBytes = rawMap.get(keyBytes);
476 if (valBytes == null) {
477 return Optional.absent();
478 }
479 V dev = deserialize(valBytes);
480 return Optional.of(dev);
481 }
482 }
tom8cc5aa72014-09-19 15:14:43 -0700483}