<properties>\r
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\r
<nexusproxy>http://nexus.opendaylight.org/content</nexusproxy>\r
- <yang.version>0.5.7-SNAPSHOT</yang.version>\r
+ <yang.version>0.5.8-SNAPSHOT</yang.version>\r
<maven.bundle.version>2.4.0</maven.bundle.version>\r
</properties>\r
\r
<plugin>\r
<groupId>org.opendaylight.yangtools</groupId>\r
<artifactId>yang-maven-plugin</artifactId>\r
- <version>0.5.7-SNAPSHOT</version>\r
+ <version>${yang.version}</version>\r
<executions>\r
<execution>\r
<goals>\r
<dependency>\r
<groupId>org.opendaylight.yangtools</groupId>\r
<artifactId>maven-sal-api-gen-plugin</artifactId>\r
- <version>0.5.7-SNAPSHOT</version>\r
+ <version>${yang.version}</version>\r
<type>jar</type>\r
</dependency>\r
</dependencies>\r
</build>\r
<pluginRepositories>\r
<pluginRepository>\r
- <id>central2</id>\r
- <name>central2</name>\r
- <url>http://repo2.maven.org/maven2</url>\r
+ <id>central</id>\r
+ <name>maven repo1</name>\r
+ <url>http://repo1.maven.org/maven2</url>\r
<snapshots>\r
<enabled>false</enabled>\r
</snapshots>\r
<name>ebr-bundles-external</name>\r
<url>${nexusproxy}/repositories/ebr-bundles-external/</url>\r
</repository>\r
- <repository>\r
- <id>central2</id>\r
- <name>central2</name>\r
- <url>http://repo2.maven.org/maven2</url>\r
- <snapshots>\r
- <enabled>false</enabled>\r
- </snapshots>\r
- <releases>\r
- <enabled>true</enabled>\r
- </releases>\r
- </repository>\r
<repository>\r
<id>central</id>\r
<name>central</name>\r
<!-- Sonar jacoco plugin to get integration test coverage info -->
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.jacoco.reportPath>../implementation/target/jacoco.exec</sonar.jacoco.reportPath>
- <sonar.jacoco.itReportPath>../implementaiton/target/jacoco-it.exec</sonar.jacoco.itReportPath>
+ <sonar.jacoco.itReportPath>../implementation/target/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.language>java</sonar.language>
</properties>
<build>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
<configuration>
<destFile>../implementation/target/jacoco-it.exec</destFile>
<includes>org.opendaylight.controller.*</includes>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
</plugin>
</plugins>
</pluginManagement>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-abstract public class ClusterManagerCommon implements IClusterServicesCommon {
+public abstract class ClusterManagerCommon implements IClusterServicesCommon {
protected String containerName = null;
private IClusterServices clusterService = null;
protected static final Logger logger = LoggerFactory
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-abstract public class ClusterManagerCommon implements IClusterServicesCommon {
+public abstract class ClusterManagerCommon implements IClusterServicesCommon {
protected String containerName = "";
protected static final Logger logger = LoggerFactory
.getLogger(ClusterManagerCommon.class);
<sample-toaster.version>1.0-SNAPSHOT</sample-toaster.version>
<releaseplugin.version>2.3.2</releaseplugin.version>
<commons.lang.version>3.1</commons.lang.version>
+ <jacoco.version>0.5.3.201107060350</jacoco.version>
</properties>
<pluginRepositories>
<pluginRepository>
- <id>central2</id>
- <name>central2</name>
- <url>http://repo2.maven.org/maven2</url>
+ <id>central</id>
+ <name>maven repo1</name>
+ <url>http://repo1.maven.org/maven2</url>
</pluginRepository>
</pluginRepositories>
<name>ebr-bundles-external</name>
<url>${nexusproxy}/repositories/ebr-bundles-external/</url>
</repository>
- <repository>
- <id>central2</id>
- <name>central2</name>
- <url>http://repo2.maven.org/maven2</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- <releases>
- <enabled>true</enabled>
- </releases>
- </repository>
<repository>
<id>central</id>
<name>central</name>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
- <configuration>
- <argLine>${testvm.argLine}</argLine>
- </configuration>
</plugin>
</plugins>
<pluginManagement>
<artifactId>jersey-json</artifactId>
<version>${jersey.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-all</artifactId>
+ <version>4.1</version>
+ </dependency>
</dependencies>
</project>
--- /dev/null
+package org.opendaylight.controller.connectionmanager;
+
+public enum ConnectionLocality {
+ /**
+ * This controller is the (or one of the) master for a given node
+ */
+ LOCAL("This controller is the (or one of the) master for a given node"),
+
+ /**
+ * This controller is not the master for a given node
+ */
+ NOT_LOCAL("This controller is not the master for a given node"),
+
+ /**
+ * The given node is not connected to any of the controllers in the cluster
+ */
+ NOT_CONNECTED("The given node is not connected to any of the controllers in the cluster");
+
+ private ConnectionLocality(String description) {
+ this.description = description;
+ }
+
+ private String description;
+
+ public String toString() {
+ return description;
+ }
+}
public Set<Node> getLocalNodes();
/**
+ * @deprecated Use getLocalityStatus(Node node) instead.
+ *
* Method to test if a node is local to a controller.
*
- * @return true if node is local to this controller. false otherwise.
+ * @param node The node for which the locality is being tested
+ * @return true if node is local to this controller.<br>
+ * false if either node is not connected to this controller or
+ * not connected to any other controllers in the cluster.
*/
public boolean isLocal(Node node);
+ /**
+ * getLocalityStatus provides the tri-state connectivity status as opposed to the
+ * binary status returned by isLocal.
+ * ConnectionLocality enum that is returned by this method also includes the case of
+ * a Node not connected to any of the controllers in the cluster.
+ * @param node The node for which the locality is being verified
+ * @return ConnectionLocality
+ */
+ public ConnectionLocality getLocalityStatus(Node node);
+
/**
* Disconnect a Node from the controller.
*
import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
import org.opendaylight.controller.clustering.services.ICoordinatorChangeAware;
+import org.opendaylight.controller.connectionmanager.ConnectionLocality;
import org.opendaylight.controller.connectionmanager.ConnectionMgmtScheme;
import org.opendaylight.controller.connectionmanager.IConnectionManager;
import org.opendaylight.controller.connectionmanager.scheme.AbstractScheme;
return scheme.isLocal(node);
}
+ @Override
+ public ConnectionLocality getLocalityStatus(Node node) {
+ AbstractScheme scheme = schemes.get(activeScheme);
+ if (scheme == null) return ConnectionLocality.NOT_CONNECTED;
+ return scheme.getLocalityStatus(node);
+ }
+
@Override
public void updateNode(Node node, UpdateType type, Set<Property> props) {
logger.debug("updateNode: {} type {} props {}", node, type, props);
import org.opendaylight.controller.clustering.services.CacheExistException;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.connectionmanager.ConnectionLocality;
import org.opendaylight.controller.connectionmanager.ConnectionMgmtScheme;
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.utils.Status;
return (controllers != null && controllers.contains(myController));
}
+ public ConnectionLocality getLocalityStatus(Node node) {
+ if (nodeConnections == null) return ConnectionLocality.NOT_CONNECTED;
+ Set<InetAddress> controllers = nodeConnections.get(node);
+ if (controllers == null || controllers.size() == 0) return ConnectionLocality.NOT_CONNECTED;
+ InetAddress myController = clusterServices.getMyAddress();
+ return controllers.contains(myController) ? ConnectionLocality.LOCAL:
+ ConnectionLocality.NOT_LOCAL;
+ }
+
public Status removeNode (Node node) {
return removeNodeFromController(node, clusterServices.getMyAddress());
}
*/
private Status validateName() {
// No Container configuration allowed to container default
- return (container.matches(regexName) && !container.equalsIgnoreCase(GlobalConstants.DEFAULT.toString())) ?
+ return ((container != null) && container.matches(regexName) && !container.equalsIgnoreCase(GlobalConstants.DEFAULT.toString())) ?
new Status(StatusCode.SUCCESS) : new Status(StatusCode.BADREQUEST, "Invalid container name");
}
config.getName()));
}
}
+ } else {
+ // Check for conflicting names with existing cFlows
+ List<String> conflicting = new ArrayList<String>(existingNames);
+ conflicting.retainAll(proposedNames);
+ if (!conflicting.isEmpty()) {
+ return new Status(StatusCode.CONFLICT,
+ "Invalid Flow Spec configuration: flow spec name(s) conflict with existing flow specs: "
+ + conflicting.toString());
+ }
+
+ /*
+ * Check for conflicting flow spec match (we only check for strict
+ * equality). Remove this in case (*) is reintroduced
+ */
+ if (this.containerFlows != null && !this.containerFlows.isEmpty()) {
+ Set<Match> existingMatches = new HashSet<Match>();
+ for (ContainerFlowConfig existing : this.containerFlows) {
+ existingMatches.addAll(existing.getMatches());
+ }
+ for (ContainerFlowConfig proposed : cFlowConfigs) {
+ if (existingMatches.removeAll(proposed.getMatches())) {
+ return new Status(StatusCode.CONFLICT, String.format(
+ "Invalid Flow Spec configuration: %s conflicts with existing flow spec",
+ proposed.getName()));
+ }
+ }
+ }
}
+
/*
* Revisit the following flow-spec confict validation later based on more testing.
- */
- /*
+ * (*)
if (!delete) {
// Check for overlapping container flows in the request
int size = cFlowConfigs.size();
@Override
public boolean doesContainerExist(String ContainerId) {
- // TODO Auto-generated method stub
- return false;
+ return GlobalConstants.DEFAULT.toString().equalsIgnoreCase(ContainerId);
}
@Override
<module>../../statisticsmanager/api</module>
<module>../../statisticsmanager/implementation</module>
<module>../../statisticsmanager/integrationtest</module>
- <module>../../topologymanager</module>
+ <module>../../topologymanager/implementation</module>
<module>../../usermanager/api</module>
<module>../../usermanager/implementation</module>
<module>../../connectionmanager/api</module>
<!-- Northbound bundles -->
<module>../../northbound/commons</module>
+ <module>../../northbound/bundlescanner/api</module>
+ <module>../../northbound/bundlescanner/implementation</module>
<module>../../northbound/topology</module>
<module>../../northbound/staticrouting</module>
<module>../../northbound/statistics</module>
controllerKeyStorePassword=
controllerTrustStore=
controllerTrustStorePassword=
+
+# User Manager configurations
+enableStrongPasswordCheck = false
private final FlowEntry original;
private final ContainerFlow cFlow;
private final FlowEntry install;
- transient private long requestId; // async request id
- transient private boolean deletePending;
+ private transient long requestId; // async request id
+ private transient boolean deletePending;
public FlowEntryInstall(FlowEntry original, ContainerFlow cFlow) {
this.original = original;
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
</plugin>
</plugins>
</pluginManagement>
* Serialization UID
*/
private static final long serialVersionUID = 416280377113255147L;
- final private FlowEntryInstall entry;
- final private UpdateType upType;
- final private InetAddress requestorController;
+ private final FlowEntryInstall entry;
+ private final UpdateType upType;
+ private final InetAddress requestorController;
/**
* @return the entry
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
import org.opendaylight.controller.clustering.services.IClusterServices;
import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+import org.opendaylight.controller.connectionmanager.ConnectionLocality;
import org.opendaylight.controller.connectionmanager.IConnectionManager;
import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
import org.opendaylight.controller.forwardingrulesmanager.FlowEntry;
}
Node n = e.getNode();
- if (!connectionManager.isLocal(n)) {
+ if (connectionManager.getLocalityStatus(n) == ConnectionLocality.NOT_LOCAL) {
Callable<Future<Status>> worker = new DistributeOrderCallable(e, u, t);
if (worker != null) {
Future<Future<Status>> workerRes = this.executor.submit(worker);
}
}
- logsync.trace("LOCAL Node {} so processing Entry:{} UpdateType:{}", n, e, t);
+ logsync.trace("Node {} could be local. so processing Entry:{} UpdateType:{}", n, e, t);
return null;
}
addStaticFlowInternal(allowARP, true); // skip validation on internal static flow name
}
+ /**
+ * (non-Javadoc)
+ *
+ * @see org.opendaylight.controller.switchmanager.ISwitchManagerAware#modeChangeNotify(org.opendaylight.controller.sal.core.Node,
+ * boolean)
+ *
+ * This method can be called from within the OSGi framework context,
+ * given the programming operation can take sometime, it not good
+ * pratice to have in it's context operations that can take time,
+ * hence moving off to a different thread for async processing.
+ */
@Override
- public void modeChangeNotify(Node node, boolean proactive) {
- List<FlowConfig> defaultConfigs = new ArrayList<FlowConfig>();
-
- List<String> puntAction = new ArrayList<String>();
- puntAction.add(ActionType.CONTROLLER.toString());
-
- FlowConfig allowARP = new FlowConfig();
- allowARP.setInstallInHw(true);
- allowARP.setName(FlowConfig.INTERNALSTATICFLOWBEGIN + "Punt ARP" + FlowConfig.INTERNALSTATICFLOWEND);
- allowARP.setPriority("1");
- allowARP.setNode(node);
- allowARP.setEtherType("0x" + Integer.toHexString(EtherTypes.ARP.intValue()).toUpperCase());
- allowARP.setActions(puntAction);
- defaultConfigs.add(allowARP);
-
- FlowConfig allowLLDP = new FlowConfig();
- allowLLDP.setInstallInHw(true);
- allowLLDP.setName(FlowConfig.INTERNALSTATICFLOWBEGIN + "Punt LLDP" + FlowConfig.INTERNALSTATICFLOWEND);
- allowLLDP.setPriority("1");
- allowLLDP.setNode(node);
- allowLLDP.setEtherType("0x" + Integer.toHexString(EtherTypes.LLDP.intValue()).toUpperCase());
- allowLLDP.setActions(puntAction);
- defaultConfigs.add(allowLLDP);
-
- List<String> dropAction = new ArrayList<String>();
- dropAction.add(ActionType.DROP.toString());
-
- FlowConfig dropAllConfig = new FlowConfig();
- dropAllConfig.setInstallInHw(true);
- dropAllConfig.setName(FlowConfig.INTERNALSTATICFLOWBEGIN + "Catch-All Drop" + FlowConfig.INTERNALSTATICFLOWEND);
- dropAllConfig.setPriority("0");
- dropAllConfig.setNode(node);
- dropAllConfig.setActions(dropAction);
- defaultConfigs.add(dropAllConfig);
-
- log.info("Forwarding mode for node {} set to {}", node, (proactive ? "proactive" : "reactive"));
- for (FlowConfig fc : defaultConfigs) {
- Status status = (proactive) ? addStaticFlowInternal(fc, false) : removeStaticFlow(fc);
- if (status.isSuccess()) {
- log.info("{} Proactive Static flow: {}", (proactive ? "Installed" : "Removed"), fc.getName());
- } else {
- log.warn("Failed to {} Proactive Static flow: {}", (proactive ? "install" : "remove"), fc.getName());
+ public void modeChangeNotify(final Node node, final boolean proactive) {
+ Callable<Status> modeChangeCallable = new Callable<Status>() {
+ @Override
+ public Status call() throws Exception {
+ List<FlowConfig> defaultConfigs = new ArrayList<FlowConfig>();
+
+ List<String> puntAction = new ArrayList<String>();
+ puntAction.add(ActionType.CONTROLLER.toString());
+
+ FlowConfig allowARP = new FlowConfig();
+ allowARP.setInstallInHw(true);
+ allowARP.setName(FlowConfig.INTERNALSTATICFLOWBEGIN + "Punt ARP" + FlowConfig.INTERNALSTATICFLOWEND);
+ allowARP.setPriority("1");
+ allowARP.setNode(node);
+ allowARP.setEtherType("0x" + Integer.toHexString(EtherTypes.ARP.intValue())
+ .toUpperCase());
+ allowARP.setActions(puntAction);
+ defaultConfigs.add(allowARP);
+
+ FlowConfig allowLLDP = new FlowConfig();
+ allowLLDP.setInstallInHw(true);
+ allowLLDP.setName(FlowConfig.INTERNALSTATICFLOWBEGIN + "Punt LLDP" + FlowConfig.INTERNALSTATICFLOWEND);
+ allowLLDP.setPriority("1");
+ allowLLDP.setNode(node);
+ allowLLDP.setEtherType("0x" + Integer.toHexString(EtherTypes.LLDP.intValue())
+ .toUpperCase());
+ allowLLDP.setActions(puntAction);
+ defaultConfigs.add(allowLLDP);
+
+ List<String> dropAction = new ArrayList<String>();
+ dropAction.add(ActionType.DROP.toString());
+
+ FlowConfig dropAllConfig = new FlowConfig();
+ dropAllConfig.setInstallInHw(true);
+ dropAllConfig.setName(FlowConfig.INTERNALSTATICFLOWBEGIN + "Catch-All Drop"
+ + FlowConfig.INTERNALSTATICFLOWEND);
+ dropAllConfig.setPriority("0");
+ dropAllConfig.setNode(node);
+ dropAllConfig.setActions(dropAction);
+ defaultConfigs.add(dropAllConfig);
+
+ log.info("Forwarding mode for node {} set to {}", node, (proactive ? "proactive" : "reactive"));
+ for (FlowConfig fc : defaultConfigs) {
+ Status status = (proactive) ? addStaticFlowInternal(fc, false) : removeStaticFlow(fc);
+ if (status.isSuccess()) {
+ log.info("{} Proactive Static flow: {}", (proactive ? "Installed" : "Removed"), fc.getName());
+ } else {
+ log.warn("Failed to {} Proactive Static flow: {}", (proactive ? "install" : "remove"),
+ fc.getName());
+ }
+ }
+ return new Status(StatusCode.SUCCESS);
}
- }
+ };
+
+ /*
+ * Execute the work outside the caller context, this could be an
+ * expensive operation and we don't want to block the caller for it.
+ */
+ this.executor.submit(modeChangeCallable);
}
/**
return;
}
Node n = fei.getNode();
- if (connectionManager.isLocal(n)) {
+ if (connectionManager.getLocalityStatus(n) == ConnectionLocality.LOCAL) {
logsync.trace("workOrder for fe {} processed locally", fe);
// I'm the controller in charge for the request, queue it for
// processing
<!-- Sonar jacoco plugin to get integration test coverage info -->
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.jacoco.reportPath>../implementation/target/jacoco.exec</sonar.jacoco.reportPath>
- <sonar.jacoco.itReportPath>../implementaiton/target/jacoco-it.exec</sonar.jacoco.itReportPath>
+ <sonar.jacoco.itReportPath>../implementation/target/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.language>java</sonar.language>
</properties>
<build>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
<configuration>
<destFile>../implementation/target/jacoco-it.exec</destFile>
<includes>org.opendaylight.controller.*</includes>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
</plugin>
</plugins>
</pluginManagement>
<plugin>\r
<groupId>org.jacoco</groupId>\r
<artifactId>jacoco-maven-plugin</artifactId>\r
- <version>0.5.3.201107060350</version>\r
+ <version>${jacoco.version}</version>\r
<configuration>\r
<destFile>../implementation/target/jacoco-it.exec</destFile>\r
<includes>org.opendaylight.controller.*</includes>\r
<!-- Sonar properties using jacoco to retrieve integration test results -->
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
- <sonar.jacoco.Reportpath>target/jacoco.exec</sonar.jacoco.Reportpath>
+ <sonar.jacoco.reportpath>target/jacoco.exec</sonar.jacoco.reportpath>
<sonar.jacoco.itReportPath>target/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.language>java</sonar.language>
</properties>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
</plugin>
</plugins>
</pluginManagement>
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <relativePath>../../sal/yang-prototype/sal/pom.xml</relativePath>
+ </parent>
+ <artifactId>sal-compability</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-service</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-statistics</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <type>bundle</type>
+ <version>14.0.1</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+package org.opendaylight.controller.sal.compability;
+
+import static org.opendaylight.controller.sal.match.MatchType.DL_DST;
+import static org.opendaylight.controller.sal.match.MatchType.DL_SRC;
+import static org.opendaylight.controller.sal.match.MatchType.DL_TYPE;
+import static org.opendaylight.controller.sal.match.MatchType.DL_VLAN;
+import static org.opendaylight.controller.sal.match.MatchType.DL_VLAN_PR;
+import static org.opendaylight.controller.sal.match.MatchType.NW_DST;
+import static org.opendaylight.controller.sal.match.MatchType.NW_PROTO;
+import static org.opendaylight.controller.sal.match.MatchType.NW_SRC;
+import static org.opendaylight.controller.sal.match.MatchType.NW_TOS;
+import static org.opendaylight.controller.sal.match.MatchType.TP_DST;
+import static org.opendaylight.controller.sal.match.MatchType.TP_SRC;
+
+import java.net.InetAddress;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.action.Controller;
+import org.opendaylight.controller.sal.action.Output;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Dscp;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.action.action.ControllerAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.action.action.OutputAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.action.action.PopMplsAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.action.action.PushMplsAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.action.action.PushPbbAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.action.action.PushVlanAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.action.action.SetMplsTtlAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.action.action.SetNwTtlAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.action.action.SetQueueAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.flow.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanPcp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.MacAddressFilter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.ethernet.match.fields.EthernetType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.IpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.Layer3Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.Layer4Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.VlanMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.layer._3.match.ArpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.layer._3.match.Ipv4Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.layer._3.match.Ipv6Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.layer._4.match.SctpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.layer._4.match.TcpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.match.layer._4.match.UdpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev130819.vlan.match.fields.VlanId;
+
+import com.google.common.net.InetAddresses;
+
+public class ToSalConversionsUtils {
+
+ private ToSalConversionsUtils() {
+
+ }
+
+ public static Flow flowFrom(NodeFlow source) {
+ final Flow target = new Flow();
+
+ Integer hardTimeout = source.getHardTimeout();
+ if (hardTimeout != null) {
+ target.setHardTimeout(hardTimeout.shortValue());
+ }
+
+ Integer idleTimeout = source.getIdleTimeout();
+ if (idleTimeout != null) {
+ target.setIdleTimeout(idleTimeout.shortValue());
+ }
+
+ Integer priority = source.getPriority();
+ if (priority != null) {
+ target.setPriority(priority.shortValue());
+ }
+
+ target.setMatch(matchFrom(source.getMatch()));
+
+ List<Action> actions = source.getAction();
+ if (actions != null) {
+ for (Action sourceAction : actions) {
+ Set<org.opendaylight.controller.sal.action.Action> targetActions = actionFrom(sourceAction);
+ for (org.opendaylight.controller.sal.action.Action targetAction : targetActions) {
+ target.addAction(targetAction);
+ }
+ }
+ }
+
+ target.setId(source.getCookie().longValue());
+ return target;
+ }
+
+ public static Set<org.opendaylight.controller.sal.action.Action> actionFrom(Action source) {
+ org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.action.Action sourceAction = source
+ .getAction();
+ Set<org.opendaylight.controller.sal.action.Action> targetAction = new HashSet<>();
+ if (sourceAction instanceof ControllerAction) {
+ targetAction.add(new Controller());
+ } else if (sourceAction instanceof OutputAction) {
+
+ List<Uri> nodeConnectors = ((OutputAction) sourceAction).getOutputNodeConnector();
+ for (Uri uri : nodeConnectors) {
+ targetAction.add(new Output(fromNodeConnectorRef(uri)));
+ }
+ } else if (sourceAction instanceof PopMplsAction) {
+ // TODO: define maping
+ } else if (sourceAction instanceof PushMplsAction) {
+ // TODO: define maping
+ } else if (sourceAction instanceof PushPbbAction) {
+ // TODO: define maping
+ } else if (sourceAction instanceof PushVlanAction) {
+ // TODO: define maping
+ // PushVlanAction vlanAction = (PushVlanAction) sourceAction;
+ // targetAction.add(new PushVlan(vlanAction., pcp, cfi, vlanId);
+ } else if (sourceAction instanceof SetMplsTtlAction) {
+ // TODO: define maping
+ // targetAction = //no action to map
+ } else if (sourceAction instanceof SetNwTtlAction) {
+ // TODO: define maping
+ } else if (sourceAction instanceof SetQueueAction) {
+ // TODO: define maping
+ // targetAction = //no action to map
+ }
+
+ return targetAction;
+ }
+
+ private static NodeConnector fromNodeConnectorRef(Uri uri) {
+ // TODO: Define mapping
+ return null;
+ }
+
+ public static Match matchFrom(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev130819.flow.Match source) {
+ Match target = new Match();
+ if (source != null) {
+ fillFrom(target, source.getVlanMatch());
+ fillFrom(target, source.getEthernetMatch());
+ fillFrom(target, source.getLayer3Match());
+ fillFrom(target, source.getLayer4Match());
+ fillFrom(target, source.getIpMatch());
+ }
+
+ return target;
+ }
+
+ private static void fillFrom(Match target, VlanMatch vlanMatch) {
+ if (vlanMatch != null) {
+ VlanId vlanId = vlanMatch.getVlanId();
+ if (vlanId != null) {
+ org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId vlanIdInner = vlanId
+ .getVlanId();
+ if (vlanIdInner != null) {
+ Integer vlanValue = vlanIdInner.getValue();
+ if (vlanValue != null) {
+ target.setField(DL_VLAN, vlanValue.shortValue());
+ }
+ }
+ }
+ VlanPcp vlanPcp = vlanMatch.getVlanPcp();
+ if (vlanPcp != null) {
+ Short vlanPcpValue = vlanPcp.getValue();
+ if (vlanPcpValue != null) {
+ target.setField(DL_VLAN_PR, vlanPcpValue.byteValue());
+ }
+ }
+ }
+ }
+
+ private static void fillFrom(Match target, IpMatch ipMatch) {
+ if (ipMatch != null) {
+ Short ipProtocol = ipMatch.getIpProtocol();
+ if (ipProtocol != null) {
+ target.setField(NW_PROTO, ipProtocol.byteValue());
+ }
+ Dscp dscp = ipMatch.getIpDscp();
+ if (dscp != null) {
+ Short dscpValue = dscp.getValue();
+ if (dscpValue != null) {
+ target.setField(NW_TOS, dscpValue.byteValue());
+ }
+ }
+ }
+ }
+
+ private static void fillFrom(Match target, Layer4Match layer4Match) {
+ if (layer4Match == null) {
+ return;
+ }
+ if (layer4Match instanceof SctpMatch) {
+ fillTransportLayer(target, (SctpMatch) layer4Match);
+ } else if (layer4Match instanceof TcpMatch) {
+ fillTransportLayer(target, (TcpMatch) layer4Match);
+ } else if (layer4Match instanceof UdpMatch) {
+ fillTransportLayer(target, (UdpMatch) layer4Match);
+ }
+ }
+
+ private static void fillTransportLayer(Match target, UdpMatch source) {
+ PortNumber udpSourcePort = source.getUdpSourcePort();
+ if (udpSourcePort != null) {
+ Integer udpSourcePortValue = udpSourcePort.getValue();
+ if (udpSourcePortValue != null) {
+ target.setField(TP_SRC, udpSourcePortValue.shortValue());
+ }
+ }
+
+ PortNumber udpDestPort = source.getUdpDestinationPort();
+ if (udpDestPort != null) {
+ Integer udpDestPortValue = udpDestPort.getValue();
+ if (udpDestPortValue != null) {
+ target.setField(TP_DST, udpDestPortValue.shortValue());
+ }
+ }
+ }
+
+ private static void fillTransportLayer(Match target, TcpMatch source) {
+ PortNumber tcpSourcePort = source.getTcpSourcePort();
+ if (tcpSourcePort != null) {
+ Integer tcpSourcePortValue = tcpSourcePort.getValue();
+ if (tcpSourcePortValue != null) {
+ target.setField(TP_SRC, tcpSourcePortValue.shortValue());
+ }
+ }
+
+ PortNumber tcpDestPort = source.getTcpDestinationPort();
+ if (tcpDestPort != null) {
+ Integer tcpDestPortValue = tcpDestPort.getValue();
+ if (tcpDestPortValue != null) {
+ target.setField(TP_DST, tcpDestPortValue.shortValue());
+ }
+ }
+ }
+
+ private static void fillTransportLayer(Match target, SctpMatch source) {
+ PortNumber sctpSourcePort = source.getSctpSourcePort();
+ if (sctpSourcePort != null) {
+ Integer sctpSourcePortValue = sctpSourcePort.getValue();
+ if (sctpSourcePortValue != null) {
+ target.setField(TP_SRC, sctpSourcePortValue.shortValue());
+ }
+ }
+ PortNumber sctpDestPort = source.getSctpDestinationPort();
+ if (sctpDestPort != null) {
+ Integer sctpDestPortValue = sctpDestPort.getValue();
+ if (sctpDestPortValue != null) {
+ target.setField(TP_DST, sctpDestPortValue.shortValue());
+ }
+ }
+ }
+
+ private static void fillFrom(Match target, Layer3Match source) {
+ if (source == null)
+ return;
+ if (source instanceof Ipv4Match) {
+ fillFromIpv4(target, (Ipv4Match) source);
+ } else if (source instanceof Ipv6Match) {
+ fillFromIpv6(target, (Ipv6Match) source);
+ } else if (source instanceof ArpMatch) {
+ fillFromArp(target, (ArpMatch) source);
+ }
+ }
+
+ private static void fillFromArp(Match target, ArpMatch source) {
+ Ipv4Prefix sourceAddress = source.getArpSourceTransportAddress();
+ if (sourceAddress != null) {
+ target.setField(NW_SRC, (InetAddress) inetAddressFrom(sourceAddress), null);
+ }
+ Ipv4Prefix destAddress = source.getArpSourceTransportAddress();
+ if (destAddress != null) {
+ target.setField(NW_DST, (InetAddress) inetAddressFrom(destAddress), null);
+ }
+ }
+
+ private static void fillFromIpv6(Match target, Ipv6Match source) {
+ Ipv6Prefix sourceAddress = source.getIpv6Source();
+ if (sourceAddress != null) {
+ target.setField(NW_SRC, (InetAddress) inetAddressFrom(sourceAddress), null);
+ }
+ Ipv6Prefix destAddress = source.getIpv6Source();
+ if (destAddress != null) {
+ target.setField(NW_DST, (InetAddress) inetAddressFrom(destAddress), null);
+ }
+ }
+
+ private static void fillFromIpv4(Match target, Ipv4Match source) {
+ Ipv4Prefix sourceAddress = source.getIpv4Source();
+ if (sourceAddress != null) {
+ target.setField(NW_SRC, (InetAddress) inetAddressFrom(sourceAddress), null);
+ }
+ Ipv4Prefix destAddress = source.getIpv4Source();
+ if (destAddress != null) {
+ target.setField(NW_DST, (InetAddress) inetAddressFrom(destAddress), null);
+ }
+ }
+
+ private static InetAddress inetAddressFrom(Ipv4Prefix source) {
+ if (source != null) {
+ String[] parts = source.getValue().split("/");
+ return InetAddresses.forString(parts[0]);
+ }
+ return null;
+ }
+
+ private static InetAddress inetAddressFrom(Ipv6Prefix source) {
+ if (source != null) {
+ String[] parts = source.getValue().split("/");
+ return InetAddresses.forString(parts[0]);
+ }
+ return null;
+ }
+
+ private static void fillFrom(Match target, EthernetMatch source) {
+ if (source == null)
+ return;
+ EthernetType ethType = source.getEthernetType();
+ if (ethType != null) {
+ EtherType ethInnerType = ethType.getType();
+ if (ethInnerType != null) {
+ Long value = ethInnerType.getValue();
+ target.setField(DL_TYPE, value.shortValue());
+ }
+ }
+
+ MacAddressFilter ethSource = source.getEthernetSource();
+ if (ethSource != null) {
+ target.setField(DL_SRC, bytesFrom(ethSource.getAddress()));
+ }
+
+ MacAddressFilter ethDest = source.getEthernetDestination();
+ if (ethDest != null) {
+ target.setField(DL_DST, bytesFrom(ethDest.getAddress()));
+ }
+ }
+
+ private static byte[] bytesFrom(MacAddress address) {
+ if (address != null) {
+ return address.getValue().getBytes();
+ }
+ return null;
+ }
+}
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../../commons/opendaylight</relativePath>
+ </parent>
+
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+ </scm>
+
+ <artifactId>bundlescanner</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.osgi.framework,
+ org.slf4j,
+ javax.ws.rs,
+ javax.ws.rs.core,
+ javax.xml.bind.annotation,
+ javax.xml.bind,
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.controller.northbound.bundlescanner
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.northbound.bundlescanner;
+
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * The bundle scan service provides services which allow introspection of
+ * bundle classes for detecting annotated classes. The scanning is performed
+ * when a bundle is RESOLVED.
+ */
+public interface IBundleScanService {
+ /**
+ * The list of annotations to be scanned
+ */
+ public final String[] ANNOTATIONS_TO_SCAN = {
+ "javax.xml.bind.annotation.*", // JAXB annotatinos
+ "javax.ws.rs.*" // JAX-RS annotatinos
+ };
+
+
+ public List<Class<?>> getAnnotatedClasses(
+ BundleContext context,
+ String[] annotations,
+ boolean includeDependentBundleClasses);
+}
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+ <relativePath>../../../commons/opendaylight</relativePath>
+ </parent>
+
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+ </scm>
+
+ <artifactId>bundlescanner.implementation</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.6</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.apache.felix.dm,
+ org.objectweb.asm,
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.northbound.bundlescanner,
+ org.osgi.framework,
+ org.slf4j,
+ javax.ws.rs,
+ javax.ws.rs.core,
+ javax.xml.bind.annotation,
+ javax.xml.bind,
+ </Import-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.northbound.bundlescanner.internal.Activator
+ </Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-all</artifactId>
+ <version>4.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-mock</artifactId>
+ <version>1.2.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>bundlescanner</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.northbound.bundlescanner.internal;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.northbound.bundlescanner.IBundleScanService;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+
+/**
+ * The activator registers the BundleScanner.
+ */
+public class Activator extends ComponentActivatorAbstractBase {
+
+ @Override
+ protected void init() {
+ }
+
+ @Override
+ protected void destroy() {
+ }
+
+ @Override
+ protected Object[] getGlobalImplementations() {
+ return new Object[] { BundleScanServiceImpl.class };
+ }
+
+ @Override
+ protected void configureGlobalInstance(Component c, Object imp) {
+ if (!imp.equals(BundleScanServiceImpl.class)) return;
+ // export service
+ c.setInterface(
+ new String[] { IBundleScanService.class.getName() },
+ null);
+ }
+
+}
--- /dev/null
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.northbound.bundlescanner.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * BundleInfo holds information related to the bundle obtained during the
+ * bundle scan process.
+ */
+/*package*/ class BundleInfo {
+ private static final Logger LOGGER = LoggerFactory.getLogger(BundleInfo.class);
+
+ private final Bundle bundle;
+ private final Map<String, Set<String>> annotatedClasses;
+ private final Set<String> exportPkgs;
+ private final Set<String> importPkgs;
+
+ public BundleInfo(Bundle bundle, Map<String, Set<String>> classes) {
+ this.bundle = bundle;
+ this.annotatedClasses = classes;
+ Dictionary<String, String> dict = bundle.getHeaders();
+ this.importPkgs = parsePackages(dict.get(Constants.IMPORT_PACKAGE));
+ this.exportPkgs = parsePackages(dict.get(Constants.EXPORT_PACKAGE));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(super.toString());
+ sb.append("{name:").append(bundle.getSymbolicName())
+ .append(" id:").append(getId())
+ .append(" annotated-classes:").append(annotatedClasses)
+ .append(" imports:").append(importPkgs)
+ .append(" exports:").append(exportPkgs).append("}");
+ return sb.toString();
+ }
+
+ public Bundle getBundle() {
+ return bundle;
+ }
+
+ public long getId() {
+ return bundle.getBundleId();
+ }
+
+ public List<Class<?>> getAnnotatedClasses(Pattern pattern) {
+ List<String> result = new ArrayList<String>();
+ for (Map.Entry<String, Set<String>> entry : annotatedClasses.entrySet()) {
+ if (matches(pattern, entry.getValue())) {
+ result.add(entry.getKey());
+ }
+ }
+ return BundleScanner.loadClasses(bundle, result);
+ }
+
+ private boolean matches(Pattern pattern, Set<String> values) {
+ if (pattern == null) return true;
+ //LOGGER.debug("Matching: {} {}", pattern.toString(), values);
+ for (String s : values) {
+ if (pattern.matcher(s).find()) return true;
+ }
+ return false;
+ }
+
+ public List<Class<?>> getAnnotatedClasses(
+ Collection<BundleInfo> allbundles,
+ Pattern pattern)
+ {
+ List<Class<?>> classes = getAnnotatedClasses(pattern);
+ processAnnotatedClassesInternal(this, allbundles, pattern,
+ new HashSet<BundleInfo>(), classes);
+ return classes;
+ }
+
+ private List<String> getExportedAnnotatedClasses(Pattern pattern) {
+ List<String> classes = new ArrayList<String>();
+ for (Map.Entry<String, Set<String>> entry : annotatedClasses.entrySet()) {
+ String cls = entry.getKey();
+ int idx = cls.lastIndexOf(".");
+ String pkg = (idx == -1 ? "" : cls.substring(0, idx));
+ // for a class to match, the package has to be exported and
+ // annotations should match the given pattern
+ if (exportPkgs.contains(pkg) && matches(pattern, entry.getValue())) {
+ classes.add(cls);
+ }
+ }
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Found in bundle:{} exported classes:[{}]",
+ getBundle().getSymbolicName(), classes);
+ }
+ return classes;
+ }
+
+ private static void processAnnotatedClassesInternal(
+ BundleInfo target,
+ Collection<BundleInfo> bundlesToScan,
+ Pattern pattern,
+ Collection<BundleInfo> visited,
+ List<Class<?>> classes)
+ {
+ for (BundleInfo other : bundlesToScan) {
+ if (other.getId() == target.getId()) continue;
+ if (target.isDependantOn(other)) {
+ if (!visited.contains(other)) {
+ classes.addAll(BundleScanner.loadClasses(other.getBundle(),
+ other.getExportedAnnotatedClasses(pattern)));
+ visited.add(other);
+ processAnnotatedClassesInternal(other, bundlesToScan,
+ pattern, visited, classes);
+ }
+ }
+ }
+ }
+
+ private boolean isDependantOn(BundleInfo other) {
+ for (String pkg : importPkgs) {
+ if (other.exportPkgs.contains(pkg)) return true;
+ }
+ return false;
+ }
+
+ public List<BundleInfo> getDependencies(Collection<BundleInfo> bundles) {
+ List<BundleInfo> result = new ArrayList<BundleInfo>();
+ for(BundleInfo bundle : bundles) {
+ if (isDependantOn(bundle)) result.add(bundle);
+ }
+ return result;
+ }
+
+
+ private static Set<String> parsePackages(String packageString) {
+ if (packageString == null) return Collections.emptySet();
+ String[] packages = packageString.split(",");
+ Set<String> result = new HashSet<String>();
+ for (int i=0; i<packages.length; i++) {
+ String[] nameAndAttrs = packages[i].split(";");
+ String packageName = nameAndAttrs[0].trim();
+ result.add(packageName);
+ }
+ return result;
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.northbound.bundlescanner.internal;
+
+import java.util.List;
+
+import org.opendaylight.controller.northbound.bundlescanner.IBundleScanService;
+import org.osgi.framework.BundleContext;
+
+public class BundleScanServiceImpl implements IBundleScanService {
+
+ public BundleScanServiceImpl() {}
+
+
+ @Override
+ public List<Class<?>> getAnnotatedClasses(BundleContext context,
+ String[] annotations,
+ boolean includeDependentBundleClasses)
+ {
+ return BundleScanner.getInstance().getAnnotatedClasses(
+ context, annotations, includeDependentBundleClasses);
+ }
+
+}
--- /dev/null
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.northbound.bundlescanner.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+import org.opendaylight.controller.northbound.bundlescanner.IBundleScanService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.SynchronousBundleListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The custom bundle scanner scans annotations on bundles and is used for
+ * constructing JAXBContext instances. It listens for bundle events and updates
+ * the metadata in realtime.
+ */
+/*package*/ class BundleScanner implements SynchronousBundleListener {
+ private static final Logger LOGGER = LoggerFactory.getLogger(BundleScanner.class);
+ private static BundleScanner INSTANCE; // singleton
+
+ private final Pattern annotationPattern;
+ private final Map<Long,BundleInfo> bundleAnnotations =
+ new HashMap<Long, BundleInfo>();
+
+ public static synchronized BundleScanner getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new BundleScanner();
+ }
+ return INSTANCE;
+ }
+
+ /*package*/ BundleScanner(Bundle[] bundles) {
+ annotationPattern = mergePatterns(IBundleScanService.ANNOTATIONS_TO_SCAN, true);
+ init(bundles);
+ }
+
+ /*package*/ BundleScanner() {
+ this(FrameworkUtil.getBundle(BundleScanner.class).getBundleContext().getBundles());
+ }
+
+ public List<Class<?>> getAnnotatedClasses(BundleContext context,
+ String[] annotations,
+ boolean includeDependentBundleClasses)
+ {
+ BundleInfo info = bundleAnnotations.get(context.getBundle().getBundleId());
+ if (info == null) return Collections.emptyList();
+ Pattern pattern = mergePatterns(annotations, false);
+ List<Class<?>> result = null;
+ if (includeDependentBundleClasses) {
+ result = info.getAnnotatedClasses(bundleAnnotations.values(), pattern);
+ } else {
+ result = info.getAnnotatedClasses(pattern);
+ }
+ LOGGER.debug("Annotated classes detected: {} matching: {}", result, pattern);
+ return result;
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // SynchronousBundleListener implementation
+ ////////////////////////////////////////////////////////////////
+
+ @Override
+ public void bundleChanged(BundleEvent event) {
+ Bundle bundle = event.getBundle();
+ long id = bundle.getBundleId();
+ switch(event.getType()) {
+ case BundleEvent.RESOLVED :
+ scan(bundle);
+ return;
+ case BundleEvent.UNRESOLVED :
+ case BundleEvent.UNINSTALLED :
+ bundleAnnotations.remove(id);
+ return;
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////
+ // ClassVisitor implementation
+ ////////////////////////////////////////////////////////////////
+
+ private static class AnnotationDetector extends ClassVisitor {
+ private final Map<String, Set<String>> matchedClasses =
+ new HashMap<String, Set<String>>();
+
+ private final Pattern annotationsPattern;
+ private Set<String> annotations;
+ private String className;
+ private boolean accessible;
+ private boolean matchedAnnotation;
+
+ public AnnotationDetector(Pattern pattern) {
+ super(Opcodes.ASM4);
+ this.annotationsPattern = pattern;
+ }
+
+ public Map<String, Set<String>> getMatchedClasses() {
+ return new HashMap<String, Set<String>>(matchedClasses);
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature,
+ String superName, String[] interfaces)
+ {
+ //LOGGER.debug("Visiting class:" + name);
+ className = name;
+ accessible = ((access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC);
+ matchedAnnotation = false;
+ annotations = new HashSet<String>();
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ //LOGGER.debug("Visiting annotation:" + desc);
+ annotations.add(signature2class(desc));
+ if (!matchedAnnotation) {
+ matchedAnnotation = (annotationsPattern == null ||
+ annotationsPattern.matcher(desc).find());
+ }
+ return null;
+ }
+
+ @Override
+ public void visitEnd() {
+ if (matchedAnnotation && accessible) {
+ className = path2class(className);
+ matchedClasses.put(className, new HashSet<String>(annotations));
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // Helpers
+ ////////////////////////////////////////////////////////////////
+
+ private synchronized void init(Bundle[] bundles) {
+ for (Bundle bundle : bundles) {
+ int state = bundle.getState();
+ if (state == Bundle.RESOLVED ||
+ state == Bundle.STARTING ||
+ state == Bundle.ACTIVE)
+ {
+ scan(bundle);
+ }
+ }
+ }
+
+ private static String path2class(String path) {
+ return path.replace(".class", "").replaceAll("/", ".");
+ }
+
+ private static String class2path(String clz) {
+ return clz.replaceAll("\\.", "/");
+ }
+
+ @SuppressWarnings("unused")
+ private static String class2signature(String clz) {
+ return "L" + class2path(clz) + ";";
+ }
+
+ private static String signature2class(String sig) {
+ if (sig.startsWith("L") && sig.endsWith(";")) {
+ sig = sig.substring(1, sig.length()-1);
+ }
+ return path2class(sig);
+ }
+
+ private static List<URL> getBundleClasses(Bundle bundle, String[] pkgs) {
+ List<URL> result = new ArrayList<URL>();
+ boolean recurse = false;
+ if (pkgs == null) {
+ recurse = true;
+ pkgs = new String[] { "/" } ;
+ }
+ for (String pkg : pkgs) {
+ pkg = class2path(pkg);
+ final Enumeration<URL> e = bundle.findEntries(pkg, "*.class", recurse);
+ if (e != null) {
+ while (e.hasMoreElements()) {
+ URL url = e.nextElement();
+ result.add(url);
+ }
+ }
+ }
+ return result;
+ }
+
+ private synchronized void scan(Bundle bundle) {
+ AnnotationDetector detector = new AnnotationDetector(annotationPattern);
+ try {
+ for (URL u : getBundleClasses(bundle, null)) {
+ InputStream is = u.openStream();
+ new ClassReader(is).accept(detector, 0);
+ is.close();
+ }
+ } catch (IOException ioe) {
+ LOGGER.error("Error scanning classes in bundle: {}", bundle.getSymbolicName(), ioe);
+ }
+ Map<String, Set<String>> classes = detector.getMatchedClasses();
+ if (classes != null && classes.size() > 0) {
+ BundleInfo info = new BundleInfo(bundle, classes);
+ bundleAnnotations.put(bundle.getBundleId(),info);
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("bindings found in bundle: {}[{}] " +
+ "dependencies {} classes {}", bundle.getSymbolicName(),
+ bundle.getBundleId(),
+ info.getDependencies(bundleAnnotations.values()),
+ classes);
+ }
+ }
+ // find bundle dependencies
+ }
+
+ public static List<Class<?>> loadClasses(Bundle bundle,
+ Collection<String> annotatedClasses)
+ {
+ List<Class<?>> result = new ArrayList<Class<?>>();
+ for (String name : annotatedClasses) {
+ try {
+ result.add(bundle.loadClass(name));
+ } catch (Exception e) {
+ LOGGER.error("Unable to load class: {}", name, e);
+ }
+ }
+ return result;
+ }
+
+ public static Pattern mergePatterns(String[] patterns, boolean convert2signature) {
+ if (patterns == null || patterns.length == 0) {
+ return null;
+ }
+ StringBuilder regex = new StringBuilder();
+ for (String c : patterns) {
+ if (c.endsWith("*")) {
+ c = c.substring(0, c.length() - 1);
+ }
+ if (regex.length() > 0) regex.append("|");
+ regex.append("^");
+ if (convert2signature) {
+ regex.append("L").append(c.replaceAll("\\.", "/"));
+ } else {
+ regex.append(c);
+ }
+ }
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Merged regex: [{}]", regex.toString());
+ }
+ return Pattern.compile(regex.toString());
+ }
+
+}
--- /dev/null
+package bundle_base;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public abstract class Animal { }
--- /dev/null
+package bundle_base;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSeeAlso;
+
+@XmlRootElement
+public class BasePerson { }
\ No newline at end of file
--- /dev/null
+package bundle_base;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class Mammal extends Animal { }
--- /dev/null
+package bundle_base;
+
+public class NoAnnotation { }
--- /dev/null
+package bundle_base;
+
+@Deprecated
+public class NonRelevantAnnotation {
+
+}
--- /dev/null
+package bundle_base;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+@XmlTransient
+@Deprecated
+public class Person extends BasePerson {
+
+ @XmlElement
+ protected String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @XmlRootElement
+ public static class Info { }
+
+ @XmlRootElement
+ private static class PrivateInfo { }
+}
--- /dev/null
+package bundle_misc;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class Misc {
+
+}
--- /dev/null
+package bundle_sub1;
+
+public class NoAnnotation {
+
+}
--- /dev/null
+package bundle_sub1;
+
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import bundle_base.Animal;
+import bundle_base.Mammal;
+
+
+@XmlRootElement
+public class Zoo {
+ private Animal creature;
+
+ @XmlElementRef
+ public Animal getCreature() {
+ return creature;
+ }
+
+ public void setCreature(Animal creature) {
+ this.creature = creature;
+ }
+
+ public Zoo() {
+ creature = new Mammal();
+ }
+}
--- /dev/null
+package bundle_sub2;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import bundle_base.Person;
+
+
+@XmlRootElement
+public class Agent extends Person {
+
+}
\ No newline at end of file
--- /dev/null
+package bundle_sub2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import bundle_base.BasePerson;
+import bundle_base.Person;
+
+
+@XmlRootElement
+public class Customer extends Person {
+
+ private String password;
+ private List<String> phoneNumbers;
+ @XmlElementRef
+ @XmlElementWrapper
+ private final List<BasePerson> agents = new ArrayList<BasePerson>();
+
+ @XmlTransient
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ @XmlElement(name = "phone-number")
+ public List<String> getPhoneNumbers() {
+ return phoneNumbers;
+ }
+
+ public void setPhoneNumbers(List<String> phoneNumbers) {
+ this.phoneNumbers = phoneNumbers;
+ }
+
+ public void addAgent(Person mgr) {
+ this.agents.add(mgr);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(super.toString());
+ sb.append(" password:").append(password);
+ sb.append(" phoneNumbers:").append(phoneNumbers);
+ sb.append(" agents:").append(agents);
+ return sb.toString();
+ }
+}
+
--- /dev/null
+package bundle_sub2;
+
+public class NoAnnotation {
+
+}
--- /dev/null
+package org.opendaylight.controller.northbound.bundlescanner.internal;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.JAXBException;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.opendaylight.controller.northbound.bundlescanner.IBundleScanService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.springframework.osgi.mock.MockBundle;
+import org.springframework.osgi.mock.MockBundleContext;
+import org.springframework.osgi.mock.MockFrameworkUtil;
+
+public class BundleScannerTest {
+
+ private static BundleScanner bundleScanner;
+ private static List<Bundle> bundles;
+ @Rule
+ public final TestName testName = new TestName();
+
+ @BeforeClass
+ public static void init() throws Exception {
+ bundles = makeMockBundles();
+ bundleScanner = new BundleScanner(bundles.toArray(new Bundle[bundles.size()]));
+ }
+
+ @AfterClass
+ public static void destroy() throws Exception {
+ }
+
+ @Before
+ public void setup() {
+ System.out.println("==== " + testName.getMethodName());
+ }
+
+ @Test
+ public void testValidateBundles() {
+ assertNotNull(bundleScanner);
+ BundleContext context = bundles.get(0).getBundleContext();
+ assertNotNull(context.getBundle());
+ assertNotNull(context.getBundles());
+ assertNotNull(context.getBundles().length >= 4);
+ }
+
+ @Test
+ public void testBundleEvents() throws Exception {
+ MockBundle newBundle = new TestMockBundle("misc", "", "bundle_misc");
+ assertTrue(bundleScanner.getAnnotatedClasses(
+ newBundle.getBundleContext(), null, false).size() == 0);
+ BundleEvent event = new BundleEvent(BundleEvent.RESOLVED, newBundle);
+ bundleScanner.bundleChanged(event);
+ assertTrue(bundleScanner.getAnnotatedClasses(
+ newBundle.getBundleContext(), null, false).size() == 1);
+ }
+
+ @Test
+ public void testAnnotatedClassesWithDependencies() throws Exception {
+ for (Bundle bundle : bundles) {
+ List<Class<?>> classes = bundleScanner.getAnnotatedClasses(
+ bundle.getBundleContext(), null, true);
+ String name = bundle.getSymbolicName();
+ System.out.println("name:" + name + " classes:" + classes.size());
+ if ("misc".equals(name)) {
+ assertTrue(classes.size() == 1);
+ } else if ("base".equals(name)) {
+ assertTrue(classes.size() == 5);
+ } else if ("sub1".equals(name)) {
+ assertTrue(classes.size() == 6);
+ } else if ("sub2".equals(name)) {
+ assertTrue(classes.size() == 7);
+ }
+ }
+ }
+
+ @Test
+ public void testExactFiltering() {
+ Bundle bundle = findBundle("sub1");
+ String[] annos = { "javax.xml.bind.annotation.XmlTransient" };
+ List<Class<?>> classes = bundleScanner.getAnnotatedClasses(
+ bundle.getBundleContext(), annos, true);
+ assertTrue(classes.size() == 1);
+ }
+
+ @Test
+ public void testNonExactFiltering() {
+ Bundle bundle = findBundle("sub1");
+ String[] annos = { "javax.xml.bind.annotation.*" };
+ List<Class<?>> classes = bundleScanner.getAnnotatedClasses(
+ bundle.getBundleContext(), annos, true);
+ assertTrue(classes.size() == 6);
+ }
+
+ @Test
+ public void testFilteringUnmatched() {
+ Bundle bundle = findBundle("sub1");
+ String[] annos = { "non.existent.pkg" };
+ List<Class<?>> classes = bundleScanner.getAnnotatedClasses(
+ bundle.getBundleContext(), annos, true);
+ assertTrue(classes.size() == 0);
+ }
+
+ @Test
+ public void testRegexMerge() {
+ Pattern pattern = BundleScanner.mergePatterns(
+ new String[] {
+ "javax.xml.bind.annotation.*",
+ "javax.ws.rs.Path"
+ },
+ true
+ );
+ assertTrue(pattern.matcher("Ljavax/xml/bind/annotation/FOO;").find());
+ assertFalse(pattern.matcher("Ljavax/servlet/FOO;").find());
+ }
+
+ private static Bundle findBundle(String symName) {
+ for (Bundle bundle : bundles) {
+ if (bundle.getSymbolicName().equals(symName)) return bundle;
+ }
+ return null;
+ }
+
+ private static List<Bundle> makeMockBundles() throws Exception {
+ List<Bundle> result = new ArrayList<Bundle>();
+ result.add(new MockBundle());
+ result.add(new TestMockBundle("base", "", "bundle_base"));
+ result.add(new TestMockBundle("sub1", "bundle_base", "bundle_sub1"));
+ result.add(new TestMockBundle("sub2", "bundle_base", "bundle_sub2"));
+ return result;
+ }
+
+ private static List<URL> findClasses(String pkg) throws URISyntaxException {
+ if (pkg == null) return Collections.EMPTY_LIST;
+ String npkg = pkg.replaceAll("\\.", "/");
+ URL dirUrl = BundleScannerTest.class.getClassLoader().getResource(npkg);
+ final List<URL> result = new ArrayList<URL>();
+ File dir = new File(dirUrl.toURI());
+ dir.listFiles(new FileFilter() {
+
+ @Override
+ public boolean accept(File file) {
+ if (file.isFile() && file.getName().endsWith(".class")) {
+ try {
+ result.add(file.toURI().toURL());
+ } catch (MalformedURLException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ return false;
+ }
+
+ });
+ return result;
+ }
+
+ public static class TestMockBundle extends MockBundle {
+ List<URL> classes;
+ public TestMockBundle(String name, String imports, String exports) throws Exception {
+ super(name, makeHeaders(name, imports, exports), new MockBundleContext() {
+ @Override
+ public Bundle[] getBundles() {
+ return bundles.toArray(new Bundle[bundles.size()]);
+ }
+ });
+ MockBundleContext ctx = (MockBundleContext) this.getBundleContext();
+ ctx.setBundle(this);
+ this.classes = findClasses(exports);
+ }
+
+ private static Dictionary<String,String> makeHeaders(
+ String name, String imports, String exports)
+ {
+ Dictionary<String,String> headers = new Hashtable<String,String>();
+ headers.put(Constants.IMPORT_PACKAGE, imports);
+ headers.put(Constants.EXPORT_PACKAGE, exports);
+ headers.put(Constants.BUNDLE_SYMBOLICNAME, name);
+ return headers;
+ }
+
+ @Override
+ public Enumeration findEntries(String path, String filePattern, boolean recurse) {
+ return Collections.enumeration(classes);
+ }
+
+ @Override
+ public long getBundleId() {
+ return hashCode();
+ }
+ }
+}
</Export-Package>
<Import-Package>
javax.ws.rs,
+ javax.ws.rs.ext,
javax.ws.rs.core,
+ javax.xml.bind,
javax.xml.bind.annotation,
+ org.objectweb.asm,
org.opendaylight.controller.sal.utils,
org.opendaylight.controller.sal.authorization,
org.opendaylight.controller.containermanager,
org.opendaylight.controller.usermanager,
+ org.opendaylight.controller.northbound.bundlescanner,
+ org.osgi.framework,
+ org.osgi.service.packageadmin,
+ org.osgi.util.tracker,
javax.servlet.http,
+ org.codehaus.jackson,
+ org.codehaus.jackson.jaxrs,
+ org.codehaus.jackson.map,
org.slf4j
</Import-Package>
</instructions>
<artifactId>usermanager</artifactId>
<version>0.4.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>bundlescanner</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
</dependencies>
</project>
--- /dev/null
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.northbound.commons;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.codehaus.jackson.JsonProcessingException;
+
+/**
+ * A custom exception mapper for handling Jackson JsonProcessingException types
+ */
+@Provider
+@Consumes({MediaType.APPLICATION_JSON, "text/json"})
+public class JacksonJsonProcessingExceptionMapper
+ implements ExceptionMapper<JsonProcessingException>
+{
+
+ @Override
+ public Response toResponse(JsonProcessingException exception) {
+ GenericEntity<String> entity =
+ new GenericEntity<String>(exception.getMessage()) {};
+ return Response.status(Response.Status.BAD_REQUEST).entity(entity).build();
+ }
+}
+
--- /dev/null
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.northbound.commons;
+
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.ext.ContextResolver;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
+import org.codehaus.jackson.map.DeserializationConfig;
+import org.opendaylight.controller.northbound.bundlescanner.IBundleScanService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceException;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Instance of javax.ws.rs.core.Application used to return the classes
+ * that will be instantiated for JAXRS processing. This hooks onto the
+ * bundle scanner service to provide JAXB classes to JAX-RS for prorcessing.
+ */
+@SuppressWarnings("unchecked")
+public class NorthboundApplication extends Application {
+ public static final String JAXRS_RESOURCES_MANIFEST_NAME = "Jaxrs-Resources";
+ private static final Logger LOGGER = LoggerFactory.getLogger(NorthboundApplication.class);
+
+ ////////////////////////////////////////////////////////////////
+ // Application overrides
+ ////////////////////////////////////////////////////////////////
+
+ @Override
+ public Set<Object> getSingletons() {
+ Set<Object> singletons = new HashSet<Object>();
+ singletons.add(new ContextResolver<JAXBContext>() {
+ @Override
+ public JAXBContext getContext(Class<?> type) {
+ return newJAXBContext();
+ }
+
+ } );
+ singletons.add(getJsonProvider());
+ singletons.add(new JacksonJsonProcessingExceptionMapper());
+ return singletons;
+ }
+
+ @Override
+ public Set<Class<?>> getClasses() {
+ Set<Class<?>> result = new HashSet<Class<?>>();
+ result.addAll(findJAXRSResourceClasses());
+ return result;
+ }
+
+ private static final JacksonJaxbJsonProvider getJsonProvider() {
+ JacksonJaxbJsonProvider jsonProvider = new JacksonJaxbJsonProvider();
+ jsonProvider.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,
+ false);
+ return jsonProvider;
+ }
+
+ private BundleContext getBundleContext() {
+ ClassLoader tlcl = Thread.currentThread().getContextClassLoader();
+ Bundle bundle = null;
+
+ if (tlcl instanceof BundleReference) {
+ bundle = ((BundleReference) tlcl).getBundle();
+ } else {
+ LOGGER.warn("Unable to determine the bundle context based on " +
+ "thread context classloader.");
+ bundle = FrameworkUtil.getBundle(this.getClass());
+ }
+ return (bundle == null ? null : bundle.getBundleContext());
+ }
+
+ private static final IBundleScanService lookupBundleScanner(BundleContext ctx) {
+ ServiceReference svcRef = ctx.getServiceReference(IBundleScanService.class);
+ if (svcRef == null) {
+ throw new ServiceException("Unable to lookup IBundleScanService");
+ }
+ return IBundleScanService.class.cast(ctx.getService(svcRef));
+ }
+
+ private final JAXBContext newJAXBContext() {
+ BundleContext ctx = getBundleContext();
+ IBundleScanService svc = lookupBundleScanner(ctx);
+ try {
+ List<Class<?>> cls = svc.getAnnotatedClasses(ctx,
+ new String[] { XmlRootElement.class.getPackage().getName() },
+ true);
+ return JAXBContext.newInstance(cls.toArray(new Class[cls.size()]));
+ } catch (JAXBException je) {
+ LOGGER.error("Error creating JAXBContext", je);
+ return null;
+ }
+ }
+
+ private final Set<Class<?>> findJAXRSResourceClasses() {
+ BundleContext ctx = getBundleContext();
+ String bundleName = ctx.getBundle().getSymbolicName();
+ Set<Class<?>> result = new HashSet<Class<?>>();
+ ServiceException recordException = null;
+ try {
+ IBundleScanService svc = lookupBundleScanner(ctx);
+ result.addAll(svc.getAnnotatedClasses(ctx,
+ new String[] { javax.ws.rs.Path.class.getName() }, false));
+ } catch (ServiceException se) {
+ recordException = se;
+ LOGGER.debug("Error finding JAXRS resource annotated classes in " +
+ "bundle: {} error: {}.", bundleName, se.getMessage());
+ // the bundle scan service cannot be lookedup. Lets attempt to
+ // lookup the resources from the bundle manifest header
+ Dictionary<String,String> headers = ctx.getBundle().getHeaders();
+ String header = headers.get(JAXRS_RESOURCES_MANIFEST_NAME);
+ if (header != null) {
+ for (String s : header.split(",")) {
+ s = s.trim();
+ if (s.length() > 0) {
+ try {
+ result.add(ctx.getBundle().loadClass(s));
+ } catch (ClassNotFoundException cnfe) {
+ LOGGER.error("Cannot load class: {} in bundle: {} " +
+ "defined as MANIFEST JAX-RS resource", s, bundleName, cnfe);
+ }
+ }
+ }
+ }
+
+ }
+
+ if (result.size() == 0) {
+ if (recordException != null) {
+ throw recordException;
+ } else {
+ throw new ServiceException("No resource classes found in bundle:" +
+ ctx.getBundle().getSymbolicName());
+ }
+ }
+ return result;
+ }
+
+}
!org.codehaus.enunciate.jaxrs
</Import-Package>
<Web-ContextPath>/controller/nb/v2/containermanager</Web-ContextPath>
+ <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
</instructions>
</configuration>
</plugin>
<Export-Package>
</Export-Package>
<Web-ContextPath>/controller/nb/v2/flowprogrammer</Web-ContextPath>
+ <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
+++ /dev/null
-
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.flowprogrammer.northbound;
-
-import java.util.HashSet;
-import java.util.Set;
-import javax.ws.rs.core.Application;
-
-import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
-
-/**
- * Instance of javax.ws.rs.core.Application used to return the classes
- * that will be instantiated for JAXRS processing, this is necessary
- * because the package scanning in jersey doesn't yet work in OSGi
- * environment.
- *
- */
-public class FlowProgrammerNorthboundRSApplication extends Application {
- @Override
- public Set<Class<?>> getClasses() {
- Set<Class<?>> classes = new HashSet<Class<?>>();
- classes.add(FlowProgrammerNorthbound.class);
- classes.add(JacksonJaxbJsonProvider.class);
- return classes;
- }
-}
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.flowprogrammer.northbound.FlowProgrammerNorthboundRSApplication</param-value>
+ <param-value>org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
!org.codehaus.enunciate.jaxrs
</Import-Package>
<Web-ContextPath>/controller/nb/v2/hosttracker</Web-ContextPath>
+ <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
+++ /dev/null
-
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.hosttracker.northbound;
-
-import java.util.HashSet;
-import java.util.Set;
-import javax.ws.rs.core.Application;
-
-import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
-
-/**
- * This class is an instance of javax.ws.rs.core.Application used to return the classes
- * that will be instantiated for JAXRS processing, this is necessary
- * because the package scanning in jersey doesn't yet work in OSGi
- * environment.
- *
- */
-public class HostTrackerNorthboundRSApplication extends Application {
- @Override
- public Set<Class<?>> getClasses() {
- Set<Class<?>> classes = new HashSet<Class<?>>();
- classes.add(HostTrackerNorthbound.class);
- classes.add(JacksonJaxbJsonProvider.class);
- return classes;
- }
-}
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.hosttracker.northbound.HostTrackerNorthboundRSApplication</param-value>
+ <param-value>org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<version>0.4.0-SNAPSHOT</version>
<pluginRepositories>
<pluginRepository>
- <id>central2</id>
- <name>central2</name>
- <url>http://repo2.maven.org/maven2</url>
+ <id>central</id>
+ <name>maven repo1</name>
+ <url>http://repo1.maven.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<version>0.4.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>bundlescanner</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>bundlescanner.implementation</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
<dependency>
<groupId>org.codehaus.enunciate</groupId>
<version>${url.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-all</artifactId>
+ <version>4.1</version>
+ </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.asm</artifactId>
// Test GET deleted subnet1
result = getJsonResult(baseURL + "default/subnet/" + name1);
Assert.assertEquals(404, httpResponseCode.intValue());
+
+ // TEST PUT bad subnet, expect 400, validate JSON exception mapper
+ JSONObject joBad = new JSONObject().put("foo", "bar");
+ result = getJsonResult(baseURL + "default/subnet/foo", "PUT", joBad.toString());
+ Assert.assertEquals(400, httpResponseCode.intValue());
}
@Test
mavenBundle("org.opendaylight.controller", "logging.bridge").versionAsInProject(),
// mavenBundle("org.opendaylight.controller", "clustering.test").versionAsInProject(),
mavenBundle("org.opendaylight.controller", "forwarding.staticrouting").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller", "bundlescanner").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller", "bundlescanner.implementation").versionAsInProject(),
// Northbound bundles
mavenBundle("org.opendaylight.controller", "commons.northbound").versionAsInProject(),
mavenBundle("org.ops4j.pax.exam", "pax-exam-link-mvn").versionAsInProject(),
mavenBundle("org.ops4j.pax.url", "pax-url-aether").versionAsInProject(),
+ mavenBundle("org.ow2.asm", "asm-all").versionAsInProject(),
+
mavenBundle("org.springframework", "org.springframework.asm").versionAsInProject(),
mavenBundle("org.springframework", "org.springframework.aop").versionAsInProject(),
mavenBundle("org.springframework", "org.springframework.context").versionAsInProject(),
<Export-Package>
</Export-Package>
<Web-ContextPath>/controller/nb/v2/networkconfig/bridgedomain</Web-ContextPath>
+ <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
+++ /dev/null
-
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.networkconfig.bridgedomain.northbound;
-
-import java.util.HashSet;
-import java.util.Set;
-import javax.ws.rs.core.Application;
-
-import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
-
-/**
- * Instance of javax.ws.rs.core.Application used to return the classes
- * that will be instantiated for JAXRS processing, this is necessary
- * because the package scanning in jersey doesn't yet work in OSGi
- * environment.
- *
- */
-public class BridgeDomainNorthboundApplication extends Application {
- @Override
- public Set<Class<?>> getClasses() {
- Set<Class<?>> classes = new HashSet<Class<?>>();
- classes.add(BridgeDomainNorthbound.class);
- classes.add(JacksonJaxbJsonProvider.class);
- return classes;
- }
-}
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.networkconfig.bridgedomain.northbound.BridgeDomainNorthboundApplication</param-value>
+ <param-value>org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<Export-Package>
</Export-Package>
<Web-ContextPath>/controller/nb/v2/staticroute</Web-ContextPath>
+ <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
+++ /dev/null
-
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.forwarding.staticrouting.northbound;
-
-import java.util.HashSet;
-import java.util.Set;
-import javax.ws.rs.core.Application;
-
-import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
-
-/**
- * Instance of javax.ws.rs.core.Application used to return the classes
- * that will be instantiated for JAXRS processing, this is necessary
- * because the package scanning in jersey doesn't yet work in OSGi
- * environment.
- *
- */
-public class StaticRoutingNorthboundRSApplication extends Application {
- @Override
- public Set<Class<?>> getClasses() {
- Set<Class<?>> classes = new HashSet<Class<?>>();
- classes.add(StaticRoutingNorthbound.class);
- classes.add(JacksonJaxbJsonProvider.class);
- return classes;
- }
-}
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.forwarding.staticrouting.northbound.StaticRoutingNorthboundRSApplication</param-value>
+ <param-value>org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<Export-Package>
</Export-Package>
<Web-ContextPath>/controller/nb/v2/statistics</Web-ContextPath>
+ <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.statistics.northbound;
-
-import java.util.HashSet;
-import java.util.Set;
-import javax.ws.rs.core.Application;
-
-import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
-
-/**
- * Instance of javax.ws.rs.core.Application used to return the classes
- * that will be instantiated for JAXRS processing, this is necessary
- * because the package scanning in jersey doesn't yet work in OSGi
- * environment.
- *
- */
-public class StatisticsNorthboundRSApplication extends Application {
- @Override
- public Set<Class<?>> getClasses() {
- Set<Class<?>> classes = new HashSet<Class<?>>();
- classes.add(StatisticsNorthbound.class);
- classes.add(JacksonJaxbJsonProvider.class);
- return classes;
- }
-}
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.statistics.northbound.StatisticsNorthboundRSApplication</param-value>
+ <param-value>org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<Export-Package>
</Export-Package>
<Web-ContextPath>/controller/nb/v2/subnetservice</Web-ContextPath>
+ <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.subnets.northbound;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.ws.rs.core.Application;
-
-import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
-
-/**
- * Instance of javax.ws.rs.core.Application used to return the classes
- * that will be instantiated for JAXRS processing, this is necessary
- * because the package scanning in jersey doesn't yet work in OSGi
- * environment.
- *
- */
-public class SubnetsNorthboundRSApplication extends Application {
- @Override
- public Set<Class<?>> getClasses() {
- Set<Class<?>> classes = new HashSet<Class<?>>();
- classes.add(SubnetsNorthbound.class);
- classes.add(JacksonJaxbJsonProvider.class);
- return classes;
- }
-}
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.subnets.northbound.SubnetsNorthboundRSApplication</param-value>
+ <param-value> org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
!org.codehaus.enunciate.jaxrs
</Import-Package>
<Web-ContextPath>/controller/nb/v2/switchmanager</Web-ContextPath>
+ <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
+++ /dev/null
-
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.switchmanager.northbound;
-
-import java.util.HashSet;
-import java.util.Set;
-import javax.ws.rs.core.Application;
-
-import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
-
-/**
- * Instance of javax.ws.rs.core.Application used to return the classes
- * that will be instantiated for JAXRS processing, this is necessary
- * because the package scanning in jersey doesn't yet work in OSGi
- * environment.
- *
- */
-public class SwitchNorthboundRSApplication extends Application {
- @Override
- public Set<Class<?>> getClasses() {
- Set<Class<?>> classes = new HashSet<Class<?>>();
- classes.add(SwitchNorthbound.class);
- classes.add(JacksonJaxbJsonProvider.class);
- return classes;
- }
-}
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.switchmanager.northbound.SwitchNorthboundRSApplication</param-value>
+ <param-value>org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
!org.codehaus.enunciate.jaxrs
</Import-Package>
<Web-ContextPath>/controller/nb/v2/topology</Web-ContextPath>
+ <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
+++ /dev/null
-
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.topology.northbound;
-
-import java.util.HashSet;
-import java.util.Set;
-import javax.ws.rs.core.Application;
-
-import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
-
-/**
- * Instance of javax.ws.rs.core.Application used to return the classes
- * that will be instantiated for JAXRS processing, this is necessary
- * because the package scanning in jersey doesn't yet work in OSGi
- * environment.
- *
- */
-public class TopologyNorthboundRSApplication extends Application {
- @Override
- public Set<Class<?>> getClasses() {
- Set<Class<?>> classes = new HashSet<Class<?>>();
- classes.add(TopologyNorthboundJAXRS.class);
- classes.add(JacksonJaxbJsonProvider.class);
- return classes;
- }
-}
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.topology.northbound.TopologyNorthboundRSApplication</param-value>
+ <param-value>org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
* until the Barrier reply arrives.
*/
public Object asyncSendBarrierMessage();
+
+ /**
+ * Send a FLOW_MOD message with a wildcard match and action=DELETE.
+ */
+ public void deleteAllFlows();
}
// send feature request
OFMessage featureRequest = factory.getMessage(OFType.FEATURES_REQUEST);
asyncFastSend(featureRequest);
- // delete all pre-existing flows
- OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL);
- OFFlowMod flowMod = (OFFlowMod) factory.getMessage(OFType.FLOW_MOD);
- flowMod.setMatch(match).setCommand(OFFlowMod.OFPFC_DELETE).setOutPort(OFPort.OFPP_NONE)
- .setLength((short) OFFlowMod.MINIMUM_LENGTH);
- asyncFastSend(flowMod);
this.state = SwitchState.WAIT_FEATURES_REPLY;
startSwitchTimer();
break;
return result;
}
}
+
+ @Override
+ public void deleteAllFlows() {
+ logger.trace("deleteAllFlows on switch {}", HexString.toHexString(this.sid));
+ OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL);
+ OFFlowMod flowMod = (OFFlowMod) factory.getMessage(OFType.FLOW_MOD);
+ flowMod.setMatch(match).setCommand(OFFlowMod.OFPFC_DELETE).setOutPort(OFPort.OFPP_NONE)
+ .setLength((short) OFFlowMod.MINIMUM_LENGTH);
+ asyncFastSend(flowMod);
+ }
}
flow.setPriority(ofFlowStat.getPriority());
flow.setIdleTimeout(ofFlowStat.getIdleTimeout());
flow.setHardTimeout(ofFlowStat.getHardTimeout());
+ flow.setId(ofFlowStat.getCookie());
flowOnNode = new FlowOnNode(flow);
flowOnNode.setByteCount(ofFlowStat.getByteCount());
flowOnNode.setPacketCount(ofFlowStat.getPacketCount());
flow.setPriority(v6StatsReply.getPriority());
flow.setIdleTimeout(v6StatsReply.getIdleTimeout());
flow.setHardTimeout(v6StatsReply.getHardTimeout());
+ flow.setId(v6StatsReply.getCookie());
flowOnNode = new FlowOnNode(flow);
flowOnNode.setByteCount(v6StatsReply.getByteCount());
flowOnNode.setPacketCount(v6StatsReply.getPacketCount());
if (sw == null) {
return;
}
+ Node node = NodeCreator.createOFNode(sw.getId());
+ if ((nodeProps.get(node) != null) && (connectionOutService.isLocal(node))) {
+ logger.debug("Ignore switchAdded {}", sw);
+ return;
+ }
// Add all the nodeConnectors of this switch
Map<NodeConnector, Set<Property>> ncProps = InventoryServiceHelper
for (String container : containers) {
notifyInventoryShimInternalListener(container, node, type, props);
}
-
// Notify external listener
notifyInventoryShimExternalListener(node, type, props);
props.add(b);
}
+ if ((nodeProps.get(node) == null) && (connectionOutService.isLocal(node))) {
+ // The switch is connected for the first time, flush all flows
+ // that may exist on this switch
+ sw.deleteAllFlows();
+ }
nodeProps.put(node, props);
// Notify all internal and external listeners
notifyInventoryShimListener(node, type, props);
}
public static AppRoleLevel fromString(String levelString) {
- for (AppRoleLevel level : AppRoleLevel.values()) {
- if (level.toString().equals(levelString)) {
- return level;
+ for (AppRoleLevel rolelevel : AppRoleLevel.values()) {
+ if (rolelevel.toString().equals(levelString)) {
+ return rolelevel;
}
}
return null;
@Override
public String toString() {
if (this.nodeType.equals(NodeIDType.OPENFLOW)) {
- return this.nodeType.toString() + "|"
+ return this.nodeType + "|"
+ HexEncode.longToHexString((Long) this.nodeID);
} else {
- return this.nodeType.toString() + "|" + this.nodeID.toString();
+ return this.nodeType + "|" + this.nodeID.toString();
}
}
.equals(NodeConnectorIDType.SWSTACK) ||
this.nodeConnectorType
.equals(NodeConnectorIDType.HWPATH)) {
- return this.nodeConnectorType.toString();
+ return this.nodeConnectorType;
} else {
- return this.nodeConnectorType.toString() + "|"
+ return this.nodeConnectorType + "|"
+ this.nodeConnectorID.toString();
}
}
}
public String getNodeTableIdAsString() {
- return this.nodeTableType.toString() + "|"
+ return this.nodeTableType + "|"
+ this.nodeTableID.toString();
}
Flow flow = new Flow(match, actions);
flow.setPriority((short) 100);
flow.setHardTimeout((short) 360);
+ flow.setId(1234L);
return flow;
}
Flow flow = new Flow(match, actions);
flow.setPriority((short) 300);
flow.setHardTimeout((short) 240);
+ flow.setId(65536L);
return flow;
}
<pluginRepositories>
<pluginRepository>
- <id>central2</id>
- <name>central2</name>
- <url>http://repo2.maven.org/maven2</url>
+ <id>central</id>
+ <name>maven repo1</name>
+ <url>http://repo1.maven.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<name>ebr-bundles-external</name>
<url>${nexusproxy}/repositories/ebr-bundles-external/</url>
</repository>
- <repository>
- <id>central2</id>
- <name>central2</name>
- <url>http://repo2.maven.org/maven2</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- <releases>
- <enabled>true</enabled>
- </releases>
- </repository>
<repository>
<id>central</id>
<name>central</name>
--- /dev/null
+module opendaylight-group-types {
+ namespace "urn:opendaylight:group:types";
+ prefix group;
+
+ import ietf-inet-types {prefix inet;}
+ import ietf-yang-types {prefix yang;}
+ import opendaylight-flow-types {prefix flow-types;}
+
+ revision "2013-09-17" {
+ description "Initial revision of group service";
+ }
+
+ typedef group-ref {
+ type instance-identifier;
+ }
+
+ grouping group-types {
+ leaf group-type {
+ type enumeration {
+ enum group-all;
+ enum group_select;
+ enum group_indirect;
+ enum group_ff;
+ }
+ }
+ }
+
+ grouping group {
+
+ uses group-types;
+
+ leaf group-id {
+ type group-ref;
+ }
+
+ container buckets {
+ list bucket {
+ key "order";
+ leaf order {
+ type int32;
+ }
+
+ leaf weight {
+ type uint16;
+ }
+
+ leaf watch_port {
+ type uint32;
+ }
+
+ leaf watch_group {
+ type uint32;
+ }
+
+ container actions {
+ list action {
+ key "action-order";
+ leaf action-order {
+ type int32;
+ }
+
+ uses flow-types:action;
+ }
+ }
+ }
+ }
+ }
+
+ grouping group-statistics-request {
+ list group-stats {
+ key "group-id";
+
+ leaf group-id {
+ type int32;
+ }
+ }
+ }
+
+ grouping group-statistics {
+
+ leaf group-id {
+ type int32;
+ }
+
+ leaf ref-count {
+ type yang:counter32;
+ }
+
+ leaf packet-count {
+ type yang:counter64;
+ }
+
+ leaf byte-count {
+ type yang:counter64;
+ }
+
+ container duration {
+ leaf second {
+ type yang:counter32;
+ }
+ leaf nanosecond {
+ type yang:counter32;
+ }
+ }
+
+ container buckets {
+ list bucket-counter {
+ key "order";
+ leaf order {
+ type int32;
+ }
+
+ leaf packet-count {
+ type yang:counter64;
+ }
+
+ leaf byte-count {
+ type yang:counter64;
+ }
+ }
+ }
+ }
+
+ grouping group-statistics-reply {
+ list group-stats {
+ key "group-stats-order";
+ leaf group-stats-order {
+ type int32;
+ }
+
+ uses group-statistics;
+ }
+ }
+
+ grouping group-desc-stats {
+ list group-desc-stats {
+ key "order-id";
+
+ leaf order-id {
+ type int32;
+ }
+
+ uses group;
+ }
+ }
+
+ grouping group-features {
+ list group-features {
+ key "order";
+ leaf order {
+ type int32;
+ }
+
+ uses group-types;
+ type capabilities {
+ enum select-weight;
+ enum select-liveness;
+ enum chaining;
+ enum chaining-checks;
+ }
+
+ leaf-list max-groups {
+ type uint32;
+ description "Maximum number of groups for each type";
+ max-elements 4;
+ }
+
+ leaf-list actions {
+ type uint32;
+ description "Bitmap number OFPAT_* that are supported";
+ max-elements 4;
+ }
+ }
+ }
+}
\ No newline at end of file
grouping "ip-match-fields" {
leaf ip-protocol {
description "IP protocol.";
- type uint8; // TODO define IP protocol number
+ type uint8;
}
leaf ip-dscp {
description "IP DSCP (6 bits in ToS field).";
- type inet:dscp; // TODO: Define DSCP type
+ type inet:dscp;
}
leaf ip-ecn {
description "IP ECN (2 bits in ToS field).";
- type uint8; // TODO define ECN
+ type uint8;
}
}
description "SCTP source port.";
type inet:port-number;
}
- leaf sctp-destination-dst {
+ leaf sctp-destination-port {
description "SCTP destination port.";
type inet:port-number;
}
--- /dev/null
+module sal-group {
+ namespace "urn:opendaylight:group:service";
+ prefix group;
+
+ import yang-ext {prefix ext;}
+ import opendaylight-inventory {prefix inv;}
+ import ietf-inet-types {prefix inet;}
+ import opendaylight-group-types {prefix group-type;}
+
+ revision "2013-09-17" {
+ description "Initial revision of group service";
+ }
+
+ grouping node-group {
+ leaf node {
+ type inv:node-ref;
+ }
+
+ uses group-type:group;
+ }
+
+ /** Base configuration structure **/
+ grouping group-update {
+ container original-group {
+ uses group-type:group;
+ }
+ container updated-group {
+ uses group-type:group;
+ }
+ }
+
+ rpc add-group {
+ input {
+ uses node-group;
+ }
+ }
+
+ rpc remove-group {
+ input {
+ uses node-group;
+ }
+ }
+
+ rpc update-group {
+ input {
+ uses node-group;
+ }
+ }
+}
\ No newline at end of file
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>1.7.2</slf4j.version>
<nexusproxy>http://nexus.opendaylight.org/content</nexusproxy>
- <yang.version>0.5.7-SNAPSHOT</yang.version>
+ <yang.version>0.5.8-SNAPSHOT</yang.version>
<maven.bundle.version>2.4.0</maven.bundle.version>
<releaseplugin.version>2.3.2</releaseplugin.version>
<guava.version>14.0.1</guava.version>
<pluginRepositories>
<pluginRepository>
- <id>central2</id>
- <name>central2</name>
- <url>http://repo2.maven.org/maven2</url>
+ <id>central</id>
+ <name>maven repo1</name>
+ <url>http://repo1.maven.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<repositories>
- <repository>
- <id>central2</id>
- <name>central2</name>
- <url>http://repo2.maven.org/maven2</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- <releases>
- <enabled>true</enabled>
- </releases>
- </repository>
<repository>
<id>central</id>
<name>central</name>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-parent</artifactId>
- <version>1.0-SNAPSHOT</version>
- </parent>
- <artifactId>sal-binding-broker-impl</artifactId>
- <packaging>bundle</packaging>
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <packaging>bundle</packaging>
<scm>
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
</scm>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <version>${maven.bundle.version}</version>
- <extensions>true</extensions>
- <configuration>
- <instructions>
- <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
- <Bundle-Activator>org.opendaylight.controller.sal.binding.impl.BrokerActivator</Bundle-Activator>
- <Private-Package>
- org.opendaylight.controller.sal.binding.impl,
- org.opendaylight.controller.sal.binding.impl.utils,
- org.eclipse.xtend2.lib,
- org.eclipse.xtext.xbase.*
- </Private-Package>
- </instructions>
- </configuration>
- </plugin>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${maven.bundle.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Bundle-Activator>org.opendaylight.controller.sal.binding.impl.BrokerActivator</Bundle-Activator>
+ <Private-Package>
+ org.opendaylight.controller.sal.binding.impl,
+ org.opendaylight.controller.sal.binding.impl.*,
+ org.opendaylight.controller.sal.binding.codegen.*,
+ org.eclipse.xtend2.lib,
+ org.eclipse.xtend.lib,
+ org.eclipse.xtext.xbase.*
+ </Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
- <plugin>
- <groupId>org.eclipse.xtend</groupId>
- <artifactId>xtend-maven-plugin</artifactId>
- <version>2.4.2</version>
- <executions>
- <execution>
- <goals>
- <goal>compile</goal>
- </goals>
- <configuration>
- <outputDirectory>${basedir}/src/main/xtend-gen</outputDirectory>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <artifactId>maven-clean-plugin</artifactId>
- <version>2.4.1</version>
- <configuration>
- <filesets>
- <fileset>
- <directory>${basedir}/src/main/xtend-gen</directory>
- <includes>
- <include>**</include>
- </includes>
- </fileset>
- </filesets>
- </configuration>
- </plugin>
- </plugins>
- </build>
+ <plugin>
+ <groupId>org.eclipse.xtend</groupId>
+ <artifactId>xtend-maven-plugin</artifactId>
+ <version>2.4.2</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${basedir}/src/main/xtend-gen</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-clean-plugin</artifactId>
+ <version>2.4.1</version>
+ <configuration>
+ <filesets>
+ <fileset>
+ <directory>${basedir}/src/main/xtend-gen</directory>
+ <includes>
+ <include>**</include>
+ </includes>
+ </fileset>
+ </filesets>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-api</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
- <!-- >dependency> <groupId>org.opendaylight.controller</groupId> <artifactId>sal-core-api</artifactId>
- <version>1.0-SNAPSHOT</version> </dependency -->
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <!-- >dependency> <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
- <version>5.0.0</version>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.reflections</groupId>
- <artifactId>reflections</artifactId>
- <version>0.9.9-RC1</version>
- </dependency>
- <dependency>
- <groupId>org.javassist</groupId>
- <artifactId>javassist</artifactId>
- <version>3.17.1-GA</version>
- </dependency>
- <dependency>
- <groupId>org.eclipse.xtend</groupId>
- <artifactId>org.eclipse.xtend.lib</artifactId>
- <version>2.4.2</version>
- </dependency>
- </dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>5.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.reflections</groupId>
+ <artifactId>reflections</artifactId>
+ <version>0.9.9-RC1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.javassist</groupId>
+ <artifactId>javassist</artifactId>
+ <version>3.17.1-GA</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.xtend</groupId>
+ <artifactId>org.eclipse.xtend.lib</artifactId>
+ <version>2.4.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
</project>
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen
+
+import java.util.Map
+
+import org.opendaylight.yangtools.yang.binding.BaseIdentity
+import org.opendaylight.yangtools.yang.binding.RpcService
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+
+import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
+
+class RuntimeCodeHelper {
+ /**
+ * Helper method to return delegate from ManagedDirectedProxy with use of reflection.
+ *
+ * Note: This method uses reflection, but access to delegate field should be
+ * avoided and called only if neccessary.
+ *
+ */
+ public static def <T extends RpcService> getDelegate(RpcService proxy) {
+ val field = proxy.class.getField(DELEGATE_FIELD)
+ if (field == null) throw new UnsupportedOperationException("Unable to get delegate from proxy");
+ return field.get(proxy) as T
+ }
+
+ /**
+ * Helper method to set delegate to ManagedDirectedProxy with use of reflection.
+ *
+ * Note: This method uses reflection, but setting delegate field should not occur too much
+ * to introduce any significant performance hits.
+ *
+ */
+ public static def void setDelegate(RpcService proxy, RpcService delegate) {
+ val field = proxy.class.getField(DELEGATE_FIELD)
+ if (field == null) throw new UnsupportedOperationException("Unable to set delegate to proxy");
+ if (field.type.isAssignableFrom(delegate.class)) {
+ field.set(proxy, delegate)
+ } else
+ throw new IllegalArgumentException("delegate class is not assignable to proxy");
+ }
+
+ public static def Map<InstanceIdentifier, ? extends RpcService> getRoutingTable(RpcService target,
+ Class<? extends BaseIdentity> tableClass) {
+ val field = target.class.getField(tableClass.routingTableField)
+ if (field == null) throw new UnsupportedOperationException(
+ "Unable to get routing table. Table field does not exists");
+ return field.get(target) as Map<InstanceIdentifier, ? extends RpcService>;
+ }
+
+ public static def void setRoutingTable(RpcService target, Class<? extends BaseIdentity> tableClass,
+ Map<InstanceIdentifier, ? extends RpcService> routingTable) {
+ val field = target.class.getField(tableClass.routingTableField)
+ if (field == null) throw new UnsupportedOperationException(
+ "Unable to set routing table. Table field does not exists");
+ field.set(target,routingTable);
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen
+
+import org.opendaylight.yangtools.yang.binding.RpcService
+import org.opendaylight.yangtools.yang.binding.BaseIdentity
+import org.opendaylight.yangtools.yang.binding.NotificationListener
+
+/**
+ *
+ *
+ */
+class RuntimeCodeSpecification {
+
+ public static val PACKAGE_PREFIX = "_gen.";
+
+ public static val DIRECT_PROXY_SUFFIX = "DirectProxy";
+ public static val ROUTER_SUFFIX = "Router";
+ public static val INVOKER_SUFFIX = "Invoker";
+
+ public static val DELEGATE_FIELD = "_delegate"
+ public static val ROUTING_TABLE_FIELD_PREFIX = "_routes_"
+
+ public static def getInvokerName(Class<? extends NotificationListener> listener) {
+ getGeneratedName(listener, INVOKER_SUFFIX);
+ }
+
+ /**
+ * Returns a name for DirectProxy implementation
+ *
+ *
+ */
+ public static def getDirectProxyName(Class<? extends RpcService> base) {
+ getGeneratedName(base, DIRECT_PROXY_SUFFIX);
+ }
+
+ /**
+ * Returns a name for Router implementation
+ *
+ */
+ public static def getRouterName(Class<? extends RpcService> base) {
+ getGeneratedName(base, ROUTER_SUFFIX);
+ }
+
+ /**
+ * Returns a name for generated interface
+ *
+ */
+ public static def getGeneratedName(Class<?> cls, String suffix) {
+ '''«PACKAGE_PREFIX»«cls.package.name».«cls.simpleName»$«suffix»'''.toString()
+ }
+
+ /**
+ * Returns a field name for specified routing context
+ *
+ */
+ public static def getRoutingTableField(Class<? extends BaseIdentity> routingContext) {
+ return '''_routes_«routingContext.simpleName»'''.toString;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen
+
+import java.lang.reflect.Method
+import org.opendaylight.yangtools.yang.binding.Notification
+
+public static class YangtoolsMappingHelper {
+
+ public static def boolean isNotificationCallback(Method it) {
+ return name.startsWith("on") && parameterTypes.size === 1 &&
+ Notification.isAssignableFrom(parameterTypes.get(0))
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen.impl;
+
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+
+public class JavassistUtils {
+
+ public static interface ClassGenerator {
+ void process(CtClass cls);
+ }
+
+ public static interface MethodGenerator {
+ void process(CtMethod method);
+ }
+
+ public static interface FieldGenerator {
+ void process(CtField field);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen.impl
+
+import org.opendaylight.yangtools.yang.binding.BaseIdentity
+import javassist.CtMethod
+import java.lang.reflect.Method
+
+@Data
+class RoutingPair {
+
+ @Property
+ val Class<? extends BaseIdentity> context;
+ @Property
+ val CtMethod getter;
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen.impl
+
+import javassist.ClassPool
+import org.opendaylight.yangtools.yang.binding.RpcService
+
+import javassist.CtClass
+import static com.google.common.base.Preconditions.*
+
+import javassist.CtField
+import javassist.Modifier
+import javassist.CtMethod
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext
+import org.opendaylight.yangtools.yang.binding.BaseIdentity
+
+import java.util.Map
+import java.util.HashMap
+import javassist.NotFoundException
+import javassist.LoaderClassPath
+import org.opendaylight.controller.sal.binding.codegen.impl.JavassistUtils.MethodGenerator
+import org.opendaylight.controller.sal.binding.codegen.impl.JavassistUtils.ClassGenerator
+import org.opendaylight.yangtools.yang.binding.NotificationListener
+import org.opendaylight.yangtools.yang.binding.Notification
+import java.util.Arrays
+
+import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
+import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
+
+class RuntimeCodeGenerator {
+
+ val ClassPool classPool;
+
+ public new(ClassPool pool) {
+ classPool = pool;
+ }
+
+ def <T extends RpcService> Class<? extends T> generateDirectProxy(Class<T> iface) {
+ val supertype = iface.asCtClass
+ val targetCls = createClass(iface.directProxyName, supertype) [
+ field(DELEGATE_FIELD, iface);
+ implementMethodsFrom(supertype) [
+ body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
+ ]
+ ]
+ return targetCls.toClass(iface.classLoader)
+ }
+
+ def <T extends RpcService> Class<? extends T> generateRouter(Class<T> iface) {
+ val supertype = iface.asCtClass
+ val targetCls = createClass(iface.routerName, supertype) [
+ //field(ROUTING_TABLE_FIELD,Map)
+ field(DELEGATE_FIELD, iface)
+ val contexts = new HashMap<String, Class<? extends BaseIdentity>>();
+ // We search for routing pairs and add fields
+ supertype.methods.filter[declaringClass == supertype && parameterTypes.size === 1].forEach [ method |
+ val routingPair = method.routingContextInput;
+ if (routingPair !== null)
+ contexts.put(routingPair.context.routingTableField, routingPair.context);
+ ]
+ for (ctx : contexts.entrySet) {
+ field(ctx.key, Map)
+ }
+ implementMethodsFrom(supertype) [
+ if (parameterTypes.size === 1) {
+ val routingPair = routingContextInput;
+ val bodyTmp = '''
+ {
+ final «InstanceIdentifier.name» identifier = $1.«routingPair.getter.name»();
+ «supertype.name» instance = («supertype.name») «routingPair.context.routingTableField».get(identifier);
+ if(instance == null) {
+ instance = «DELEGATE_FIELD»;
+ }
+ return ($r) instance.«it.name»($$);
+ }'''
+ body = bodyTmp
+ } else if (parameterTypes.size === 0) {
+ body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
+ }
+ ]
+ ]
+ return targetCls.toClass(iface.classLoader)
+ }
+
+ def Class<?> generateListenerInvoker(Class<? extends NotificationListener> iface) {
+ val targetCls = createClass(iface.invokerName) [
+ field(DELEGATE_FIELD, iface)
+ it.method(Void, "invoke", Notification) [
+ val callbacks = iface.methods.filter[notificationCallback]
+ body = '''
+ {
+ «FOR callback : callbacks SEPARATOR " else "»
+ if($1 instanceof «val cls = callback.parameterTypes.get(0).name») {
+ «DELEGATE_FIELD».«callback.name»((«cls») $1);
+ return;
+ }
+ «ENDFOR»
+ }
+ '''
+ ]
+ ]
+ return targetCls.toClass(iface.classLoader);
+ }
+
+ def void method(CtClass it, Class<?> returnType, String name, Class<?> parameter, MethodGenerator function1) {
+ val method = new CtMethod(returnType.asCtClass, name, Arrays.asList(parameter.asCtClass), it);
+ function1.process(method);
+ it.addMethod(method);
+ }
+
+ private def routingContextInput(CtMethod method) {
+ val inputClass = method.parameterTypes.get(0);
+ return inputClass.contextInstance;
+ }
+
+ private def RoutingPair getContextInstance(CtClass dataClass) {
+ for (method : dataClass.methods) {
+ if (method.parameterTypes.size === 0 && method.name.startsWith("get")) {
+ for (annotation : method.availableAnnotations) {
+ if (annotation instanceof RoutingContext) {
+ return new RoutingPair((annotation as RoutingContext).value, method)
+ }
+ }
+ }
+ }
+ for (iface : dataClass.interfaces) {
+ val ret = getContextInstance(iface);
+ if (ret != null) return ret;
+ }
+ return null;
+ }
+
+ private def void implementMethodsFrom(CtClass target, CtClass source, MethodGenerator function1) {
+ for (method : source.methods) {
+ if (method.declaringClass == source) {
+ val redeclaredMethod = new CtMethod(method, target, null);
+ function1.process(redeclaredMethod);
+ target.addMethod(redeclaredMethod);
+ }
+ }
+ }
+
+ private def CtClass createClass(String fqn, ClassGenerator cls) {
+ val target = classPool.makeClass(fqn);
+ cls.process(target);
+ return target;
+ }
+
+ private def CtClass createClass(String fqn, CtClass superInterface, ClassGenerator cls) {
+ val target = classPool.makeClass(fqn);
+ target.implementsType(superInterface);
+ cls.process(target);
+ return target;
+ }
+
+ private def void implementsType(CtClass it, CtClass supertype) {
+ checkArgument(supertype.interface, "Supertype must be interface");
+ addInterface(supertype);
+ }
+
+ private def asCtClass(Class<?> class1) {
+ classPool.get(class1);
+ }
+
+ private def CtField field(CtClass it, String name, Class<?> returnValue) {
+ val field = new CtField(returnValue.asCtClass, name, it);
+ field.modifiers = Modifier.PUBLIC
+ addField(field);
+ return field;
+ }
+
+ def get(ClassPool pool, Class<?> cls) {
+ try {
+ return pool.get(cls.name)
+ } catch (NotFoundException e) {
+ pool.appendClassPath(new LoaderClassPath(cls.classLoader));
+ return pool.get(cls.name)
+ }
+ }
+}
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider
import org.opendaylight.yangtools.yang.binding.RpcService
import javassist.ClassPool
-import javassist.CtMethod
-import javassist.CtField
import org.osgi.framework.BundleContext
import java.util.Map
import java.util.HashMap
import javassist.LoaderClassPath
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker
import java.util.Hashtable
+import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.*
-import static extension org.opendaylight.controller.sal.binding.impl.utils.PropertiesUtils.*
-import static extension org.opendaylight.controller.sal.binding.impl.utils.GeneratorUtils.*
import org.opendaylight.controller.sal.binding.api.NotificationProviderService
import org.osgi.framework.ServiceRegistration
-import org.opendaylight.controller.sal.binding.impl.utils.PropertiesUtils
+import static org.opendaylight.controller.sal.binding.impl.osgi.Constants.*
+import static extension org.opendaylight.controller.sal.binding.impl.osgi.PropertiesUtils.*
import org.opendaylight.controller.sal.binding.api.NotificationService
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext
-import javassist.Modifier
+
import org.slf4j.LoggerFactory
+import org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator
class BindingAwareBrokerImpl implements BindingAwareBroker {
- private static val DELEGATE_FIELD = "_delegate"
private static val log = LoggerFactory.getLogger(BindingAwareBrokerImpl)
private val clsPool = ClassPool.getDefault()
+ private var RuntimeCodeGenerator generator;
private Map<Class<? extends RpcService>, RpcProxyContext> managedProxies = new HashMap();
private var NotificationBrokerImpl notifyBroker
private var ServiceRegistration<NotificationProviderService> notifyBrokerRegistration
// Initialization of notificationBroker
notifyBroker = new NotificationBrokerImpl(null);
- val brokerProperties = PropertiesUtils.newProperties();
+ val brokerProperties = newProperties();
notifyBrokerRegistration = brokerBundleContext.registerService(NotificationProviderService, notifyBroker,
brokerProperties)
brokerBundleContext.registerService(NotificationService, notifyBroker, brokerProperties)
def initGenerator() {
// YANG Binding Class Loader
- clsPool.appendClassPath(new LoaderClassPath(RpcService.classLoader))
+ clsPool.appendClassPath(new LoaderClassPath(RpcService.classLoader));
+ generator = new RuntimeCodeGenerator(clsPool);
}
override registerConsumer(BindingAwareConsumer consumer, BundleContext bundleCtx) {
if ((existing = managedProxies.get(service)) != null) {
return existing.proxy
}
- val proxyClass = service.generateDirectProxy()
+ val proxyClass = generator.generateDirectProxy(service)
val rpcProxyCtx = new RpcProxyContext(proxyClass)
val properties = new Hashtable<String, String>()
rpcProxyCtx.proxy = proxyClass.newInstance as RpcService
- properties.salServiceType = Constants.SAL_SERVICE_TYPE_CONSUMER_PROXY
+ properties.salServiceType = SAL_SERVICE_TYPE_CONSUMER_PROXY
rpcProxyCtx.registration = brokerBundleContext.registerService(service, rpcProxyCtx.proxy as T, properties)
managedProxies.put(service, rpcProxyCtx)
return rpcProxyCtx.proxy
}
-
- protected def generateDirectProxy(Class<? extends RpcService> delegate) {
- val targetFqn = delegate.generatedName(Constants.PROXY_DIRECT_SUFFIX)
- log.debug("Generating DirectProxy for {} Proxy name: {}",delegate,targetFqn);
- val objCls = clsPool.get(Object)
- val delegateCls = clsPool.get(delegate)
- val proxyCls = clsPool.makeClass(targetFqn)
- proxyCls.addInterface(delegateCls)
- val delField = new CtField(delegateCls, DELEGATE_FIELD, proxyCls);
- delField.modifiers = Modifier.PUBLIC
- proxyCls.addField(delField)
- delegateCls.methods.filter[it.declaringClass != objCls].forEach [
- val proxyMethod = new CtMethod(it, proxyCls, null);
- proxyMethod.body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
- proxyCls.addMethod(proxyMethod)
- ]
- return proxyCls.toClass(delegate.classLoader)
- }
-
/**
* Registers RPC Implementation
*
proxy.delegate = service;
return new RpcServiceRegistrationImpl<T>(type, service, osgiReg);
}
-
- /**
- * Helper method to return delegate from ManagedDirectedProxy with use of reflection.
- *
- * Note: This method uses reflection, but access to delegate field should be
- * avoided and called only if neccessary.
- *
- */
- def <T extends RpcService> getDelegate(RpcService proxy) {
- val field = proxy.class.getField(DELEGATE_FIELD)
- if(field == null) throw new UnsupportedOperationException("Unable to get delegate from proxy");
- return field.get(proxy) as T
- }
-
- /**
- * Helper method to set delegate to ManagedDirectedProxy with use of reflection.
- *
- * Note: This method uses reflection, but setting delegate field should not occur too much
- * to introduce any significant performance hits.
- *
- */
- def void setDelegate(RpcService proxy, RpcService delegate) {
- val field = proxy.class.getField(DELEGATE_FIELD)
- if(field == null) throw new UnsupportedOperationException("Unable to set delegate to proxy");
- if (field.type.isAssignableFrom(delegate.class)) {
- field.set(proxy,delegate)
- } else throw new IllegalArgumentException("delegate class is not assignable to proxy");
- }
-
-
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl
+
+import org.opendaylight.controller.sal.common.DataStoreIdentifier
+import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
+
+class DataProviderContext {
+
+ @Property
+ var DataStoreIdentifier identifier;
+ @Property
+ var RuntimeDataProvider provider;
+}
notification.class.interfaces.filter[it != Notification && Notification.isAssignableFrom(it)]
}
+ @SuppressWarnings("unchecked")
def notifyAll(Collection<NotificationListener<?>> listeners, Notification notification) {
listeners.forEach[(it as NotificationListener).onNotification(notification)]
}
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
package org.opendaylight.controller.sal.binding.impl;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.slf4j.LoggerFactory
+import static org.opendaylight.controller.sal.binding.impl.osgi.Constants.*
class OsgiConsumerContext implements ConsumerContext {
- static val log = LoggerFactory.getLogger(OsgiConsumerContext)
- protected val BundleContext bundleContext;
- protected val BindingAwareBrokerImpl broker;
-
- new(BundleContext ctx,BindingAwareBrokerImpl broker) {
- this.bundleContext = ctx;
- this.broker = broker;
- }
-
-
- override def <T extends BindingAwareService> getSALService(Class<T> service) {
- // SAL Services are global
- var ref = bundleContext.getServiceReference(service);
- return bundleContext.getService(ref) as T;
- }
-
-
-
- override def <T extends RpcService> T getRpcService(Class<T> module) {
- try {
-
- val services = bundleContext.getServiceReferences(module, getProxyFilter());
-
- // Proxy service found / using first implementation
- // FIXME: Add advanced logic to retrieve service with right set of models
- if(false == services.empty) {
- val ref = services.iterator().next() as ServiceReference<T>;
- return bundleContext.getService(ref) as T;
- }
- } catch (InvalidSyntaxException e) {
- log.error("Created filter was invalid:", e.message,e)
- }
- return null;
-
-
- }
-
- private def getProxyFilter() {
- return '''(«Constants.SAL_SERVICE_TYPE»=«Constants.SAL_SERVICE_TYPE_CONSUMER_PROXY»)'''
- }
+ static val log = LoggerFactory.getLogger(OsgiConsumerContext)
+ protected val BundleContext bundleContext;
+ protected val BindingAwareBrokerImpl broker;
+
+ new(BundleContext ctx, BindingAwareBrokerImpl broker) {
+ this.bundleContext = ctx;
+ this.broker = broker;
+ }
+
+ override def <T extends BindingAwareService> getSALService(Class<T> service) {
+
+ // SAL Services are global
+ var ref = bundleContext.getServiceReference(service);
+ return bundleContext.getService(ref) as T;
+ }
+
+ override def <T extends RpcService> T getRpcService(Class<T> module) {
+ try {
+
+ val services = bundleContext.getServiceReferences(module, getProxyFilter());
+
+ // Proxy service found / using first implementation
+ // FIXME: Add advanced logic to retrieve service with right set of models
+ if (false == services.empty) {
+ val ref = services.iterator().next() as ServiceReference<T>;
+ return bundleContext.getService(ref) as T;
+ }
+ } catch (InvalidSyntaxException e) {
+ log.error("Created filter was invalid:", e.message, e)
+ }
+ return null;
+
+ }
+
+ private def getProxyFilter() {
+ return '''(«SAL_SERVICE_TYPE»=«SAL_SERVICE_TYPE_CONSUMER_PROXY»)'''
+ }
}
import org.opendaylight.yangtools.yang.binding.RpcService;
import org.osgi.framework.BundleContext;
-import static extension org.opendaylight.controller.sal.binding.impl.utils.PropertiesUtils.*;
+import static org.opendaylight.controller.sal.binding.impl.osgi.Constants.*;
+import static extension org.opendaylight.controller.sal.binding.impl.osgi.PropertiesUtils.*;
class OsgiProviderContext extends OsgiConsumerContext implements ProviderContext {
// TODO Auto-generated method stub
val properties = new Hashtable<String, String>();
- properties.salServiceType = Constants.SAL_SERVICE_TYPE_PROVIDER
+ properties.salServiceType = SAL_SERVICE_TYPE_PROVIDER
// Fill requirements
val salReg = broker.registerRpcImplementation(type, implementation, this, properties)
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl
+
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService
+import org.opendaylight.controller.sal.common.DataStoreIdentifier
+import org.opendaylight.yangtools.yang.binding.DataRoot
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService
+import org.opendaylight.controller.sal.binding.api.data.DataCommitHandler
+import org.opendaylight.controller.sal.binding.api.data.DataRefresher
+import org.opendaylight.controller.sal.binding.api.data.DataValidator
+import org.opendaylight.yangtools.yang.common.RpcResult
+import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
+import java.util.Map
+
+class _DataBrokerImpl implements DataProviderService {
+
+ Map<DataStoreIdentifier, DataProviderContext> dataProviders;
+ var DataProviderContext defaultDataProvider;
+
+ override <T extends DataRoot> getData(DataStoreIdentifier store, Class<T> rootType) {
+ val dataStore = resolveProvider(store, rootType);
+ return dataStore.provider.getData(store, rootType);
+ }
+
+ override <T extends DataRoot> getData(DataStoreIdentifier store, T filter) {
+ }
+
+ override <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, Class<T> rootType) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub");
+ }
+
+ override <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, T filter) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub");
+ }
+
+ override commit(DataStoreIdentifier store) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+ }
+
+ override editCandidateData(DataStoreIdentifier store, DataRoot changeSet) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+ }
+
+ override addCommitHandler(DataStoreIdentifier store, DataCommitHandler provider) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+ }
+
+ override addRefresher(DataStoreIdentifier store, DataRefresher refresher) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+ }
+
+ override addValidator(DataStoreIdentifier store, DataValidator validator) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+ }
+
+ override removeRefresher(DataStoreIdentifier store, DataRefresher refresher) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+ }
+
+ override removeCommitHandler(DataStoreIdentifier store, DataCommitHandler provider) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+
+ }
+
+ override removeValidator(DataStoreIdentifier store, DataValidator validator) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+ }
+
+ def DataProviderContext resolveProvider(DataStoreIdentifier store, Class<? extends DataRoot> root) {
+ }
+
+}
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.sal.binding.impl
+package org.opendaylight.controller.sal.binding.impl.osgi
class Constants {
public static val SAL_SERVICE_TYPE_CONSUMER_PROXY = "consumerProxy"
public static val SAL_SERVICE_TYPE_PROVIDER = "provider"
public static val SAL_SERVICE_TYPE_CONNECTOR = "connector"
-
- public static val PROXY_DIRECT_SUFFIX = "DirectProxy";
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl.osgi
+
+import java.util.Hashtable
+import static org.opendaylight.controller.sal.binding.impl.osgi.Constants.*
+
+class PropertiesUtils {
+
+ private new() {
+ }
+
+ static def setSalServiceType(Hashtable<String, String> properties, String value) {
+ properties.put(SAL_SERVICE_TYPE, value)
+ return properties
+ }
+
+ static def getSalServiceType(Hashtable<String, String> properties) {
+ return properties.get(SAL_SERVICE_TYPE)
+ }
+
+ static def newProperties() {
+ new Hashtable<String, String>()
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl.osgi;
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.binding.impl.utils
-
-import javassist.ClassPool
-import javassist.NotFoundException
-import javassist.LoaderClassPath
-
-class GeneratorUtils {
-
- static val PREFIX = "_gen.";
-
- public static def generatedName(Class<?> cls, String suffix) {
- '''«PREFIX»«cls.package.name».«cls.simpleName»$«suffix»'''.toString()
- }
-
- public static def get(ClassPool pool, Class<?> cls) {
- try {
- return pool.get(cls.name)
- } catch (NotFoundException e) {
- pool.appendClassPath(new LoaderClassPath(cls.classLoader));
- return pool.get(cls.name)
- }
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.binding.impl.utils
-
-import java.util.Hashtable
-import org.opendaylight.controller.sal.binding.impl.Constants
-
-class PropertiesUtils {
-
- private new() {}
-
- static def setSalServiceType(Hashtable<String,String> properties, String value) {
- properties.put(Constants.SAL_SERVICE_TYPE,value)
- return properties
- }
-
- static def getSalServiceType(Hashtable<String,String> properties) {
- return properties.get(Constants.SAL_SERVICE_TYPE)
- }
-
- static def newProperties() {
- new Hashtable<String,String>()
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.binding.impl.utils;
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.binding.test;
+import static org.junit.Assert.*;
+
+import java.util.concurrent.Future;
+
+import org.junit.Test;
+import org.opendaylight.controller.sal.binding.impl.ProxyFactoryGenerator;
+import org.opendaylight.controller.sal.binding.impl.RpcServiceProxy;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+
+public class GenerationTest {
+
+ public interface MockService extends RpcService {
+
+ Future<RpcResult<java.lang.Void>> cancelToast();
+
+ Future<RpcResult<java.lang.Void>> makeToast(String input);
+ }
+
+ @Test
+ public void test() {
+ ProxyFactoryGenerator generator = new ProxyFactoryGenerator();
+ Class<? extends RpcServiceProxy<MockService>> ret = generator.generate(MockService.class);
+
+ assertTrue(RpcServiceProxy.class.isAssignableFrom(ret));
+ assertTrue(MockService.class.isAssignableFrom(ret));
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test;
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import javassist.ClassPool;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.*;
+import org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator;
+import org.opendaylight.controller.sal.binding.test.mock.FooService;
+import org.opendaylight.controller.sal.binding.test.mock.ReferencableObject;
+import org.opendaylight.controller.sal.binding.test.mock.ReferencableObjectKey;
+import org.opendaylight.controller.sal.binding.test.mock.SimpleInput;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+
+import static org.mockito.Mockito.*;
+
+
+public class RuntimeCodeGeneratorTest {
+
+ private RuntimeCodeGenerator codeGenerator;
+
+
+ @Before
+ public void initialize() {
+ this.codeGenerator = new RuntimeCodeGenerator(ClassPool.getDefault());
+ }
+
+ @Test
+ public void testGenerateDirectProxy() {
+ Class<? extends FooService> product = codeGenerator.generateDirectProxy(FooService.class);
+ assertNotNull(product);
+ }
+
+ @Test
+ public void testGenerateRouter() throws Exception {
+ Class<? extends FooService> product = codeGenerator.generateRouter(FooService.class);
+ assertNotNull(product);
+ assertNotNull(product.getSimpleName());
+ assertEquals("2 fields should be generated.",2,product.getFields().length);
+
+ verifyRouting(product.newInstance());
+ }
+
+ private void verifyRouting(FooService product) {
+ Map<InstanceIdentifier,FooService> routingTable = new HashMap<>();
+ setRoutingTable(product, BaseIdentity.class, routingTable);
+
+ assertSame("Returned routing table should be same instance",routingTable,getRoutingTable(product, BaseIdentity.class));
+
+ int servicesCount = 2;
+ int instancesPerService = 3;
+
+ InstanceIdentifier[][] identifiers = identifiers(servicesCount,instancesPerService);
+ FooService service[] = new FooService[] {
+ mock(FooService.class, "Instance 0"),
+ mock(FooService.class,"Instance 1")
+ };
+
+ for(int i = 0;i<service.length;i++) {
+ for (InstanceIdentifier instance : identifiers[i]) {
+ routingTable.put(instance, service[i]);
+ }
+ }
+
+ assertEquals("All instances should be registered.", servicesCount*instancesPerService, routingTable.size());
+
+ SimpleInput[] instance_0_input = new SimpleInputImpl[] {
+ new SimpleInputImpl(identifiers[0][0]),
+ new SimpleInputImpl(identifiers[0][1]),
+ new SimpleInputImpl(identifiers[0][2])
+ };
+
+ SimpleInput[] instance_1_input = new SimpleInputImpl[] {
+ new SimpleInputImpl(identifiers[1][0]),
+ new SimpleInputImpl(identifiers[1][1]),
+ new SimpleInputImpl(identifiers[1][2])
+ };
+
+ // We test sending mock messages
+
+ product.simple(instance_0_input[0]);
+ verify(service[0]).simple(instance_0_input[0]);
+
+ product.simple(instance_0_input[1]);
+ product.simple(instance_0_input[2]);
+
+ verify(service[0]).simple(instance_0_input[1]);
+ verify(service[0]).simple(instance_0_input[2]);
+
+ product.simple(instance_1_input[0]);
+ verify(service[1]).simple(instance_1_input[0]);
+ }
+
+ private InstanceIdentifier[][] identifiers(int serviceSize, int instancesPerService) {
+ InstanceIdentifier[][] ret = new InstanceIdentifier[serviceSize][];
+ int service = 0;
+ for (int i = 0;i<serviceSize;i++) {
+
+ InstanceIdentifier[] instanceIdentifiers = new InstanceIdentifier[instancesPerService];
+ ret[i] = instanceIdentifiers;
+ for(int id = 0;id<instancesPerService;id++) {
+ instanceIdentifiers[id] = referencableIdentifier(service*instancesPerService+id);
+ }
+ service++;
+ }
+
+ return ret;
+ }
+
+ private InstanceIdentifier referencableIdentifier(int i) {
+ ReferencableObjectKey key = new ReferencableObjectKey(i);
+ IdentifiableItem<ReferencableObject,ReferencableObjectKey> pathArg = new IdentifiableItem<>(ReferencableObject.class,key);
+ return new InstanceIdentifier(Arrays.<PathArgument>asList(pathArg), ReferencableObject.class);
+ }
+
+ private static class SimpleInputImpl implements SimpleInput {
+ private final InstanceIdentifier identifier;
+
+ public SimpleInputImpl(InstanceIdentifier _identifier) {
+ this.identifier = _identifier;
+ }
+
+ @Override
+ public <E extends Augmentation<SimpleInput>> E getAugmentation(Class<E> augmentationType) {
+ return null;
+ }
+
+ @Override
+ public InstanceIdentifier getIdentifier() {
+ return this.identifier;
+ }
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public interface BarUpdate extends Grouping,Notification {
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public interface FooListener extends NotificationListener {
+
+ void onFooUpdate(FooUpdate notification);
+ void onBarUpdate(BarUpdate notification);
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public interface FooService extends RpcService {
+
+ Future<RpcResult<Void>> foo();
+
+ Future<RpcResult<Void>> simple(SimpleInput obj);
+
+ Future<RpcResult<Void>> inheritedContextInput(InheritedContextInput obj);
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public interface FooUpdate extends Notification {
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
+
+public interface Grouping {
+
+ @RoutingContext(BaseIdentity.class)
+ InstanceIdentifier getInheritedIdentifier();
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.mock;
+
+public interface InheritedContextInput extends Grouping {
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+
+public interface ReferencableObject extends DataObject,Identifiable<ReferencableObjectKey> {
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Identifier;
+
+public class ReferencableObjectKey implements Identifier<ReferencableObject> {
+
+ final Integer value;
+
+ public ReferencableObjectKey(Integer _value) {
+ this.value = _value;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ReferencableObjectKey other = (ReferencableObjectKey) obj;
+ if (value == null) {
+ if (other.value != null)
+ return false;
+ } else if (!value.equals(other.value))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "ReferencableObjectKey [value=" + value + "]";
+ }
+
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
+
+public interface SimpleInput extends DataObject,Augmentable<SimpleInput> {
+
+ @RoutingContext(BaseIdentity.class)
+ InstanceIdentifier getIdentifier();
+}
!org.codehaus.enunciate.jaxrs
</Import-Package>
<Web-ContextPath>/one/nb/v2/lb</Web-ContextPath>
+ <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
</instructions>
<manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
</configuration>
+++ /dev/null
-/*
- * Copyright IBM Corporation, 2013. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.samples.loadbalancer.northbound;
-
-import java.util.HashSet;
-import java.util.Set;
-import javax.ws.rs.core.Application;
-
-import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
-
-/**
- * This class is an instance of javax.ws.rs.core.Application and is used to return the classes
- * that will be instantiated for JAXRS processing. This is necessary
- * because package scanning in jersey doesn't yet work in OSGi environment.
- *
- */
-public class LoadBalancerNorthboundRSApplication extends Application {
- @Override
- public Set<Class<?>> getClasses() {
- Set<Class<?>> classes = new HashSet<Class<?>>();
- classes.add(LoadBalancerNorthbound.class);
- classes.add(JacksonJaxbJsonProvider.class);
- return classes;
- }
-}
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.samples.loadbalancer.northbound.LoadBalancerNorthboundRSApplication</param-value>
+ <param-value>org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Sonar properties using jacoco to retrieve integration test results -->
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
- <sonar.jacoco.Reportpath>target/jacoco.exec</sonar.jacoco.Reportpath>
+ <sonar.jacoco.reportpath>target/jacoco.exec</sonar.jacoco.reportpath>
<sonar.jacoco.itReportPath>target/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.language>java</sonar.language>
</properties>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
</plugin>
</plugins>
</pluginManagement>
<!-- Sonar jacoco plugin to get integration test coverage info -->
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.jacoco.reportPath>../implementation/target/jacoco.exec</sonar.jacoco.reportPath>
- <sonar.jacoco.itReportPath>../implementaiton/target/jacoco-it.exec</sonar.jacoco.itReportPath>
+ <sonar.jacoco.itReportPath>../implementation/target/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.language>java</sonar.language>
</properties>
<build>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
<configuration>
<destFile>../implementation/target/jacoco-it.exec</destFile>
<includes>org.opendaylight.controller.*</includes>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
</plugin>
</plugins>
</pluginManagement>
import java.util.ArrayList;
import java.util.List;
-import org.opendaylight.controller.sal.core.ConstructionException;
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.core.NodeConnector;
-import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
import org.opendaylight.controller.sal.utils.GUIField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
}
public ArrayList<NodeConnector> getPortArrayList() {
- Node node = Node.fromString(nodeId);
ArrayList<NodeConnector> portList = new ArrayList<NodeConnector>();
String[] elemArray = spanPort.split(",");
for (String elem : elemArray) {
- if (elem.contains("-")) {
- String[] limits = elem.split("-");
- for (short j = Short.valueOf(limits[0]); j <= Short
- .valueOf(limits[1]); j++) {
- try {
- portList.add(new NodeConnector(
- NodeConnectorIDType.OPENFLOW, Short.valueOf(j),
- node));
- } catch (ConstructionException e) {
- logger.error("",e);
- }
- }
- } else {
- try {
- portList.add(new NodeConnector(
- NodeConnectorIDType.OPENFLOW, Short.valueOf(elem),
- node));
- } catch (NumberFormatException e) {
- logger.error("",e);
- } catch (ConstructionException e) {
- logger.error("",e);
- }
+ NodeConnector nodeConnector = NodeConnector.fromString(elem);
+ if (nodeConnector != null) {
+ portList.add(nodeConnector);
}
}
return portList;
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
</plugin>
</plugins>
</pluginManagement>
<!-- Sonar jacoco plugin to get integration test coverage info -->
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.jacoco.reportPath>../implementation/target/jacoco.exec</sonar.jacoco.reportPath>
- <sonar.jacoco.itReportPath>../implementaiton/target/jacoco-it.exec</sonar.jacoco.itReportPath>
+ <sonar.jacoco.itReportPath>../implementation/target/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.language>java</sonar.language>
</properties>
<build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.jacoco</groupId>
- <artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
- </plugin>
- </plugins>
- </pluginManagement>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.5.3.201107060350</version>
+ <version>${jacoco.version}</version>
<configuration>
<destFile>../implementation/target/jacoco-it.exec</destFile>
<includes>org.opendaylight.controller.*</includes>
<groupId>org.opendaylight.controller</groupId>
<artifactId>commons.opendaylight</artifactId>
<version>1.4.0-SNAPSHOT</version>
- <relativePath>../commons/opendaylight</relativePath>
+ <relativePath>../../commons/opendaylight</relativePath>
</parent>
<scm>
<connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
protected String user;
protected List<String> roles;
private String password;
+
+ private static final boolean strongPasswordCheck = Boolean.getBoolean("enableStrongPasswordCheck");
+ private static final String BAD_PASSWORD = "Bad Password";
private static final int USERNAME_MAXLENGTH = 32;
- private static final int PASSWORD_MINLENGTH = 5;
- private static final int PASSWORD_MAXLENGTH = 256;
+ protected static final String PASSWORD_REGEX = "(?=.*[^\\w])(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,256}$";
private static final Pattern INVALID_USERNAME_CHARACTERS = Pattern.compile("([/\\s\\.\\?#%;\\\\]+)");
private static MessageDigest oneWayFunction = null;
static {
public UserConfig(String user, String password, List<String> roles) {
this.user = user;
- this.password = password;
- if (this.validatePassword().isSuccess()) {
- /*
- * Only if the password is a valid one, hash it. So in case it is not
- * valid, when UserConfig.validate() is called, the proper
- * validation error will be returned to the caller. If we hashed a
- * priori instead, the mis-configuration would be masked
- */
- this.password = hash(this.password);
- }
+ /*
+ * Password validation to be done on clear text password. If fails, mark
+ * the password with a well known label, so that object validation can
+ * report the proper error. Only if password is a valid one, hash it.
+ */
+ this.password = (validatePassword(password).isSuccess()) ? hash(password) : BAD_PASSWORD;
this.roles = (roles == null) ? new ArrayList<String>() : new ArrayList<String>(roles);
}
}
public Status validate() {
- Status validCheck = validateRoles();
+ Status validCheck = validateUsername();
if (validCheck.isSuccess()) {
- validCheck = validateUsername();
+ validCheck = (!password.equals(BAD_PASSWORD)) ? new Status(StatusCode.SUCCESS) : new Status(
+ StatusCode.BADREQUEST,
+ "Password should be 8 to 256 characters long, contain both upper and lower case letters, "
+ + "at least one number and at least one non alphanumeric character");
}
if (validCheck.isSuccess()) {
- validCheck = validatePassword();
+ validCheck = validateRoles();
}
return validCheck;
}
return new Status(StatusCode.SUCCESS);
}
- private Status validatePassword() {
+ private Status validatePassword(String password) {
if (password == null || password.isEmpty()) {
return new Status(StatusCode.BADREQUEST, "Password cannot be empty");
}
- if (password.length() < UserConfig.PASSWORD_MINLENGTH
- || password.length() > UserConfig.PASSWORD_MAXLENGTH) {
- return new Status(StatusCode.BADREQUEST,
- "Password should have 5-256 characters");
+ if (strongPasswordCheck && !password.matches(UserConfig.PASSWORD_REGEX)) {
+ return new Status(StatusCode.BADREQUEST, "Password should be 8 to 256 characters long, "
+ + "contain both upper and lower case letters, at least one number "
+ + "and at least one non alphanumeric character");
}
return new Status(StatusCode.SUCCESS);
}
UserConfig.oneWayFunction.reset();
return HexEncode.bytesToHexString(UserConfig.oneWayFunction.digest(message.getBytes(Charset.defaultCharset())));
}
+
+ /**
+ * Returns UserConfig instance populated with the passed parameters. It does
+ * not run any checks on the passed parameters.
+ *
+ * @param userName
+ * the user name
+ * @param password
+ * the plain text password
+ * @param roles
+ * the list of roles
+ * @return the UserConfig object populated with the passed parameters. No
+ * validity check is run on the input parameters.
+ */
+ public static UserConfig getUncheckedUserConfig(String userName, String password, List<String> roles) {
+ UserConfig config = new UserConfig();
+ config.user = userName;
+ config.password = hash(password);
+ config.roles = roles;
+ return config;
+ }
}
UserConfig userConfig2 = new UserConfig("uname", "ciscocisco", roles);
assertEquals(userConfig, userConfig2);
}
+
+ @Test
+ public void userConfigPasswordTest() {
+
+ String regex = UserConfig.PASSWORD_REGEX;
+ String password = null;
+
+ // Good password
+ password = "aBc@eF#h9";
+ assertTrue(password.matches(regex));
+ password = "^aBc@eF#h9$88ad*o&";
+ assertTrue(password.matches(regex));
+ password = "_^aBc@\":eF#h;9$\\8|8ad*o&-(){}/,.><?+-";
+ assertTrue(password.matches(regex));
+ password = "culonE1)";
+ assertTrue(password.matches(regex));
+
+ // Too short
+ password = "aB3@eF#";
+ assertFalse(password.matches(regex));
+
+ // No number
+ password = "#BeCCC#CeDfDf";
+ assertFalse(password.matches(regex));
+
+ // No lower case
+ password = "AB8C#CC@C4";
+ assertFalse(password.matches(regex));
+
+ // No upper case
+ password = "ab8defg9!";
+ assertFalse(password.matches(regex));
+
+ // No special characters
+ password = "aBc4ef7H8";
+ assertFalse(password.matches(regex));
+ }
}
*/
@Override
public Object[] getImplementations() {
- return null;
+ return new Object[]{};
}
/**
package org.opendaylight.controller.usermanager.internal;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class UserManager implements IUserManager, IObjectReader,
IConfigurationAware, CommandProvider, AuthenticationProvider {
private static final Logger logger = LoggerFactory.getLogger(UserManager.class);
- private static final String defaultAdmin = "admin";
- private static final String defaultAdminPassword = "admin";
- private static final String defaultAdminRole = UserLevel.NETWORKADMIN.toString();
+ private static final String DEFAULT_ADMIN = "admin";
+ private static final String DEFAULT_ADMIN_PASSWORD = "admin";
+ private static final String DEFAULT_ADMIN_ROLE = UserLevel.NETWORKADMIN.toString();
private static final String ROOT = GlobalConstants.STARTUPHOME.toString();
- private static final String usersFileName = ROOT + "users.conf";
- private static final String serversFileName = ROOT + "servers.conf";
- private static final String authFileName = ROOT + "authorization.conf";
+ private static final String USERS_FILE_NAME = ROOT + "users.conf";
+ private static final String SERVERS_FILE_NAME = ROOT + "servers.conf";
+ private static final String AUTH_FILE_NAME = ROOT + "authorization.conf";
+ private static final String RECOVERY_FILE = ROOT + "NETWORK_ADMIN_PASSWORD_RECOVERY";
private ConcurrentMap<String, UserConfig> localUserConfigList;
private ConcurrentMap<String, ServerConfig> remoteServerConfigList;
// local authorization info for remotely authenticated users
}
private void checkDefaultNetworkAdmin() {
- // If startup config is not there, it's old or it was deleted,
- // need to add Default Network Admin User
- if (!localUserConfigList.containsKey(defaultAdmin)) {
+ /*
+ * If startup config is not there, it's old or it was deleted or if a
+ * password recovery was run, need to add Default Network Admin User
+ */
+ if (!localUserConfigList.containsKey(DEFAULT_ADMIN)) {
List<String> roles = new ArrayList<String>(1);
- roles.add(defaultAdminRole);
- localUserConfigList.put(defaultAdmin, new UserConfig(defaultAdmin, defaultAdminPassword, roles));
+ roles.add(DEFAULT_ADMIN_ROLE);
+ // Need to skip the strong password check for the default admin
+ UserConfig defaultAdmin = UserConfig.getUncheckedUserConfig(UserManager.DEFAULT_ADMIN,
+ UserManager.DEFAULT_ADMIN_PASSWORD, roles);
+ localUserConfigList.put(UserManager.DEFAULT_ADMIN, defaultAdmin);
+ }
+ }
+
+ private void checkPasswordRecovery() {
+ final String fileDescription = "Default Network Administrator password recovery file";
+ try {
+ FileInputStream fis = new FileInputStream(UserManager.RECOVERY_FILE);
+ /*
+ * Recovery file detected, remove current default network
+ * administrator entry from local users configuration list.
+ * Warn user and delete recovery file.
+ */
+ this.localUserConfigList.remove(UserManager.DEFAULT_ADMIN);
+ logger.info("Default Network Administrator password has been reset to factory default.");
+ logger.info("Please change the default Network Administrator password as soon as possible");
+ File filePointer = new File(UserManager.RECOVERY_FILE);
+ boolean status = filePointer.delete();
+ if (!status) {
+ logger.warn("Failed to delete {}", fileDescription);
+ } else {
+ logger.trace("{} deleted", fileDescription);
+ }
+ fis.close();
+ } catch (FileNotFoundException fnf) {
+ logger.trace("{} not present", fileDescription);
+ } catch (IOException e) {
+ logger.warn("Failed to close file stream for {}", fileDescription);
}
}
private Status saveLocalUserListInternal() {
ObjectWriter objWriter = new ObjectWriter();
return objWriter.write(new ConcurrentHashMap<String, UserConfig>(
- localUserConfigList), usersFileName);
+ localUserConfigList), USERS_FILE_NAME);
}
@Override
private Status saveAAAServerListInternal() {
ObjectWriter objWriter = new ObjectWriter();
return objWriter.write(new ConcurrentHashMap<String, ServerConfig>(
- remoteServerConfigList), serversFileName);
+ remoteServerConfigList), SERVERS_FILE_NAME);
}
@Override
ObjectWriter objWriter = new ObjectWriter();
return objWriter.write(
new ConcurrentHashMap<String, AuthorizationConfig>(
- authorizationConfList), authFileName);
+ authorizationConfList), AUTH_FILE_NAME);
}
@Override
private void loadUserConfig() {
ObjectReader objReader = new ObjectReader();
ConcurrentMap<String, UserConfig> confList = (ConcurrentMap<String, UserConfig>) objReader
- .read(this, usersFileName);
+ .read(this, USERS_FILE_NAME);
if (confList == null) {
return;
private void loadServerConfig() {
ObjectReader objReader = new ObjectReader();
ConcurrentMap<String, ServerConfig> confList = (ConcurrentMap<String, ServerConfig>) objReader
- .read(this, serversFileName);
+ .read(this, SERVERS_FILE_NAME);
if (confList == null) {
return;
private void loadAuthConfig() {
ObjectReader objReader = new ObjectReader();
ConcurrentMap<String, AuthorizationConfig> confList = (ConcurrentMap<String, AuthorizationConfig>) objReader
- .read(this, authFileName);
+ .read(this, AUTH_FILE_NAME);
if (confList == null) {
return;
String user = AAAconf.getUser();
// Check default admin user
- if (user.equals(UserManager.defaultAdmin)) {
+ if (user.equals(UserManager.DEFAULT_ADMIN)) {
String msg = "Invalid Request: Default Network Admin User cannot be " + ((delete)? "removed" : "added");
logger.debug(msg);
return new Status(StatusCode.NOTALLOWED, msg);
// Read startup configuration and populate databases
loadConfigurations();
+ // Check if a password recovery was triggered for default network admin user
+ checkPasswordRecovery();
+
// Make sure default Network Admin account is there
checkDefaultNetworkAdmin();
- BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
- .getBundleContext();
- bundleContext.registerService(CommandProvider.class.getName(), this,
- null);
+
+ BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
+ bundleContext.registerService(CommandProvider.class.getName(), this, null);
}
/**
org.opendaylight.controller.usermanager,
org.opendaylight.controller.web,
com.google.gson,
+ com.google.gson.reflect,
javax.annotation,
javax.naming,
javax.servlet,
package org.opendaylight.controller.devices.web;
+import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.springframework.web.bind.annotation.ResponseBody;
import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
@Controller
@RequestMapping("/")
// Add switch portName value (non-configuration field)
config.put("nodeName",
getNodeDesc(config_data.get("nodeId"), containerName));
+ NodeConnector spanPortNodeConnector = NodeConnector.fromString(config_data.get("spanPort"));
+ Name ncName = ((Name) switchManager.getNodeConnectorProp(spanPortNodeConnector,
+ Name.NamePropName));
+ String spanPortName = (ncName != null) ? ncName.getValue() : "";
+ config.put("spanPortName", spanPortName);
}
config.put("json", config_json);
spanConfigs.add(config);
Gson gson = new Gson();
ISwitchManager switchManager = (ISwitchManager) ServiceHelper
.getInstance(ISwitchManager.class, containerName, this);
- String[] spans = spanPortsToDelete.split("###");
- resultBean.setStatus(true);
- resultBean.setMessage("SPAN Port(s) deleted successfully");
- for (String span : spans) {
- if (!span.isEmpty()) {
- SpanConfig cfgObject = gson
- .fromJson(span, SpanConfig.class);
- Status result = switchManager.removeSpanConfig(cfgObject);
- if (!result.isSuccess()) {
- resultBean.setStatus(false);
- resultBean.setMessage(result.getDescription());
- break;
- }
- DaylightWebUtil.auditlog("SPAN Port", userName, "removed", cfgObject.getNodeId(), containerName);
+ Type collectionType = new TypeToken<List<SpanPortJsonBean>>() {}.getType();
+ List<SpanPortJsonBean> jsonBeanList = gson.fromJson(spanPortsToDelete, collectionType);
+ for (SpanPortJsonBean jsonBean : jsonBeanList) {
+ SpanConfig cfgObject = gson
+ .fromJson(gson.toJson(jsonBean), SpanConfig.class);
+ Status result = switchManager.removeSpanConfig(cfgObject);
+ if (!result.isSuccess()) {
+ resultBean.setStatus(false);
+ resultBean.setMessage(result.getDescription());
+ break;
}
+ DaylightWebUtil.auditlog("SPAN Port", userName, "removed", cfgObject.getNodeId(), containerName);
}
+ resultBean.setStatus(true);
+ resultBean.setMessage("SPAN Port(s) deleted successfully");
} catch (Exception e) {
resultBean.setStatus(false);
resultBean.setMessage("Error occurred while deleting span port. "
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.devices.web;
+
+public class SpanPortJsonBean {
+
+ private String nodeId;
+ private String spanPort;
+
+ public String getNodeId() {
+ return nodeId;
+ }
+
+ public void setNodeId(String nodeId) {
+ this.nodeId = nodeId;
+ }
+
+ public String getSpanPort() {
+ return spanPort;
+ }
+
+ public void setSpanPort(String spanPort) {
+ this.spanPort = spanPort;
+ }
+
+}
return false;
}
checkedCheckBoxes.each(function(index, value) {
- spanPortsToDelete.push(decodeURIComponent(checkedCheckBoxes[index].getAttribute("spanPort")));
+ var spanPortObj = {};
+ spanPortObj['spanPortJson'] = decodeURIComponent(checkedCheckBoxes[index].getAttribute("spanPort"));
+ spanPortObj['spanPortNodeName'] = checkedCheckBoxes[index].getAttribute("spanPortNode");
+ spanPortObj['spanPortPortName'] = checkedCheckBoxes[index].getAttribute("spanPortPort");
+
+ spanPortsToDelete.push(spanPortObj);
});
one.f.switchmanager.spanPortConfig.modal.removeMultiple.dialog(spanPortsToDelete);
});
// bind remove rule button
$('#'+one.f.switchmanager.spanPortConfig.id.modal.remove, $modal).click(this, function(e) {
var requestData = {};
- var spanPorts="";
- $(spanPortsToDelete).each(function(){
- spanPorts = spanPorts + "###" + this.toString();
+ var spanPorts = [];
+ $(spanPortsToDelete).each(function(index, spanPort) {
+ spanPorts.push(JSON.parse(spanPort.spanPortJson));
});
- requestData["spanPortsToDelete"] = spanPorts.slice(3,spanPorts.length);
-
+ requestData["spanPortsToDelete"] = JSON.stringify(spanPorts);
+
var url = one.f.switchmanager.rootUrl + "/spanPorts/delete";
one.f.switchmanager.spanPortConfig.ajax.main(url, requestData, function(response) {
$modal.modal('hide');
var p = 'Remove the following Span Port(s)?';
//creata a BS label for each rule and append to list
- var spanPortList = JSON.parse("["+spanPortToDelete.toString()+"]");
- $(spanPortList).each(function(){
+ $(spanPortToDelete).each(function(index, spanPortItem) {
var $span = $(document.createElement('span'));
- $span.append(this.nodeId+"-"+this.spanPort);
+ $span.append(this.spanPortNodeName+"-"+this.spanPortPortName);
p += '<br/>' + $span[0].outerHTML;
});
$p.append(p);
sortable: true
},
{
- property: 'spanPort',
+ property: 'spanPortName',
label: 'SPAN Port',
sortable: true
},
data: data.nodeData,
formatter: function(items) {
$.each(items, function(index, item) {
- item["selector"] = '<input type="checkbox" class="spanPortConfig" spanPort=' + encodeURIComponent(item["json"]) + '></input>';
+ item["selector"] = '<input type="checkbox" class="spanPortConfig" spanPort=' + encodeURIComponent(item["json"]) + ' spanPortNode=' + item["nodeName"] + ' spanPortPort=' + item["spanPortName"] + '></input>';
});
},
delay: 0
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
import org.opendaylight.controller.connectionmanager.IConnectionManager;
@RequestMapping(value = "/users/password/{username}", method = RequestMethod.POST)
@ResponseBody
- public Status changePassword(@PathVariable("username") String username, HttpServletRequest request,
- @RequestParam("currentPassword") String currentPassword, @RequestParam("newPassword") String newPassword) {
+ public Status changePassword(
+ @PathVariable("username") String username, HttpServletRequest request,
+ @RequestParam(value = "currentPassword", required=false) String currentPassword,
+ @RequestParam("newPassword") String newPassword) {
IUserManager userManager = (IUserManager) ServiceHelper.getGlobalInstance(IUserManager.class, this);
if (userManager == null) {
- return new Status(StatusCode.GONE, "User Manager not found");
+ return new Status(StatusCode.NOSERVICE, "User Manager unavailable");
}
- if (!authorize(userManager, UserLevel.NETWORKADMIN, request)) {
- return new Status(StatusCode.FORBIDDEN, "Operation not permitted");
- }
+ Status status;
+ String requestingUser = request.getUserPrincipal().getName();
+
+ //changing own password
+ if (requestingUser.equals(username) ) {
+ status = userManager.changeLocalUserPassword(username, currentPassword, newPassword);
+ //enforce the user to re-login with new password
+ if (status.isSuccess() && !newPassword.equals(currentPassword)) {
+ userManager.userLogout(username);
+ HttpSession session = request.getSession(false);
+ if ( session != null) {
+ session.invalidate();
+ }
+ }
+
+ //admin level user resetting other's password
+ } else if (authorize(userManager, UserLevel.NETWORKADMIN, request)) {
+
+ //Since User Manager doesn't have an unprotected password change API,
+ //we re-create the user with the new password (and current roles).
+ List<String> roles = userManager.getUserRoles(username);
+ UserConfig newConfig = new UserConfig(username, newPassword, roles);
+
+ //validate before removing existing config, so we don't remove but fail to add
+ status = newConfig.validate();
+ if (!status.isSuccess()) {
+ return status;
+ }
+
+ userManager.userLogout(username);
+ status = userManager.removeLocalUser(username);
+ if (!status.isSuccess()) {
+ return status;
+ }
+ if (userManager.addLocalUser(newConfig).isSuccess()) {
+ status = new Status(StatusCode.SUCCESS, "Password for user " + username + " reset successfully.");
+ } else {
+ //unexpected
+ status = new Status(StatusCode.INTERNALERROR, "Failed resetting password for user " + username + ". User is now removed.");
+ }
- if (newPassword.isEmpty()) {
- return new Status(StatusCode.BADREQUEST, "Empty passwords not allowed");
+ //unauthorized
+ } else {
+ status = new Status(StatusCode.UNAUTHORIZED, "Operation not permitted");
}
- Status status = userManager.changeLocalUserPassword(username, currentPassword, newPassword);
if (status.isSuccess()) {
- DaylightWebUtil.auditlog("User", request.getUserPrincipal().getName(), "changed password for", username);
+ DaylightWebUtil.auditlog("User", requestingUser, "changed password for", username);
}
return status;
}
</div>
<div class="span3">
<div id="toolbar" class="btn-group">
+ <input type="hidden" id="currentuser" value="${username}" data-role="${role}">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
<div class="icon-user"></div> ${username} <span class="caret"></span>
</a>
// change password binding
$('#'+one.main.admin.id.modal.password.submit, $modal).click(function() {
one.main.admin.password.submit(id, $modal, function(result) {
- if (result.code == 'SUCCESS') {
- $modal.modal('hide');
- successCallback();
+ if (result.success) {
+ //if changed own password, enforce relogin
+ if (id.trim() == $('#currentuser').val().trim()) {
+ alert("Password changed successfully. Please re-login with your new password.");
+ window.location = '/';
+ }
} else {
alert(result.code+': '+result.description);
}
ITopologyManager topology, String containerName) {
for (Map.Entry<Node, Set<NodeConnector>> e : hostEdges.entrySet()) {
for (NodeConnector connector : e.getValue()) {
- Host host = topology.getHostAttachedToNodeConnector(connector);
- EthernetAddress dmac = (EthernetAddress) host.getDataLayerAddress();
+ List<Host> hosts = topology.getHostsAttachedToNodeConnector(connector);
+ for (Host host : hosts) {
+ EthernetAddress dmac = (EthernetAddress) host.getDataLayerAddress();
- ByteBuffer addressByteBuffer = ByteBuffer.allocate(8);
- addressByteBuffer.putShort((short) 0);
- addressByteBuffer.put(dmac.getValue());
- addressByteBuffer.rewind();
+ ByteBuffer addressByteBuffer = ByteBuffer.allocate(8);
+ addressByteBuffer.putShort((short) 0);
+ addressByteBuffer.put(dmac.getValue());
+ addressByteBuffer.rewind();
- long hid = addressByteBuffer.getLong();
- String hostId = String.valueOf(hid);
+ long hid = addressByteBuffer.getLong();
+ String hostId = String.valueOf(hid);
- NodeBean hostBean = new NodeBean(hostId, host.getNetworkAddressAsString(), NodeType.HOST);
- List<Map<String, Object>> adjacencies = new LinkedList<Map<String, Object>>();
- EdgeBean edge = new EdgeBean(connector, hid);
- adjacencies.add(edge.out());
- hostBean.setLinks(adjacencies);
+ NodeBean hostBean = new NodeBean(hostId, host.getNetworkAddressAsString(), NodeType.HOST);
+ List<Map<String, Object>> adjacencies = new LinkedList<Map<String, Object>>();
+ EdgeBean edge = new EdgeBean(connector, hid);
+ adjacencies.add(edge.out());
+ hostBean.setLinks(adjacencies);
- if (metaCache.get(containerName).containsKey(hostId)) {
+ if (metaCache.get(containerName).containsKey(hostId)) {
Map<String, Object> hostEntry = metaCache.get(containerName).get(hostId);
hostEntry.put("adjacencies", adjacencies);
stagedNodes.put(hostId, hostEntry);
- } else {
+ } else {
newNodes.put(String.valueOf(hid), hostBean.out());
+ }
}
}
}
* @param i OpenFlow wire protocol type
* @param t type
*/
- static public void addMapping(byte i, OFType t) {
+ public static void addMapping(byte i, OFType t) {
if (mapping == null)
mapping = new OFType[32];
OFType.mapping[i] = t;
*
* @param i OpenFlow wire protocol type
*/
- static public void removeMapping(byte i) {
+ public static void removeMapping(byte i) {
OFType.mapping[i] = null;
}
* @return OFType enum type
*/
- static public OFType valueOf(Byte i) {
+ public static OFType valueOf(Byte i) {
return OFType.mapping[i];
}