blob: 1d0d8ea97b915e906e0668b5e26a85df574cd6a8 [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;
9import java.util.concurrent.ConcurrentMap;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070010
11import net.floodlightcontroller.core.IFloodlightProviderService;
12import net.floodlightcontroller.core.module.FloodlightModuleContext;
13import net.floodlightcontroller.core.module.FloodlightModuleException;
14import net.floodlightcontroller.core.module.IFloodlightModule;
15import net.floodlightcontroller.core.module.IFloodlightService;
Pavlin Radoslavovda7ef612013-10-30 16:12:14 -070016import net.floodlightcontroller.restserver.IRestApiService;
Jonathan Hart6df90172014-04-03 10:13:11 -070017import net.onrc.onos.core.datagrid.web.DatagridWebRoutable;
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070018
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070019import org.slf4j.Logger;
20import org.slf4j.LoggerFactory;
21
22import com.hazelcast.config.Config;
23import com.hazelcast.config.FileSystemXmlConfig;
24import com.hazelcast.core.Hazelcast;
25import com.hazelcast.core.HazelcastInstance;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070026
27/**
28 * A datagrid service that uses Hazelcast as a datagrid.
29 * The relevant data is stored in the Hazelcast datagrid and shared as
30 * appropriate in a multi-node cluster.
31 */
Brian O'Connor119672d2014-08-03 21:36:58 -070032public class HazelcastDatagrid implements IFloodlightModule, IDatagridService,
33 ISharedCollectionsService {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070034 static final Logger log = LoggerFactory.getLogger(HazelcastDatagrid.class);
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070035
36 /**
37 * The name of Hazelcast instance in this JVM.
38 */
39 public static final String ONOS_HAZELCAST_INSTANCE = "ONOS_HazelcastInstance";
40
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070041 private IRestApiService restApi;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070042
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070043 static final String HAZELCAST_CONFIG_FILE = "datagridConfig";
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070044 private static final String HAZELCAST_DEFAULT_XML = "conf/hazelcast.default.xml";
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070045 private HazelcastInstance hazelcastInstance;
46 private Config hazelcastConfig;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070047
Pavlin Radoslavov07af5f22014-03-21 15:17:58 -070048 //
49 // NOTE: eventChannels is kept thread safe by using explicit "synchronized"
50 // blocks below. Those are needed to protect the integrity of each entry
51 // instance, and avoid preemption during channel creation/startup.
52 //
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070053 private final Map<String, IEventChannel<?, ?>> eventChannels = new HashMap<>();
Pavlin Radoslavov7940b652014-02-13 19:42:05 -080054
Pavlin Radoslavovaaace7f2013-10-25 19:42:00 -070055 /**
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070056 * Load the Hazelcast Datagrid configuration file.
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070057 *
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070058 * @param configFilename the configuration filename.
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070059 * @return Hazelcast configuration
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070060 */
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070061 public static Config loadHazelcastConfig(String configFilename) {
62
63 Config hzConfig = null;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070064 /*
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070065 System.setProperty("hazelcast.socket.receive.buffer.size", "32");
66 System.setProperty("hazelcast.socket.send.buffer.size", "32");
67 */
68 // System.setProperty("hazelcast.heartbeat.interval.seconds", "100");
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -080069
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070070 // Init from configuration file
71 try {
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070072 hzConfig = new FileSystemXmlConfig(configFilename);
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070073 } catch (FileNotFoundException e) {
74 log.error("Error opening Hazelcast XML configuration. File not found: " + configFilename, e);
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070075
76 // Fallback mechanism to support running unit test without setup.
77 log.error("Falling back to default Hazelcast XML {}", HAZELCAST_DEFAULT_XML);
78 try {
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070079 hzConfig = new FileSystemXmlConfig(HAZELCAST_DEFAULT_XML);
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070080 } catch (FileNotFoundException e2) {
81 log.error("Error opening fall back Hazelcast XML configuration. "
82 + "File not found: " + HAZELCAST_DEFAULT_XML, e2);
83 // XXX probably should throw some exception to kill ONOS instead.
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070084 hzConfig = new Config();
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070085 }
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070086 }
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070087
88 // set the name of Hazelcast instance in this JVM.
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070089 hzConfig.setInstanceName(ONOS_HAZELCAST_INSTANCE);
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070090
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070091 /*
92 hazelcastConfig.setProperty(GroupProperties.PROP_IO_THREAD_COUNT, "1");
93 hazelcastConfig.setProperty(GroupProperties.PROP_OPERATION_THREAD_COUNT, "1");
94 hazelcastConfig.setProperty(GroupProperties.PROP_EVENT_THREAD_COUNT, "1");
95 */
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070096
97 return hzConfig;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070098 }
99
100 /**
101 * Shutdown the Hazelcast Datagrid operation.
102 */
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800103 @Override
104 protected void finalize() {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700105 close();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700106 }
107
108 /**
109 * Shutdown the Hazelcast Datagrid operation.
110 */
111 public void close() {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700112 Hazelcast.shutdownAll();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700113 }
114
115 /**
116 * Get the collection of offered module services.
117 *
118 * @return the collection of offered module services.
119 */
120 @Override
121 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800122 Collection<Class<? extends IFloodlightService>> l =
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700123 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700124 l.add(IDatagridService.class);
Brian O'Connor119672d2014-08-03 21:36:58 -0700125 l.add(ISharedCollectionsService.class);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700126 return l;
127 }
128
129 /**
130 * Get the collection of implemented services.
131 *
132 * @return the collection of implemented services.
133 */
134 @Override
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800135 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700136 getServiceImpls() {
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700137 Map<Class<? extends IFloodlightService>,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700138 IFloodlightService> m =
139 new HashMap<Class<? extends IFloodlightService>,
140 IFloodlightService>();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700141 m.put(IDatagridService.class, this);
Brian O'Connor119672d2014-08-03 21:36:58 -0700142 m.put(ISharedCollectionsService.class, this);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700143 return m;
144 }
145
146 /**
147 * Get the collection of modules this module depends on.
148 *
149 * @return the collection of modules this module depends on.
150 */
151 @Override
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800152 public Collection<Class<? extends IFloodlightService>>
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700153 getModuleDependencies() {
154 Collection<Class<? extends IFloodlightService>> l =
155 new ArrayList<Class<? extends IFloodlightService>>();
156 l.add(IFloodlightProviderService.class);
157 l.add(IRestApiService.class);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700158 return l;
159 }
160
161 /**
162 * Initialize the module.
163 *
164 * @param context the module context to use for the initialization.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700165 * @throws FloodlightModuleException on error
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700166 */
167 @Override
168 public void init(FloodlightModuleContext context)
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700169 throws FloodlightModuleException {
170 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700171
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700172 // Get the configuration file name and configure the Datagrid
173 Map<String, String> configMap = context.getConfigParams(this);
174 String configFilename = configMap.get(HAZELCAST_CONFIG_FILE);
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -0700175 hazelcastConfig = loadHazelcastConfig(configFilename);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700176 }
177
178 /**
179 * Startup module operation.
180 *
181 * @param context the module context to use for the startup.
182 */
183 @Override
184 public void startUp(FloodlightModuleContext context) {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700185 hazelcastInstance = Hazelcast.newHazelcastInstance(hazelcastConfig);
Pavlin Radoslavovda7ef612013-10-30 16:12:14 -0700186
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700187 restApi.addRestletRoutable(new DatagridWebRoutable());
Pavlin Radoslavov1308dc62013-10-25 15:54:31 -0700188 }
189
190 /**
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800191 * Create an event channel.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700192 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800193 * If the channel already exists, just return it.
194 * NOTE: The channel is started automatically.
195 *
196 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700197 * @param <K> the type of the Key in the Key-Value store.
198 * @param <V> the type of the Value in the Key-Value store.
199 * @param typeK the type of the Key in the Key-Value store.
200 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800201 * @return the event channel for the channel name.
202 */
203 @Override
204 public <K, V> IEventChannel<K, V> createChannel(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700205 Class<K> typeK, Class<V> typeV) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700206 synchronized (eventChannels) {
207 IEventChannel<K, V> eventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700208 createChannelImpl(channelName, typeK, typeV);
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700209 eventChannel.startup();
210 return eventChannel;
Ray Milkey9c8a2132014-04-02 15:16:42 -0700211 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800212 }
213
214 /**
215 * Create an event channel implementation.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700216 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800217 * If the channel already exists, just return it.
218 * NOTE: The caller must call IEventChannel.startup() to startup the
219 * channel operation.
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700220 * NOTE: The caller must own the lock on "eventChannels".
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800221 *
222 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700223 * @param <K> the type of the Key in the Key-Value store.
224 * @param <V> the type of the Value in the Key-Value store.
225 * @param typeK the type of the Key in the Key-Value store.
226 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800227 * @return the event channel for the channel name.
228 */
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700229 private <K, V> IEventChannel<K, V> createChannelImpl(
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700230 String channelName,
231 Class<K> typeK, Class<V> typeV) {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700232 IEventChannel<?, ?> genericEventChannel =
233 eventChannels.get(channelName);
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800234
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700235 // Add the channel if the first listener
236 if (genericEventChannel == null) {
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700237 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700238 new HazelcastEventChannel<K, V>(hazelcastInstance,
239 channelName, typeK, typeV);
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700240 eventChannels.put(channelName, castedEventChannel);
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700241 return castedEventChannel;
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700242 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800243
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700244 //
245 // TODO: Find if we can use Java internal support to check for
246 // type mismatch.
247 //
248 if (!genericEventChannel.verifyKeyValueTypes(typeK, typeV)) {
249 throw new ClassCastException("Key-value type mismatch for event channel " + channelName);
250 }
251 @SuppressWarnings("unchecked")
252 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700253 (IEventChannel<K, V>) genericEventChannel;
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700254 return castedEventChannel;
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800255 }
256
257 /**
258 * Add event channel listener.
Ray Milkey9c8a2132014-04-02 15:16:42 -0700259 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800260 * NOTE: The channel is started automatically right after the listener
261 * is added.
262 *
263 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700264 * @param listener the listener to add.
265 * @param <K> the type of the Key in the Key-Value store.
266 * @param <V> the type of the Value in the Key-Value store.
267 * @param typeK the type of the Key in the Key-Value store.
268 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800269 * @return the event channel for the channel name.
270 */
271 @Override
272 public <K, V> IEventChannel<K, V> addListener(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700273 IEventChannelListener<K, V> listener,
274 Class<K> typeK, Class<V> typeV) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700275 synchronized (eventChannels) {
276 IEventChannel<K, V> eventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700277 createChannelImpl(channelName, typeK, typeV);
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700278 eventChannel.addListener(listener);
279 eventChannel.startup();
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800280
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700281 return eventChannel;
Ray Milkey9c8a2132014-04-02 15:16:42 -0700282 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800283 }
284
285 /**
286 * Remove event channel listener.
287 *
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700288 * @param <K> the type of the Key in the Key-Value store.
289 * @param <V> the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800290 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700291 * @param listener the listener to remove.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800292 */
293 @Override
294 public <K, V> void removeListener(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700295 IEventChannelListener<K, V> listener) {
Ray Milkey9c8a2132014-04-02 15:16:42 -0700296 synchronized (eventChannels) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700297 IEventChannel<?, ?> genericEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700298 eventChannels.get(channelName);
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800299
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700300 if (genericEventChannel != null) {
301 //
302 // TODO: Find if we can use Java internal support to check for
303 // type mismatch.
304 // NOTE: Using "ClassCastException" exception below doesn't
305 // work.
306 //
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700307 @SuppressWarnings("unchecked")
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700308 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700309 (IEventChannel<K, V>) genericEventChannel;
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700310 castedEventChannel.removeListener(listener);
311 }
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700312 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800313 }
Brian O'Connor119672d2014-08-03 21:36:58 -0700314
315 /**
316 * Create an shared, concurrent map.
317 *
318 * @param mapName the shared map name.
319 * @param typeK the type of the Key in the map.
320 * @param typeV the type of the Value in the map.
321 * @return the shared map for the channel name.
322 */
323 @Override
324 public <K, V> ConcurrentMap<K, V> getConcurrentMap(String mapName, Class<K> typeK,
325 Class<V> typeV) {
326 return hazelcastInstance.getMap(mapName);
327 }
328
329 /**
330 * Create an shared, blocking queue.
331 *
332 * @param queueName the shared queue name.
333 * @param typeT the type of the queue.
334 * @return the shared queue for the queue name.
335 */
336 @Override
337 public <T> BlockingQueue<T> getBlockingQueue(String queueName, Class<T> typeT) {
338 return hazelcastInstance.getQueue(queueName);
339 }
340
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700341}