Address @XmlSeeAlso limitation. Provide the ability to inject the JAXB types 07/1207/1
authorPrasanth Pallamreddy <ppallamr@cisco.com>
Mon, 9 Sep 2013 21:50:33 +0000 (14:50 -0700)
committerPrasanth Pallamreddy <ppallamr@cisco.com>
Mon, 16 Sep 2013 20:21:16 +0000 (13:21 -0700)
without having the need to explicitly define the inherited types with
@XmlSeelAlso.  Introduced BundleScanner service which scans bundle classes for
annotations dynamically and provides proper artifacts for NB applications to
bootstrap. Eliminate the need for NB application extensions.

Change-Id: I08b6a30a551f63a304732e8fdda9a594d1a6ca93
Signed-off-by: Prasanth Pallamreddy <ppallamr@cisco.com>
54 files changed:
opendaylight/commons/opendaylight/pom.xml
opendaylight/distribution/opendaylight/pom.xml
opendaylight/northbound/bundlescanner/api/pom.xml [new file with mode: 0644]
opendaylight/northbound/bundlescanner/api/src/main/java/org/opendaylight/controller/northbound/bundlescanner/IBundleScanService.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/pom.xml [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/Activator.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleInfo.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScanServiceImpl.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScanner.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Animal.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/BasePerson.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Mammal.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/NoAnnotation.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/NonRelevantAnnotation.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Person.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_misc/Misc.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub1/NoAnnotation.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub1/Zoo.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/Agent.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/Customer.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/NoAnnotation.java [new file with mode: 0644]
opendaylight/northbound/bundlescanner/implementation/src/test/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScannerTest.java [new file with mode: 0644]
opendaylight/northbound/commons/pom.xml
opendaylight/northbound/commons/src/main/java/org/opendaylight/controller/northbound/commons/NorthboundApplication.java [new file with mode: 0644]
opendaylight/northbound/containermanager/pom.xml
opendaylight/northbound/flowprogrammer/pom.xml
opendaylight/northbound/flowprogrammer/src/main/java/org/opendaylight/controller/flowprogrammer/northbound/FlowProgrammerNorthboundRSApplication.java [deleted file]
opendaylight/northbound/flowprogrammer/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/hosttracker/pom.xml
opendaylight/northbound/hosttracker/src/main/java/org/opendaylight/controller/hosttracker/northbound/HostTrackerNorthboundRSApplication.java [deleted file]
opendaylight/northbound/hosttracker/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/integrationtest/pom.xml
opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java
opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml
opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthboundApplication.java [deleted file]
opendaylight/northbound/networkconfiguration/bridgedomain/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/staticrouting/pom.xml
opendaylight/northbound/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/northbound/StaticRoutingNorthboundRSApplication.java [deleted file]
opendaylight/northbound/staticrouting/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/statistics/pom.xml
opendaylight/northbound/statistics/src/main/java/org/opendaylight/controller/statistics/northbound/StatisticsNorthboundRSApplication.java [deleted file]
opendaylight/northbound/statistics/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/subnets/pom.xml
opendaylight/northbound/subnets/src/main/java/org/opendaylight/controller/subnets/northbound/SubnetsNorthboundRSApplication.java [deleted file]
opendaylight/northbound/subnets/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/switchmanager/pom.xml
opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthboundRSApplication.java [deleted file]
opendaylight/northbound/switchmanager/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/topology/pom.xml
opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundRSApplication.java [deleted file]
opendaylight/northbound/topology/src/main/resources/WEB-INF/web.xml
opendaylight/samples/northbound/loadbalancer/pom.xml
opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthboundRSApplication.java [deleted file]
opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/web.xml

index 075ccb1..cb1892a 100644 (file)
       <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>
index 56ca0c9..1fb131e 100644 (file)
 
     <!-- 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>
diff --git a/opendaylight/northbound/bundlescanner/api/pom.xml b/opendaylight/northbound/bundlescanner/api/pom.xml
new file mode 100644 (file)
index 0000000..2c96ec2
--- /dev/null
@@ -0,0 +1,46 @@
+<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>
diff --git a/opendaylight/northbound/bundlescanner/api/src/main/java/org/opendaylight/controller/northbound/bundlescanner/IBundleScanService.java b/opendaylight/northbound/bundlescanner/api/src/main/java/org/opendaylight/controller/northbound/bundlescanner/IBundleScanService.java
new file mode 100644 (file)
index 0000000..5cb0b63
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * 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);
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/pom.xml b/opendaylight/northbound/bundlescanner/implementation/pom.xml
new file mode 100644 (file)
index 0000000..ef4efa6
--- /dev/null
@@ -0,0 +1,74 @@
+<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>
+        <type>bundle</type>
+      </dependency>
+  </dependencies>
+</project>
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/Activator.java b/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/Activator.java
new file mode 100644 (file)
index 0000000..6b0718c
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * 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);
+    }
+
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleInfo.java b/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleInfo.java
new file mode 100644 (file)
index 0000000..a108931
--- /dev/null
@@ -0,0 +1,162 @@
+/**
+ * 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;
+    }
+
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScanServiceImpl.java b/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScanServiceImpl.java
new file mode 100644 (file)
index 0000000..ad6d8e9
--- /dev/null
@@ -0,0 +1,22 @@
+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);
+    }
+
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScanner.java b/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScanner.java
new file mode 100644 (file)
index 0000000..3e517e9
--- /dev/null
@@ -0,0 +1,277 @@
+/**
+ * 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());
+    }
+
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Animal.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Animal.java
new file mode 100644 (file)
index 0000000..e9fb31f
--- /dev/null
@@ -0,0 +1,6 @@
+package bundle_base;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public abstract class Animal { }
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/BasePerson.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/BasePerson.java
new file mode 100644 (file)
index 0000000..831d66b
--- /dev/null
@@ -0,0 +1,10 @@
+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
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Mammal.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Mammal.java
new file mode 100644 (file)
index 0000000..53ab14d
--- /dev/null
@@ -0,0 +1,6 @@
+package bundle_base;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class Mammal extends Animal { }
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/NoAnnotation.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/NoAnnotation.java
new file mode 100644 (file)
index 0000000..b8f3d10
--- /dev/null
@@ -0,0 +1,3 @@
+package bundle_base;
+
+public class NoAnnotation { }
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/NonRelevantAnnotation.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/NonRelevantAnnotation.java
new file mode 100644 (file)
index 0000000..9429345
--- /dev/null
@@ -0,0 +1,6 @@
+package bundle_base;
+
+@Deprecated
+public class NonRelevantAnnotation {
+
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Person.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Person.java
new file mode 100644 (file)
index 0000000..9a6acc0
--- /dev/null
@@ -0,0 +1,27 @@
+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 { }
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_misc/Misc.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_misc/Misc.java
new file mode 100644 (file)
index 0000000..cb33ce9
--- /dev/null
@@ -0,0 +1,7 @@
+package bundle_misc;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class Misc {
+
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub1/NoAnnotation.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub1/NoAnnotation.java
new file mode 100644 (file)
index 0000000..9a08fc9
--- /dev/null
@@ -0,0 +1,5 @@
+package bundle_sub1;
+
+public class NoAnnotation {
+
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub1/Zoo.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub1/Zoo.java
new file mode 100644 (file)
index 0000000..1eadad6
--- /dev/null
@@ -0,0 +1,26 @@
+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();
+    }
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/Agent.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/Agent.java
new file mode 100644 (file)
index 0000000..2b98270
--- /dev/null
@@ -0,0 +1,11 @@
+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
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/Customer.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/Customer.java
new file mode 100644 (file)
index 0000000..a3f5dca
--- /dev/null
@@ -0,0 +1,56 @@
+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();
+    }
+}
+
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/NoAnnotation.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/NoAnnotation.java
new file mode 100644 (file)
index 0000000..430f10b
--- /dev/null
@@ -0,0 +1,5 @@
+package bundle_sub2;
+
+public class NoAnnotation {
+
+}
diff --git a/opendaylight/northbound/bundlescanner/implementation/src/test/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScannerTest.java b/opendaylight/northbound/bundlescanner/implementation/src/test/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScannerTest.java
new file mode 100644 (file)
index 0000000..0d7d7da
--- /dev/null
@@ -0,0 +1,212 @@
+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();
+        }
+    }
+}
index b88375f..692b17a 100644 (file)
             </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.jaxrs,
               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>
diff --git a/opendaylight/northbound/commons/src/main/java/org/opendaylight/controller/northbound/commons/NorthboundApplication.java b/opendaylight/northbound/commons/src/main/java/org/opendaylight/controller/northbound/commons/NorthboundApplication.java
new file mode 100644 (file)
index 0000000..1d3919f
--- /dev/null
@@ -0,0 +1,148 @@
+/**
+ * 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.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(new JacksonJaxbJsonProvider());
+        return singletons;
+    }
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        Set<Class<?>> result = new HashSet<Class<?>>();
+        result.addAll(findJAXRSResourceClasses());
+        return result;
+    }
+
+    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;
+    }
+
+}
index 067e5f1..75341a6 100644 (file)
@@ -49,6 +49,7 @@
               !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>
index 2c729ba..addf857 100644 (file)
@@ -61,6 +61,7 @@
             <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>
diff --git a/opendaylight/northbound/flowprogrammer/src/main/java/org/opendaylight/controller/flowprogrammer/northbound/FlowProgrammerNorthboundRSApplication.java b/opendaylight/northbound/flowprogrammer/src/main/java/org/opendaylight/controller/flowprogrammer/northbound/FlowProgrammerNorthboundRSApplication.java
deleted file mode 100644 (file)
index 68c0ec1..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-
-/*
- * 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;
-    }
-}
index 5b3cec2..f5c1ae5 100644 (file)
@@ -8,7 +8,7 @@
           <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>
index c1f6598..e0315fb 100644 (file)
@@ -62,6 +62,7 @@
               !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>
diff --git a/opendaylight/northbound/hosttracker/src/main/java/org/opendaylight/controller/hosttracker/northbound/HostTrackerNorthboundRSApplication.java b/opendaylight/northbound/hosttracker/src/main/java/org/opendaylight/controller/hosttracker/northbound/HostTrackerNorthboundRSApplication.java
deleted file mode 100644 (file)
index 5d50dbf..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-
-/*
- * 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;
-    }
-}
index 01b8fed..d4ace8d 100644 (file)
@@ -7,7 +7,7 @@
     <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>
index f0c04ae..9f4e05d 100644 (file)
       <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>
index b0a5f20..4404951 100644 (file)
@@ -1426,6 +1426,8 @@ public class NorthboundIT {
                 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(),
@@ -1508,6 +1510,8 @@ public class NorthboundIT {
                 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(),
index a8d81a7..c1eb56a 100644 (file)
@@ -64,6 +64,7 @@
             <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>
diff --git a/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthboundApplication.java b/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthboundApplication.java
deleted file mode 100644 (file)
index 0d98d22..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-
-/*
- * 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;
-    }
-}
index f4de222..bb871a1 100644 (file)
@@ -7,7 +7,7 @@
     <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>
index eb285f2..1028ca5 100644 (file)
@@ -62,6 +62,7 @@
             <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>
diff --git a/opendaylight/northbound/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/northbound/StaticRoutingNorthboundRSApplication.java b/opendaylight/northbound/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/northbound/StaticRoutingNorthboundRSApplication.java
deleted file mode 100644 (file)
index a98a124..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-
-/*
- * 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;
-    }
-}
index 0bf186b..9f7c21a 100644 (file)
@@ -7,7 +7,7 @@
     <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>
index 76129f4..74f64c2 100644 (file)
@@ -70,6 +70,7 @@
             <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>
diff --git a/opendaylight/northbound/statistics/src/main/java/org/opendaylight/controller/statistics/northbound/StatisticsNorthboundRSApplication.java b/opendaylight/northbound/statistics/src/main/java/org/opendaylight/controller/statistics/northbound/StatisticsNorthboundRSApplication.java
deleted file mode 100644 (file)
index 2b6dccc..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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;
-    }
-}
index db0460b..179db60 100644 (file)
@@ -7,7 +7,7 @@
     <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>
index c3ad783..ba58e06 100644 (file)
@@ -77,6 +77,7 @@
             <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>
diff --git a/opendaylight/northbound/subnets/src/main/java/org/opendaylight/controller/subnets/northbound/SubnetsNorthboundRSApplication.java b/opendaylight/northbound/subnets/src/main/java/org/opendaylight/controller/subnets/northbound/SubnetsNorthboundRSApplication.java
deleted file mode 100644 (file)
index e03cac6..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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;
-    }
-}
index a5c70ee..2300b8d 100644 (file)
@@ -7,7 +7,7 @@
     <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>
index a416414..d46b17e 100644 (file)
@@ -61,6 +61,7 @@
               !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>
diff --git a/opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthboundRSApplication.java b/opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthboundRSApplication.java
deleted file mode 100644 (file)
index 1cdfd31..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-
-/*
- * 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;
-    }
-}
index ea6fcc9..cce0dfb 100644 (file)
@@ -7,7 +7,7 @@
     <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>
index 803a280..ec3489a 100644 (file)
@@ -64,6 +64,7 @@
               !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>
diff --git a/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundRSApplication.java b/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundRSApplication.java
deleted file mode 100644 (file)
index 992cf6a..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-
-/*
- * 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;
-    }
-}
index bc818c8..29f2049 100644 (file)
@@ -7,7 +7,7 @@
     <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>
index 2ccb440..e001dc8 100644 (file)
@@ -62,6 +62,7 @@
               !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>
diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthboundRSApplication.java b/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthboundRSApplication.java
deleted file mode 100644 (file)
index 2f5aaca..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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;
-    }
-}
index 0e8f8c1..ae2fce5 100644 (file)
@@ -7,7 +7,7 @@
     <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>