blob: 09d3a235981017f5495ea4c2de1e26f2934b545f [file] [log] [blame]
package net.onrc.onos.core.newintent;
import com.hazelcast.core.EntryEvent;
import com.hazelcast.core.EntryListener;
import com.hazelcast.core.IMap;
import net.onrc.onos.api.newintent.IntentId;
import net.onrc.onos.core.datagrid.ISharedCollectionsService;
import net.onrc.onos.core.util.serializers.KryoFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A class representing map storing an intent related value associated with
* intent ID as key.
* <p>
* Implementation-Specific: The backend of this data structure is Hazelcast IMap.
* </p>
* FIXME: refactor this class to aggregate logic for distributed listenable map.
* Intent Service, Flow Manager, and Match-Action Service would want to have similar
* logic to store and load the data from a distributed data structure, but these logic
* is scattered in each package now.
*
* @param <V> the type of value
*/
class IntentMap<V> {
private static final Logger log = LoggerFactory.getLogger(IntentMap.class);
private final Class<V> valueType;
private final IMap<String, byte[]> map;
/**
* Constructs a map which stores intent related information with the specified arguments.
*
* @param name name of the map
* @param valueType type of value
* @param collectionsService service for creating Hazelcast IMap
*/
public IntentMap(String name, Class<V> valueType, ISharedCollectionsService collectionsService) {
this.valueType = checkNotNull(valueType);
this.map = checkNotNull(collectionsService.getConcurrentMap(name, String.class, byte[].class));
}
/**
* Stores the specified value associated with the intent ID.
*
* @param id intent ID
* @param value value
*/
public void put(IntentId id, V value) {
checkNotNull(id);
checkNotNull(value);
map.set(id.toString(), KryoFactory.serialize(value));
}
/**
* Returns the value associated with the specified intent ID.
*
* @param id intent ID
* @return the value associated with the key
*/
public V get(IntentId id) {
checkNotNull(id);
byte[] bytes = map.get(id.toString());
if (bytes == null) {
return null;
}
return KryoFactory.deserialize(bytes);
}
/**
* Removes the value associated with the specified intent ID.
*
* @param id intent ID
*/
public void remove(IntentId id) {
checkNotNull(id);
map.remove(id.toString());
}
/**
* Returns all values stored in the instance.
*
* @return all values stored in the sintance.
*/
public Collection<V> values() {
Collection<V> values = new ArrayList<>();
for (byte[] bytes: map.values()) {
V value = KryoFactory.deserialize(bytes);
if (value == null) {
continue;
}
values.add(value);
}
return values;
}
/**
* Adds an entry listener for this map. Listener will get notified for all events.
*
* @param listener entry listener
*/
public void addListener(final EntryListener<IntentId, V> listener) {
checkNotNull(listener);
EntryListener<String, byte[]> internalListener = new EntryListener<String, byte[]>() {
@Override
public void entryAdded(EntryEvent<String, byte[]> event) {
listener.entryAdded(convertEntryEvent(event));
}
@Override
public void entryRemoved(EntryEvent<String, byte[]> event) {
listener.entryRemoved(convertEntryEvent(event));
}
@Override
public void entryUpdated(EntryEvent<String, byte[]> event) {
listener.entryUpdated(convertEntryEvent(event));
}
@Override
public void entryEvicted(EntryEvent<String, byte[]> event) {
listener.entryEvicted(convertEntryEvent(event));
}
/**
* Converts an entry event used internally to another entry event exposed externally.
*
* @param internalEvent entry event used internally used
* @return entry event exposed externally
*/
private EntryEvent<IntentId, V> convertEntryEvent(EntryEvent<String, byte[]> internalEvent) {
EntryEvent<IntentId, V> converted =
new EntryEvent<>(
internalEvent.getSource(),
internalEvent.getMember(),
internalEvent.getEventType().getType(),
IntentId.valueOf(internalEvent.getKey()),
KryoFactory.<V>deserialize(internalEvent.getValue())
);
return converted;
}
};
map.addEntryListener(internalListener, true);
}
/**
* Destroys the backend Hazelcast IMap. This method is only for testing purpose.
*/
void destroy() {
map.destroy();
}
}