Adding ability to project device, link and host model as alternate entities.
Change-Id: If23c018b024a3bbe693f0e66888c5f1707e3f66d
diff --git a/core/api/src/main/java/org/onosproject/net/AbstractProjectableModel.java b/core/api/src/main/java/org/onosproject/net/AbstractProjectableModel.java
new file mode 100644
index 0000000..6fee974
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/AbstractProjectableModel.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2014-2016 Open Networking Laboratory
+ *
+ * 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 org.onosproject.net;
+
+import com.google.common.annotations.Beta;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.net.driver.Behaviour;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.driver.Projectable;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Base model entity, capable of being extended via projection mechanism.
+ */
+@Beta
+public abstract class AbstractProjectableModel extends AbstractModel implements Projectable {
+
+ private static Logger log = LoggerFactory.getLogger(AbstractProjectableModel.class);
+
+ protected static final String NO_DRIVER_SERVICE = "Driver service not bound yet";
+ protected static final String NO_DRIVER = "Driver has not been bound to %s";
+
+ // Static reference to the driver service; injected via setDriverService
+ private static DriverService driverService;
+
+ private Driver driver;
+
+ // For serialization
+ public AbstractProjectableModel() {
+ }
+
+ /**
+ * Creates a model entity attributed to the specified provider and
+ * optionally annotated.
+ *
+ * @param providerId identity of the provider
+ * @param annotations optional key/value annotations
+ */
+ public AbstractProjectableModel(ProviderId providerId, Annotations[] annotations) {
+ super(providerId, annotations);
+ }
+
+ /**
+ * Injects the driver service reference for use during projections into
+ * various behaviours.
+ * <p>
+ * This is a privileged call; unauthorized invocations will result in
+ * illegal state exception
+ *
+ * @param key opaque admin key object
+ * @param driverService injected driver service
+ * @throws IllegalStateException when invoked sans authorization
+ */
+ public static void setDriverService(Object key, DriverService driverService) {
+ // TODO: Rework this once we have means to enforce access to admin services in general
+ checkState(AbstractProjectableModel.driverService == key, "Unauthorized invocation");
+ AbstractProjectableModel.driverService = driverService;
+ }
+
+ /**
+ * Returns the currently bound driver service reference.
+ *
+ * @return driver service
+ */
+ protected static DriverService driverService() {
+ return driverService;
+ }
+
+ /**
+ * Returns the currently bound driver or null of no driver is bound.
+ *
+ * @return bound driver; null if none
+ */
+ protected Driver driver() {
+ return driver;
+ }
+
+ @Override
+ public <B extends Behaviour> B as(Class<B> projectionClass) {
+ checkState(driverService != null, NO_DRIVER_SERVICE);
+ if (driver == null) {
+ driver = locateDriver();
+ }
+ checkState(driver != null, NO_DRIVER, this);
+ return driver.createBehaviour(asData(), projectionClass);
+ }
+
+ @Override
+ public <B extends Behaviour> boolean is(Class<B> projectionClass) {
+ checkState(driverService != null, NO_DRIVER_SERVICE);
+ if (driver == null) {
+ driver = locateDriver();
+ }
+ checkState(driver != null, "Driver has not been bound to %s", this);
+ return driver.hasBehaviour(projectionClass);
+ }
+
+ /**
+ * Locates the driver to be used by this entity.
+ * <p>
+ * The default implementation derives the driver based on the {@code driver}
+ * annotation value.
+ *
+ * @return driver for alternate projections of this model entity or null
+ * if no driver is expected or driver is not found
+ */
+ protected Driver locateDriver() {
+ String driverName = annotations().value(AnnotationKeys.DRIVER);
+ if (driverName != null) {
+ try {
+ return driverService.getDriver(driverName);
+ } catch (ItemNotFoundException e) {
+ log.warn("Driver {} not found.", driverName);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns self as an immutable driver data instance.
+ *
+ * @return self as driver data
+ */
+ protected DriverData asData() {
+ return new AnnotationDriverData();
+ }
+
+
+ /**
+ * Projection of the parent entity as a driver data entity.
+ */
+ protected class AnnotationDriverData implements DriverData {
+ @Override
+ public Driver driver() {
+ return driver;
+ }
+
+ @Override
+ public DeviceId deviceId() {
+ throw new UnsupportedOperationException("Entity not a device");
+ }
+
+ @Override
+ public MutableAnnotations set(String key, String value) {
+ throw new UnsupportedOperationException("Entity is immutable");
+ }
+
+ @Override
+ public MutableAnnotations clear(String... keys) {
+ throw new UnsupportedOperationException("Entity is immutable");
+ }
+
+ @Override
+ public Set<String> keys() {
+ return annotations().keys();
+ }
+
+ @Override
+ public String value(String key) {
+ return annotations().value(key);
+ }
+ }
+
+}