Adding port, port number, port description implementations and related tests.
diff --git a/net/api/src/main/java/org/onlab/onos/net/DeviceId.java b/net/api/src/main/java/org/onlab/onos/net/DeviceId.java
index 6f4cff2..19577a1 100644
--- a/net/api/src/main/java/org/onlab/onos/net/DeviceId.java
+++ b/net/api/src/main/java/org/onlab/onos/net/DeviceId.java
@@ -5,16 +5,29 @@
 /**
  * Immutable representation of a device identity.
  */
-public class DeviceId extends ElementId {
+public final class DeviceId extends ElementId {
 
-    // TODO: Discuss whether we should just use ElementId for Device and Host alike
+    // Public construction is prohibited
+    private DeviceId(URI uri) {
+        super(uri);
+    }
+
     /**
      * Creates a device id using the supplied URI.
      *
-     * @param uri backing device URI
+     * @param uri device URI
      */
-    public DeviceId(URI uri) {
-        super(uri);
+    public static DeviceId deviceId(URI uri) {
+        return new DeviceId(uri);
+    }
+
+    /**
+     * Creates a device id using the supplied URI string.
+     *
+     * @param string device URI string
+     */
+    public static DeviceId deviceId(String string) {
+        return new DeviceId(URI.create(string));
     }
 
 }
diff --git a/net/api/src/main/java/org/onlab/onos/net/ElementId.java b/net/api/src/main/java/org/onlab/onos/net/ElementId.java
index e0f3add..9f7dffb 100644
--- a/net/api/src/main/java/org/onlab/onos/net/ElementId.java
+++ b/net/api/src/main/java/org/onlab/onos/net/ElementId.java
@@ -8,7 +8,7 @@
 /**
  * Immutable representation of a network element identity.
  */
-public class ElementId {
+public abstract class ElementId {
 
     private final URI uri;
 
@@ -17,7 +17,7 @@
      *
      * @param uri backing URI
      */
-    public ElementId(URI uri) {
+    protected ElementId(URI uri) {
         this.uri = uri;
     }
 
@@ -37,14 +37,12 @@
 
     @Override
     public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
+        if (obj instanceof ElementId) {
+            final ElementId that = (ElementId) obj;
+            return this.getClass() == that.getClass() &&
+                    Objects.equals(this.uri, that.uri);
         }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-        final ElementId other = (ElementId) obj;
-        return Objects.equals(this.uri, other.uri);
+        return false;
     }
 
     @Override
diff --git a/net/api/src/main/java/org/onlab/onos/net/Port.java b/net/api/src/main/java/org/onlab/onos/net/Port.java
index 3135b77..24fd533 100644
--- a/net/api/src/main/java/org/onlab/onos/net/Port.java
+++ b/net/api/src/main/java/org/onlab/onos/net/Port.java
@@ -1,11 +1,18 @@
 package org.onlab.onos.net;
 
+import java.util.Set;
+
 /**
  * Abstraction of a network port.
  */
 public interface Port {
 
-    // Notion of port state: enabled, disabled, blocked
+    /**
+     * Port state.
+     */
+    enum State {
+        UP, DOWN, BLOCKED, UNKNOWN
+    }
 
     /**
      * Returns the port number.
@@ -15,6 +22,27 @@
     PortNumber number();
 
     /**
+     * Returns the port state(s).
+     *
+     * @return port state set
+     */
+    Set<State> state();
+
+    /**
+     * Indicates whether or not the port is currently up and active.
+     *
+     * @return true if the port is operational
+     */
+    boolean isEnabled();
+
+    /**
+     * Indicates whether or not the port is administratively blocked.
+     *
+     * @return true if the port is blocked
+     */
+    boolean isBlocked();
+
+    /**
      * Returns the identifier of the network element to which this port belongs.
      *
      * @return parent network element
diff --git a/net/api/src/main/java/org/onlab/onos/net/PortNumber.java b/net/api/src/main/java/org/onlab/onos/net/PortNumber.java
index 4ea4e24..45ee9ee 100644
--- a/net/api/src/main/java/org/onlab/onos/net/PortNumber.java
+++ b/net/api/src/main/java/org/onlab/onos/net/PortNumber.java
@@ -1,7 +1,73 @@
 package org.onlab.onos.net;
 
+import com.google.common.primitives.UnsignedLongs;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
 /**
  * Representation of a port number.
  */
-public interface PortNumber {
+public final class PortNumber {
+
+    private static final long MAX_NUMBER = (2L * Integer.MAX_VALUE) + 1;
+
+    private final long number;
+
+    // Public creation is prohibited
+    private PortNumber(long number) {
+        checkArgument(number >= 0 && number < MAX_NUMBER,
+                      "Port number %d is outside the supported range [0, %d)",
+                      number, MAX_NUMBER);
+        this.number = number;
+    }
+
+    /**
+     * Returns the port number representing the specified long value.
+     *
+     * @param number port number as long value
+     * @return port number
+     */
+    public static PortNumber portNumber(long number) {
+        return new PortNumber(number);
+    }
+
+    /**
+     * Returns the port number representing the specified string value.
+     *
+     * @param string port number as string value
+     * @return port number
+     */
+    public static PortNumber portNumber(String string) {
+        return new PortNumber(UnsignedLongs.decode(string));
+    }
+
+    /**
+     * Returns the backing long value.
+     *
+     * @return port number as long
+     */
+    public long toLong() {
+        return number;
+    }
+
+    @Override
+    public String toString() {
+        return UnsignedLongs.toString(number);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(number);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof PortNumber) {
+            final PortNumber other = (PortNumber) obj;
+            return this.number == other.number;
+        }
+        return false;
+    }
 }
diff --git a/net/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java b/net/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java
index c171098..c434ff5 100644
--- a/net/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java
+++ b/net/api/src/main/java/org/onlab/onos/net/device/DefaultPortDescription.java
@@ -1,7 +1,32 @@
 package org.onlab.onos.net.device;
 
+import com.google.common.collect.ImmutableSet;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+
+import java.util.Set;
+
 /**
  * Default implementation of immutable port description.
  */
 public class DefaultPortDescription implements PortDescription {
+
+    private final PortNumber number;
+    private final Set<Port.State> state;
+
+    public DefaultPortDescription(PortNumber number, Set<Port.State> state) {
+        this.number = number;
+        this.state = ImmutableSet.copyOf(state);
+    }
+
+    @Override
+    public PortNumber portNumber() {
+        return number;
+    }
+
+    @Override
+    public Set<Port.State> portState() {
+        return state;
+    }
+
 }
diff --git a/net/api/src/main/java/org/onlab/onos/net/device/PortDescription.java b/net/api/src/main/java/org/onlab/onos/net/device/PortDescription.java
index 4c944ab..2f86248 100644
--- a/net/api/src/main/java/org/onlab/onos/net/device/PortDescription.java
+++ b/net/api/src/main/java/org/onlab/onos/net/device/PortDescription.java
@@ -1,5 +1,10 @@
 package org.onlab.onos.net.device;
 
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+
+import java.util.Set;
+
 /**
  * Information about a port.
  */
@@ -7,4 +12,18 @@
 
     // TODO: possibly relocate this to a common ground so that this can also used by host tracking if required
 
+    /**
+     * Returns the port number.
+     *
+     * @return port number
+     */
+    PortNumber portNumber();
+
+    /**
+     * Returns the port state set.
+     *
+     * @return set of port states
+     */
+    Set<Port.State> portState();
+
 }
diff --git a/net/api/src/test/java/org/onlab/onos/event/AbstractEventTest.java b/net/api/src/test/java/org/onlab/onos/event/AbstractEventTest.java
index 5ec3669..b79b836 100644
--- a/net/api/src/test/java/org/onlab/onos/event/AbstractEventTest.java
+++ b/net/api/src/test/java/org/onlab/onos/event/AbstractEventTest.java
@@ -11,12 +11,46 @@
  */
 public class AbstractEventTest {
 
+    /**
+     * Validates the base attributes of an event.
+     *
+     * @param event   event to validate
+     * @param type    event type
+     * @param subject event subject
+     * @param time    event time
+     * @param <T>     type of event
+     * @param <S>     type of subject
+     */
+    protected static <T extends Enum, S>
+    void validateEvent(Event<T, S> event, T type, S subject, long time) {
+        assertEquals("incorrect type", type, event.type());
+        assertEquals("incorrect subject", subject, event.subject());
+        assertEquals("incorrect time", time, event.time());
+    }
+
+    /**
+     * Validates the base attributes of an event.
+     *
+     * @param event   event to validate
+     * @param type    event type
+     * @param subject event subject
+     * @param minTime minimum event time inclusive
+     * @param maxTime maximum event time inclusive
+     * @param <T>     type of event
+     * @param <S>     type of subject
+     */
+    protected static <T extends Enum, S>
+    void validateEvent(Event<T, S> event, T type, S subject,
+                       long minTime, long maxTime) {
+        assertEquals("incorrect type", type, event.type());
+        assertEquals("incorrect subject", subject, event.subject());
+        assertTrue("incorrect time", minTime <= event.time() && event.time() <= maxTime);
+    }
+
     @Test
     public void withTime() {
         TestEvent event = new TestEvent(FOO, "foo", 123L);
-        assertEquals("incorrect type", FOO, event.type());
-        assertEquals("incorrect subject", "foo", event.subject());
-        assertEquals("incorrect time", 123L, event.time());
+        validateEvent(event, FOO, "foo", 123L);
     }
 
     @Test
@@ -24,8 +58,7 @@
         long before = System.currentTimeMillis();
         TestEvent event = new TestEvent(FOO, "foo");
         long after = System.currentTimeMillis();
-        assertEquals("incorrect type", FOO, event.type());
-        assertEquals("incorrect subject", "foo", event.subject());
-        assertTrue("incorrect time", before <= event.time() && event.time() <= after);
+        validateEvent(event, FOO, "foo", before, after);
     }
+
 }
diff --git a/net/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java b/net/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java
index 1d174ca..c37a15c 100644
--- a/net/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java
+++ b/net/api/src/test/java/org/onlab/onos/net/DefaultDeviceTest.java
@@ -4,10 +4,9 @@
 import org.junit.Test;
 import org.onlab.onos.net.provider.ProviderId;
 
-import java.net.URI;
-
 import static org.junit.Assert.assertEquals;
 import static org.onlab.onos.net.Device.Type.SWITCH;
+import static org.onlab.onos.net.DeviceId.deviceId;
 
 /**
  * Test of the default device model entity.
@@ -15,8 +14,8 @@
 public class DefaultDeviceTest {
 
     private static final ProviderId PID = new ProviderId("foo");
-    private static final DeviceId DID1 = new DeviceId(URI.create("of:foo"));
-    private static final DeviceId DID2 = new DeviceId(URI.create("of:bar"));
+    private static final DeviceId DID1 = deviceId("of:foo");
+    private static final DeviceId DID2 = deviceId("of:bar");
     private static final String MFR = "whitebox";
     private static final String HW = "1.1.x";
     private static final String SW = "3.9.1";
diff --git a/net/api/src/test/java/org/onlab/onos/net/DeviceIdTest.java b/net/api/src/test/java/org/onlab/onos/net/DeviceIdTest.java
index f373678..eaee54c 100644
--- a/net/api/src/test/java/org/onlab/onos/net/DeviceIdTest.java
+++ b/net/api/src/test/java/org/onlab/onos/net/DeviceIdTest.java
@@ -3,17 +3,19 @@
 import com.google.common.testing.EqualsTester;
 import org.junit.Test;
 
+import static org.onlab.onos.net.DeviceId.deviceId;
+
 /**
- * Test of the provider identifier.
+ * Test of the device identifier.
  */
 public class DeviceIdTest extends ElementIdTest {
 
     @Test
     public void basics() {
         new EqualsTester()
-                .addEqualityGroup(new DeviceId(uri("of:foo")),
-                                  new DeviceId(uri("of:foo")))
-                .addEqualityGroup(new DeviceId(uri("of:bar")))
+                .addEqualityGroup(deviceId("of:foo"),
+                                  deviceId("of:foo"))
+                .addEqualityGroup(deviceId("of:bar"))
                 .testEquals();
     }
 
diff --git a/net/api/src/test/java/org/onlab/onos/net/ElementIdTest.java b/net/api/src/test/java/org/onlab/onos/net/ElementIdTest.java
index 4de68dd..cf209b3 100644
--- a/net/api/src/test/java/org/onlab/onos/net/ElementIdTest.java
+++ b/net/api/src/test/java/org/onlab/onos/net/ElementIdTest.java
@@ -8,10 +8,16 @@
 import static org.junit.Assert.assertEquals;
 
 /**
- * Test of the provider identifier.
+ * Test of the network element identifier.
  */
 public class ElementIdTest {
 
+    private static class FooId extends ElementId {
+        public FooId(URI uri) {
+            super(uri);
+        }
+    }
+
     public static URI uri(String str) {
         return URI.create(str);
     }
@@ -19,12 +25,12 @@
     @Test
     public void basics() {
         new EqualsTester()
-                .addEqualityGroup(new ElementId(uri("of:foo")),
-                                  new ElementId(uri("of:foo")))
-                .addEqualityGroup(new ElementId(uri("of:bar")))
+                .addEqualityGroup(new FooId(uri("of:foo")),
+                                  new FooId(uri("of:foo")))
+                .addEqualityGroup(new FooId(uri("of:bar")))
                 .testEquals();
         assertEquals("wrong uri", uri("ofcfg:foo"),
-                     new ElementId(uri("ofcfg:foo")).uri());
+                     new FooId(uri("ofcfg:foo")).uri());
     }
 
 }
diff --git a/net/api/src/test/java/org/onlab/onos/net/PortNumberTest.java b/net/api/src/test/java/org/onlab/onos/net/PortNumberTest.java
new file mode 100644
index 0000000..fced985
--- /dev/null
+++ b/net/api/src/test/java/org/onlab/onos/net/PortNumberTest.java
@@ -0,0 +1,37 @@
+package org.onlab.onos.net;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.onlab.onos.net.PortNumber.portNumber;
+
+/**
+ * Test of the port number.
+ */
+public class PortNumberTest extends ElementIdTest {
+
+    @Test
+    public void basics() {
+        new EqualsTester()
+                .addEqualityGroup(portNumber(123),
+                                  portNumber("123"))
+                .addEqualityGroup(portNumber(321))
+                .testEquals();
+    }
+
+    @Test
+    public void number() {
+        assertEquals("incorrect long value", 12345, portNumber(12345).toLong());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void negative() {
+        portNumber(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void tooBig() {
+        portNumber((2L * Integer.MAX_VALUE) + 2);
+    }
+}
diff --git a/net/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java b/net/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java
new file mode 100644
index 0000000..df2b419
--- /dev/null
+++ b/net/api/src/test/java/org/onlab/onos/net/device/DeviceEventTest.java
@@ -0,0 +1,39 @@
+package org.onlab.onos.net.device;
+
+import org.junit.Test;
+import org.onlab.onos.event.AbstractEventTest;
+import org.onlab.onos.net.DefaultDevice;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.provider.ProviderId;
+
+import static org.onlab.onos.net.DeviceId.deviceId;
+
+/**
+ * Tests of the device event.
+ */
+public class DeviceEventTest extends AbstractEventTest {
+
+    private Device createDevice() {
+        return new DefaultDevice(new ProviderId("foo"), deviceId("of:foo"),
+                                 Device.Type.SWITCH, "box", "hw", "sw", "sn");
+    }
+
+    @Test
+    public void withTime() {
+        Device device = createDevice();
+        DeviceEvent event = new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED,
+                                            device, 123L);
+        validateEvent(event, DeviceEvent.Type.DEVICE_ADDED, device, 123L);
+    }
+
+    @Test
+    public void withoutTime() {
+        Device device = createDevice();
+        long before = System.currentTimeMillis();
+        DeviceEvent event = new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED,
+                                            device);
+        long after = System.currentTimeMillis();
+        validateEvent(event, DeviceEvent.Type.DEVICE_ADDED, device, before, after);
+    }
+
+}