blob: 863266934866522b4fa0b32f90d981052857da41 [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;
Pavlin Radoslavov1308dc62013-10-25 15:54:31 -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;
24import com.hazelcast.instance.GroupProperties;
25
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 */
31public class HazelcastDatagrid implements IFloodlightModule, IDatagridService {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070032 static final Logger log = LoggerFactory.getLogger(HazelcastDatagrid.class);
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070033
34 /**
35 * The name of Hazelcast instance in this JVM.
36 */
37 public static final String ONOS_HAZELCAST_INSTANCE = "ONOS_HazelcastInstance";
38
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070039 private IRestApiService restApi;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070040
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070041 static final String HAZELCAST_CONFIG_FILE = "datagridConfig";
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070042 private static final String HAZELCAST_DEFAULT_XML = "conf/hazelcast.default.xml";
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070043 private HazelcastInstance hazelcastInstance;
44 private Config hazelcastConfig;
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070045
Pavlin Radoslavov07af5f22014-03-21 15:17:58 -070046 //
47 // NOTE: eventChannels is kept thread safe by using explicit "synchronized"
48 // blocks below. Those are needed to protect the integrity of each entry
49 // instance, and avoid preemption during channel creation/startup.
50 //
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070051 private final Map<String, IEventChannel<?, ?>> eventChannels = new HashMap<>();
Pavlin Radoslavov7940b652014-02-13 19:42:05 -080052
Pavlin Radoslavovaaace7f2013-10-25 19:42:00 -070053 /**
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070054 * Load the Hazelcast Datagrid configuration file.
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070055 *
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070056 * @param configFilename the configuration filename.
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070057 */
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070058 public void loadHazelcastConfig(String configFilename) {
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070059 /*
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070060 System.setProperty("hazelcast.socket.receive.buffer.size", "32");
61 System.setProperty("hazelcast.socket.send.buffer.size", "32");
62 */
63 // System.setProperty("hazelcast.heartbeat.interval.seconds", "100");
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -080064
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070065 // Init from configuration file
66 try {
67 hazelcastConfig = new FileSystemXmlConfig(configFilename);
68 } catch (FileNotFoundException e) {
69 log.error("Error opening Hazelcast XML configuration. File not found: " + configFilename, e);
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -070070
71 // Fallback mechanism to support running unit test without setup.
72 log.error("Falling back to default Hazelcast XML {}", HAZELCAST_DEFAULT_XML);
73 try {
74 hazelcastConfig = new FileSystemXmlConfig(HAZELCAST_DEFAULT_XML);
75 } catch (FileNotFoundException e2) {
76 log.error("Error opening fall back Hazelcast XML configuration. "
77 + "File not found: " + HAZELCAST_DEFAULT_XML, e2);
78 // XXX probably should throw some exception to kill ONOS instead.
79 }
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070080 }
Yuta HIGUCHI6dfba392014-05-28 15:45:44 -070081
82 // set the name of Hazelcast instance in this JVM.
83 hazelcastConfig.setInstanceName(ONOS_HAZELCAST_INSTANCE);
84
Ray Milkey0ab2d8a2014-03-20 14:30:10 -070085 /*
86 hazelcastConfig.setProperty(GroupProperties.PROP_IO_THREAD_COUNT, "1");
87 hazelcastConfig.setProperty(GroupProperties.PROP_OPERATION_THREAD_COUNT, "1");
88 hazelcastConfig.setProperty(GroupProperties.PROP_EVENT_THREAD_COUNT, "1");
89 */
90 //
91 hazelcastConfig.setProperty(GroupProperties.PROP_EVENT_QUEUE_CAPACITY, "4000000");
92 hazelcastConfig.setProperty(GroupProperties.PROP_SOCKET_RECEIVE_BUFFER_SIZE, "4096");
93 hazelcastConfig.setProperty(GroupProperties.PROP_SOCKET_SEND_BUFFER_SIZE, "4096");
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -070094 }
95
96 /**
97 * Shutdown the Hazelcast Datagrid operation.
98 */
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -080099 @Override
100 protected void finalize() {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700101 close();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700102 }
103
104 /**
105 * Shutdown the Hazelcast Datagrid operation.
106 */
107 public void close() {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700108 Hazelcast.shutdownAll();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700109 }
110
111 /**
112 * Get the collection of offered module services.
113 *
114 * @return the collection of offered module services.
115 */
116 @Override
117 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800118 Collection<Class<? extends IFloodlightService>> l =
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700119 new ArrayList<Class<? extends IFloodlightService>>();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700120 l.add(IDatagridService.class);
121 return l;
122 }
123
124 /**
125 * Get the collection of implemented services.
126 *
127 * @return the collection of implemented services.
128 */
129 @Override
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800130 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700131 getServiceImpls() {
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700132 Map<Class<? extends IFloodlightService>,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700133 IFloodlightService> m =
134 new HashMap<Class<? extends IFloodlightService>,
135 IFloodlightService>();
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700136 m.put(IDatagridService.class, this);
137 return m;
138 }
139
140 /**
141 * Get the collection of modules this module depends on.
142 *
143 * @return the collection of modules this module depends on.
144 */
145 @Override
Yuta HIGUCHI67a7a3e2014-01-03 14:51:34 -0800146 public Collection<Class<? extends IFloodlightService>>
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700147 getModuleDependencies() {
148 Collection<Class<? extends IFloodlightService>> l =
149 new ArrayList<Class<? extends IFloodlightService>>();
150 l.add(IFloodlightProviderService.class);
151 l.add(IRestApiService.class);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700152 return l;
153 }
154
155 /**
156 * Initialize the module.
157 *
158 * @param context the module context to use for the initialization.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700159 * @throws FloodlightModuleException on error
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700160 */
161 @Override
162 public void init(FloodlightModuleContext context)
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700163 throws FloodlightModuleException {
164 restApi = context.getServiceImpl(IRestApiService.class);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700165
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700166 // Get the configuration file name and configure the Datagrid
167 Map<String, String> configMap = context.getConfigParams(this);
168 String configFilename = configMap.get(HAZELCAST_CONFIG_FILE);
Yuta HIGUCHI3ebc9482014-05-08 16:28:28 -0700169 this.loadHazelcastConfig(configFilename);
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700170 }
171
172 /**
173 * Startup module operation.
174 *
175 * @param context the module context to use for the startup.
176 */
177 @Override
178 public void startUp(FloodlightModuleContext context) {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700179 hazelcastInstance = Hazelcast.newHazelcastInstance(hazelcastConfig);
Pavlin Radoslavovda7ef612013-10-30 16:12:14 -0700180
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700181 restApi.addRestletRoutable(new DatagridWebRoutable());
Pavlin Radoslavov1308dc62013-10-25 15:54:31 -0700182 }
183
184 /**
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800185 * Create an event channel.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700186 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800187 * If the channel already exists, just return it.
188 * NOTE: The channel is started automatically.
189 *
190 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700191 * @param <K> the type of the Key in the Key-Value store.
192 * @param <V> the type of the Value in the Key-Value store.
193 * @param typeK the type of the Key in the Key-Value store.
194 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800195 * @return the event channel for the channel name.
196 */
197 @Override
198 public <K, V> IEventChannel<K, V> createChannel(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700199 Class<K> typeK, Class<V> typeV) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700200 synchronized (eventChannels) {
201 IEventChannel<K, V> eventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700202 createChannelImpl(channelName, typeK, typeV);
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700203 eventChannel.startup();
204 return eventChannel;
Ray Milkey9c8a2132014-04-02 15:16:42 -0700205 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800206 }
207
208 /**
209 * Create an event channel implementation.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700210 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800211 * If the channel already exists, just return it.
212 * NOTE: The caller must call IEventChannel.startup() to startup the
213 * channel operation.
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700214 * NOTE: The caller must own the lock on "eventChannels".
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800215 *
216 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700217 * @param <K> the type of the Key in the Key-Value store.
218 * @param <V> the type of the Value in the Key-Value store.
219 * @param typeK the type of the Key in the Key-Value store.
220 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800221 * @return the event channel for the channel name.
222 */
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700223 private <K, V> IEventChannel<K, V> createChannelImpl(
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700224 String channelName,
225 Class<K> typeK, Class<V> typeV) {
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700226 IEventChannel<?, ?> genericEventChannel =
227 eventChannels.get(channelName);
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800228
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700229 // Add the channel if the first listener
230 if (genericEventChannel == null) {
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700231 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700232 new HazelcastEventChannel<K, V>(hazelcastInstance,
233 channelName, typeK, typeV);
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700234 eventChannels.put(channelName, castedEventChannel);
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700235 return castedEventChannel;
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700236 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800237
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700238 //
239 // TODO: Find if we can use Java internal support to check for
240 // type mismatch.
241 //
242 if (!genericEventChannel.verifyKeyValueTypes(typeK, typeV)) {
243 throw new ClassCastException("Key-value type mismatch for event channel " + channelName);
244 }
245 @SuppressWarnings("unchecked")
246 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700247 (IEventChannel<K, V>) genericEventChannel;
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700248 return castedEventChannel;
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800249 }
250
251 /**
252 * Add event channel listener.
Ray Milkey9c8a2132014-04-02 15:16:42 -0700253 * <p/>
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800254 * NOTE: The channel is started automatically right after the listener
255 * is added.
256 *
257 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700258 * @param listener the listener to add.
259 * @param <K> the type of the Key in the Key-Value store.
260 * @param <V> the type of the Value in the Key-Value store.
261 * @param typeK the type of the Key in the Key-Value store.
262 * @param typeV the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800263 * @return the event channel for the channel name.
264 */
265 @Override
266 public <K, V> IEventChannel<K, V> addListener(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700267 IEventChannelListener<K, V> listener,
268 Class<K> typeK, Class<V> typeV) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700269 synchronized (eventChannels) {
270 IEventChannel<K, V> eventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700271 createChannelImpl(channelName, typeK, typeV);
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700272 eventChannel.addListener(listener);
273 eventChannel.startup();
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800274
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700275 return eventChannel;
Ray Milkey9c8a2132014-04-02 15:16:42 -0700276 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800277 }
278
279 /**
280 * Remove event channel listener.
281 *
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700282 * @param <K> the type of the Key in the Key-Value store.
283 * @param <V> the type of the Value in the Key-Value store.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800284 * @param channelName the event channel name.
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700285 * @param listener the listener to remove.
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800286 */
287 @Override
288 public <K, V> void removeListener(String channelName,
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700289 IEventChannelListener<K, V> listener) {
Ray Milkey9c8a2132014-04-02 15:16:42 -0700290 synchronized (eventChannels) {
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700291 IEventChannel<?, ?> genericEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700292 eventChannels.get(channelName);
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800293
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700294 if (genericEventChannel != null) {
295 //
296 // TODO: Find if we can use Java internal support to check for
297 // type mismatch.
298 // NOTE: Using "ClassCastException" exception below doesn't
299 // work.
300 //
Pavlin Radoslavove561a4c2014-04-01 14:10:55 -0700301 @SuppressWarnings("unchecked")
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700302 IEventChannel<K, V> castedEventChannel =
Ray Milkey9c8a2132014-04-02 15:16:42 -0700303 (IEventChannel<K, V>) genericEventChannel;
Pavlin Radoslavov00fad592014-03-21 11:32:34 -0700304 castedEventChannel.removeListener(listener);
305 }
Ray Milkey0ab2d8a2014-03-20 14:30:10 -0700306 }
Pavlin Radoslavov7940b652014-02-13 19:42:05 -0800307 }
Pavlin Radoslavov1eee2c82013-10-15 02:30:32 -0700308}