blob: 05a73b9fd2ffd655774266e361be73ffa429e368 [file] [log] [blame]
Sho SHIMIZU5d62ba82014-08-21 10:23:47 -07001package net.onrc.onos.core.newintent;
2
Sho SHIMIZU5d62ba82014-08-21 10:23:47 -07003import com.hazelcast.core.EntryEvent;
4import com.hazelcast.core.EntryListener;
5import com.hazelcast.core.IMap;
6import net.onrc.onos.api.newintent.IntentId;
7import net.onrc.onos.core.datagrid.ISharedCollectionsService;
8import net.onrc.onos.core.util.serializers.KryoFactory;
9import org.slf4j.Logger;
10import org.slf4j.LoggerFactory;
11
12import java.util.ArrayList;
13import java.util.Collection;
14
15import static com.google.common.base.Preconditions.checkNotNull;
16
17/**
18 * A class representing map storing an intent related value associated with
19 * intent ID as key.
20 * <p>
21 * Implementation-Specific: The backend of this data structure is Hazelcast IMap.
22 * </p>
23 * FIXME: refactor this class to aggregate logic for distributed listenable map.
24 * Intent Service, Flow Manager, and Match-Action Service would want to have similar
25 * logic to store and load the data from a distributed data structure, but these logic
26 * is scattered in each package now.
27 *
28 * @param <V> the type of value
29 */
30class IntentMap<V> {
31 private static final Logger log = LoggerFactory.getLogger(IntentMap.class);
Sho SHIMIZU5d62ba82014-08-21 10:23:47 -070032
Sho SHIMIZU5d62ba82014-08-21 10:23:47 -070033 private final Class<V> valueType;
34 private final IMap<String, byte[]> map;
35
36 /**
37 * Constructs a map which stores intent related information with the specified arguments.
38 *
39 * @param name name of the map
40 * @param valueType type of value
41 * @param collectionsService service for creating Hazelcast IMap
42 */
43 public IntentMap(String name, Class<V> valueType, ISharedCollectionsService collectionsService) {
44 this.valueType = checkNotNull(valueType);
45
46 this.map = checkNotNull(collectionsService.getConcurrentMap(name, String.class, byte[].class));
47 }
48
49 /**
50 * Stores the specified value associated with the intent ID.
51 *
52 * @param id intent ID
53 * @param value value
54 */
55 public void put(IntentId id, V value) {
56 checkNotNull(id);
57 checkNotNull(value);
58
Sho SHIMIZU168c3a32014-08-28 14:15:48 -070059 map.set(id.toString(), KryoFactory.serialize(value));
Sho SHIMIZU5d62ba82014-08-21 10:23:47 -070060 }
61
62 /**
63 * Returns the value associated with the specified intent ID.
64 *
65 * @param id intent ID
66 * @return the value associated with the key
67 */
68 public V get(IntentId id) {
69 checkNotNull(id);
70
71 byte[] bytes = map.get(id.toString());
72 if (bytes == null) {
73 return null;
74 }
75
Sho SHIMIZU168c3a32014-08-28 14:15:48 -070076 return KryoFactory.deserialize(bytes);
Sho SHIMIZU5d62ba82014-08-21 10:23:47 -070077 }
78
79 /**
80 * Removes the value associated with the specified intent ID.
81 *
82 * @param id intent ID
83 */
84 public void remove(IntentId id) {
85 checkNotNull(id);
86
87 map.remove(id.toString());
88 }
89
90 /**
91 * Returns all values stored in the instance.
92 *
93 * @return all values stored in the sintance.
94 */
95 public Collection<V> values() {
96 Collection<V> values = new ArrayList<>();
Yuta HIGUCHI04f16a62014-09-02 15:00:01 -070097 for (byte[] bytes : map.values()) {
Sho SHIMIZU168c3a32014-08-28 14:15:48 -070098 V value = KryoFactory.deserialize(bytes);
Sho SHIMIZU5d62ba82014-08-21 10:23:47 -070099 if (value == null) {
100 continue;
101 }
102
103 values.add(value);
104 }
105
106 return values;
107 }
108
109 /**
110 * Adds an entry listener for this map. Listener will get notified for all events.
111 *
112 * @param listener entry listener
113 */
114 public void addListener(final EntryListener<IntentId, V> listener) {
115 checkNotNull(listener);
116
117 EntryListener<String, byte[]> internalListener = new EntryListener<String, byte[]>() {
118 @Override
119 public void entryAdded(EntryEvent<String, byte[]> event) {
120 listener.entryAdded(convertEntryEvent(event));
121 }
122
123 @Override
124 public void entryRemoved(EntryEvent<String, byte[]> event) {
125 listener.entryRemoved(convertEntryEvent(event));
126 }
127
128 @Override
129 public void entryUpdated(EntryEvent<String, byte[]> event) {
130 listener.entryUpdated(convertEntryEvent(event));
131 }
132
133 @Override
134 public void entryEvicted(EntryEvent<String, byte[]> event) {
135 listener.entryEvicted(convertEntryEvent(event));
136 }
137
138 /**
139 * Converts an entry event used internally to another entry event exposed externally.
140 *
141 * @param internalEvent entry event used internally used
142 * @return entry event exposed externally
143 */
144 private EntryEvent<IntentId, V> convertEntryEvent(EntryEvent<String, byte[]> internalEvent) {
145 EntryEvent<IntentId, V> converted =
146 new EntryEvent<>(
147 internalEvent.getSource(),
148 internalEvent.getMember(),
149 internalEvent.getEventType().getType(),
150 IntentId.valueOf(internalEvent.getKey()),
Sho SHIMIZU168c3a32014-08-28 14:15:48 -0700151 KryoFactory.<V>deserialize(internalEvent.getValue())
Sho SHIMIZU5d62ba82014-08-21 10:23:47 -0700152 );
153 return converted;
154 }
155 };
156
157 map.addEntryListener(internalListener, true);
158 }
159
160 /**
161 * Destroys the backend Hazelcast IMap. This method is only for testing purpose.
162 */
163 void destroy() {
164 map.destroy();
165 }
Sho SHIMIZU5d62ba82014-08-21 10:23:47 -0700166}