From e5415588f1a1ee3506a8edc362b7654d5991016d Mon Sep 17 00:00:00 2001
From: Gregor Maier <gregor.maier@bigswitch.com>
Date: Thu, 28 Jun 2012 21:02:17 -0700
Subject: [PATCH] Update entity classification

* IEntityClassifier only returns a single entity class per entity and all
  entities of a device must belong to the same entity class. Semantics for
  multiple classes was unclear.

* IEntityClassifier is now an IFloodlightService to have a cleaner way of
  setting the classifier to use (the classifier needs to be set before
  DeviceManager.startUp() is called).

[BSC-1838]
---
 .../devicemanager/IDevice.java                |  10 +-
 .../devicemanager/IDeviceService.java         |  25 ++--
 .../devicemanager/IEntityClass.java           |   8 +-
 ...ier.java => IEntityClassifierService.java} |  25 ++--
 .../internal/DefaultEntityClassifier.java     |  70 ++++++++--
 .../devicemanager/internal/Device.java        |  72 ++++------
 .../internal/DeviceIterator.java              |  23 ++-
 .../internal/DeviceManagerImpl.java           | 131 +++++++-----------
 .../devicemanager/web/DeviceSerializer.java   |   8 +-
 .../internal/DeviceManagerImplTest.java       |  38 ++---
 .../devicemanager/test/MockDevice.java        |  13 +-
 .../devicemanager/test/MockDeviceManager.java |  13 +-
 .../forwarding/ForwardingTest.java            |   6 +
 .../VirtualNetworkFilterTest.java             |   6 +
 14 files changed, 228 insertions(+), 220 deletions(-)
 rename src/main/java/net/floodlightcontroller/devicemanager/{IEntityClassifier.java => IEntityClassifierService.java} (83%)

diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java
index 4deba4b62..39bce1cf0 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java
@@ -80,10 +80,10 @@ public interface IDevice {
     public Date getLastSeen();
     
     /**
-     * Get the entity classes for the device.
-     * @return the entity classes
-     * @see IEntityClassifier
-     * @see IDeviceService#setEntityClassifier(IEntityClassifier)
+     * Get the entity class for the device.
+     * @return the entity class
+     * @see IEntityClassifierService
+     * @see IDeviceService#setEntityClassifier(IEntityClassifierService)
      */
-    public IEntityClass[] getEntityClasses();
+    public IEntityClass getEntityClass();
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
index 79cb2699b..4bb6a7bad 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
@@ -67,17 +67,19 @@ public interface IDeviceService extends IFloodlightService {
     public IDevice getDevice(Long deviceKey);
     
     /**
-     * Search for a device using exactly matching the provided device fields.
-     * Only the key fields as defined by the {@link IEntityClassifier} will
-     * be important in this search.
+     * Search for a device exactly matching the provided device fields.
+     * Only the key fields as defined by the {@link IEntityClassifierService} will
+     * be important in this search. All key fields MUST be supplied, otherwise
+     * use queryDevices().
      * 
      * @param macAddress The MAC address
-     * @param vlan the VLAN
+     * @param vlan the VLAN. Null means no VLAN and is valid even if VLAN is a 
+     *        key field.
      * @param ipv4Address the ipv4 address
      * @param switchDPID the switch DPID
      * @param switchPort the switch port
      * @return an {@link IDevice} or null if no device is found.
-     * @see IDeviceManager#setEntityClassifier(IEntityClassifier)
+     * @see IDeviceManager#setEntityClassifier(IEntityClassifierService)
      */
     public IDevice findDevice(long macAddress, Short vlan,
                               Integer ipv4Address, Long switchDPID,
@@ -87,7 +89,7 @@ public interface IDeviceService extends IFloodlightService {
      * Get a destination device using entity fields that corresponds with
      * the given source device.  The source device is important since
      * there could be ambiguity in the destination device without the
-     * attachment point information.
+     * attachment point information. 
      * 
      * @param source the source device.  The returned destination will be
      * in the same entity class as the source.
@@ -177,16 +179,7 @@ public interface IDeviceService extends IFloodlightService {
      */
     public void addListener(IDeviceListener listener);
     
-    /**
-     * Set the entity classifier for the device manager to use to
-     * differentiate devices on the network.  If no classifier is set,
-     * the {@link DefaultEntityClassifer} will be used.  This should be 
-     * registered in the application initialization phase before startup.
-     * 
-     * @param classifier the classifier to set.
-     */
-    public void setEntityClassifier(IEntityClassifier classifier);
-
+    
     /**
      * Flush and/or reclassify all entities in a class
      *
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java
index 1053a79a1..bb077f116 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClass.java
@@ -28,7 +28,7 @@ import net.floodlightcontroller.devicemanager.internal.Device;
  * class. A set of entities are considered to be the same device if and only 
  * if they belong to the same entity class and they match on all key fields 
  * for that entity class. A field is effectively wildcarded by not including 
- * it in the list of key fields returned by {@link IEntityClassifier} and/or 
+ * it in the list of key fields returned by {@link IEntityClassifierService} and/or 
  * {@link IEntityClass}.
  * 
  * Note that if you're not using static objects, you'll need to override
@@ -49,5 +49,11 @@ public interface IEntityClass {
      * be wildcarded.  May be null to indicate that all fields are key fields.
      */
     EnumSet<DeviceField> getKeyFields();
+    
+    /**
+     * Returns a user-friendly, unique name for this EntityClass
+     * @return the name of the entity class
+     */
+    String getName();
 }
 
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifierService.java
similarity index 83%
rename from src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java
rename to src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifierService.java
index 31cf4a70d..8d693ce92 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifier.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassifierService.java
@@ -19,6 +19,8 @@ package net.floodlightcontroller.devicemanager;
 
 import java.util.Collection;
 import java.util.EnumSet;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField;
 import net.floodlightcontroller.devicemanager.internal.Entity;
 
@@ -30,32 +32,27 @@ import net.floodlightcontroller.devicemanager.internal.Entity;
  * 
  * @author readams
  */
-public interface IEntityClassifier {
+public interface IEntityClassifierService extends IFloodlightService {
     /**
-    * Classify the given entity into a set of classes.  It is important
-    * that the key fields returned by {@link IEntityClassifier#getKeyFields()}
+    * Classify the given entity into an IEntityClass.  It is important
+    * that the key fields returned by {@link IEntityClassifierService#getKeyFields()}
     * be sufficient for classifying entities.  That is, if two entities are
     * identical except for a field that is not a key field, they must be
     * assigned the same class.  Furthermore, entity classification must be
     * transitive: For all entities x, y, z, if x and y belong to a class c, and 
     * y and z belong class c, then x and z must belong to class c.
     * 
-    * <p>Note further that you must take steps to ensure you always return
-    * classes in some consistent ordering.  This could be achieved by sorting
-    * the returned collection before returning it.
-    * 
     * @param entity the entity to classify
-    * @return the IEntityClass resulting from the classification.  When
-    * iterating, must return results in a sorted order.
-    * @see IEntityClassifier#getKeyFields()
+    * @return the IEntityClass resulting from the classification.
+    * @see IEntityClassifierService#getKeyFields()
     */
-   Collection<IEntityClass> classifyEntity(Entity entity);
+   IEntityClass classifyEntity(Entity entity);
 
    /**
     * Return the most general list of fields that should be used as key 
     * fields.  If devices differ in any fields not listed here, they can
     * never be considered a different device by any {@link IEntityClass} 
-    * returned by {@link IEntityClassifier#classifyEntity}.  The key fields
+    * returned by {@link IEntityClassifierService#classifyEntity}.  The key fields
     * for an entity classifier must not change unless associated with a 
     * flush of all entity state.  The list of key fields must be the union
     * of all key fields that could be returned by
@@ -64,7 +61,7 @@ public interface IEntityClassifier {
     * @return a set containing the fields that should not be
     * wildcarded.  May be null to indicate that all fields are key fields.
     * @see {@link IEntityClass#getKeyFields()}
-    * @see {@link IEntityClassifier#classifyEntity}
+    * @see {@link IEntityClassifierService#classifyEntity}
     */
    EnumSet<DeviceField> getKeyFields();
 
@@ -83,7 +80,7 @@ public interface IEntityClassifier {
     * @param entity the entity to reclassify
     * @return the IEntityClass resulting from the classification
     */
-   Collection<IEntityClass> reclassifyEntity(IDevice curDevice,
+   IEntityClass reclassifyEntity(IDevice curDevice,
                                              Entity entity);
 
    /**
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java
index 32bbeabb6..353a72d1b 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java
@@ -17,22 +17,31 @@
 
 package net.floodlightcontroller.devicemanager.internal;
 
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
 
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.devicemanager.IDevice;
 import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField;
 import net.floodlightcontroller.devicemanager.IEntityClass;
-import net.floodlightcontroller.devicemanager.IEntityClassifier;
+import net.floodlightcontroller.devicemanager.IEntityClassifierService;
 
 /**
  * This is a default entity classifier that simply classifies all
  * entities into a fixed entity class, with key fields of MAC and VLAN.
  * @author readams
  */
-public class DefaultEntityClassifier implements IEntityClassifier {
+public class DefaultEntityClassifier implements
+        IEntityClassifierService,
+        IFloodlightModule 
+{
     /**
      * A default fixed entity class
      */
@@ -41,6 +50,11 @@ public class DefaultEntityClassifier implements IEntityClassifier {
         public EnumSet<IDeviceService.DeviceField> getKeyFields() {
             return keyFields;
         }
+
+        @Override
+        public String getName() {
+            return "DefaultEntityClass";
+        }
     }
     
     protected static EnumSet<DeviceField> keyFields;
@@ -49,20 +63,15 @@ public class DefaultEntityClassifier implements IEntityClassifier {
     }
     protected static IEntityClass entityClass = new DefaultEntityClass();
     
-    public static Collection<IEntityClass> entityClasses;
-    static {
-        entityClasses = Arrays.asList(entityClass);
-    }
-
     @Override
-    public Collection<IEntityClass> classifyEntity(Entity entity) {
-        return entityClasses;
+    public IEntityClass classifyEntity(Entity entity) {
+        return entityClass;
     }
 
     @Override
-    public Collection<IEntityClass> reclassifyEntity(IDevice curDevice,
+    public IEntityClass reclassifyEntity(IDevice curDevice,
                                                      Entity entity) {
-        return entityClasses;
+        return entityClass;
     }
 
     @Override
@@ -75,4 +84,41 @@ public class DefaultEntityClassifier implements IEntityClassifier {
     public EnumSet<DeviceField> getKeyFields() {
         return keyFields;
     }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+        Collection<Class<? extends IFloodlightService>> l = 
+                new ArrayList<Class<? extends IFloodlightService>>();
+        l.add(IEntityClassifierService.class);
+        return l;
+    }
+
+    @Override
+    public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+        Map<Class<? extends IFloodlightService>,
+        IFloodlightService> m = 
+        new HashMap<Class<? extends IFloodlightService>,
+                    IFloodlightService>();
+        // We are the class that implements the service
+        m.put(IEntityClassifierService.class, this);
+        return m;
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>>
+            getModuleDependencies() {
+        // No dependencies
+        return null;
+    }
+
+    @Override
+    public void init(FloodlightModuleContext context)
+                                                 throws FloodlightModuleException {
+        // no-op
+    }
+
+    @Override
+    public void startUp(FloodlightModuleContext context) {
+        // no-op
+    }
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
index 447be63f3..443c5b40c 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
@@ -46,7 +46,7 @@ public class Device implements IDevice {
     protected DeviceManagerImpl deviceManager;
 
     protected Entity[] entities;
-    protected IEntityClass[] entityClasses;
+    protected IEntityClass entityClass;
 
     protected String macAddressString;
 
@@ -59,19 +59,18 @@ public class Device implements IDevice {
      * @param deviceManager the device manager for this device
      * @param deviceKey the unique identifier for this device object
      * @param entity the initial entity for the device
-     * @param entityClasses the entity classes associated with the entity
+     * @param entityClass the entity classes associated with the entity
      */
     public Device(DeviceManagerImpl deviceManager,
                   Long deviceKey,
                   Entity entity,
-                  Collection<IEntityClass> entityClasses) {
+                  IEntityClass entityClass) {
         this.deviceManager = deviceManager;
         this.deviceKey = deviceKey;
         this.entities = new Entity[] {entity};
         this.macAddressString =
                 HexString.toHexString(entity.getMacAddress(), 6);
-        this.entityClasses =
-                entityClasses.toArray(new IEntityClass[entityClasses.size()]);
+        this.entityClass = entityClass;
         Arrays.sort(this.entities);
     }
 
@@ -80,18 +79,18 @@ public class Device implements IDevice {
      * @param deviceManager the device manager for this device
      * @param deviceKey the unique identifier for this device object
      * @param entities the initial entities for the device
-     * @param entityClasses the entity classes associated with the entity
+     * @param entityClass the entity class associated with the entities
      */
     public Device(DeviceManagerImpl deviceManager,
                   Long deviceKey,
                   Collection<Entity> entities,
-                  IEntityClass[] entityClasses) {
+                  IEntityClass entityClass) {
         this.deviceManager = deviceManager;
         this.deviceKey = deviceKey;
         this.entities = entities.toArray(new Entity[entities.size()]);
         this.macAddressString =
                 HexString.toHexString(this.entities[0].getMacAddress(), 6);
-        this.entityClasses = entityClasses;
+        this.entityClass = entityClass;
         Arrays.sort(this.entities);
     }
 
@@ -99,12 +98,11 @@ public class Device implements IDevice {
      * Construct a new device consisting of the entities from the old device
      * plus an additional entity
      * @param device the old device object
-     * @param newEntity the entity to add
-     * @param entityClasses the entity classes associated with the entities
+     * @param newEntity the entity to add. newEntity must be have the same
+     *        entity class as device
      */
     public Device(Device device,
-                  Entity newEntity,
-                  Collection<IEntityClass> entityClasses) {
+                  Entity newEntity) {
         this.deviceManager = device.deviceManager;
         this.deviceKey = device.deviceKey;
         this.entities = Arrays.<Entity>copyOf(device.entities,
@@ -115,15 +113,7 @@ public class Device implements IDevice {
         this.macAddressString =
                 HexString.toHexString(this.entities[0].getMacAddress(), 6);
 
-        if (entityClasses != null &&
-                entityClasses.size() > device.entityClasses.length) {
-            IEntityClass[] classes = new IEntityClass[entityClasses.size()];
-            this.entityClasses =
-                    entityClasses.toArray(classes);
-        } else {
-            // same actual array, not a copy
-            this.entityClasses = device.entityClasses;
-        }
+        this.entityClass = device.entityClass;
     }
 
     // *******
@@ -180,36 +170,34 @@ public class Device implements IDevice {
                 return new Integer[0];
             }
         }
-
+        
         TreeSet<Integer> vals = new TreeSet<Integer>();
         for (Entity e : entities) {
             if (e.getIpv4Address() == null) continue;
-
+            
             // We have an IP address only if among the devices within the class
             // we have the most recent entity with that IP.
             boolean validIP = true;
-            for (IEntityClass clazz : entityClasses) {
-                Iterator<Device> devices =
-                        deviceManager.queryClassByEntity(clazz, ipv4Fields, e);
-                while (devices.hasNext()) {
-                    Device d = devices.next();
-                    for (Entity se : d.entities) {
-                        if (se.ipv4Address != null &&
-                                se.ipv4Address.equals(e.ipv4Address) &&
-                                se.lastSeenTimestamp != null &&
-                                0 < se.lastSeenTimestamp.
-                                compareTo(e.lastSeenTimestamp)) {
-                            validIP = false;
-                            break;
-                        }
-                    }
-                    if (!validIP)
+            Iterator<Device> devices =
+                    deviceManager.queryClassByEntity(entityClass, ipv4Fields, e);
+            while (devices.hasNext()) {
+                Device d = devices.next();
+                for (Entity se : d.entities) {
+                    if (se.ipv4Address != null &&
+                            se.ipv4Address.equals(e.ipv4Address) &&
+                            se.lastSeenTimestamp != null &&
+                            0 < se.lastSeenTimestamp.
+                            compareTo(e.lastSeenTimestamp)) {
+                        validIP = false;
                         break;
+                    }
                 }
                 if (!validIP)
                     break;
             }
-
+            if (!validIP)
+                break;
+            
             if (validIP)
                 vals.add(e.getIpv4Address());
         }
@@ -379,8 +367,8 @@ public class Device implements IDevice {
     // Getters/Setters
     // ***************
 
-    public IEntityClass[] getEntityClasses() {
-        return entityClasses;
+    public IEntityClass getEntityClass() {
+        return entityClass;
     }
 
     public Entity[] getEntities() {
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java
index 795775c15..2cbea66e5 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java
@@ -67,37 +67,34 @@ public class DeviceIterator extends FilterIterator<Device> {
     protected boolean matches(Device value) {
         boolean match;
         if (entityClasses != null) {
-            IEntityClass[] classes = next.getEntityClasses();
-            if (classes == null) return false;
+            IEntityClass clazz = value.getEntityClass();
+            if (clazz == null) return false;
 
             match = false;
-            for (IEntityClass clazz : classes) {
-                for (IEntityClass entityClass : entityClasses) {
-                    if (clazz.equals(entityClass)) {
-                        match = true;
-                        break;
-                    }
+            for (IEntityClass entityClass : entityClasses) {
+                if (clazz.equals(entityClass)) {
+                    match = true;
+                    break;
                 }
-                if (match == true) break;
             }
             if (!match) return false;                
         }
         if (macAddress != null) {
-            if (macAddress.longValue() != next.getMACAddress())
+            if (macAddress.longValue() != value.getMACAddress())
                 return false;
         }
         if (vlan != null) {
-            Short[] vlans = next.getVlanId();
+            Short[] vlans = value.getVlanId();
             if (Arrays.binarySearch(vlans, vlan) < 0) 
                 return false;
         }
         if (ipv4Address != null) {
-            Integer[] ipv4Addresses = next.getIPv4Addresses();
+            Integer[] ipv4Addresses = value.getIPv4Addresses();
             if (Arrays.binarySearch(ipv4Addresses, ipv4Address) < 0) 
                 return false;
         }
         if (switchDPID != null || switchPort != null) {
-            SwitchPort[] sps = next.getAttachmentPoints();
+            SwitchPort[] sps = value.getAttachmentPoints();
             if (sps == null) return false;
             
             match = false;
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
index ab1d8788d..06000e2ea 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
@@ -18,7 +18,6 @@
 package net.floodlightcontroller.devicemanager.internal;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
@@ -50,7 +49,7 @@ import net.floodlightcontroller.core.util.SingletonTask;
 import net.floodlightcontroller.devicemanager.IDevice;
 import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.devicemanager.IEntityClass;
-import net.floodlightcontroller.devicemanager.IEntityClassifier;
+import net.floodlightcontroller.devicemanager.IEntityClassifierService;
 import net.floodlightcontroller.devicemanager.IDeviceListener;
 import net.floodlightcontroller.devicemanager.SwitchPort;
 import net.floodlightcontroller.devicemanager.web.DeviceRoutable;
@@ -157,7 +156,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
     /**
      * The entity classifier currently in use
      */
-    IEntityClassifier entityClassifier;
+    IEntityClassifierService entityClassifier;
 
     /**
      * Used to cache state about specific entity classes
@@ -406,11 +405,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                                                          Integer ipv4Address,
                                                          Long switchDPID,
                                                          Integer switchPort) {
-        IEntityClass[] entityClasses = reference.getEntityClasses();
+        IEntityClass entityClass = reference.getEntityClass();
         ArrayList<Iterator<Device>> iterators =
                 new ArrayList<Iterator<Device>>();
-        for (IEntityClass clazz : entityClasses) {
-            ClassState classState = getClassState(clazz);
+            ClassState classState = getClassState(entityClass);
 
             DeviceIndex index = null;
             if (classState.secondaryIndexMap.size() > 0) {
@@ -426,7 +424,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                 if (index == null) {
                     // scan all devices
                     return new DeviceIterator(deviceMap.values().iterator(),
-                                              entityClasses,
+                                              new IEntityClass[] { entityClass },
                                               macAddress, vlan, ipv4Address,
                                               switchDPID, switchPort);
                 } else {
@@ -446,7 +444,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                                                 index.queryByEntity(entity));
             }
             iterators.add(iter);
-        }
 
         return new MultiIterator<Device>(iterators.iterator());
     }
@@ -456,11 +453,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         deviceListeners.add(listener);
     }
 
-    @Override
-    public void setEntityClassifier(IEntityClassifier classifier) {
-        entityClassifier = classifier;
-    }
-
     @Override
     public void flushEntityCache(IEntityClass entityClass,
                                  boolean reclassify) {
@@ -622,13 +614,11 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         this.restApi = fmc.getServiceImpl(IRestApiService.class);
         this.threadPool = fmc.getServiceImpl(IThreadPoolService.class);
         this.flowReconcileMgr = fmc.getServiceImpl(IFlowReconcileService.class);
+        this.entityClassifier = fmc.getServiceImpl(IEntityClassifierService.class);
     }
 
     @Override
     public void startUp(FloodlightModuleContext fmc) {
-        if (entityClassifier == null)
-            setEntityClassifier(new DefaultEntityClassifier());
-
         primaryIndex = new DeviceUniqueIndex(entityClassifier.getKeyFields());
         secondaryIndexMap = new HashMap<EnumSet<DeviceField>, DeviceIndex>();
 
@@ -967,21 +957,19 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
             // Look up the fully-qualified entity to see if it already
             // exists in the primary entity index.
             Long deviceKey = primaryIndex.findByEntity(entity);
-            Collection<IEntityClass> classes = null;
+            IEntityClass entityClass = null;
 
             if (deviceKey == null) {
                 // If the entity does not exist in the primary entity index,
                 // use the entity classifier for find the classes for the
-                // entity. Look up the entity in each of the returned classes'
-                // class entity indexes.
-                classes = entityClassifier.classifyEntity(entity);
-                for (IEntityClass clazz : classes) {
-                    ClassState classState = getClassState(clazz);
-
-                    if (classState.classIndex != null) {
-                        deviceKey =
-                                classState.classIndex.findByEntity(entity);
-                    }
+                // entity. Look up the entity in the returned class'
+                // class entity index.
+                entityClass = entityClassifier.classifyEntity(entity);
+                ClassState classState = getClassState(entityClass);
+
+                if (classState.classIndex != null) {
+                    deviceKey =
+                            classState.classIndex.findByEntity(entity);
                 }
             }
             if (deviceKey != null) {
@@ -998,7 +986,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                 synchronized (deviceKeyLock) {
                     deviceKey = Long.valueOf(deviceKeyCounter++);
                 }
-                device = allocateDevice(deviceKey, entity, classes);
+                device = allocateDevice(deviceKey, entity, entityClass);
 
                 // Add the new device to the primary map with a simple put
                 deviceMap.put(deviceKey, device);
@@ -1011,7 +999,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                     continue;
                 }
 
-                updateSecondaryIndices(entity, classes, deviceKey);
+                updateSecondaryIndices(entity, entityClass, deviceKey);
 
                 // generate new device update
                 deviceUpdates =
@@ -1029,7 +1017,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                 device.entities[entityindex].setLastSeenTimestamp(lastSeen);
                 break;
             } else {
-                Device newDevice = allocateDevice(device, entity, classes);
+                Device newDevice = allocateDevice(device, entity);
 
                 // generate updates
                 EnumSet<DeviceField> changedFields =
@@ -1055,7 +1043,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                     continue;
                 }
                 updateSecondaryIndices(entity,
-                                       device.getEntityClasses(),
+                                       device.getEntityClass(),
                                        deviceKey);
                 break;
             }
@@ -1178,7 +1166,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
 
     /**
      * Update both the primary and class indices for the provided device.
-     * If the update fails because of aEn concurrent update, will return false.
+     * If the update fails because of an concurrent update, will return false.
      * @param device the device to update
      * @param deviceKey the device key for the device
      * @return true if the update succeeded, false otherwise.
@@ -1187,52 +1175,36 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         if (!primaryIndex.updateIndex(device, deviceKey)) {
             return false;
         }
-        for (IEntityClass clazz : device.getEntityClasses()) {
-            ClassState classState = getClassState(clazz);
+        IEntityClass entityClass = device.getEntityClass();
+        ClassState classState = getClassState(entityClass);
 
-            if (classState.classIndex != null) {
-                if (!classState.classIndex.updateIndex(device,
-                                                       deviceKey))
-                    return false;
-            }
+        if (classState.classIndex != null) {
+            if (!classState.classIndex.updateIndex(device,
+                                                   deviceKey))
+                return false;
         }
-        return true;
+    return true;
     }
 
     /**
      * Update the secondary indices for the given entity and associated
      * entity classes
      * @param entity the entity to update
-     * @param entityClasses the entity classes for the entity
+     * @param entityClass the entity class for the entity
      * @param deviceKey the device key to set up
      */
     private void updateSecondaryIndices(Entity entity,
-                                        Collection<IEntityClass> entityClasses,
+                                        IEntityClass entityClass,
                                         Long deviceKey) {
         for (DeviceIndex index : secondaryIndexMap.values()) {
             index.updateIndex(entity, deviceKey);
         }
-        for (IEntityClass clazz : entityClasses) {
-            ClassState state = getClassState(clazz);
-            for (DeviceIndex index : state.secondaryIndexMap.values()) {
-                index.updateIndex(entity, deviceKey);
-            }
+        ClassState state = getClassState(entityClass);
+        for (DeviceIndex index : state.secondaryIndexMap.values()) {
+            index.updateIndex(entity, deviceKey);
         }
     }
 
-    /**
-     * Update the secondary indices for the given entity and associated
-     * entity classes
-     * @param entity the entity to update
-     * @param entityClasses the entity classes for the entity
-     * @param deviceKey the device key to set up
-     */
-    private void updateSecondaryIndices(Entity entity,
-                                        IEntityClass[] entityClasses,
-                                        Long deviceKey) {
-        updateSecondaryIndices(entity, Arrays.asList(entityClasses), deviceKey);
-    }
-
     /**
      * Clean up expired entities/devices
      */
@@ -1269,13 +1241,13 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                 }
 
                 for (Entity e : toRemove) {
-                    removeEntity(e, d.getEntityClasses(), d.deviceKey, toKeep);
+                    removeEntity(e, d.getEntityClass(), d.deviceKey, toKeep);
                 }
 
                 if (toKeep.size() > 0) {
                     Device newDevice = allocateDevice(d.getDeviceKey(),
                                                       toKeep,
-                                                      d.entityClasses);
+                                                      d.entityClass);
 
                     EnumSet<DeviceField> changedFields =
                             EnumSet.noneOf(DeviceField.class);
@@ -1305,29 +1277,23 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
     }
 
     private void removeEntity(Entity removed,
-                              IEntityClass[] classes,
+                              IEntityClass entityClass,
                               Long deviceKey,
                               Collection<Entity> others) {
         for (DeviceIndex index : secondaryIndexMap.values()) {
             index.removeEntityIfNeeded(removed, deviceKey, others);
         }
-        for (IEntityClass clazz : classes) {
-            ClassState classState = getClassState(clazz);
-            for (DeviceIndex index : classState.secondaryIndexMap.values()) {
-                index.removeEntityIfNeeded(removed, deviceKey, others);
-            }
+        ClassState classState = getClassState(entityClass);
+        for (DeviceIndex index : classState.secondaryIndexMap.values()) {
+            index.removeEntityIfNeeded(removed, deviceKey, others);
         }
 
         primaryIndex.removeEntityIfNeeded(removed, deviceKey, others);
 
-        for (IEntityClass clazz : classes) {
-            ClassState classState = getClassState(clazz);
-
-            if (classState.classIndex != null) {
-                classState.classIndex.removeEntityIfNeeded(removed,
-                                                           deviceKey,
-                                                           others);
-            }
+        if (classState.classIndex != null) {
+            classState.classIndex.removeEntityIfNeeded(removed,
+                                                       deviceKey,
+                                                       others);
         }
     }
 
@@ -1357,20 +1323,19 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
 
     protected Device allocateDevice(Long deviceKey,
                                     Entity entity,
-                                    Collection<IEntityClass> entityClasses) {
-        return new Device(this, deviceKey, entity, entityClasses);
+                                    IEntityClass entityClass) {
+        return new Device(this, deviceKey, entity, entityClass);
     }
 
     protected Device allocateDevice(Long deviceKey,
                                     Collection<Entity> entities,
-                                    IEntityClass[] entityClasses) {
-        return new Device(this, deviceKey, entities, entityClasses);
+                                    IEntityClass entityClass) {
+        return new Device(this, deviceKey, entities, entityClass);
     }
 
     protected Device allocateDevice(Device device,
-                                    Entity entity,
-                                    Collection<IEntityClass> entityClasses) {
-        return new Device(device, entity, entityClasses);
+                                    Entity entity) {
+        return new Device(device, entity);
     }
 
     @Override
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java
index fa9bcc793..66bdaef93 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java
@@ -40,6 +40,8 @@ public class DeviceSerializer extends JsonSerializer<Device> {
             JsonProcessingException {
         jGen.writeStartObject();
         
+        jGen.writeStringField("entityClass", device.getEntityClass().getName());
+        
         jGen.writeArrayFieldStart("mac");
         jGen.writeString(HexString.toHexString(device.getMACAddress(), 6));
         jGen.writeEndArray();
@@ -50,9 +52,9 @@ public class DeviceSerializer extends JsonSerializer<Device> {
         jGen.writeEndArray();
 
         jGen.writeArrayFieldStart("vlan");
-        for (Short ip : device.getVlanId())
-            if (ip >= 0)
-                jGen.writeNumber(ip);
+        for (Short vlan : device.getVlanId())
+            if (vlan >= 0)
+                jGen.writeNumber(vlan);
         jGen.writeEndArray();
         jGen.writeArrayFieldStart("attachmentPoint");
         for (SwitchPort ap : device.getAttachmentPoints(true)) {
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
index 25ea828b0..82eb8c925 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
@@ -17,9 +17,7 @@
 
 package net.floodlightcontroller.devicemanager.internal;
 
-import java.util.Arrays;
 import java.util.Calendar;
-import java.util.Collection;
 import java.util.Date;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -46,6 +44,7 @@ import net.floodlightcontroller.devicemanager.IDeviceListener;
 import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField;
 import net.floodlightcontroller.devicemanager.IDevice;
 import net.floodlightcontroller.devicemanager.IEntityClass;
+import net.floodlightcontroller.devicemanager.IEntityClassifierService;
 import net.floodlightcontroller.devicemanager.SwitchPort;
 import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.flowcache.FlowReconcileManager;
@@ -108,21 +107,25 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         mockFloodlightProvider = getMockFloodlightProvider();
         deviceManager = new DeviceManagerImpl();
         flowReconcileMgr = new FlowReconcileManager();
+        DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier();
         fmc.addService(IDeviceService.class, deviceManager);
         storageSource = new MemoryStorageSource();
         fmc.addService(IStorageSourceService.class, storageSource);
         fmc.addService(IFloodlightProviderService.class, mockFloodlightProvider);
         fmc.addService(IRestApiService.class, restApi);
         fmc.addService(IFlowReconcileService.class, flowReconcileMgr);
+        fmc.addService(IEntityClassifierService.class, entityClassifier);
         tp.init(fmc);
         restApi.init(fmc);
         storageSource.init(fmc);
         deviceManager.init(fmc);
         flowReconcileMgr.init(fmc);
+        entityClassifier.init(fmc);
         storageSource.startUp(fmc);
         deviceManager.startUp(fmc);
         flowReconcileMgr.startUp(fmc);
         tp.startUp(fmc);
+        entityClassifier.startUp(fmc);
 
         IOFSwitch mockSwitch1 = makeSwitchMock(1L);
         IOFSwitch mockSwitch10 = makeSwitchMock(10L);
@@ -229,6 +232,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         public EnumSet<DeviceField> getKeyFields() {
             return testKeyFields;
         }
+
+        @Override
+        public String getName() {
+            return "TestEntityClass";
+        }
     }
 
     protected static IEntityClass testEC = new TestEntityClass();
@@ -236,11 +244,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
     public static class TestEntityClassifier extends DefaultEntityClassifier {
 
         @Override
-        public Collection<IEntityClass> classifyEntity(Entity entity) {
+        public IEntityClass classifyEntity(Entity entity) {
             if (entity.switchDPID >= 10L) {
-                return Arrays.asList(testEC);
+                return testEC;
             }
-            return DefaultEntityClassifier.entityClasses;
+            return DefaultEntityClassifier.entityClass;
         }
 
         @Override
@@ -276,7 +284,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                 createStrictMock(IDeviceListener.class);
 
         deviceManager.addListener(mockListener);
-        deviceManager.setEntityClassifier(new TestEntityClassifier());
+        deviceManager.entityClassifier= new TestEntityClassifier();
         deviceManager.startUp(null);
 
         ITopologyService mockTopology = createMock(ITopologyService.class);
@@ -314,8 +322,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         Device d1 = deviceManager.learnDeviceByEntity(entity1);
         assertSame(d1, deviceManager.learnDeviceByEntity(entity1));
         assertSame(d1, deviceManager.findDeviceByEntity(entity1));
-        assertArrayEquals(new IEntityClass[]{ DefaultEntityClassifier.entityClass },
-                          d1.entityClasses);
+        assertEquals(DefaultEntityClassifier.entityClass ,
+                          d1.entityClass);
         assertArrayEquals(new Short[] { -1 }, d1.getVlanId());
         assertArrayEquals(new Integer[] { }, d1.getIPv4Addresses());
 
@@ -329,8 +337,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         Device d2 = deviceManager.learnDeviceByEntity(entity2);
         assertFalse(d1.equals(d2));
         assertNotSame(d1, d2);
-        assertArrayEquals(new IEntityClass[]{ testEC },
-                          d2.entityClasses);
+        assertEquals(testEC, d2.entityClass);
         assertArrayEquals(new Short[] { -1 }, d2.getVlanId());
         assertArrayEquals(new Integer[] { }, d2.getIPv4Addresses());
 
@@ -343,8 +350,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
         Device d3 = deviceManager.learnDeviceByEntity(entity3);
         assertNotSame(d2, d3);
-        assertArrayEquals(new IEntityClass[]{ testEC },
-                          d3.entityClasses);
+        assertEquals(testEC, d3.entityClass);
         assertArrayEquals(new Integer[] { 1 },
                           d3.getIPv4Addresses());
         assertArrayEquals(new SwitchPort[] { new SwitchPort(10L, 1) },
@@ -363,8 +369,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
         Device d4 = deviceManager.learnDeviceByEntity(entity4);
         assertNotSame(d1, d4);
-        assertArrayEquals(new IEntityClass[]{ DefaultEntityClassifier.entityClass },
-                          d4.entityClasses);
+        assertEquals(DefaultEntityClassifier.entityClass, d4.entityClass);
         assertArrayEquals(new Integer[] { 1 },
                           d4.getIPv4Addresses());
         assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, 1) },
@@ -699,7 +704,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                                       1L,
                                       1,
                                       currentDate),
-                                      DefaultEntityClassifier.entityClasses);
+                                      DefaultEntityClassifier.entityClass);
 
         expect(mockTopology.isAllowed(EasyMock.anyLong(),
                                       EasyMock.anyShort())).
@@ -738,8 +743,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                                       ipaddr,
                                       5L,
                                       2,
-                                      currentDate),
-                                      DefaultEntityClassifier.entityClasses);
+                                      currentDate));
 
         reset(mockTopology);
         expect(mockTopology.isAttachmentPointPort(anyLong(),
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java
index 47c39d176..6e91f314f 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java
@@ -36,19 +36,18 @@ public class MockDevice extends Device {
     public MockDevice(DeviceManagerImpl deviceManager,
                       Long deviceKey,
                       Entity entity, 
-                      Collection<IEntityClass> entityClasses)  {
-        super(deviceManager, deviceKey, entity, entityClasses);
+                      IEntityClass entityClass)  {
+        super(deviceManager, deviceKey, entity, entityClass);
     }
 
-    public MockDevice(Device device, Entity newEntity,
-                      Collection<IEntityClass> entityClasses) {
-        super(device, newEntity, entityClasses);
+    public MockDevice(Device device, Entity newEntity) {
+        super(device, newEntity);
     }
     
     public MockDevice(DeviceManagerImpl deviceManager, Long deviceKey,
                       Collection<Entity> entities,
-                      IEntityClass[] entityClasses) {
-        super(deviceManager, deviceKey, entities, entityClasses);
+                      IEntityClass entityClass) {
+        super(deviceManager, deviceKey, entities, entityClass);
     }
 
     @Override
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java
index 2502f7c04..24379b3d8 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java
@@ -66,21 +66,20 @@ public class MockDeviceManager extends DeviceManagerImpl {
     @Override
     protected Device allocateDevice(Long deviceKey,
                                     Entity entity, 
-                                    Collection<IEntityClass> entityClasses) {
-        return new MockDevice(this, deviceKey, entity, entityClasses);
+                                    IEntityClass entityClass) {
+        return new MockDevice(this, deviceKey, entity, entityClass);
     }
     
     @Override
     protected Device allocateDevice(Long deviceKey,
                                     Collection<Entity> entities, 
-                                    IEntityClass[] entityClasses) {
-        return new MockDevice(this, deviceKey, entities, entityClasses);
+                                    IEntityClass entityClass) {
+        return new MockDevice(this, deviceKey, entities, entityClass);
     }
     
     @Override
     protected Device allocateDevice(Device device,
-                                    Entity entity, 
-                                    Collection<IEntityClass> entityClasses) {
-        return new MockDevice(device, entity, entityClasses);
+                                    Entity entity) {
+        return new MockDevice(device, entity);
     }
 }
diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
index 96f1a2bc3..0d850a789 100644
--- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
+++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
@@ -38,11 +38,13 @@ import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.test.MockFloodlightProvider;
 import net.floodlightcontroller.core.test.MockThreadPoolService;
+import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier;
 import net.floodlightcontroller.devicemanager.test.MockDeviceManager;
 import net.floodlightcontroller.counter.CounterStore;
 import net.floodlightcontroller.counter.ICounterStoreService;
 import net.floodlightcontroller.devicemanager.IDevice;
 import net.floodlightcontroller.devicemanager.IDeviceService;
+import net.floodlightcontroller.devicemanager.IEntityClassifierService;
 import net.floodlightcontroller.packet.Data;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPacket;
@@ -103,6 +105,7 @@ public class ForwardingTest extends FloodlightTestCase {
         flowReconcileMgr = new FlowReconcileManager();
         routingEngine = createMock(IRoutingService.class);
         topology = createMock(ITopologyService.class);
+        DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier();
         
 
         FloodlightModuleContext fmc = new FloodlightModuleContext();
@@ -114,15 +117,18 @@ public class ForwardingTest extends FloodlightTestCase {
         fmc.addService(ICounterStoreService.class, new CounterStore());
         fmc.addService(IDeviceService.class, deviceManager);
         fmc.addService(IFlowReconcileService.class, flowReconcileMgr);
+        fmc.addService(IEntityClassifierService.class, entityClassifier);
 
         threadPool.init(fmc);
         forwarding.init(fmc);
         deviceManager.init(fmc);
         flowReconcileMgr.init(fmc);
+        entityClassifier.init(fmc);
         threadPool.startUp(fmc);
         deviceManager.startUp(fmc);
         forwarding.startUp(fmc);
         flowReconcileMgr.startUp(fmc);
+        entityClassifier.startUp(fmc);
         
         // Mock switches
         sw1 = EasyMock.createNiceMock(IOFSwitch.class);
diff --git a/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java b/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java
index bcf0ac2be..824fe0337 100644
--- a/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java
+++ b/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java
@@ -22,6 +22,8 @@ import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.test.MockThreadPoolService;
 import net.floodlightcontroller.core.test.PacketFactory;
 import net.floodlightcontroller.devicemanager.IDeviceService;
+import net.floodlightcontroller.devicemanager.IEntityClassifierService;
+import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier;
 import net.floodlightcontroller.devicemanager.test.MockDeviceManager;
 import net.floodlightcontroller.flowcache.FlowReconcileManager;
 import net.floodlightcontroller.flowcache.IFlowReconcileService;
@@ -88,16 +90,19 @@ public class VirtualNetworkFilterTest extends FloodlightTestCase {
         FlowReconcileManager frm = new FlowReconcileManager();
         MockThreadPoolService tps = new MockThreadPoolService();
         vns = new VirtualNetworkFilter();
+        DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier();
         fmc.addService(IRestApiService.class, restApi);
         fmc.addService(IFloodlightProviderService.class, getMockFloodlightProvider());
         fmc.addService(IDeviceService.class, deviceService);
         fmc.addService(IFlowReconcileService.class, frm);
         fmc.addService(IThreadPoolService.class, tps);
+        fmc.addService(IEntityClassifierService.class, entityClassifier);
         tps.init(fmc);
         frm.init(fmc);
         deviceService.init(fmc);
         restApi.init(fmc);
         getMockFloodlightProvider().init(fmc);
+        entityClassifier.init(fmc);
         tps.startUp(fmc);
         vns.init(fmc);
         frm.startUp(fmc);
@@ -105,6 +110,7 @@ public class VirtualNetworkFilterTest extends FloodlightTestCase {
         restApi.startUp(fmc);
         getMockFloodlightProvider().startUp(fmc);
         vns.startUp(fmc);
+        entityClassifier.startUp(fmc);
         
         // Mock switches
         //fastWilcards mocked as this constant
-- 
GitLab