blob: ec870e42a5b7538c1208efffed0f06315657b504 [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;
8
9import 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;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070024
25/**
26 * A datagrid service that uses Hazelcast as a datagrid.
27 * The relevant data is stored in the Hazelcast datagrid and shared as
28 * appropriate in a multi-node cluster.
29 */
30public class HazelcastDatagrid implements IFloodlightModule, IDatagridService {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070031 static final Logger log = LoggerFactory.getLogger(HazelcastDatagrid.class);
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070032
33 /**
34 * The name of Hazelcast instance in this JVM.
35 */
36 public static final String ONOS_HAZELCAST_INSTANCE = "ONOS_HazelcastInstance";
37
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070038 private IRestApiService restApi;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070039
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070040 static final String HAZELCAST_CONFIG_FILE = "datagridConfig";
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070041 private static final String HAZELCAST_DEFAULT_XML = "conf/hazelcast.default.xml";
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070042 private HazelcastInstance hazelcastInstance;
43 private Config hazelcastConfig;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070044
Pavlin Radoslavov07af5f22014-03-21 15:17:58 -070045 //
46 // NOTE: eventChannels is kept thread safe by using explicit "synchronized"
47 // blocks below. Those are needed to protect the integrity of each entry
48 // instance, and avoid preemption during channel creation/startup.
49 //
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070050 private final Map<String, IEventChannel<?, ?>> eventChannels = new HashMap<>();
Pavlin Radoslavov7940b652014-02-13 19:42:05 -080051
Pavlin Radoslavovaaace7f2013-10-25 19:42:00 -070052 /**
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070053 * Load the Hazelcast Datagrid configuration file.
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070054 *
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070055 * @param configFilename the configuration filename.
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070056 * @return Hazelcast configuration
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070057 */
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070058 public static Config loadHazelcastConfig(String configFilename) {
59
60 Config hzConfig = null;
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070061 /*
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070062 System.setProperty("hazelcast.socket.receive.buffer.size", "32");
63 System.setProperty("hazelcast.socket.send.buffer.size", "32");
64 */
65 // System.setProperty("hazelcast.heartbeat.interval.seconds", "100");
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -080066
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070067 // Init from configuration file
68 try {
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070069 hzConfig = new FileSystemXmlConfig(configFilename);
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070070 } catch (FileNotFoundException e) {
71 log.error("Error opening Hazelcast XML configuration. File not found: " + configFilename, e);
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070072
73 // Fallback mechanism to support running unit test without setup.
74 log.error("Falling back to default Hazelcast XML {}", HAZELCAST_DEFAULT_XML);
75 try {
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070076 hzConfig = new FileSystemXmlConfig(HAZELCAST_DEFAULT_XML);
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070077 } catch (FileNotFoundException e2) {
78 log.error("Error opening fall back Hazelcast XML configuration. "
79 + "File not found: " + HAZELCAST_DEFAULT_XML, e2);
80 // XXX probably should throw some exception to kill ONOS instead.
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070081 hzConfig = new Config();
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070082 }
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070083 }
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070084
85 // set the name of Hazelcast instance in this JVM.
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070086 hzConfig.setInstanceName(ONOS_HAZELCAST_INSTANCE);
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070087
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070088 /*
89 hazelcastConfig.setProperty(GroupProperties.PROP_IO_THREAD_COUNT, "1");
90 hazelcastConfig.setProperty(GroupProperties.PROP_OPERATION_THREAD_COUNT, "1");
91 hazelcastConfig.setProperty(GroupProperties.PROP_EVENT_THREAD_COUNT, "1");
92 */
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -070093
94 return hzConfig;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070095 }
96
97 /**
98 * Shutdown the Hazelcast Datagrid operation.
99 */
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800100 @Override
101 protected void finalize() {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700102 close();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700103 }
104
105 /**
106 * Shutdown the Hazelcast Datagrid operation.
107 */
108 public void close() {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700109 Hazelcast.shutdownAll();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700110 }
111
112 /**
113 * Get the collection of offered module services.
114 *
115 * @return the collection of offered module services.
116 */
117 @Override
118 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800119 Collection<Class<? extends IFloodlightService>> l =
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700120 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700121 l.add(IDatagridService.class);
122 return l;
123 }
124
125 /**
126 * Get the collection of implemented services.
127 *
128 * @return the collection of implemented services.
129 */
130 @Override
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800131 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700132 getServiceImpls() {
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700133 Map<Class<? extends IFloodlightService>,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700134 IFloodlightService> m =
135 new HashMap<Class<? extends IFloodlightService>,
136 IFloodlightService>();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700137 m.put(IDatagridService.class, this);
138 return m;
139 }
140
141 /**
142 * Get the collection of modules this module depends on.
143 *
144 * @return the collection of modules this module depends on.
145 */
146 @Override
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800147 public Collection<Class<? extends IFloodlightService>>
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700148 getModuleDependencies() {
149 Collection<Class<? extends IFloodlightService>> l =
150 new ArrayList<Class<? extends IFloodlightService>>();
151 l.add(IFloodlightProviderService.class);
152 l.add(IRestApiService.class);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700153 return l;
154 }
155
156 /**
157 * Initialize the module.
158 *
159 * @param context the module context to use for the initialization.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700160 * @throws FloodlightModuleException on error
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700161 */
162 @Override
163 public void init(FloodlightModuleContext context)
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700164 throws FloodlightModuleException {
165 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700166
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700167 // Get the configuration file name and configure the Datagrid
168 Map<String, String> configMap = context.getConfigParams(this);
169 String configFilename = configMap.get(HAZELCAST_CONFIG_FILE);
Yuta HIGUCHI8aa2f9b2014-07-28 11:33:45 -0700170 hazelcastConfig = loadHazelcastConfig(configFilename);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700171 }
172
173 /**
174 * Startup module operation.
175 *
176 * @param context the module context to use for the startup.
177 */
178 @Override
179 public void startUp(FloodlightModuleContext context) {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700180 hazelcastInstance = Hazelcast.newHazelcastInstance(hazelcastConfig);
Pavlin Radoslavovda7ef612013-10-30 16:12:14 -0700181
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700182 restApi.addRestletRoutable(new DatagridWebRoutable());
Pavlin Radoslavov1308dc62013-10-25 15:54:31 -0700183 }
184
185 /**
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800186 * Create an event channel.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700187 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800188 * If the channel already exists, just return it.
189 * NOTE: The channel is started automatically.
190 *
191 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700192 * @param <K> the type of the Key in the Key-Value store.
193 * @param <V> the type of the Value in the Key-Value store.
194 * @param typeK the type of the Key in the Key-Value store.
195 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800196 * @return the event channel for the channel name.
197 */
198 @Override
199 public <K, V> IEventChannel<K, V> createChannel(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700200 Class<K> typeK, Class<V> typeV) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700201 synchronized (eventChannels) {
202 IEventChannel<K, V> eventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700203 createChannelImpl(channelName, typeK, typeV);
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700204 eventChannel.startup();
205 return eventChannel;
Ray Milkey9c8a2132014-04-02 15:16:42 -0700206 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800207 }
208
209 /**
210 * Create an event channel implementation.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700211 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800212 * If the channel already exists, just return it.
213 * NOTE: The caller must call IEventChannel.startup() to startup the
214 * channel operation.
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700215 * NOTE: The caller must own the lock on "eventChannels".
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800216 *
217 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700218 * @param <K> the type of the Key in the Key-Value store.
219 * @param <V> the type of the Value in the Key-Value store.
220 * @param typeK the type of the Key in the Key-Value store.
221 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800222 * @return the event channel for the channel name.
223 */
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700224 private <K, V> IEventChannel<K, V> createChannelImpl(
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700225 String channelName,
226 Class<K> typeK, Class<V> typeV) {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700227 IEventChannel<?, ?> genericEventChannel =
228 eventChannels.get(channelName);
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800229
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700230 // Add the channel if the first listener
231 if (genericEventChannel == null) {
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700232 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700233 new HazelcastEventChannel<K, V>(hazelcastInstance,
234 channelName, typeK, typeV);
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700235 eventChannels.put(channelName, castedEventChannel);
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700236 return castedEventChannel;
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700237 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800238
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700239 //
240 // TODO: Find if we can use Java internal support to check for
241 // type mismatch.
242 //
243 if (!genericEventChannel.verifyKeyValueTypes(typeK, typeV)) {
244 throw new ClassCastException("Key-value type mismatch for event channel " + channelName);
245 }
246 @SuppressWarnings("unchecked")
247 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700248 (IEventChannel<K, V>) genericEventChannel;
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700249 return castedEventChannel;
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800250 }
251
252 /**
253 * Add event channel listener.
Ray Milkey9c8a2132014-04-02 15:16:42 -0700254 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800255 * NOTE: The channel is started automatically right after the listener
256 * is added.
257 *
258 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700259 * @param listener the listener to add.
260 * @param <K> the type of the Key in the Key-Value store.
261 * @param <V> the type of the Value in the Key-Value store.
262 * @param typeK the type of the Key in the Key-Value store.
263 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800264 * @return the event channel for the channel name.
265 */
266 @Override
267 public <K, V> IEventChannel<K, V> addListener(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700268 IEventChannelListener<K, V> listener,
269 Class<K> typeK, Class<V> typeV) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700270 synchronized (eventChannels) {
271 IEventChannel<K, V> eventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700272 createChannelImpl(channelName, typeK, typeV);
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700273 eventChannel.addListener(listener);
274 eventChannel.startup();
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800275
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700276 return eventChannel;
Ray Milkey9c8a2132014-04-02 15:16:42 -0700277 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800278 }
279
280 /**
281 * Remove event channel listener.
282 *
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700283 * @param <K> the type of the Key in the Key-Value store.
284 * @param <V> the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800285 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700286 * @param listener the listener to remove.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800287 */
288 @Override
289 public <K, V> void removeListener(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700290 IEventChannelListener<K, V> listener) {
Ray Milkey9c8a2132014-04-02 15:16:42 -0700291 synchronized (eventChannels) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700292 IEventChannel<?, ?> genericEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700293 eventChannels.get(channelName);
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800294
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700295 if (genericEventChannel != null) {
296 //
297 // TODO: Find if we can use Java internal support to check for
298 // type mismatch.
299 // NOTE: Using "ClassCastException" exception below doesn't
300 // work.
301 //
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700302 @SuppressWarnings("unchecked")
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700303 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700304 (IEventChannel<K, V>) genericEventChannel;
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700305 castedEventChannel.removeListener(listener);
306 }
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700307 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800308 }
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700309}