@JsonSerialize(using=IOFSwitchSerializer.class)//Json序列化
public interface IOFSwitch extends IOFMessageWriter {
    // Attribute keys
    // These match our YANG schema, make sure they are in sync
    public static final String SWITCH_DESCRIPTION_FUTURE = "description-future";
    public static final String SWITCH_DESCRIPTION_DATA = "description-data";
    public static final String SWITCH_SUPPORTS_NX_ROLE = "supports-nx-role";
    public static final String PROP_FASTWILDCARDS = "fast-wildcards";
    public static final String PROP_REQUIRES_L3_MATCH = "requires-l3-match";
    public static final String PROP_SUPPORTS_OFPP_TABLE = "supports-ofpp-table";
    public static final String PROP_SUPPORTS_OFPP_FLOOD = "supports-ofpp-flood";
    public static final String PROP_SUPPORTS_NETMASK_TBL = "supports-netmask-table";
    public static final String PROP_SUPPORTS_BSN_SET_TUNNEL_DST_ACTION =
            "supports-set-tunnel-dst-action";
    public static final String PROP_SUPPORTS_NX_TTL_DECREMENT = "supports-nx-ttl-decrement";

    public enum SwitchStatus {
       /** this switch is in the process of being handshaked. Initial State. 
        * 这个交换机在握手进程中初始状态
       */
    	HANDSHAKE(false),
       /** the OF channel to this switch is currently in SLAVE role - the switch will not accept
        *  state-mutating messages from this controller node.
        *  OF链路连接的控制器当前是SLAVE角色，交换机将不承认此控制器修改的信息
        */
       SLAVE(true),
       /** the OF channel to this switch is currently in MASTER (or EQUALS) role - the switch is
        *  controllable from this controller node.
        *  OF链路连接的交换机当前是MASTER或者EQUALS角色。控制器能够控制交换机
        */
       MASTER(true),
       /** the switch has been sorted out and quarantined by the handshake. It does not show up
        *  in normal switch listings
        *  这个交换机通过握手被挑选和隔离。它不能呈现在正常的交换机表
        */
       QUARANTINED(false),
       /** the switch has disconnected, and will soon be removed from the switch database 
        * 这个交换机已断开连接，并且不就将会从交换机表中移除
        * */
       DISCONNECTED(false);

       private final boolean visible;

       SwitchStatus(boolean visible) {
        this.visible = visible;
       }

       /** wether this switch is currently visible for normal operation 
        * 对于正常运行当前交换机是否可见
        * */
       public boolean isVisible() {
            return visible;
       }

       /** wether this switch is currently ready to be controlled by this controller
        * 当前交换机是否已经准备好被控制器控制
        *  */
       public boolean isControllable() {
            return this == MASTER;
       }
    }
    /**
     * 得到交换机状态
     * @return
     */
    SwitchStatus getStatus();

    /**
     * Returns switch features from features Reply
     * 在特征回复中回复交换机特征
     * @return
     */
    long getBuffers();

    /**
     * Disconnect all the switch's channels and mark the switch as disconnected
     * 断开所有交换机连接,并标记交换机为disconnected
     */
    void disconnect();
    /**
     * 得到Actions集合
     * @return 
     */
    Set<OFActionType> getActions();
    /**
     * 得到Capabilities集合
     * @return
     */
    Set<OFCapabilities> getCapabilities();
    /**
     * 得到流表
     * @return
     */
    short getTables();

    /**
     * 获取一个对这台交换机描述性统计的副本 
     * @return a copy of the description statistics for this switch
     */
    SwitchDescription getSwitchDescription();

    /**
     * Get the IP address of the remote (switch) end of the connection
     * 得到交换机IP
     * @return the inet address
     */
    SocketAddress getInetAddress();

    /**
     * Get list of all enabled ports. This will typically be different from
     * the list of ports in the OFFeaturesReply, since that one is a static
     * snapshot of the ports at the time the switch connected to the controller
     * whereas this port list also reflects the port status messages that have
     * been received.
     * 得到全部使能端口列表。
     * 这通常与OFFeaturesReply回复中的端口列表不同，因为这是此刻交换机连接控制器的端口快照。
     * 然而这个端口列表也能反映出端口状态信息已经收到
     * 
     * @return Unmodifiable list of ports not backed by the underlying collection
     * 不可修改的列表
     */
    Collection<OFPortDesc> getEnabledPorts();

    /**
     * Get list of the port numbers of all enabled ports. This will typically
     * be different from the list of ports in the OFFeaturesReply, since that
     * one is a static snapshot of the ports at the time the switch connected
     * to the controller whereas this port list also reflects the port status
     * messages that have been received.
     * 获得所有活动端口的端口号。
     * @return Unmodifiable list of ports not backed by the underlying collection
     * 不可修改的列表
     */
    Collection<OFPort> getEnabledPortNumbers();

    /**
     * Retrieve the port object by the port number. The port object
     * is the one that reflects the port status updates that have been
     * received, not the one from the features reply.
     * 通过端口号获取端口对象.
     * 这个端口对象反映端口状态更新已被接收，不来自features回复
     * @param portNumber 端口号
     * @return port object 端口对象
     */
    OFPortDesc getPort(OFPort portNumber);

    /**
     * Retrieve the port object by the port name. The port object
     * is the one that reflects the port status updates that have been
     * received, not the one from the features reply.
     * Port names are case insentive
     * 通过端口名获取端口对象.
     * @param portName
     * @return port object
     */
    OFPortDesc getPort(String portName);

    /**
     * Get list of all ports. This will typically be different from
     * the list of ports in the OFFeaturesReply, since that one is a static
     * snapshot of the ports at the time the switch connected to the controller
     * whereas this port list also reflects the port status messages that have
     * been received.
     * 获得所有端口列表
     * @return Unmodifiable list of ports
     */
    Collection<OFPortDesc> getPorts();

    /**
     * This is mainly for the benefit of the DB code which currently has the
     * requirement that list elements be sorted by key. Hopefully soon the
     * DB will handle the sorting automatically or not require it at all, in
     * which case we could get rid of this method.
     * 这主要是为了当前数据库代码要求列表元素根据key排序。
     * 希望很快数据库能自动排序或不再需要排序，这种情况下我们可以拜托这个方法。
     * @return 已排序的端口列表
     */
    Collection<OFPortDesc> getSortedPorts();

    /**
     * 端口是否处在活动状态
     * 该端口是否被启用。（configured down、link down和生成树端口阻塞的情况不在此列） 
     * @param portNumber
     * @return Whether a port is enabled per latest port status message
     * (not configured down nor link down nor in spanning tree blocking state)
     */
    boolean portEnabled(OFPort portNumber);

    /**
     * @param portNumber
     * @return Whether a port is enabled per latest port status message
     * (not configured down nor link down nor in spanning tree blocking state)
     */
    boolean portEnabled(String portName);

    /**
     * Check is switch is connected
     * 检查是否连接
     * @return Whether or not this switch is connected
     */
    boolean isConnected();

    /**
     * Retrieves the date the switch connected to this controller
     * 获取此交换机连接到控制器的时间
     * @return the date
     */
    Date getConnectedSince();

    /**
     * Get the datapathId of the switch
     * 获取此交换机的DPID
     * @return
     */
    DatapathId getId();

    /**
     * Retrieves attributes of this switch
     * 获取此交换机的属性
     * @return
     */
    Map<Object, Object> getAttributes();

    /**
     * 检查该交换机是否处于活动状态。
     * 交换机连接这个控制器并且处于master角色
     * Check if the switch is active. I.e., the switch is connected to this
     * controller and is in master role
     * @return
     */
    boolean isActive();

    /**
     * Get the current role of the controller for the switch
     * 获得当前控制器对于交换机的角色
     * @return the role of the controller
     */
    OFControllerRole getControllerRole();

    /**
     * Checks if a specific switch property exists for this switch
     * 检查一个特定的属性是否存在于这个交换机
     * @param name name of property
     * @return value for name
     */
    boolean hasAttribute(String name);

    /**
     * Set properties for switch specific behavior
     * 获取交换机特定行为的属性
     * @param name name of property
     * @return value for name
     */
    Object getAttribute(String name);

    /**
     * Check if the given attribute is present and if so whether it is equal
     * to "other"
     * 检查给定的属性是否存在，如果是，该属性是否为“other”
     * @param name the name of the attribute to check
     * @param other the object to compare the attribute against.
     * @return true if the specified attribute is set and equals() the given
     * other object.
     */
    boolean attributeEquals(String name, Object other);

    /**
     * Set properties for switch specific behavior
     * 设置交换机特定行为的属性
     * @param name name of property
     * @param value value for name
     */
    void setAttribute(String name, Object value);

    /**
     * Set properties for switch specific behavior
     * 移除交换机特定行为的属性
     * @param name name of property
     * @return current value for name or null (if not present)
     */
    Object removeAttribute(String name);

    /**
     * Returns a factory object that can be used to create OpenFlow messages.
     * 返回一个能够创造OpenFlow消息的工厂对象
     * @return
     */
    OFFactory getOFFactory();

    /**
     * Flush all flows queued for this switch on all connections that were written by the current thread.
     * 通过当前线程刷新所有连接在此交换机上流队列
     *
     */
    void flush();

    /**
     * Gets the OF connections for this switch instance
     * 获得此交换机实例的OF连接
     * @return Collection of IOFConnection
     */
    ImmutableList<IOFConnection> getConnections();

    /**
     * Writes a message to the connection specified by the logical OFMessage category
     * 写入一个正确的OFMessage到指定的连接
     * @param m an OF Message
     * @param category the category of the OF Message to be sent
     */
    void write(OFMessage m, LogicalOFMessageCategory category);

    /**
     * Writes a message list to the connection specified by the logical OFMessage category
     * 写入一个正确的OFMessage列表到指定的连接
     * @param msglist an OF Message list
     * @param category the category of the OF Message list to be sent
     */
    void write(Iterable<OFMessage> msglist, LogicalOFMessageCategory category);

    /**
     * Get a connection specified by the logical OFMessage category
     * 通过获得一个指定的连接
     * @param category the category for the connection the user desires
     * @return an OF Connection
     */
    OFConnection getConnectionByCategory(LogicalOFMessageCategory category);

    /** write a Stats (Multipart-) request, register for all corresponding reply messages.
     * 写入一个状态请求，登记所有相应的回复信息
     * Returns a Future object that can be used to retrieve the List of asynchronous
     * OFStatsReply messages when it is available.
     * 返回一个Future对象能被用来获得异步OFStatsReply回复报文列表当它可用
     *
     * @param request stats request
     * @param category the category for the connection that this request should be written to
     * @return Future object wrapping OFStatsReply
     *         If the connection is not currently connected, will
     *         return a Future that immediately fails with a @link{SwitchDisconnectedException}.
     */
    <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> writeStatsRequest(OFStatsRequest<REPLY> request, LogicalOFMessageCategory category);

    /** write an OpenFlow Request message, register for a single corresponding reply message
     *  or error message.
     *	写入一个OpenFlow请求信息，登记单个相应的回复报文
     * @param request
     * @param category the category for the connection that this request should be written to
     * @return a Future object that can be used to retrieve the asynchrounous
     *         response when available.
     *
     *         If the connection is not currently connected, will
     *         return a Future that immediately fails with a @link{SwitchDisconnectedException}.
     */
    <R extends OFMessage> ListenableFuture<R> writeRequest(OFRequest<R> request, LogicalOFMessageCategory category);
} public enum SwitchStatus {
   /** this switch is in the process of being handshaked. Initial State. 
    * 这个交换机在握手进程中初始状态
   */
	HANDSHAKE(false),
   /** the OF channel to this switch is currently in SLAVE role - the switch will not accept
    *  state-mutating messages from this controller node.
    *  OF链路连接的控制器当前是SLAVE角色，交换机将不承认此控制器修改的信息
    */
   SLAVE(true),
   /** the OF channel to this switch is currently in MASTER (or EQUALS) role - the switch is
    *  controllable from this controller node.
    *  OF链路连接的交换机当前是MASTER或者EQUALS角色。控制器能够控制交换机
    */
   MASTER(true),
   /** the switch has been sorted out and quarantined by the handshake. It does not show up
    *  in normal switch listings
    *  这个交换机通过握手被挑选和隔离。它不能呈现在正常的交换机表
    */
   QUARANTINED(false),
   /** the switch has disconnected, and will soon be removed from the switch database 
    * 这个交换机已断开连接，并且不就将会从交换机表中移除
    * */
   DISCONNECTED(false);

   private final boolean visible;

   SwitchStatus(boolean visible) {
    this.visible = visible;
   }

   /** wether this switch is currently visible for normal operation 
    * 对于正常运行当前交换机是否可见
    * */
   public boolean isVisible() {
        return visible;
   }

   /** wether this switch is currently ready to be controlled by this controller
    * 当前交换机是否已经准备好被控制器控制
    *  */
   public boolean isControllable() {
        return this == MASTER;
   }
} public interface IOFSwitchBackend extends IOFSwitch {
    /**
     * Set the netty Channel this switch instance is associated with
     * Called immediately after instantiation
     * 注册连接
     * 实例化完成后立刻通知
     * @param channel
     */
    void registerConnection(IOFConnectionBackend connection);

    /**
     * Remove the netty Channels associated with this switch
     * 移除交换机与网络通道的联系
     * @param channel
     */
    void removeConnections();

    /**
     * Remove the netty Channel belonging to the specified connection
     * 移除属于特定连接的网络通道
     * @param connection
     */
    void removeConnection(IOFConnectionBackend connection);

    /**
     * Set the OFFeaturesReply message returned by the switch during initial
     * handshake.
     * 设置OFFeaturesReply报文回复在交换机最初的握手时
     * @param featuresReply
     */
    void setFeaturesReply(OFFeaturesReply featuresReply);

    /**
     * Add or modify a switch port.
     * This is called by the core controller
     * code in response to a OFPortStatus message. It should not typically be
     * called by other floodlight applications.
     *	添加或修改一个交换机端口
     *	这就是所谓的核心控制器
     *	代码在OFPortStatus消息的回复中。
     *	它不应该代表被称为其他floodl应用
     * OFPPR_MODIFY and OFPPR_ADD will be treated as equivalent. The OpenFlow
     * spec is not clear on whether portNames are portNumbers are considered
     * authoritative identifiers. We treat portNames <-> portNumber mappings
     * as fixed. If they change, we delete all previous conflicting ports and
     * add all new ports.
     *	OFPPR_MODIFY 和 OFPPR_ADD将被视为相同。
     *	OpenFlow细则不清楚portNames和portNumbers哪个是权威的标识符
     *	我们看待portNames <-> portNumber映射不变。
     *	如果它们改变，我们删除所有以前的节点，并创建新的节点
     *	
     * @param ps the port status message
     * @return the ordered Collection of changes "applied" to the old ports
     * of the switch according to the PortStatus message. A single PortStatus
     * message can result in multiple changes.
     * 返回一个有序的集合应用于旧交换机节点根据PortStatus消息
     * 单个PortStatus消息能导致多个变化
     * If portName <-> portNumber mappings have
     * changed, the iteration order ensures that delete events for old
     * conflicting appear before before events adding new ports
     * 如果portName <-> portNumbe映射发生变化，通过迭代确保删除旧的冲突出现，在添加新节点之前
     */
    OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps);

    /**
     * Compute the changes that would be required to replace the old ports
     * of this switch with the new ports
     * @param ports new ports to set
     * @return the ordered collection of changes "applied" to the old ports
     * of the switch in order to set them to the new set.
     * If portName <-> portNumber mappings have
     * changed, the iteration order ensures that delete events for old
     * conflicting appear before before events adding new ports
     */
    OrderedCollection<PortChangeEvent>
            comparePorts(Collection<OFPortDesc> ports);

    /**
     * Replace the ports of this switch with the given ports.
     * @param ports new ports to set
     * @return the ordered collection of changes "applied" to the old ports
     * of the switch in order to set them to the new set.
     * If portName <-> portNumber mappings have
     * changed, the iteration order ensures that delete events for old
     * conflicting appear before before events adding new ports
     */
    OrderedCollection<PortChangeEvent>
            setPorts(Collection<OFPortDesc> ports);

    /***********************************************
     * The following method can be overridden by
     * specific types of switches
     ***********************************************
     */

    /**
     * Set the SwitchProperties based on it's description
     * @param description
     */
    void setSwitchProperties(SwitchDescription description);

    /**
     * Set the flow table full flag in the switch
     */
    void setTableFull(boolean isFull);

    /**
     * Start this switch driver's sub handshake. This might be a no-op but
     * this method must be called at least once for the switch to be become
     * ready.
     * This method must only be called from the I/O thread
     * @throws SwitchDriverSubHandshakeAlreadyStarted if the sub-handshake has
     * already been started
     */
    void startDriverHandshake();

    /**
     * Check if the sub-handshake for this switch driver has been completed.
     * This method can only be called after startDriverHandshake()
     *
     * This methods must only be called from the I/O thread
     * @return true if the sub-handshake has been completed. False otherwise
     * @throws SwitchDriverSubHandshakeNotStarted if startDriverHandshake() has
     * not been called yet.
     */
    boolean isDriverHandshakeComplete();

    /**
     * Pass the given OFMessage to the driver as part of this driver's
     * sub-handshake. Must not be called after the handshake has been completed
     * This methods must only be called from the I/O thread
     * @param m The message that the driver should process
     * @throws SwitchDriverSubHandshakeCompleted if isDriverHandshake() returns
     * false before this method call
     * @throws SwitchDriverSubHandshakeNotStarted if startDriverHandshake() has
     * not been called yet.
     */
    void processDriverHandshakeMessage(OFMessage m);

    void setPortDescStats(OFPortDescStatsReply portDescStats);

    /**
     * Cancel all pending request
     */
    void cancelAllPendingRequests();

    /** the the current HA role of this switch */
    void setControllerRole(OFControllerRole role);

    void setStatus(SwitchStatus switchStatus);

    /**
     * Updates the switch's mapping of controller connections
     * 更新交换机和控制器连接映射
     * @param controllerCxnsReply the controller connections message sent from the switch
     */
    void updateControllerConnections(OFBsnControllerConnectionsReply controllerCxnsReply);

    /**
     * Determines whether there is another master controller that the switches are
     * connected to by looking at the controller connections.
     * 是否另一个master存在
     * @return true if another viable master exists
     */
    boolean hasAnotherMaster();
} public interface IOFSwitchDriver {
    /**
     * Return an IOFSwitch object based on switch's manufacturer description
     * from OFDescriptionStatitics.
     * 返回IOFSwitch对象基于交换机制造商的OF统计信息描述
     * @param description DescriptionStatistics from the switch instance 从交换机实例中得到的统计信息
     * @return A IOFSwitch instance if the driver found an implementation 
     * for the given description. Null otherwise
     * 如果根据这个描述找到一个实现则返回否则为NULL
     */
    public IOFSwitchBackend getOFSwitchImpl(SwitchDescription description, OFFactory factory);
} public interface IShutdownListener {
    public void floodlightIsShuttingDown();
} public interface IShutdownService extends IFloodlightService {
    /**
     * Terminate floodlight process by calling System.exit() with the given
     * exitCode. If reason is non-null, reason will be logged. Before
     * terminating the shutdownListeners are called
     * 通过调用System.exit()并传入一个exitCode来终止floodlight线程。
     * 如果原因不为null，原因将会被记录。
     * 在终止前shutdownListeners将会被调用
     * If exitCode == 0 the termination cause is deemed "normal" and info
     * level log is used. In all other cases the exit is abnormal and an error
     * is logged.
     * 如果exitCode==0终止的原因是正常
     * 在所有其他反常的退出原因中错误将会被记录
     * @param reason 终止原因
     * @param exitCode 退出码
     */
    public void terminate(@Nullable String reason, int exitCode);

    /**
     * Terminate floodlight process by calling System.exit() with the given
     * exitCode. If reason is non-null, reason will be logged. In addition,
     * the throwable will be logged as well.
     * Before terminating the shutdownListeners are called
     *
     * This method is generally used to terminate floodlight due to an
     * unhandled Exception. As such all messages are logged as error and it is
     * recommended that an exitCode != 0 is used.
     * 这个方法通常用用来终止floodflight由于一个不能处理的异常。
     * 因此所有的信息被记录为错误并且推荐使用exitCode不为0
     * @param reason
     * @param e The throwable causing floodlight to terminate
     * @param exitCode
     */
    public void terminate(@Nullable String reason,
                          @Nonnull Throwable e, int exitCode);

    /**
     * Register a shutdown listener. Registered listeners are called when
     * floodlight is about to terminate due to a call to terminate()
     * 注册一个监听器。
     * @param listener
     */
    public void registerShutdownListener(@Nonnull IShutdownListener listener);
} public class FloodlightModuleContext implements IFloodlightModuleContext {
    protected Map<Class<? extends IFloodlightService>, IFloodlightService> serviceMap;
    protected Map<Class<? extends IFloodlightModule>, Map<String, String>> configParams;
    private final FloodlightModuleLoader moduleLoader;
    
    /**
     * Creates the ModuleContext for use with this IFloodlightProvider.
     * 创建一个ModuleContext来使用
     * This will be used as a module registry for all IFloodlightModule(s).
     * 它将被用来当作模块注册表
     */
    public FloodlightModuleContext(FloodlightModuleLoader moduleLoader) {
        serviceMap = 
                new HashMap<Class<? extends IFloodlightService>,
                                      IFloodlightService>();
        configParams =
                new HashMap<Class<? extends IFloodlightModule>,
                                Map<String, String>>();
        this.moduleLoader = moduleLoader;
    }

    /**
     * Allocate a module context without a module loader; useful for testing
     * 测试用
     */
    public FloodlightModuleContext() {
        this(null);
    }

    // ************************
    // IFloodlightModuleContext
    // ************************

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IFloodlightService> T getServiceImpl(Class<T> service) {
        IFloodlightService s = serviceMap.get(service);
        return (T)s;
    }
    
    @Override
    public Collection<Class<? extends IFloodlightService>> getAllServices() {
        return serviceMap.keySet();
    }

    @Override
    public Map<String, String> getConfigParams(IFloodlightModule module) {
        Class<? extends IFloodlightModule> clazz = module.getClass();
        return getConfigParams(clazz);
    }

    @Override
    public Map<String, String> getConfigParams(Class<? extends IFloodlightModule> clazz) {
        Map<String, String> retMap = configParams.get(clazz);
        if (retMap == null) {
            // Return an empty map if none exists so the module does not
            // need to null check the map
            retMap = new HashMap<String, String>();
            configParams.put(clazz, retMap);
        }

        // also add any configuration parameters for superclasses, but
        // only if more specific configuration does not override it
        for (Class<? extends IFloodlightModule> c : configParams.keySet()) {
            if (c.isAssignableFrom(clazz)) {
                for (Map.Entry<String, String> ent : configParams.get(c).entrySet()) {
                    if (!retMap.containsKey(ent.getKey())) {
                        retMap.put(ent.getKey(), ent.getValue());
                    }
                }
            }
        }

        return retMap;
    }

    // ***********************
    // FloodlightModuleContext
    // ***********************

    /**
     * Adds a configuration parameter for a module
     * @param mod The fully qualified module name to add the parameter to
     * @param key The configuration parameter key
     * @param value The configuration parameter value
     */
    public void addConfigParam(IFloodlightModule mod, String key, String value) {
        Map<String, String> moduleParams = configParams.get(mod.getClass());
        if (moduleParams == null) {
            moduleParams = new HashMap<String, String>();
            configParams.put(mod.getClass(), moduleParams);
        }
        moduleParams.put(key, value);
    }

    /**
     * Get the module loader associated with this context
     * @return the {@link FloodlightModuleLoader} object
     */
    public FloodlightModuleLoader getModuleLoader() {
        return moduleLoader;
    }
    
    /**
     * Adds a IFloodlightModule for this Context.
     * @param clazz the service class
     * @param service The IFloodlightService to add to the registry
     */
    public void addService(Class<? extends IFloodlightService> clazz, 
                           IFloodlightService service) {
        serviceMap.put(clazz, service);
    }
 } public interface IDebugCounterService extends IFloodlightService {
    public enum MetaData {
        WARN,
        DROP,
        ERROR
    }

    /**
     * All modules that wish to have the DebugCounterService count for them, must
     * register themselves. If a module is registered multiple times subsequent
     * registrations will reset all counter in the module.
     *	所有希望拥有DebugCounterService的模块必须注册。
     *	如果模块在多个时间被注册，随后的注册将会重置模块中所有的计数器
     * @param moduleName
     * @return true if the module is registered for the first time, false if
     * the modue was previously registered
     * 当模块第一次注册时返回true，否则返回false
     */
    public boolean registerModule(String moduleName);

    /**
     * All modules that wish to have the DebugCounterService count for them, must
     * register their counters by making this call (typically from that module's
     * 'startUp' method). The counter can then be updated, displayed, reset etc.
     * using the registered moduleName and counterHierarchy.
     *	所有希望拥有DebugCounterService的模块必须注册他们的计数器通过调用这个方法(通常从模块的startup方法)。
     *	计数器之后能被更新，显示，重置等
     *	使用moduleName和conterHierarchy
     * @param moduleName           the name of the module which is registering the
     *                             counter eg. linkdiscovery or controller or switch
     *                             哪一个模块注册的计数器
     * @param counterHierarchy     the hierarchical counter name specifying all
     *                             the hierarchical levels that come above it.
     *                             For example: to register a drop counter for
     *                             packet-ins from a switch, the counterHierarchy
     *                             can be "00:00:00:00:01:02:03:04/pktin/drops"
     *                             It is necessary that counters in hierarchical levels
     *                             above have already been pre-registered - in this
     *                             example: "00:00:00:00:01:02:03:04/pktin" and
     *                             "00:00:00:00:01:02:03:04"
     *                             
     * @param counterDescription   a descriptive string that gives more information
     *                             of what the counter is measuring. For example,
     *                             "Measures the number of incoming packets seen by
     *                             this module".
     * @param metaData             variable arguments that qualify a counter
     *                             eg. warn, error etc.
     * @return                     IDebugCounter with update methods that can be
     *                             used to update a counter.
     */
    public IDebugCounter
    registerCounter(String moduleName, String counterHierarchy,
                    String counterDescription, MetaData... metaData);


    /**
     * Resets the value of counters in the hierarchy to zero. Note that the reset
     * applies to the level of counter hierarchy specified AND ALL LEVELS BELOW it
     * in the hierarchy.
     * 重置控制器层次
     * 备注：重置适用于指定的计数器层次或所有的低于这个层次的计数器
     * For example: If a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops"
     *              specifying a reset hierarchy: "00:00:00:00:01:02:03:04"
     *              will reset all counters for the switch dpid specified;
     *              while specifying a reset hierarchy: ""00:00:00:00:01:02:03:04/pktin"
     *              will reset the pktin counter and all levels below it (like drops)
     *              for the switch dpid specified.
     * @return false if the given moduleName, counterHierarchy
     * does not exist
     */
    public boolean resetCounterHierarchy(String moduleName, String counterHierarchy);

    /**
     * Resets the values of all counters in the system.
     * 重置所有计数器
     */
    public void resetAllCounters();

    /**
     * Resets the values of all counters belonging
     * to a module with the given 'moduleName'.
     * 重置属于指定模块的所有计数器
     * @return false if the given module name does not exists
     */
    public boolean resetAllModuleCounters(String moduleName);
    
    /**
     * Removes/deletes the counter hierarchy AND ALL LEVELS BELOW it in the hierarchy.
     * 移除/删除指定的计数器层次和低于此层次的计数器。
     * For example: If a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops"
     *              specifying a remove hierarchy: "00:00:00:00:01:02:03:04"
     *              will remove all counters for the switch dpid specified;
     *              while specifying a remove hierarchy: ""00:00:00:00:01:02:03:04/pktin"
     *              will remove the pktin counter and all levels below it (like drops)
     *              for the switch dpid specified.
     * @return false if the given moduleName, counterHierarchy does not exist
     */
    public boolean removeCounterHierarchy(String moduleName, String counterHierarchy);


    /**
     * Get counter value and associated information for the specified counterHierarchy.
     * Note that information on the level of counter hierarchy specified
     * AND ALL LEVELS BELOW it in the hierarchy will be returned.
     * 得到计数器值和相应的信息从指定的层次中
     * 备注指定层级的信息和所有低于这个层级将会被返回
     * For example,
     * if a hierarchy exists like "00:00:00:00:01:02:03:04/pktin/drops", then
     * specifying a counterHierarchy of "00:00:00:00:01:02:03:04/pktin" in the
     * get call will return information on the 'pktin' as well as the 'drops'
     * counters for the switch dpid specified.
     *
     * If the module or hierarchy is not registered, returns an empty list
     *
     * @return A list of DebugCounterResource or an empty list if the counter
     *         could not be found
     */
    public List<DebugCounterResource>
    getCounterHierarchy(String moduleName, String counterHierarchy);

    /**
     * Get counter values and associated information for all counters in the
     * system
     *	得到所有的计数器值
     * @return the list of values/info or an empty list
     */
    public List<DebugCounterResource> getAllCounterValues();

    /**
     * Get counter values and associated information for all counters associated
     * with a module.
     * If the module is not registered, returns an empty list
     *	得到所有的模块计数器值
     * @param moduleName
     * @return the list of values/info or an empty list
     */
    public  List<DebugCounterResource> getModuleCounterValues(String moduleName);

} public class DebugEventService implements IFloodlightModule, IDebugEventService {
    protected static final Logger log = LoggerFactory.getLogger(DebugEventService.class);

    /**
     * Every registered event type gets an event id AtomicInt to make the get
     * operation thread-safe
     */
    private final AtomicInteger eventIdCounter = new AtomicInteger();

    /**
     * Unique Event Instance Id for ack-ing
     */
    private final AtomicLong eventInstanceId = new AtomicLong(Long.MAX_VALUE);

    private static final int PCT_LOCAL_CAP = 10; // % of global capacity
    private static final int MIN_LOCAL_CAPACITY = 10; // elements

    /**
     * EnumMap from {@link EventFieldType} to {@link CustomFormatter}
     */
    static final ImmutableMap<EventFieldType, CustomFormatter<?>> customFormatter =
            new ImmutableMap.Builder<EventFieldType, CustomFormatter<?>>()
            .put(EventFieldType.DPID, new CustomFormatterDpid())
            .put(EventFieldType.IPv4, new CustomFormatterIpv4())
              .put(EventFieldType.MAC, new CustomFormatterMac())
              .put(EventFieldType.STRING, new CustomFormatterString())
              .put(EventFieldType.OBJECT, new CustomFormatterObject())
              .put(EventFieldType.PRIMITIVE, new CustomFormatterPrimitive())
              .put(EventFieldType.COLLECTION_IPV4, new CustomFormatterCollectionIpv4())
              .put(EventFieldType.COLLECTION_ATTACHMENT_POINT, new CustomFormatterCollectionAttachmentPoint())
              .put(EventFieldType.COLLECTION_OBJECT, new CustomFormatterCollectionObject())
              .put(EventFieldType.SREF_COLLECTION_OBJECT, new CustomFormatterSrefCollectionObject())
              .put(EventFieldType.SREF_OBJECT, new CustomFormatterSrefObject())
              .build();

    /**
     * Event Information
     */
    public static class EventInfo {
        private final int eventId;
        private final boolean enabled;
        private final int bufferCapacity;
        private int numOfEvents;
        private final EventType etype;
        private final String eventDesc;
        private final String eventName;
        private final String moduleName;
        private final String moduleEventName;
        private final Class<?> eventClass;
        private final boolean ackable;

        public EventInfo(int eventId, boolean enabled, boolean ackable,
                         int bufferCapacity, EventType etype,
                         Class<?> eventClass, String eventDesc,
                         String eventName, String moduleName) {
            this.enabled = enabled;
            this.ackable = ackable;
            this.eventId = eventId;
            this.bufferCapacity = bufferCapacity;
            this.numOfEvents = bufferCapacity;
            this.etype = etype;
            this.eventClass = eventClass;
            this.eventDesc = eventDesc;
            this.eventName = eventName;
            this.moduleName = moduleName;
            this.moduleEventName = moduleName + "/" + eventName;
        }

        public int getEventId() {
            return eventId;
        }

        public boolean isEnabled() {
            return enabled;
        }

        public boolean isAckable() {
            return ackable;
        }

        public int getBufferCapacity() {
            return bufferCapacity;
        }

        public int getNumOfEvents() {
            return numOfEvents;
        }

        public EventType getEtype() {
            return etype;
        }

        public String getEventDesc() {
            return eventDesc;
        }

        public String getEventName() {
            return eventName;
        }

        public String getModuleName() {
            return moduleName;
        }

        public String getModuleEventName() {
            return moduleEventName;
        }
    }

    // ******************
    // Global stores
    // ******************

    /**
     * Event history for a particular event-id is stored in a circular buffer
     */
    protected static class DebugEventHistory {
        EventInfo einfo;
        LinkedBlockingDeque<Event> circularEventBuffer;

        public DebugEventHistory(EventInfo einfo, int capacity) {
            this.einfo = einfo;
            this.circularEventBuffer = new LinkedBlockingDeque<Event>(
                                                                      capacity);
        }
    }

    /**
     * Global storage for all event types and their corresponding event buffers.
     * A particular event type is accessed by directly indexing into the Map
     * with the corresponding event-id. <br/>
     * It is a <b>Map</b> with <br/>
     * <b>Key</b> Integer eventId <br/>
     * <b>Value</b> DebugEventHistory
     */
    protected final ConcurrentHashMap<Integer, DebugEventHistory> allEvents = new ConcurrentHashMap<Integer, DebugEventHistory>();

    /**
     * Global storage for all event ids registered for a module. The map is
     * indexed by the module name and event name and returns the event-ids that
     * correspond to the event types registered by that module (for example
     * module 'linkdiscovery' may register events that have ids 0 and 1 that
     * correspond to link up/down events, and receiving malformed LLDP packets,
     * respectively).
     */
    protected final ConcurrentHashMap<String, ConcurrentHashMap<String, Integer>> moduleEvents = new ConcurrentHashMap<String, ConcurrentHashMap<String, Integer>>();

    /**
     * A collection of event ids that are currently enabled for logging
     */
    protected final Set<Integer> currentEvents = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());

    // ******************
    // Thread local stores
    // ******************

    /**
     * Thread local storage for events
     */
    protected static class LocalEventHistory {
        private boolean enabled;
        private final int capacity;
        private ArrayList<Event> eventList;

        public LocalEventHistory(boolean enabled, int maxCapacity) {
            this.enabled = enabled;
            this.eventList = new ArrayList<Event>(maxCapacity);
            this.capacity = maxCapacity;
        }

        public boolean add(Event e) {
            if (this.eventList.size() < capacity) {
                this.eventList.add(e);
                return true;
            }
            return false;
        }

        public int drainTo(List<Event> eventList) {
            int size = this.eventList.size();
            Iterator<Event> iter = this.eventList.iterator();
            while (iter.hasNext()) {
                eventList.add(iter.next());
            }
            this.eventList.clear();
            return size;
        }

        public boolean isFull() {
            if (eventList.size() == capacity) return true;
            return false;
        }

        public boolean isEmpty() {
            return this.eventList.isEmpty();
        }
    }

    /**
     * Thread local event buffers used for maintaining event history local to a
     * thread. Eventually this locally maintained information is flushed into
     * the global event buffers. <br/>
     * It is a <b>Map</b> with <br/>
     * <b>Key</b> Integer eventId <br/>
     * <b>Value</b> LocalEventHistory
     */
    protected final ThreadLocal<Map<Integer, LocalEventHistory>> threadlocalEvents = new ThreadLocal<Map<Integer, LocalEventHistory>>() {
        @Override
        protected Map<Integer, LocalEventHistory> initialValue() {
            return new HashMap<Integer, LocalEventHistory>();
        }
    };

    /**
     * Thread local cache for event-ids that are currently active.
     */
    protected final ThreadLocal<Set<Integer>> threadlocalCurrentEvents = new ThreadLocal<Set<Integer>>() {
        @Override
        protected Set<Integer> initialValue() {
            return new HashSet<Integer>();
        }
    };

    // *******************************
    // IEventCategory
    // *******************************

    protected class EventCategory<T> implements IEventCategory<T> {
        private final int eventId;

        public EventCategory(int evId) {
            this.eventId = evId;
        }

        @Override
        public void newEventNoFlush(Object event) {
            if (!validEventId()) return;
            newEvent(eventId, false, event);
        }

        @Override
        public void newEventWithFlush(Object event) {
            if (!validEventId()) return;
            newEvent(eventId, true, event);
        }

        private boolean validEventId() {
            if (eventId < 0) {
                throw new IllegalStateException();
            }
            return true;
        }
    }

    public class EventCategoryBuilder<T> {
        private int eventId;
        private String moduleName;
        private String eventName;
        private String eventDescription;
        private EventType eventType;
        private Class<T> eventClass;
        private int bufferCapacity;
        private boolean ackable;

        public EventCategoryBuilder(Class<T> evClass) {
            this.eventId = eventIdCounter.incrementAndGet();
            this.eventClass = evClass;
        }

        public EventCategoryBuilder<T> setModuleName(String moduleName) {
            this.moduleName = moduleName;
            return this;
        }

        public EventCategoryBuilder<T> setEventName(String eventName) {
            this.eventName = eventName;
            return this;
        }

        public EventCategoryBuilder<T> setEventDescription(String eventDescription) {
            this.eventDescription = eventDescription;
            return this;
        }

        public EventCategoryBuilder<T> setEventType(EventType et) {
            this.eventType = et;
            return this;
        }

        public EventCategoryBuilder<T> setBufferCapacity(int bufferCapacity) {
            this.bufferCapacity = bufferCapacity;
            return this;
        }

        public EventCategoryBuilder<T> setAckable(boolean ackable) {
            this.ackable = ackable;
            return this;
        }

        /**
         * Build a new {@link EventCategory<T>} <br/>
         * Requires the following parameters to be set before being called:
         *
         * @param moduleName
         *            module registering event eg. linkdiscovery, virtualrouting.
         * @param eventName
         *            name given to event.
         * @param eventDescription
         *            A descriptive string describing the event.
         * @param eventType
         *            EventType for this event. On-demand events have to be
         *            explicitly enabled using other methods in this API
         * @param eventClass
         *            A user defined class that annotates the fields with
         *            @EventColumn. This class specifies the fields/columns for this
         *            event.
         * @param bufferCapacity
         *            Number of events to store for this event in a circular buffer.
         *            Older events will be discarded once the buffer is full.
         * @param ackable
         *            is the event used as part of ackable-event framework boolean
         *
         * @return IEventCategory with <b>newEvent</b> method that can be used
         * to create instances of event of the given eventClass
         */
        public EventCategory<T> register() {
            // register event id for moduleName
            moduleEvents.putIfAbsent(moduleName,
                                     new ConcurrentHashMap<String, Integer>());
            Integer eventExists = moduleEvents.get(moduleName)
                                              .putIfAbsent(eventName, eventId);
            if (eventExists != null) {
                log.error("Duplicate event registration for moduleName {} eventName {}",
                          moduleName, eventName);
                return new EventCategory<T>(eventExists);
            }

            // create storage for event-type
            boolean enabled = (eventType == EventType.ALWAYS_LOG) ? true : false;
            EventInfo ei = new EventInfo(eventId, enabled, ackable,
                                         bufferCapacity, eventType, eventClass,
                                         eventDescription, eventName, moduleName);
            allEvents.put(eventId, new DebugEventHistory(ei, bufferCapacity));
            if (enabled) {
                currentEvents.add(eventId);
            }

            return new EventCategory<T>(this.eventId);
        }
    }

    // *******************************
    // IDebugEventService
    // *******************************

    @Override
    public <T> EventCategoryBuilder<T> buildEvent(Class<T> evClass) {
        return new EventCategoryBuilder<T>(evClass);
    }

    private void flushLocalToGlobal(int eventId, LocalEventHistory le) {
        DebugEventHistory de = allEvents.get(eventId);
        if (de.einfo.enabled) {
            List<Event> transferEvents = new ArrayList<Event>();
            // drain local buffer to Collection
            int size = le.drainTo(transferEvents);
            // if global buffer doesn't have enough space, clear
            // some space
            int requiredSpace = size
                                - de.circularEventBuffer.remainingCapacity();
            if (requiredSpace > 0) {
                for (int i = 0; i < requiredSpace; i++) {
                    de.circularEventBuffer.removeFirst();
                }
            }
            de.circularEventBuffer.addAll(transferEvents);
        } else {
            le.enabled = false;
            this.threadlocalCurrentEvents.get().remove(eventId);
        }
    }

    private void newEvent(int eventId, boolean flushNow, Object eventData) {
        if (eventId < 0) {
            throw new IllegalStateException("Invalid eventId");
        }

        Map<Integer, LocalEventHistory> thishist = this.threadlocalEvents.get();
        if (!thishist.containsKey(eventId)) {
            // seeing this event for the first time in this thread - create
            // local
            // store by consulting global store
            if (allEvents.containsKey(eventId)) {
                DebugEventHistory de = allEvents.get(eventId);
                boolean enabled = de.einfo.enabled;
                int localCapacity = de.einfo.bufferCapacity * PCT_LOCAL_CAP
                                    / 100;
                if (localCapacity < 10) localCapacity = MIN_LOCAL_CAPACITY;
                thishist.put(eventId, new LocalEventHistory(enabled,
                                                            localCapacity));
                if (enabled) {
                    Set<Integer> thisset = this.threadlocalCurrentEvents.get();
                    thisset.add(eventId);
                }
            } else {
                log.error("updateEvent seen locally for event {} but no global"
                                  + "storage exists for it yet .. not updating",
                          eventId);
                return;
            }
        }

        // update local store if enabled locally for updating
        LocalEventHistory le = thishist.get(eventId);
        if (le.enabled) {
            try {
                le.add(new Event(System.currentTimeMillis(),
                                 Thread.currentThread().getId(),
                                 Thread.currentThread().getName(),
                                 eventData,
                                 /*
                                  * the eventInstanceId is started in reverse
                                  * order so BigDB gets the values in ascending
                                  * order Initialization in startUp()
                                  */
                                 eventInstanceId.decrementAndGet()));

                if (le.isFull() || flushNow) {
                    flushLocalToGlobal(eventId, le);
                }
            } catch (IllegalStateException ise) {
                log.debug("Exception while adding event locally: "
                          + ise.getMessage());
            }
        }
    }

    @Override
    public void flushEvents() {
        Map<Integer, LocalEventHistory> thishist = this.threadlocalEvents.get();
        Set<Integer> thisset = this.threadlocalCurrentEvents.get();

        for (int eventId : thisset) {
            if (thishist.containsKey(eventId)) {
                LocalEventHistory le = thishist.get(eventId);
                if (!le.isEmpty()) {
                    flushLocalToGlobal(eventId, le);
                }
            }
        }

        // sync thread local currently enabled set of eventIds with global set.
        Sets.SetView<Integer> sv = Sets.difference(currentEvents, thisset);
        for (int eventId : sv) {
            if (thishist.containsKey(eventId)) {
                thishist.get(eventId).enabled = true;
                thisset.add(eventId);
            }
        }

    }

    @Override
    public boolean containsModuleEventName(String moduleName,
                                           String eventName) {
        if (!moduleEvents.containsKey(moduleName)) return false;
        if (moduleEvents.get(moduleName).containsKey(eventName))
                                                                return true;
        return false;
    }

    @Override
    public boolean containsModuleName(String moduleName) {
        return moduleEvents.containsKey(moduleName);
    }

    @Override
    public List<EventInfoResource> getAllEventHistory() {
        List<EventInfoResource> moduleEventList = new ArrayList<EventInfoResource>();
        for (Map<String, Integer> modev : moduleEvents.values()) {
            for (int eventId : modev.values()) {
                if (allEvents.containsKey(eventId)) {
                    DebugEventHistory de = allEvents.get(eventId);

                    List<EventResource> eventData = new ArrayList<EventResource>();
                    // take snapshot and iterate
                    Iterator<Event> iter = de.circularEventBuffer.descendingIterator();
                    while (iter.hasNext()) {
                        Event e = iter.next();
                        eventData.add(e.getFormattedEvent(de.einfo.eventClass,
                                                          de.einfo.moduleEventName));
                    }
                    moduleEventList.add(new EventInfoResource(de.einfo,
                                                              eventData));
                }
            }
        }
        traceLogDebugHistory(moduleEventList);
        return moduleEventList;
    }

    @Override
    public List<EventInfoResource> getModuleEventHistory(String moduleName) {
        if (!moduleEvents.containsKey(moduleName))
                                                  return Collections.emptyList();
        List<EventInfoResource> moduleEventList = new ArrayList<EventInfoResource>();
        for (int eventId : moduleEvents.get(moduleName).values()) {
            if (allEvents.containsKey(eventId)) {
                DebugEventHistory de = allEvents.get(eventId);

                List<EventResource> eventData = new ArrayList<EventResource>();
                // take snapshot and iterate
                Iterator<Event> iter = de.circularEventBuffer.descendingIterator();
                while (iter.hasNext()) {
                    Event e = iter.next();
                    eventData.add(e.getFormattedEvent(de.einfo.eventClass,
                                                      de.einfo.moduleEventName));
                }
                moduleEventList.add(new EventInfoResource(de.einfo,
                                                          eventData));
            }
        }
        traceLogDebugHistory(moduleEventList);
        return moduleEventList;
    }

    @Override
    public EventInfoResource getSingleEventHistory(String moduleName,
                                                   String eventName,
                                                   int numOfEvents) {
        if (!moduleEvents.containsKey(moduleName)) return null;
        Integer eventId = moduleEvents.get(moduleName).get(eventName);
        if (eventId == null) return null;

        if (!allEvents.containsKey(eventId)) return null;

        DebugEventHistory de = allEvents.get(eventId);
        if (numOfEvents == 0) numOfEvents = de.einfo.bufferCapacity;

        de.einfo.numOfEvents = numOfEvents;
        int num = 1;
        List<EventResource> eventData = new ArrayList<EventResource>();
        // take snapshot and iterate
        Iterator<Event> iter = de.circularEventBuffer.descendingIterator();
        while (iter.hasNext()) {
            Event e = iter.next();
            if (num > numOfEvents) break;
            eventData.add(e.getFormattedEvent(de.einfo.eventClass,
                                              de.einfo.moduleEventName));
            num++;
        }
        EventInfoResource ret = new EventInfoResource(de.einfo, eventData);
        traceLogDebugHistory(Collections.singletonList(ret));
        return ret;
    }

    @Override
    public void resetAllEvents() {
        for (Map<String, Integer> eventMap : moduleEvents.values()) {
            for (Integer evId : eventMap.values()) {
                allEvents.get(evId).circularEventBuffer.clear();
            }
        }
    }

    @Override
    public void resetAllModuleEvents(String moduleName) {
        if (!moduleEvents.containsKey(moduleName)) return;
        Map<String, Integer> modEvents = moduleEvents.get(moduleName);
        for (Integer evId : modEvents.values()) {
            allEvents.get(evId).circularEventBuffer.clear();
        }
    }

    @Override
    public void resetSingleEvent(String moduleName, String eventName) {
        if (!moduleEvents.containsKey(moduleName)) return;
        Integer eventId = moduleEvents.get(moduleName).get(eventName);
        if (eventId == null) return;

        if (allEvents.containsKey(eventId)) {
            allEvents.get(eventId).circularEventBuffer.clear();
        }
    }

    @Override
    public void setAck(int eventId, long eventInstanceId, boolean ack) {
        if (allEvents.containsKey(eventId)) {
            for (Event e : allEvents.get(eventId).circularEventBuffer) {
                if (e.getEventInstanceId() == eventInstanceId) {
                    e.setAcked(ack);
                }
            }
        }
    }

    @Override
    public List<String> getModuleList() {
        List<String> el = new ArrayList<String>();
        el.addAll(moduleEvents.keySet());
        return el;
    }

    @Override
    public List<String> getModuleEventList(String moduleName) {
        if (!moduleEvents.containsKey(moduleName))
                                                  return Collections.emptyList();
        List<String> el = new ArrayList<String>();
        el.addAll(moduleEvents.get(moduleName).keySet());
        return el;
    }


    private void traceLogDebugHistory(List<EventInfoResource> l) {
        if (!log.isTraceEnabled()) {
            return;
        }
        for (EventInfoResource eir: l) {
            for (EventResource der: eir.getEvents()) {
                log.trace("{}", der);
            }
        }
    }

    private class ShutdownListenenerDelegate implements IShutdownListener {
        @Override
        public void floodlightIsShuttingDown() {
            for (EventInfoResource eir: getAllEventHistory()) {
                for (EventResource der: eir.getEvents()) {
                    log.info("{}", der);
                }
            }
        }
    }

    // *******************************
    // IFloodlightModule
    // *******************************

    @Override
    public Collection<Class<? extends IFloodlightService>>
            getModuleServices() {
        Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
        l.add(IDebugEventService.class);
        return l;
    }

    @Override
    public Map<Class<? extends IFloodlightService>, IFloodlightService>
            getServiceImpls() {
        Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
        m.put(IDebugEventService.class, this);
        return m;
    }

    @Override
    public Collection<Class<? extends IFloodlightService>>
            getModuleDependencies() {
        ArrayList<Class<? extends IFloodlightService>> deps = new ArrayList<Class<? extends IFloodlightService>>();
        deps.add(IShutdownService.class);
        return deps;
    }

    @Override
    public void init(FloodlightModuleContext context) {
    }

    @Override
    public void startUp(FloodlightModuleContext context) {
        IShutdownService shutdownService =
                context.getServiceImpl(IShutdownService.class);
//        shutdownService.registerShutdownListener(new ShutdownListenenerDelegate());
        DebugEventAppender.setDebugEventServiceImpl(this);
    }
} public class MockSyncService extends AbstractSyncManager {
    /**
     * The storage engines that contain the locally-stored data
     */
    private HashMap<String,ListenerStorageEngine> localStores =
            new HashMap<String, ListenerStorageEngine>();
    
    
    // ************
    // ISyncService
    // ************

    @Override
    public void registerStore(String storeName, Scope scope)
            throws SyncException {
        ListenerStorageEngine store = localStores.get(storeName);
        if (store != null) return;
        IStorageEngine<ByteArray, byte[]> memstore =
                new InMemoryStorageEngine<ByteArray, byte[]>(storeName);
        store = new ListenerStorageEngine(memstore, null);
        localStores.put(storeName, store);
    }

    /**
     * Persistent stores are not actually persistent in the mock sync service
     * @see ISyncService#registerPersistentStore(String, 
     * org.sdnplatform.sync.ISyncService.Scope)
     */
    @Override
    public void registerPersistentStore(String storeName, Scope scope)
            throws SyncException {
        registerStore(storeName, scope);
    }
    
    // *****************
    // IFloodlightModule
    // *****************

    @Override
    public void init(FloodlightModuleContext context)
                    throws FloodlightModuleException {
        
    }

    @Override
    public void startUp(FloodlightModuleContext context)
            throws FloodlightModuleException {
        
    }

    @Override
    public Collection<Class<? extends IFloodlightService>>
            getModuleDependencies() {
        return null;
    }

    // *******************
    // AbstractSyncManager
    // *******************
    
    @Override
    public IStore<ByteArray, byte[]>
            getStore(String storeName) throws UnknownStoreException {
        return localStores.get(storeName);
    }

    @Override
    public short getLocalNodeId() {
        return Short.MAX_VALUE;
    }

    @Override
    public void addListener(String storeName, MappingStoreListener listener)
            throws UnknownStoreException {
        ListenerStorageEngine store = localStores.get(storeName);
        if (store == null) 
            throw new UnknownStoreException("Store " + storeName + 
                                            " has not been registered");
        store.addListener(listener);
    }

    @Override
    public void shutdown() {
        
    }

    // ***************
    // MockSyncService
    // ***************
    
    /**
     * Reset to pristine condition
     */
    public void reset() {
        localStores = new HashMap<String, ListenerStorageEngine>();
    }
}
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import net.dsc.cluster.HARole;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.IOFSwitch.SwitchStatus;
import net.floodlightcontroller.core.IOFSwitchBackend;
import net.floodlightcontroller.core.IOFSwitchDriver;
import net.floodlightcontroller.core.IOFSwitchListener;
import net.floodlightcontroller.core.IShutdownListener;
import net.floodlightcontroller.core.IShutdownService;
import net.floodlightcontroller.core.LogicalOFMessageCategory;
import net.floodlightcontroller.core.NullConnection;
import net.floodlightcontroller.core.OFSwitch;
import net.floodlightcontroller.core.PortChangeType;
import net.floodlightcontroller.core.SwitchDescription;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.debugcounter.IDebugCounterService;
import net.floodlightcontroller.debugcounter.MockDebugCounterService;
import net.floodlightcontroller.debugevent.DebugEventService;
import net.floodlightcontroller.debugevent.IDebugEventService;
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.restserver.RestApiServer;
import net.floodlightcontroller.storage.IStorageSourceService;
import net.floodlightcontroller.storage.memory.MemoryStorageSource;
import net.floodlightcontroller.threadpool.IThreadPoolService;
import net.floodlightcontroller.threadpool.ThreadPool;
import org.jboss.netty.util.Timer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.projectfloodlight.openflow.protocol.OFFactories;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFPortFeatures;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.projectfloodlight.openflow.types.DatapathId;
import org.projectfloodlight.openflow.types.OFAuxId;
import org.projectfloodlight.openflow.types.OFPort;
import org.sdnplatform.sync.ISyncService;
import org.sdnplatform.sync.test.MockSyncService;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
*    Copyright 2011, Big Switch Networks, Inc.
*    Originally created by David Erickson, Stanford University
*
*    Licensed under the Apache License, Version 2.0 (the "License"); you may
*    not use this file except in compliance with the License. You may obtain
*    a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0
*
*    Unless required by applicable law or agreed to in writing, software
*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
*    License for the specific language governing permissions and limitations
*    under the License.
**/

package net.floodlightcontroller.core.internal;






/**
 *
 * @author David Erickson (daviderickson@cs.stanford.edu)
 */
public class OFSwitchManagerTest{
    private Controller controller;
    private OFSwitchManager switchManager;

    // FIXME:LOJI: For now just work with OF 1.0
    private final OFFactory factory = OFFactories.getFactory(OFVersion.OF_10);
    private static DatapathId DATAPATH_ID_0 = DatapathId.of(0);
    private static DatapathId DATAPATH_ID_1 = DatapathId.of(1);

    @Before
    public void setUp() throws Exception {
        doSetUp(HARole.ACTIVE);
    }

    public void doSetUp(HARole role) throws Exception {
        FloodlightModuleContext fmc = new FloodlightModuleContext();

        FloodlightProvider cm = new FloodlightProvider();
        fmc.addConfigParam(cm, "role", role.toString());
        controller = (Controller)cm.getServiceImpls().get(IFloodlightProviderService.class);
        fmc.addService(IFloodlightProviderService.class, controller);

        MemoryStorageSource memstorage = new MemoryStorageSource();
        fmc.addService(IStorageSourceService.class, memstorage);
        
        RestApiServer restApi = new RestApiServer();
        fmc.addService(IRestApiService.class, restApi);
        
        ThreadPool threadPool = new ThreadPool();
        fmc.addService(IThreadPoolService.class, threadPool);
        
        // TODO: should mock IDebugCounterService and make sure
        // the expected counters are updated.
        MockDebugCounterService debugCounterService = new MockDebugCounterService();
        fmc.addService(IDebugCounterService.class, debugCounterService);