Added more event and listener interface definitions.
diff --git a/net/api/src/main/java/org/onlab/onos/net/ConnectPoint.java b/net/api/src/main/java/org/onlab/onos/net/ConnectPoint.java
new file mode 100644
index 0000000..72ea3f4
--- /dev/null
+++ b/net/api/src/main/java/org/onlab/onos/net/ConnectPoint.java
@@ -0,0 +1,23 @@
+package org.onlab.onos.net;
+
+/**
+ * Abstraction of a network connection point expressed as a pair of the
+ * device identifier and the device port number.
+ */
+public interface ConnectPoint {
+
+    /**
+     * Returns the connection device identifier.
+     *
+     * @return device id
+     */
+    DeviceId deviceId();
+
+    /**
+     * Returns the connection port number.
+     *
+     * @return port number
+     */
+    PortNumber port();
+
+}
diff --git a/net/api/src/main/java/org/onlab/onos/net/Device.java b/net/api/src/main/java/org/onlab/onos/net/Device.java
index c649479..8b6368d 100644
--- a/net/api/src/main/java/org/onlab/onos/net/Device.java
+++ b/net/api/src/main/java/org/onlab/onos/net/Device.java
@@ -1,9 +1,11 @@
 package org.onlab.onos.net;
 
+import org.onlab.onos.net.provider.Provided;
+
 /**
  * Representation of an network infrastructure device.
  */
-public class Device {
+public interface Device extends Provided {
 
     // type, e.g. switch, router, firewall, ips, controller
 
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 ebb3fb7..45528a4 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
@@ -1,9 +1,12 @@
 package org.onlab.onos.net;
 
 import java.net.URI;
+import java.util.Objects;
+
+import static com.google.common.base.Objects.toStringHelper;
 
 /**
- * Immutable representaion of a device identity.
+ * Immutable representation of a device identity.
  */
 public class DeviceId {
 
@@ -22,4 +25,25 @@
         return uri;
     }
 
+    @Override
+    public int hashCode() {
+        return Objects.hash(uri);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final DeviceId other = (DeviceId) obj;
+        return Objects.equals(this.uri, other.uri);
+    }
+    @Override
+    public String toString() {
+        return toStringHelper(this).add("uri", uri).toString();
+    }
+
 }
diff --git a/net/api/src/main/java/org/onlab/onos/net/Host.java b/net/api/src/main/java/org/onlab/onos/net/Host.java
index 15badc5..f8e2e31 100644
--- a/net/api/src/main/java/org/onlab/onos/net/Host.java
+++ b/net/api/src/main/java/org/onlab/onos/net/Host.java
@@ -1,9 +1,11 @@
 package org.onlab.onos.net;
 
+import org.onlab.onos.net.provider.Provided;
+
 /**
  * Abstraction of an end-station host on the network, essentially a NIC.
  */
-public class Host {
+public interface Host extends Provided {
 
     // MAC, IP(s), optional VLAN ID
 
diff --git a/net/api/src/main/java/org/onlab/onos/net/Link.java b/net/api/src/main/java/org/onlab/onos/net/Link.java
new file mode 100644
index 0000000..2087377
--- /dev/null
+++ b/net/api/src/main/java/org/onlab/onos/net/Link.java
@@ -0,0 +1,24 @@
+package org.onlab.onos.net;
+
+import org.onlab.onos.net.provider.Provided;
+
+/**
+ * Abstraction of a network infrastructure link.
+ */
+public interface Link extends Provided { // TODO: Also should extend graph Edge
+
+    /**
+     * Returns the link source connection point.
+     *
+     * @return link source connection point
+     */
+    ConnectPoint src();
+
+    /**
+     * Returns the link destination connection point.
+     *
+     * @return link destination connection point
+     */
+    ConnectPoint dst();
+
+}
diff --git a/net/api/src/main/java/org/onlab/onos/net/device/DeviceEvent.java b/net/api/src/main/java/org/onlab/onos/net/device/DeviceEvent.java
index d82ae42..250e140 100644
--- a/net/api/src/main/java/org/onlab/onos/net/device/DeviceEvent.java
+++ b/net/api/src/main/java/org/onlab/onos/net/device/DeviceEvent.java
@@ -12,16 +12,24 @@
      * Type of device events.
      */
     public enum Type {
-        /** Signifies that a new device has been detected. */
+        /**
+         * Signifies that a new device has been detected.
+         */
         DEVICE_ADDED,
 
-        /** Signifies that a device has been removed. */
+        /**
+         * Signifies that a device has been removed.
+         */
         DEVICE_REMOVED,
 
-        /** Signifies that a device has been administratively suspended. */
+        /**
+         * Signifies that a device has been administratively suspended.
+         */
         DEVICE_SUSPENDED,
 
-        /** Signifies that a device has come online or has gone offline. */
+        /**
+         * Signifies that a device has come online or has gone offline.
+         */
         DEVICE_AVAILABILITY_CHANGED,
 
         /**
@@ -32,25 +40,25 @@
     }
 
     /**
-     * Creates an event of a given type and for the specified subject and the
+     * Creates an event of a given type and for the specified device and the
      * current time.
      *
-     * @param type    event type
-     * @param subject event subject
+     * @param type   device event type
+     * @param device event device subject
      */
-    public DeviceEvent(Type type, Device subject) {
-        super(type, subject);
+    public DeviceEvent(Type type, Device device) {
+        super(type, device);
     }
 
     /**
-     * Creates an event of a given type and for the specified subject and time.
+     * Creates an event of a given type and for the specified device and time.
      *
-     * @param type    event type
-     * @param subject event subject
-     * @param time    occurrence time
+     * @param type   device event type
+     * @param device event device subject
+     * @param time   occurrence time
      */
-    public DeviceEvent(Type type, Device subject, long time) {
-        super(type, subject, time);
+    public DeviceEvent(Type type, Device device, long time) {
+        super(type, device, time);
     }
 
 }
diff --git a/net/api/src/main/java/org/onlab/onos/net/device/DeviceListener.java b/net/api/src/main/java/org/onlab/onos/net/device/DeviceListener.java
index 232fc95..b11d617 100644
--- a/net/api/src/main/java/org/onlab/onos/net/device/DeviceListener.java
+++ b/net/api/src/main/java/org/onlab/onos/net/device/DeviceListener.java
@@ -1,7 +1,9 @@
 package org.onlab.onos.net.device;
 
+import org.onlab.onos.event.EventListener;
+
 /**
- * Entity capable of receiving device related events.
+ * Entity capable of receiving infrastructure device related events.
  */
-public interface DeviceListener {
+public interface DeviceListener extends EventListener<DeviceEvent> {
 }
diff --git a/net/api/src/main/java/org/onlab/onos/net/host/HostEvent.java b/net/api/src/main/java/org/onlab/onos/net/host/HostEvent.java
new file mode 100644
index 0000000..b06e8b8
--- /dev/null
+++ b/net/api/src/main/java/org/onlab/onos/net/host/HostEvent.java
@@ -0,0 +1,53 @@
+package org.onlab.onos.net.host;
+
+import org.onlab.onos.event.AbstractEvent;
+import org.onlab.onos.net.Host;
+
+/**
+ * Describes end-station host event.
+ */
+public class HostEvent extends AbstractEvent<HostEvent.Type, Host> {
+
+    /**
+     * Type of host events.
+     */
+    public enum Type {
+        /**
+         * Signifies that a new host has been detected.
+         */
+        HOST_ADDED,
+
+        /**
+         * Signifies that a host has been removed.
+         */
+        HOST_REMOVED,
+
+        /**
+         * Signifies that a host location has changed.
+         */
+        HOST_MOVED
+    }
+
+    /**
+     * Creates an event of a given type and for the specified host and the
+     * current time.
+     *
+     * @param type host event type
+     * @param host event host subject
+     */
+    public HostEvent(Type type, Host host) {
+        super(type, host);
+    }
+
+    /**
+     * Creates an event of a given type and for the specified host and time.
+     *
+     * @param type host event type
+     * @param host event host subject
+     * @param time occurrence time
+     */
+    public HostEvent(Type type, Host host, long time) {
+        super(type, host, time);
+    }
+
+}
diff --git a/net/api/src/main/java/org/onlab/onos/net/host/HostListener.java b/net/api/src/main/java/org/onlab/onos/net/host/HostListener.java
new file mode 100644
index 0000000..0e3e0e1
--- /dev/null
+++ b/net/api/src/main/java/org/onlab/onos/net/host/HostListener.java
@@ -0,0 +1,9 @@
+package org.onlab.onos.net.host;
+
+import org.onlab.onos.event.EventListener;
+
+/**
+ * Entity capable of receiving end-station host related events.
+ */
+public interface HostListener extends EventListener<HostEvent> {
+}
diff --git a/net/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java b/net/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java
new file mode 100644
index 0000000..18987ea
--- /dev/null
+++ b/net/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java
@@ -0,0 +1,48 @@
+package org.onlab.onos.net.link;
+
+import org.onlab.onos.event.AbstractEvent;
+import org.onlab.onos.net.Link;
+
+/**
+ * Describes infrastructure link event.
+ */
+public class LinkEvent extends AbstractEvent<LinkEvent.Type, Link> {
+
+    /**
+     * Type of link events.
+     */
+    public enum Type {
+        /**
+         * Signifies that a new link has been detected.
+         */
+        LINK_ADDED,
+
+        /**
+         * Signifies that a link has been removed.
+         */
+        LINK_REMOVED
+    }
+
+    /**
+     * Creates an event of a given type and for the specified link and the
+     * current time.
+     *
+     * @param type link event type
+     * @param link event link subject
+     */
+    public LinkEvent(Type type, Link link) {
+        super(type, link);
+    }
+
+    /**
+     * Creates an event of a given type and for the specified link and time.
+     *
+     * @param type link event type
+     * @param link event link subject
+     * @param time occurrence time
+     */
+    public LinkEvent(Type type, Link link, long time) {
+        super(type, link, time);
+    }
+
+}
diff --git a/net/api/src/main/java/org/onlab/onos/net/link/LinkListener.java b/net/api/src/main/java/org/onlab/onos/net/link/LinkListener.java
new file mode 100644
index 0000000..03039db
--- /dev/null
+++ b/net/api/src/main/java/org/onlab/onos/net/link/LinkListener.java
@@ -0,0 +1,9 @@
+package org.onlab.onos.net.link;
+
+import org.onlab.onos.event.EventListener;
+
+/**
+ * Entity capable of receiving infrastructure link related events.
+ */
+public interface LinkListener extends EventListener<LinkEvent> {
+}
diff --git a/net/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderBroker.java b/net/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderBroker.java
new file mode 100644
index 0000000..05545c2
--- /dev/null
+++ b/net/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderBroker.java
@@ -0,0 +1,47 @@
+package org.onlab.onos.net.provider;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Base implementation of provider broker.
+ *
+ * @param <P> type of the information provider
+ * @param <S> type of the provider service
+ */
+public abstract class AbstractProviderBroker<P extends Provider, S extends ProviderService>
+        implements ProviderBroker<P, S> {
+
+    private final Map<ProviderId, S> services = new HashMap<>();
+
+    /**
+     * Creates a new provider service bound to the specified provider.
+     *
+     * @param provider provider
+     * @return provider service
+     */
+    protected abstract S createProviderService(P provider);
+
+    @Override
+    public synchronized S register(P provider) {
+        checkNotNull(provider, "Provider cannot be null");
+        checkArgument(!services.containsKey(provider), "Provider %s already registered", provider.id());
+        S service = createProviderService(provider);
+        services.put(provider.id(), service);
+        return service;
+    }
+
+    @Override
+    public synchronized void unregister(P provider) {
+        checkNotNull(provider, "Provider cannot be null");
+        S service = services.get(provider);
+        checkArgument(service != null, "Provider %s not registered", provider.id());
+        if (service instanceof AbstractProviderService) {
+            ((AbstractProviderService) service).invalidate();
+        }
+        services.remove(provider);
+    }
+}
diff --git a/net/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderService.java b/net/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderService.java
new file mode 100644
index 0000000..5497842
--- /dev/null
+++ b/net/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderService.java
@@ -0,0 +1,46 @@
+package org.onlab.onos.net.provider;
+
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Base implementation of a provider service, which tracks the provider to
+ * which it is issued and can be invalidated.
+ *
+ * @param <P> type of the information provider
+ */
+public abstract class AbstractProviderService<P extends Provider> implements ProviderService<P> {
+
+    private boolean isValid = true;
+    private final P provider;
+
+    /**
+     * Creates a provider service on behalf of the specified provider.
+     *
+     * @param provider provider to which this service is being issued
+     */
+    protected AbstractProviderService(P provider) {
+        this.provider = provider;
+    }
+
+    /**
+     * Invalidates this provider service.
+     */
+    public void invalidate() {
+        isValid = false;
+    }
+
+    /**
+     * Checks the validity of this provider service.
+     *
+     * @throws java.lang.IllegalStateException if the service is no longer valid
+     */
+    public void checkValidity() {
+        checkState(isValid, "Provider service is no longer valid");
+    }
+
+    @Override
+    public P provider() {
+        return provider;
+    }
+
+}
diff --git a/net/api/src/main/java/org/onlab/onos/net/provider/Provided.java b/net/api/src/main/java/org/onlab/onos/net/provider/Provided.java
new file mode 100644
index 0000000..71e670a
--- /dev/null
+++ b/net/api/src/main/java/org/onlab/onos/net/provider/Provided.java
@@ -0,0 +1,15 @@
+package org.onlab.onos.net.provider;
+
+/**
+ * Abstraction of an entity supplied by a provider.
+ */
+public interface Provided {
+
+    /**
+     * Returns the identifier of the provider which supplied the entity.
+     *
+     * @return provider identification
+     */
+    ProviderId id();
+
+}
diff --git a/net/api/src/main/java/org/onlab/onos/net/provider/Provider.java b/net/api/src/main/java/org/onlab/onos/net/provider/Provider.java
index b1f159d..3771611 100644
--- a/net/api/src/main/java/org/onlab/onos/net/provider/Provider.java
+++ b/net/api/src/main/java/org/onlab/onos/net/provider/Provider.java
@@ -5,6 +5,11 @@
  */
 public interface Provider {
 
+    /**
+     * Returns the provider identifier.
+     *
+     * @return provider identification
+     */
     ProviderId id();
 
 }
diff --git a/net/api/src/main/java/org/onlab/onos/net/provider/ProviderBroker.java b/net/api/src/main/java/org/onlab/onos/net/provider/ProviderBroker.java
index 9c1b231..a8e294e 100644
--- a/net/api/src/main/java/org/onlab/onos/net/provider/ProviderBroker.java
+++ b/net/api/src/main/java/org/onlab/onos/net/provider/ProviderBroker.java
@@ -3,10 +3,10 @@
 /**
  * Broker used for registering/unregistering information providers with the core.
  *
- * @param <T> type of the information provider
+ * @param <P> type of the information provider
  * @param <S> type of the provider service
  */
-public interface ProviderBroker<T extends Provider, S extends ProviderService> {
+public interface ProviderBroker<P extends Provider, S extends ProviderService<P>> {
 
     /**
      * Registers the supplied provider with the core.
@@ -14,14 +14,15 @@
      * @param provider provider to be registered
      * @return provider service for injecting information into core
      */
-    S register(T provider);
+    S register(P provider);
 
     /**
      * Unregisters the supplied provider. As a result the previously issued
-     * provider service will be invalidated.
+     * provider service will be invalidated and any subsequent invocations
+     * of its methods may throw {@link java.lang.IllegalStateException}.
      *
      * @param provider provider to be unregistered
      */
-    void unregister(T provider);
+    void unregister(P provider);
 
 }
diff --git a/net/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java b/net/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
index 3681be5..3af20ad 100644
--- a/net/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
+++ b/net/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
@@ -1,5 +1,9 @@
 package org.onlab.onos.net.provider;
 
+import java.util.Objects;
+
+import static com.google.common.base.Objects.toStringHelper;
+
 /**
  * Notion of provider identity.
  */
@@ -7,37 +11,37 @@
 
     private final String id;
 
+    /**
+     * Creates a new provider identifier from the specified string.
+     * The providers are expected to follow the reverse DNS convention, e.g.
+     * {@code org.onlab.onos.provider.of.device}
+     *
+     * @param id string identifier
+     */
     public ProviderId(String id) {
         this.id = id;
     }
 
     @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        ProviderId that = (ProviderId) o;
-
-        if (!id.equals(that.id)) {
-            return false;
-        }
-
-        return true;
+    public int hashCode() {
+        return Objects.hash(id);
     }
 
     @Override
-    public int hashCode() {
-        return id.hashCode();
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final ProviderId other = (ProviderId) obj;
+        return Objects.equals(this.id, other.id);
     }
 
     @Override
     public String toString() {
-        return "ProviderId{" +
-                "id='" + id + '\'' +
-                '}';
+        return toStringHelper(this).add("id", id).toString();
     }
+
 }
diff --git a/net/api/src/main/java/org/onlab/onos/net/provider/ProviderService.java b/net/api/src/main/java/org/onlab/onos/net/provider/ProviderService.java
index 9afbc4b..ae21a61 100644
--- a/net/api/src/main/java/org/onlab/onos/net/provider/ProviderService.java
+++ b/net/api/src/main/java/org/onlab/onos/net/provider/ProviderService.java
@@ -3,6 +3,16 @@
 /**
  * Abstraction of a service through which providers can inject information
  * about the network environment into the core.
+ *
+ * @param <P> type of the information provider
  */
-public interface ProviderService {
+public interface ProviderService<P extends Provider> {
+
+    /**
+     * Returns the provider to which this service has been issued.
+     *
+     * @return provider to which this service has been assigned
+     */
+    P provider();
+
 }