Thomas Vachuska
Committed by Gerrit Code Review

Adding device driver inheritance mechanism.

Change-Id: I9c883d32ce0c39f961eddd5c4624dc23f794fe4d
......@@ -28,7 +28,7 @@ import org.onosproject.net.driver.DriverAdminService;
description = "Lists device drivers")
public class DriversListCommand extends AbstractShellCommand {
private static final String FMT = "driver=%s, mfr=%s, hw=%s, sw=%s";
private static final String FMT = "driver=%s, extends=%s, mfr=%s, hw=%s, sw=%s";
private static final String FMT_B = " %s via %s";
private static final String FMT_P = " %s=%s";
......@@ -48,8 +48,9 @@ public class DriversListCommand extends AbstractShellCommand {
}
private void printDriver(Driver driver) {
print(FMT, driver.name(), driver.manufacturer(),
driver.hwVersion(), driver.swVersion());
Driver parent = driver.parent();
print(FMT, driver.name(), parent != null ? parent.name() : "none",
driver.manufacturer(), driver.hwVersion(), driver.swVersion());
driver.behaviours().forEach(b -> print(FMT_B, b.getCanonicalName(),
driver.implementation(b).getCanonicalName()));
driver.properties().forEach((k, v) -> print(FMT_P, k, v));
......
......@@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static com.google.common.base.MoreObjects.toStringHelper;
......@@ -32,6 +33,7 @@ import static com.google.common.collect.ImmutableMap.copyOf;
public class DefaultDriver implements Driver {
private final String name;
private final Driver parent;
private final String manufacturer;
private final String hwVersion;
......@@ -40,22 +42,23 @@ public class DefaultDriver implements Driver {
private final Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours;
private final Map<String, String> properties;
/**
* Creates a driver with the specified name.
*
* @param name driver name
* @param parent optional parent driver
* @param manufacturer device manufacturer
* @param hwVersion device hardware version
* @param swVersion device software version
* @param behaviours device behaviour classes
* @param properties properties for configuration of device behaviour classes
*/
public DefaultDriver(String name, String manufacturer,
public DefaultDriver(String name, Driver parent, String manufacturer,
String hwVersion, String swVersion,
Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours,
Map<String, String> properties) {
this.name = checkNotNull(name, "Name cannot be null");
this.parent = parent;
this.manufacturer = checkNotNull(manufacturer, "Manufacturer cannot be null");
this.hwVersion = checkNotNull(hwVersion, "HW version cannot be null");
this.swVersion = checkNotNull(swVersion, "SW version cannot be null");
......@@ -65,6 +68,9 @@ public class DefaultDriver implements Driver {
@Override
public Driver merge(Driver other) {
checkArgument(parent == null || Objects.equals(parent, other.parent()),
"Parent drivers are not the same");
// Merge the behaviours.
Map<Class<? extends Behaviour>, Class<? extends Behaviour>>
behaviours = Maps.newHashMap();
......@@ -75,7 +81,7 @@ public class DefaultDriver implements Driver {
ImmutableMap.Builder<String, String> properties = ImmutableMap.builder();
properties.putAll(this.properties).putAll(other.properties());
return new DefaultDriver(name, manufacturer, hwVersion, swVersion,
return new DefaultDriver(name, other.parent(), manufacturer, hwVersion, swVersion,
ImmutableMap.copyOf(behaviours), properties.build());
}
......@@ -100,6 +106,11 @@ public class DefaultDriver implements Driver {
}
@Override
public Driver parent() {
return parent;
}
@Override
public Set<Class<? extends Behaviour>> behaviours() {
return behaviours.keySet();
}
......@@ -111,19 +122,32 @@ public class DefaultDriver implements Driver {
@Override
public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
return behaviours.containsKey(behaviourClass);
return behaviours.containsKey(behaviourClass) ||
(parent != null && parent.hasBehaviour(behaviourClass));
}
@Override
public <T extends Behaviour> T createBehaviour(DriverData data,
Class<T> behaviourClass) {
return createBehaviour(data, null, behaviourClass);
T behaviour = createBehaviour(data, null, behaviourClass);
if (behaviour != null) {
return behaviour;
} else if (parent != null) {
return parent.createBehaviour(data, behaviourClass);
}
throw new IllegalArgumentException(behaviourClass.getName() + " not supported");
}
@Override
public <T extends Behaviour> T createBehaviour(DriverHandler handler,
Class<T> behaviourClass) {
return createBehaviour(handler.data(), handler, behaviourClass);
T behaviour = createBehaviour(handler.data(), handler, behaviourClass);
if (behaviour != null) {
return behaviour;
} else if (parent != null) {
return parent.createBehaviour(handler, behaviourClass);
}
throw new IllegalArgumentException(behaviourClass.getName() + " not supported");
}
// Creates an instance of behaviour primed with the specified driver data.
......@@ -134,17 +158,18 @@ public class DefaultDriver implements Driver {
// Locate the implementation of the requested behaviour.
Class<? extends Behaviour> implementation = behaviours.get(behaviourClass);
checkArgument(implementation != null, "{} not supported", behaviourClass.getName());
// Create an instance of the behaviour and apply data as its context.
T behaviour = createBehaviour(behaviourClass, implementation);
behaviour.setData(data);
// If this is a handler behaviour, also apply handler as its context.
if (handler != null) {
((HandlerBehaviour) behaviour).setHandler(handler);
if (implementation != null) {
// Create an instance of the behaviour and apply data as its context.
T behaviour = createBehaviour(behaviourClass, implementation);
behaviour.setData(data);
// If this is a handler behaviour, also apply handler as its context.
if (handler != null) {
((HandlerBehaviour) behaviour).setHandler(handler);
}
return behaviour;
}
return behaviour;
return null;
}
@SuppressWarnings("unchecked")
......@@ -177,6 +202,7 @@ public class DefaultDriver implements Driver {
public String toString() {
return toStringHelper(this)
.add("name", name)
.add("parent", parent)
.add("manufacturer", manufacturer)
.add("hwVersion", hwVersion)
.add("swVersion", swVersion)
......
......@@ -35,6 +35,14 @@ public interface Driver extends Annotations {
String name();
/**
* Returns the parent driver from which this driver inherits behaviours
* and properties.
*
* @return parent driver; null if driver has no parent
*/
Driver parent();
/**
* Returns the device manufacturer name.
*
* @return manufacturer name
......@@ -57,6 +65,7 @@ public interface Driver extends Annotations {
/**
* Returns the set of behaviours supported by this driver.
* It reflects behaviours of only this driver and not its parent.
*
* @return set of device driver behaviours
*/
......@@ -64,6 +73,7 @@ public interface Driver extends Annotations {
/**
* Returns the implementation class for the specified behaviour.
* It reflects behaviours of only this driver and not its parent.
*
* @param behaviour behaviour interface
* @return implementation class
......@@ -71,8 +81,8 @@ public interface Driver extends Annotations {
Class<? extends Behaviour> implementation(Class<? extends Behaviour> behaviour);
/**
* Indicates whether or not the driver supports the specified class
* of behaviour.
* Indicates whether or not the driver, or any of its parents, support
* the specified class of behaviour. It
*
* @param behaviourClass behaviour class
* @return true if behaviour is supported
......@@ -81,6 +91,8 @@ public interface Driver extends Annotations {
/**
* Creates an instance of behaviour primed with the specified driver data.
* If the current driver does not support the specified behaviour and the
* driver has parent, the request is delegated to the parent driver.
*
* @param data driver data context
* @param behaviourClass driver behaviour class
......@@ -91,6 +103,8 @@ public interface Driver extends Annotations {
/**
* Creates an instance of behaviour primed with the specified driver data.
* If the current driver does not support the specified behaviour and the
* driver has parent, the request is delegated to the parent driver.
*
* @param handler driver handler context
* @param behaviourClass driver behaviour class
......
/*
* Copyright 2015 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.driver;
/**
* Entity capable of resolving a driver using its name.
*/
public interface DriverResolver {
/**
* Returns the specified driver.
*
* @param driverName driver name
* @return driver
* @throws org.onlab.util.ItemNotFoundException if driver with the given
* name is not found
*/
Driver getDriver(String driverName);
}
......@@ -22,7 +22,7 @@ import java.util.Set;
/**
* Service for obtaining drivers and driver behaviour implementations.
*/
public interface DriverService {
public interface DriverService extends DriverResolver {
/**
* Returns the overall set of drivers being provided.
......@@ -40,16 +40,6 @@ public interface DriverService {
Set<Driver> getDrivers(Class<? extends Behaviour> withBehaviour);
/**
* Returns the specified driver.
*
* @param driverName driver name
* @return driver
* @throws org.onlab.util.ItemNotFoundException if driver with the given
* name is not found
*/
Driver getDriver(String driverName);
/**
* Returns the driver that matches the specified primordial device
* discovery information.
*
......
......@@ -16,6 +16,7 @@
package org.onosproject.net.driver;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
......@@ -51,6 +52,7 @@ public class XmlDriverLoader {
private static final String PROPERTY = "property";
private static final String NAME = "[@name]";
private static final String EXTENDS = "[@extends]";
private static final String MFG = "[@manufacturer]";
private static final String HW = "[@hwVersion]";
private static final String SW = "[@swVersion]";
......@@ -59,6 +61,8 @@ public class XmlDriverLoader {
private final ClassLoader classLoader;
private Map<String, Driver> drivers = Maps.newHashMap();
/**
* Creates a new driver loader capable of loading drivers from the supplied
* class loader.
......@@ -74,18 +78,20 @@ public class XmlDriverLoader {
* produce a ready-to-register driver provider.
*
* @param driversStream stream containing the drivers definitions
* @param resolver driver resolver
* @return driver provider
* @throws java.io.IOException if issues are encountered reading the stream
* or parsing the driver definitions within
*/
public DefaultDriverProvider loadDrivers(InputStream driversStream) throws IOException {
public DefaultDriverProvider loadDrivers(InputStream driversStream,
DriverResolver resolver) throws IOException {
try {
XMLConfiguration cfg = new XMLConfiguration();
cfg.setRootElementName(DRIVERS);
cfg.setAttributeSplittingDisabled(true);
cfg.load(driversStream);
return loadDrivers(cfg);
return loadDrivers(cfg, resolver);
} catch (ConfigurationException e) {
throw new IOException("Unable to load drivers", e);
}
......@@ -95,13 +101,18 @@ public class XmlDriverLoader {
* Loads a driver provider from the supplied hierarchical configuration.
*
* @param driversCfg hierarchical configuration containing the drivers definitions
* @param resolver driver resolver
* @return driver provider
*/
public DefaultDriverProvider loadDrivers(HierarchicalConfiguration driversCfg) {
public DefaultDriverProvider loadDrivers(HierarchicalConfiguration driversCfg,
DriverResolver resolver) {
DefaultDriverProvider provider = new DefaultDriverProvider();
for (HierarchicalConfiguration cfg : driversCfg.configurationsAt(DRIVER)) {
provider.addDriver(loadDriver(cfg));
DefaultDriver driver = loadDriver(cfg, resolver);
drivers.put(driver.name(), driver);
provider.addDriver(driver);
}
drivers.clear();
return provider;
}
......@@ -109,19 +120,30 @@ public class XmlDriverLoader {
* Loads a driver from the supplied hierarchical configuration.
*
* @param driverCfg hierarchical configuration containing the driver definition
* @param resolver driver resolver
* @return driver
*/
public DefaultDriver loadDriver(HierarchicalConfiguration driverCfg) {
public DefaultDriver loadDriver(HierarchicalConfiguration driverCfg,
DriverResolver resolver) {
String name = driverCfg.getString(NAME);
String parentName = driverCfg.getString(EXTENDS);
String manufacturer = driverCfg.getString(MFG, "");
String hwVersion = driverCfg.getString(HW, "");
String swVersion = driverCfg.getString(SW, "");
return new DefaultDriver(name, manufacturer, hwVersion, swVersion,
Driver parent = parentName != null ? resolve(parentName, resolver) : null;
return new DefaultDriver(name, parent, manufacturer, hwVersion, swVersion,
parseBehaviours(driverCfg),
parseProperties(driverCfg));
}
// Resolves the driver by name locally at first and then using the specified resolver.
private Driver resolve(String parentName, DriverResolver resolver) {
Driver driver = drivers.get(parentName);
return driver != null ? driver :
(resolver != null ? resolver.getDriver(parentName) : null);
}
// Parses the behaviours section.
private Map<Class<? extends Behaviour>, Class<? extends Behaviour>>
parseBehaviours(HierarchicalConfiguration driverCfg) {
......
......@@ -28,7 +28,7 @@ public class DefaultDriverDataTest {
@Before
public void setUp() {
ddc = new DefaultDriver("foo.bar", "Circus", "lux", "1.2a",
ddc = new DefaultDriver("foo.bar", null, "Circus", "lux", "1.2a",
ImmutableMap.of(TestBehaviour.class,
TestBehaviourImpl.class),
ImmutableMap.of("foo", "bar"));
......
......@@ -30,7 +30,7 @@ public class DefaultDriverHandlerTest {
@Before
public void setUp() {
ddc = new DefaultDriver("foo.bar", "Circus", "lux", "1.2a",
ddc = new DefaultDriver("foo.bar", null, "Circus", "lux", "1.2a",
ImmutableMap.of(TestBehaviour.class,
TestBehaviourImpl.class,
TestBehaviourTwo.class,
......
......@@ -28,15 +28,15 @@ public class DefaultDriverProviderTest {
@Test
public void basics() {
DefaultDriverProvider ddp = new DefaultDriverProvider();
DefaultDriver one = new DefaultDriver("foo.bar", "Circus", "lux", "1.2a",
DefaultDriver one = new DefaultDriver("foo.bar", null, "Circus", "lux", "1.2a",
ImmutableMap.of(TestBehaviour.class,
TestBehaviourImpl.class),
ImmutableMap.of("foo", "bar"));
DefaultDriver two = new DefaultDriver("foo.bar", "", "", "",
DefaultDriver two = new DefaultDriver("foo.bar", null, "", "", "",
ImmutableMap.of(TestBehaviourTwo.class,
TestBehaviourTwoImpl.class),
ImmutableMap.of("goo", "wee"));
DefaultDriver three = new DefaultDriver("goo.foo", "BigTop", "better", "2.2",
DefaultDriver three = new DefaultDriver("goo.foo", null, "BigTop", "better", "2.2",
ImmutableMap.of(TestBehaviourTwo.class,
TestBehaviourTwoImpl.class),
ImmutableMap.of("goo", "gee"));
......
......@@ -25,18 +25,33 @@ public class DefaultDriverTest {
@Test
public void basics() {
DefaultDriver ddc = new DefaultDriver("foo.bar", "Circus", "lux", "1.2a",
DefaultDriver ddp = new DefaultDriver("foo.base", null, "Circus", "lux", "1.2a",
ImmutableMap.of(TestBehaviour.class,
TestBehaviourImpl.class),
TestBehaviourImpl.class,
TestBehaviourTwo.class,
TestBehaviourTwoImpl.class),
ImmutableMap.of("foo", "bar"));
DefaultDriver ddc = new DefaultDriver("foo.bar", ddp, "Circus", "lux", "1.2a",
ImmutableMap.of(),
ImmutableMap.of("foo", "bar"));
assertEquals("incorrect name", "foo.bar", ddc.name());
assertEquals("incorrect parent", ddp, ddc.parent());
assertEquals("incorrect mfr", "Circus", ddc.manufacturer());
assertEquals("incorrect hw", "lux", ddc.hwVersion());
assertEquals("incorrect sw", "1.2a", ddc.swVersion());
assertEquals("incorrect behaviour count", 1, ddc.behaviours().size());
assertEquals("incorrect behaviour count", 2, ddp.behaviours().size());
assertEquals("incorrect behaviour count", 0, ddc.behaviours().size());
assertTrue("incorrect behaviour", ddc.hasBehaviour(TestBehaviour.class));
Behaviour b1 = ddc.createBehaviour(new DefaultDriverData(ddc), TestBehaviour.class);
assertTrue("incorrect behaviour class", b1 instanceof TestBehaviourImpl);
Behaviour b2 = ddc.createBehaviour(new DefaultDriverHandler(new DefaultDriverData(ddc)),
TestBehaviourTwo.class);
assertTrue("incorrect behaviour class", b2 instanceof TestBehaviourTwoImpl);
assertEquals("incorrect property count", 1, ddc.properties().size());
assertEquals("incorrect key count", 1, ddc.keys().size());
assertEquals("incorrect property", "bar", ddc.value("foo"));
......@@ -46,12 +61,12 @@ public class DefaultDriverTest {
@Test
public void merge() {
DefaultDriver one = new DefaultDriver("foo.bar", "Circus", "lux", "1.2a",
DefaultDriver one = new DefaultDriver("foo.bar", null, "Circus", "lux", "1.2a",
ImmutableMap.of(TestBehaviour.class,
TestBehaviourImpl.class),
ImmutableMap.of("foo", "bar"));
Driver ddc =
one.merge(new DefaultDriver("foo.bar", "", "", "",
one.merge(new DefaultDriver("foo.bar", null, "", "", "",
ImmutableMap.of(TestBehaviourTwo.class,
TestBehaviourTwoImpl.class),
ImmutableMap.of("goo", "wee")));
......
......@@ -19,6 +19,7 @@ import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
......@@ -32,17 +33,22 @@ public class XmlDriverLoaderTest {
public void basics() throws IOException {
XmlDriverLoader loader = new XmlDriverLoader(getClass().getClassLoader());
InputStream stream = getClass().getResourceAsStream("drivers.1.xml");
DriverProvider provider = loader.loadDrivers(stream);
DriverProvider provider = loader.loadDrivers(stream, null);
System.out.println(provider);
assertEquals("incorrect driver count", 1, provider.getDrivers().size());
assertEquals("incorrect driver count", 2, provider.getDrivers().size());
Iterator<Driver> iterator = provider.getDrivers().iterator();
Driver driver = iterator.next();
if (!driver.name().equals("foo.1")) {
driver = iterator.next();
}
Driver driver = provider.getDrivers().iterator().next();
assertEquals("incorrect driver name", "foo.1", driver.name());
assertEquals("incorrect driver mfg", "Circus", driver.manufacturer());
assertEquals("incorrect driver hw", "1.2a", driver.hwVersion());
assertEquals("incorrect driver sw", "2.2", driver.swVersion());
assertEquals("incorrect driver behaviours", 2, driver.behaviours().size());
assertEquals("incorrect driver behaviours", 1, driver.behaviours().size());
assertTrue("incorrect driver behaviour", driver.hasBehaviour(TestBehaviour.class));
assertEquals("incorrect driver properties", 2, driver.properties().size());
......@@ -52,20 +58,20 @@ public class XmlDriverLoaderTest {
@Test(expected = IOException.class)
public void badXML() throws IOException {
XmlDriverLoader loader = new XmlDriverLoader(getClass().getClassLoader());
loader.loadDrivers(getClass().getResourceAsStream("drivers.bad.xml"));
loader.loadDrivers(getClass().getResourceAsStream("drivers.bad.xml"), null);
}
@Test(expected = IllegalArgumentException.class)
public void noClass() throws IOException {
XmlDriverLoader loader = new XmlDriverLoader(getClass().getClassLoader());
loader.loadDrivers(getClass().getResourceAsStream("drivers.noclass.xml"));
loader.loadDrivers(getClass().getResourceAsStream("drivers.noclass.xml"), null);
}
@Test(expected = IllegalArgumentException.class)
public void noConstructor() throws IOException {
XmlDriverLoader loader = new XmlDriverLoader(getClass().getClassLoader());
InputStream stream = getClass().getResourceAsStream("drivers.noconstructor.xml");
DriverProvider provider = loader.loadDrivers(stream);
DriverProvider provider = loader.loadDrivers(stream, null);
Driver driver = provider.getDrivers().iterator().next();
driver.createBehaviour(new DefaultDriverData(driver), TestBehaviour.class);
}
......
......@@ -15,12 +15,15 @@
~ limitations under the License.
-->
<drivers>
<driver name="foo.1" manufacturer="Circus" hwVersion="1.2a" swVersion="2.2">
<driver name="foo.0" manufacturer="Circus" hwVersion="1.2" swVersion="2.0">
<behaviour api="org.onosproject.net.driver.TestBehaviour"
impl="org.onosproject.net.driver.TestBehaviourImpl"/>
</driver>
<driver name="foo.1" extends="foo.0" manufacturer="Circus" hwVersion="1.2a" swVersion="2.2">
<fingerprint>ding</fingerprint>
<fingerprint>bat</fingerprint>
<behaviour api="org.onosproject.net.driver.TestBehaviour"
impl="org.onosproject.net.driver.TestBehaviourImpl"/>
<behaviour api="org.onosproject.net.driver.TestBehaviourTwo"
impl="org.onosproject.net.driver.TestBehaviourTwoImpl"/>
......
......@@ -169,5 +169,4 @@ public class DriverManager extends DefaultDriverProvider implements DriverAdminS
private String key(String mfr, String hw, String sw) {
return String.format("%s-%s-%s", mfr, hw, sw);
}
}
......
......@@ -28,7 +28,6 @@ import org.onosproject.net.driver.XmlDriverLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
/**
......@@ -52,9 +51,10 @@ public class DefaultDrivers implements DefaultDriverProviderService {
ClassLoader classLoader = getClass().getClassLoader();
try {
InputStream stream = classLoader.getResourceAsStream(DRIVERS_XML);
provider = new XmlDriverLoader(classLoader).loadDrivers(stream);
provider = new XmlDriverLoader(classLoader)
.loadDrivers(stream, driverAdminService);
driverAdminService.registerProvider(provider);
} catch (IOException e) {
} catch (Exception e) {
log.error("Unable to load default drivers", e);
}
log.info("Started");
......