3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.protocol_plugin.openflow.core.internal;
12 import java.io.IOException;
13 import java.nio.channels.SelectionKey;
14 import java.nio.channels.Selector;
15 import java.nio.channels.ServerSocketChannel;
16 import java.nio.channels.SocketChannel;
17 import java.util.Date;
18 import java.util.Iterator;
20 import java.util.Map.Entry;
22 import java.util.concurrent.BlockingQueue;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.LinkedBlockingQueue;
26 import java.util.concurrent.atomic.AtomicInteger;
28 import org.eclipse.osgi.framework.console.CommandInterpreter;
29 import org.eclipse.osgi.framework.console.CommandProvider;
30 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
31 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener;
32 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
33 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitchStateListener;
34 import org.openflow.protocol.OFMessage;
35 import org.openflow.protocol.OFType;
36 import org.openflow.util.HexString;
37 import org.osgi.framework.BundleContext;
38 import org.osgi.framework.FrameworkUtil;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
42 public class Controller implements IController, CommandProvider {
43 private static final Logger logger = LoggerFactory
44 .getLogger(Controller.class);
45 private ControllerIO controllerIO;
46 private Thread switchEventThread;
47 private ConcurrentHashMap<Long, ISwitch> switches;
48 private BlockingQueue<SwitchEvent> switchEvents;
49 // only 1 message listener per OFType
50 private ConcurrentMap<OFType, IMessageListener> messageListeners;
51 // only 1 switch state listener
52 private ISwitchStateListener switchStateListener;
53 private AtomicInteger switchInstanceNumber;
56 * this thread monitors the switchEvents queue for new incoming events from switch
58 private class EventHandler implements Runnable {
64 SwitchEvent ev = switchEvents.take();
65 SwitchEvent.SwitchEventType eType = ev.getEventType();
66 ISwitch sw = ev.getSwitch();
67 if (eType != SwitchEvent.SwitchEventType.SWITCH_MESSAGE) {
68 //logger.debug("Received " + ev.toString() + " from " + sw.toString());
72 Long sid = sw.getId();
73 ISwitch existingSwitch = switches.get(sid);
74 if (existingSwitch != null) {
75 logger.info(" Replacing existing "
76 + existingSwitch.toString() + " with New "
78 disconnectSwitch(existingSwitch);
80 switches.put(sid, sw);
81 notifySwitchAdded(sw);
90 OFMessage msg = ev.getMsg();
92 IMessageListener listener = messageListeners
94 if (listener != null) {
95 listener.receive(sw, msg);
100 logger.error("unknow switch event " + eType.ordinal());
102 } catch (InterruptedException e) {
103 switchEvents.clear();
112 * Function called by the dependency manager when all the required
113 * dependencies are satisfied
117 logger.debug("OpenFlowCore init");
118 this.switches = new ConcurrentHashMap<Long, ISwitch>();
119 this.switchEvents = new LinkedBlockingQueue<SwitchEvent>();
120 this.messageListeners = new ConcurrentHashMap<OFType, IMessageListener>();
121 this.switchStateListener = null;
122 this.switchInstanceNumber = new AtomicInteger(0);
123 registerWithOSGIConsole();
128 * Function called by dependency manager after "init ()" is called
129 * and after the services provided by the class are registered in
130 * the service registry
133 public void start() {
134 logger.debug("OpenFlowCore start() is called");
136 * start a thread to handle event coming from the switch
138 switchEventThread = new Thread(new EventHandler(), "SwitchEvent Thread");
139 switchEventThread.start();
141 // spawn a thread to start to listen on the open flow port
142 controllerIO = new ControllerIO(this);
144 controllerIO.start();
145 } catch (IOException ex) {
146 logger.error("Caught exception: " + ex + " during start");
151 * Function called by the dependency manager before the services
152 * exported by the component are unregistered, this will be
153 * followed by a "destroy ()" calls
157 for (Iterator<Entry<Long, ISwitch>> it = switches.entrySet().iterator(); it
159 Entry<Long, ISwitch> entry = it.next();
160 ((SwitchHandler) entry.getValue()).stop();
163 switchEventThread.interrupt();
165 controllerIO.shutDown();
166 } catch (IOException ex) {
167 logger.error("Caught exception: " + ex + " during stop");
172 * Function called by the dependency manager when at least one
173 * dependency become unsatisfied or when the component is shutting
174 * down because for example bundle is being stopped.
177 public void destroy() {
181 public void addMessageListener(OFType type, IMessageListener listener) {
182 IMessageListener currentListener = this.messageListeners.get(type);
183 if (currentListener != null) {
184 logger.warn(type.toString() + " already listened by "
185 + currentListener.toString());
187 this.messageListeners.put(type, listener);
188 logger.debug(type.toString() + " is now listened by "
189 + listener.toString());
193 public void removeMessageListener(OFType type, IMessageListener listener) {
194 IMessageListener currentListener = this.messageListeners.get(type);
195 if ((currentListener != null) && (currentListener == listener)) {
196 this.messageListeners.remove(type);
201 public void addSwitchStateListener(ISwitchStateListener listener) {
202 if (this.switchStateListener != null) {
203 logger.warn(this.switchStateListener.toString()
204 + "already listened to switch events");
206 this.switchStateListener = listener;
207 logger.debug(listener.toString() + " now listens to switch events");
211 public void removeSwitchStateListener(ISwitchStateListener listener) {
212 if ((this.switchStateListener != null)
213 && (this.switchStateListener == listener)) {
214 this.switchStateListener = null;
218 public void handleNewConnection(Selector selector,
219 SelectionKey serverSelectionKey) {
220 ServerSocketChannel ssc = (ServerSocketChannel) serverSelectionKey
222 SocketChannel sc = null;
226 int i = this.switchInstanceNumber.addAndGet(1);
227 String instanceName = "SwitchHandler-" + i;
228 SwitchHandler switchHandler = new SwitchHandler(this, sc,
230 switchHandler.start();
231 logger.info(instanceName + " connected: " + sc.toString());
232 } catch (IOException e) {
237 private void disconnectSwitch(ISwitch sw) {
238 if (((SwitchHandler) sw).isOperational()) {
239 Long sid = sw.getId();
240 if (this.switches.remove(sid, sw)) {
241 logger.warn(sw.toString() + " is disconnected");
242 notifySwitchDeleted(sw);
244 //logger.warn(sw.toString() + " has been replaced by " +
245 // this.switches.get(sid));
248 ((SwitchHandler) sw).stop();
252 private void notifySwitchAdded(ISwitch sw) {
253 if (switchStateListener != null) {
254 switchStateListener.switchAdded(sw);
258 private void notifySwitchDeleted(ISwitch sw) {
259 if (switchStateListener != null) {
260 switchStateListener.switchDeleted(sw);
264 private synchronized void addSwitchEvent(SwitchEvent event) {
266 this.switchEvents.put(event);
267 } catch (InterruptedException e) {
268 logger.debug("SwitchEvent caught Interrupt Exception");
272 public void takeSwtichEventAdd(ISwitch sw) {
273 SwitchEvent ev = new SwitchEvent(
274 SwitchEvent.SwitchEventType.SWITCH_ADD, sw, null);
278 public void takeSwitchEventDelete(ISwitch sw) {
279 SwitchEvent ev = new SwitchEvent(
280 SwitchEvent.SwitchEventType.SWITCH_DELETE, sw, null);
284 public void takeSwitchEventError(ISwitch sw) {
285 SwitchEvent ev = new SwitchEvent(
286 SwitchEvent.SwitchEventType.SWITCH_ERROR, sw, null);
290 public void takeSwitchEventMsg(ISwitch sw, OFMessage msg) {
291 if (messageListeners.get(msg.getType()) != null) {
292 SwitchEvent ev = new SwitchEvent(
293 SwitchEvent.SwitchEventType.SWITCH_MESSAGE, sw, msg);
299 public Map<Long, ISwitch> getSwitches() {
300 return this.switches;
304 public ISwitch getSwitch(Long switchId) {
305 return this.switches.get(switchId);
308 public void _controllerShowSwitches(CommandInterpreter ci) {
309 Set<Long> sids = switches.keySet();
310 StringBuffer s = new StringBuffer();
311 int size = sids.size();
313 ci.print("switches: empty");
316 Iterator<Long> iter = sids.iterator();
317 s.append("Total: " + size + " switches\n");
318 while (iter.hasNext()) {
319 Long sid = iter.next();
320 Date date = switches.get(sid).getConnectedDate();
321 String switchInstanceName = ((SwitchHandler) switches.get(sid)).getInstanceName();
322 s.append(switchInstanceName + "/" + HexString.toHexString(sid)
323 + " connected since " + date.toString() + "\n");
325 ci.print(s.toString());
329 public void _controllerReset(CommandInterpreter ci) {
330 ci.print("...Disconnecting the communication to all switches...\n");
334 } catch (InterruptedException ie) {
336 ci.print("...start to accept connections from switches...\n");
341 private void registerWithOSGIConsole() {
342 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
344 bundleContext.registerService(CommandProvider.class.getName(), this,
349 public String getHelp() {
350 StringBuffer help = new StringBuffer();
351 help.append("--Open Flow Controller --\n");
352 help.append("\tcontrollerShowSwitches\n");
353 help.append("\tcontrollerReset\n");
354 return help.toString();