blob: eb1ae83c4fb3bccd54ab008001b7e3a5b1eaf94f [file] [log] [blame]
Jonathan Hart6df90172014-04-03 10:13:11 -07001package net.onrc.onos.core.datagrid;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -07002
3import java.io.FileNotFoundException;
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.HashMap;
7import java.util.Map;
Brian O'Connor119672d2014-08-03 21:36:58 -07008import java.util.concurrent.BlockingQueue;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -07009import net.floodlightcontroller.core.IFloodlightProviderService;
10import net.floodlightcontroller.core.module.FloodlightModuleContext;
11import net.floodlightcontroller.core.module.FloodlightModuleException;
12import net.floodlightcontroller.core.module.IFloodlightModule;
13import net.floodlightcontroller.core.module.IFloodlightService;
Pavlin Radoslavovda7ef612013-10-30 16:12:14 -070014import net.floodlightcontroller.restserver.IRestApiService;
Jonathan Hart6df90172014-04-03 10:13:11 -070015import net.onrc.onos.core.datagrid.web.DatagridWebRoutable;
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070016
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070017import org.slf4j.Logger;
18import org.slf4j.LoggerFactory;
19
20import com.hazelcast.config.Config;
21import com.hazelcast.config.FileSystemXmlConfig;
22import com.hazelcast.core.Hazelcast;
23import com.hazelcast.core.HazelcastInstance;
Yuta HIGUCHI285ec592014-08-19 23:00:27 -070024import com.hazelcast.core.IMap;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070025
26/**
27 * A datagrid service that uses Hazelcast as a datagrid.
28 * The relevant data is stored in the Hazelcast datagrid and shared as
29 * appropriate in a multi-node cluster.
30 */
Brian O'Connor119672d2014-08-03 21:36:58 -070031public class HazelcastDatagrid implements IFloodlightModule, IDatagridService,
32 ISharedCollectionsService {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070033 static final Logger log = LoggerFactory.getLogger(HazelcastDatagrid.class);
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070034
35 /**
36 * The name of Hazelcast instance in this JVM.
37 */
38 public static final String ONOS_HAZELCAST_INSTANCE = "ONOS_HazelcastInstance";
39
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070040 private IRestApiService restApi;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070041
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070042 static final String HAZELCAST_CONFIG_FILE = "datagridConfig";
Yuta HIGUCHI285ec592014-08-19 23:00:27 -070043 public static final String HAZELCAST_DEFAULT_XML = "conf/hazelcast.default.xml";
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070044 private HazelcastInstance hazelcastInstance;
45 private Config hazelcastConfig;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070046
Pavlin Radoslavov07af5f22014-03-21 15:17:58 -070047 //
48 // NOTE: eventChannels is kept thread safe by using explicit "synchronized"
49 // blocks below. Those are needed to protect the integrity of each entry
50 // instance, and avoid preemption during channel creation/startup.
51 //
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070052 private final Map<String, IEventChannel<?, ?>> eventChannels = new HashMap<>();
Pavlin Radoslavov7940b652014-02-13 19:42:05 -080053
Pavlin Radoslavovaaace7f2013-10-25 19:42:00 -070054 /**
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070055 * Load the Hazelcast Datagrid configuration file.
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070056 *
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070057 * @param configFilename the configuration filename.
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070058 * @return Hazelcast configuration
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070059 */
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070060 public static Config loadHazelcastConfig(String configFilename) {
61
62 Config hzConfig = null;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070063 /*
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070064 System.setProperty("hazelcast.socket.receive.buffer.size", "32");
65 System.setProperty("hazelcast.socket.send.buffer.size", "32");
66 */
67 // System.setProperty("hazelcast.heartbeat.interval.seconds", "100");
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -080068
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070069 // Init from configuration file
70 try {
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070071 hzConfig = new FileSystemXmlConfig(configFilename);
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070072 } catch (FileNotFoundException e) {
73 log.error("Error opening Hazelcast XML configuration. File not found: " + configFilename, e);
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070074
75 // Fallback mechanism to support running unit test without setup.
76 log.error("Falling back to default Hazelcast XML {}", HAZELCAST_DEFAULT_XML);
77 try {
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070078 hzConfig = new FileSystemXmlConfig(HAZELCAST_DEFAULT_XML);
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070079 } catch (FileNotFoundException e2) {
80 log.error("Error opening fall back Hazelcast XML configuration. "
81 + "File not found: " + HAZELCAST_DEFAULT_XML, e2);
82 // XXX probably should throw some exception to kill ONOS instead.
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070083 hzConfig = new Config();
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070084 }
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070085 }
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070086
87 // set the name of Hazelcast instance in this JVM.
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070088 hzConfig.setInstanceName(ONOS_HAZELCAST_INSTANCE);
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070089
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070090 /*
91 hazelcastConfig.setProperty(GroupProperties.PROP_IO_THREAD_COUNT, "1");
92 hazelcastConfig.setProperty(GroupProperties.PROP_OPERATION_THREAD_COUNT, "1");
93 hazelcastConfig.setProperty(GroupProperties.PROP_EVENT_THREAD_COUNT, "1");
94 */
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070095
96 return hzConfig;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070097 }
98
99 /**
100 * Shutdown the Hazelcast Datagrid operation.
101 */
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800102 @Override
103 protected void finalize() {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700104 close();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700105 }
106
107 /**
108 * Shutdown the Hazelcast Datagrid operation.
109 */
110 public void close() {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700111 Hazelcast.shutdownAll();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700112 }
113
114 /**
115 * Get the collection of offered module services.
116 *
117 * @return the collection of offered module services.
118 */
119 @Override
120 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800121 Collection<Class<? extends IFloodlightService>> l =
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700122 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700123 l.add(IDatagridService.class);
Brian O'Connor119672d2014-08-03 21:36:58 -0700124 l.add(ISharedCollectionsService.class);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700125 return l;
126 }
127
128 /**
129 * Get the collection of implemented services.
130 *
131 * @return the collection of implemented services.
132 */
133 @Override
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800134 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700135 getServiceImpls() {
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700136 Map<Class<? extends IFloodlightService>,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700137 IFloodlightService> m =
138 new HashMap<Class<? extends IFloodlightService>,
139 IFloodlightService>();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700140 m.put(IDatagridService.class, this);
Brian O'Connor119672d2014-08-03 21:36:58 -0700141 m.put(ISharedCollectionsService.class, this);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700142 return m;
143 }
144
145 /**
146 * Get the collection of modules this module depends on.
147 *
148 * @return the collection of modules this module depends on.
149 */
150 @Override
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800151 public Collection<Class<? extends IFloodlightService>>
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700152 getModuleDependencies() {
153 Collection<Class<? extends IFloodlightService>> l =
154 new ArrayList<Class<? extends IFloodlightService>>();
155 l.add(IFloodlightProviderService.class);
156 l.add(IRestApiService.class);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700157 return l;
158 }
159
160 /**
161 * Initialize the module.
162 *
163 * @param context the module context to use for the initialization.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700164 * @throws FloodlightModuleException on error
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700165 */
166 @Override
167 public void init(FloodlightModuleContext context)
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700168 throws FloodlightModuleException {
169 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700170
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700171 // Get the configuration file name and configure the Datagrid
172 Map<String, String> configMap = context.getConfigParams(this);
173 String configFilename = configMap.get(HAZELCAST_CONFIG_FILE);
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -0700174 hazelcastConfig = loadHazelcastConfig(configFilename);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700175 }
176
177 /**
178 * Startup module operation.
179 *
180 * @param context the module context to use for the startup.
181 */
182 @Override
183 public void startUp(FloodlightModuleContext context) {
Yuta HIGUCHI448bca02014-08-13 17:08:00 -0700184 hazelcastInstance = Hazelcast.getOrCreateHazelcastInstance(hazelcastConfig);
Pavlin Radoslavovda7ef612013-10-30 16:12:14 -0700185
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700186 restApi.addRestletRoutable(new DatagridWebRoutable());
Pavlin Radoslavov1308dc62013-10-25 15:54:31 -0700187 }
188
189 /**
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800190 * Create an event channel.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700191 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800192 * If the channel already exists, just return it.
193 * NOTE: The channel is started automatically.
194 *
195 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700196 * @param <K> the type of the Key in the Key-Value store.
197 * @param <V> the type of the Value in the Key-Value store.
198 * @param typeK the type of the Key in the Key-Value store.
199 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800200 * @return the event channel for the channel name.
201 */
202 @Override
203 public <K, V> IEventChannel<K, V> createChannel(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700204 Class<K> typeK, Class<V> typeV) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700205 synchronized (eventChannels) {
206 IEventChannel<K, V> eventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700207 createChannelImpl(channelName, typeK, typeV);
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700208 eventChannel.startup();
209 return eventChannel;
Ray Milkey9c8a2132014-04-02 15:16:42 -0700210 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800211 }
212
213 /**
214 * Create an event channel implementation.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700215 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800216 * If the channel already exists, just return it.
217 * NOTE: The caller must call IEventChannel.startup() to startup the
218 * channel operation.
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700219 * NOTE: The caller must own the lock on "eventChannels".
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800220 *
221 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700222 * @param <K> the type of the Key in the Key-Value store.
223 * @param <V> the type of the Value in the Key-Value store.
224 * @param typeK the type of the Key in the Key-Value store.
225 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800226 * @return the event channel for the channel name.
227 */
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700228 private <K, V> IEventChannel<K, V> createChannelImpl(
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700229 String channelName,
230 Class<K> typeK, Class<V> typeV) {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700231 IEventChannel<?, ?> genericEventChannel =
232 eventChannels.get(channelName);
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800233
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700234 // Add the channel if the first listener
235 if (genericEventChannel == null) {
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700236 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700237 new HazelcastEventChannel<K, V>(hazelcastInstance,
238 channelName, typeK, typeV);
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700239 eventChannels.put(channelName, castedEventChannel);
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700240 return castedEventChannel;
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700241 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800242
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700243 //
244 // TODO: Find if we can use Java internal support to check for
245 // type mismatch.
246 //
247 if (!genericEventChannel.verifyKeyValueTypes(typeK, typeV)) {
248 throw new ClassCastException("Key-value type mismatch for event channel " + channelName);
249 }
250 @SuppressWarnings("unchecked")
251 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700252 (IEventChannel<K, V>) genericEventChannel;
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700253 return castedEventChannel;
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800254 }
255
256 /**
257 * Add event channel listener.
Ray Milkey9c8a2132014-04-02 15:16:42 -0700258 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800259 * NOTE: The channel is started automatically right after the listener
260 * is added.
261 *
262 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700263 * @param listener the listener to add.
264 * @param <K> the type of the Key in the Key-Value store.
265 * @param <V> the type of the Value in the Key-Value store.
266 * @param typeK the type of the Key in the Key-Value store.
267 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800268 * @return the event channel for the channel name.
269 */
270 @Override
271 public <K, V> IEventChannel<K, V> addListener(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700272 IEventChannelListener<K, V> listener,
273 Class<K> typeK, Class<V> typeV) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700274 synchronized (eventChannels) {
275 IEventChannel<K, V> eventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700276 createChannelImpl(channelName, typeK, typeV);
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700277 eventChannel.addListener(listener);
278 eventChannel.startup();
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800279
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700280 return eventChannel;
Ray Milkey9c8a2132014-04-02 15:16:42 -0700281 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800282 }
283
284 /**
285 * Remove event channel listener.
286 *
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700287 * @param <K> the type of the Key in the Key-Value store.
288 * @param <V> the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800289 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700290 * @param listener the listener to remove.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800291 */
292 @Override
293 public <K, V> void removeListener(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700294 IEventChannelListener<K, V> listener) {
Ray Milkey9c8a2132014-04-02 15:16:42 -0700295 synchronized (eventChannels) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700296 IEventChannel<?, ?> genericEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700297 eventChannels.get(channelName);
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800298
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700299 if (genericEventChannel != null) {
300 //
301 // TODO: Find if we can use Java internal support to check for
302 // type mismatch.
303 // NOTE: Using "ClassCastException" exception below doesn't
304 // work.
305 //
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700306 @SuppressWarnings("unchecked")
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700307 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700308 (IEventChannel<K, V>) genericEventChannel;
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700309 castedEventChannel.removeListener(listener);
310 }
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700311 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800312 }
Brian O'Connor119672d2014-08-03 21:36:58 -0700313
314 /**
315 * Create an shared, concurrent map.
316 *
317 * @param mapName the shared map name.
318 * @param typeK the type of the Key in the map.
319 * @param typeV the type of the Value in the map.
320 * @return the shared map for the channel name.
321 */
322 @Override
Yuta HIGUCHI285ec592014-08-19 23:00:27 -0700323 public <K, V> IMap<K, V> getConcurrentMap(String mapName, Class<K> typeK,
Brian O'Connor119672d2014-08-03 21:36:58 -0700324 Class<V> typeV) {
325 return hazelcastInstance.getMap(mapName);
326 }
327
328 /**
329 * Create an shared, blocking queue.
330 *
331 * @param queueName the shared queue name.
332 * @param typeT the type of the queue.
333 * @return the shared queue for the queue name.
334 */
335 @Override
336 public <T> BlockingQueue<T> getBlockingQueue(String queueName, Class<T> typeT) {
337 return hazelcastInstance.getQueue(queueName);
338 }
339
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700340}