From: Prasanth Pallamreddy Date: Mon, 9 Sep 2013 21:50:33 +0000 (-0700) Subject: Address @XmlSeeAlso limitation. Provide the ability to inject the JAXB types X-Git-Tag: releasepom-0.1.0~83^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=feeeee8105b8f8262154d6c7d994fdbdb7eda1e2 Address @XmlSeeAlso limitation. Provide the ability to inject the JAXB types 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 --- diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 075ccb1bfa..cb1892ae66 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -1021,5 +1021,10 @@ jersey-json ${jersey.version} + + org.ow2.asm + asm-all + 4.1 + diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 56ca0c951b..1fb131e4af 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -106,6 +106,8 @@ ../../northbound/commons + ../../northbound/bundlescanner/api + ../../northbound/bundlescanner/implementation ../../northbound/topology ../../northbound/staticrouting ../../northbound/statistics diff --git a/opendaylight/northbound/bundlescanner/api/pom.xml b/opendaylight/northbound/bundlescanner/api/pom.xml new file mode 100644 index 0000000000..2c96ec215d --- /dev/null +++ b/opendaylight/northbound/bundlescanner/api/pom.xml @@ -0,0 +1,46 @@ + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.0-SNAPSHOT + ../../../commons/opendaylight + + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main + + + bundlescanner + 0.4.0-SNAPSHOT + bundle + + + + + org.apache.felix + maven-bundle-plugin + 2.3.6 + true + + + + org.osgi.framework, + org.slf4j, + javax.ws.rs, + javax.ws.rs.core, + javax.xml.bind.annotation, + javax.xml.bind, + + + org.opendaylight.controller.northbound.bundlescanner + + + + + + + + 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 index 0000000000..5cb0b63f1c --- /dev/null +++ b/opendaylight/northbound/bundlescanner/api/src/main/java/org/opendaylight/controller/northbound/bundlescanner/IBundleScanService.java @@ -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> 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 index 0000000000..ef4efa6d2e --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/pom.xml @@ -0,0 +1,74 @@ + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.0-SNAPSHOT + ../../../commons/opendaylight + + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main + + + bundlescanner.implementation + 0.4.0-SNAPSHOT + bundle + + + + + org.apache.felix + maven-bundle-plugin + 2.3.6 + true + + + + 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, + + + org.opendaylight.controller.northbound.bundlescanner.internal.Activator + + + + + + + + + + org.ow2.asm + asm-all + 4.1 + + + org.springframework.osgi + spring-osgi-mock + 1.2.1 + test + + + org.opendaylight.controller + bundlescanner + 0.4.0-SNAPSHOT + + + org.opendaylight.controller + sal + 0.5.0-SNAPSHOT + bundle + + + 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 index 0000000000..6b0718c7e2 --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/Activator.java @@ -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 index 0000000000..a10893110c --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleInfo.java @@ -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> annotatedClasses; + private final Set exportPkgs; + private final Set importPkgs; + + public BundleInfo(Bundle bundle, Map> classes) { + this.bundle = bundle; + this.annotatedClasses = classes; + Dictionary 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> getAnnotatedClasses(Pattern pattern) { + List result = new ArrayList(); + for (Map.Entry> entry : annotatedClasses.entrySet()) { + if (matches(pattern, entry.getValue())) { + result.add(entry.getKey()); + } + } + return BundleScanner.loadClasses(bundle, result); + } + + private boolean matches(Pattern pattern, Set 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> getAnnotatedClasses( + Collection allbundles, + Pattern pattern) + { + List> classes = getAnnotatedClasses(pattern); + processAnnotatedClassesInternal(this, allbundles, pattern, + new HashSet(), classes); + return classes; + } + + private List getExportedAnnotatedClasses(Pattern pattern) { + List classes = new ArrayList(); + for (Map.Entry> 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 bundlesToScan, + Pattern pattern, + Collection visited, + List> 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 getDependencies(Collection bundles) { + List result = new ArrayList(); + for(BundleInfo bundle : bundles) { + if (isDependantOn(bundle)) result.add(bundle); + } + return result; + } + + + private static Set parsePackages(String packageString) { + if (packageString == null) return Collections.emptySet(); + String[] packages = packageString.split(","); + Set result = new HashSet(); + for (int i=0; i> 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 index 0000000000..3e517e9a1f --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/main/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScanner.java @@ -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 bundleAnnotations = + new HashMap(); + + 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> 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> 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> matchedClasses = + new HashMap>(); + + private final Pattern annotationsPattern; + private Set annotations; + private String className; + private boolean accessible; + private boolean matchedAnnotation; + + public AnnotationDetector(Pattern pattern) { + super(Opcodes.ASM4); + this.annotationsPattern = pattern; + } + + public Map> getMatchedClasses() { + return new HashMap>(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(); + } + + @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(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 getBundleClasses(Bundle bundle, String[] pkgs) { + List result = new ArrayList(); + boolean recurse = false; + if (pkgs == null) { + recurse = true; + pkgs = new String[] { "/" } ; + } + for (String pkg : pkgs) { + pkg = class2path(pkg); + final Enumeration 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> 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> loadClasses(Bundle bundle, + Collection annotatedClasses) + { + List> result = new ArrayList>(); + 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 index 0000000000..e9fb31fc35 --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Animal.java @@ -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 index 0000000000..831d66b26d --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/BasePerson.java @@ -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 index 0000000000..53ab14d6d6 --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Mammal.java @@ -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 index 0000000000..b8f3d10f71 --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/NoAnnotation.java @@ -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 index 0000000000..9429345a84 --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/NonRelevantAnnotation.java @@ -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 index 0000000000..9a6acc0ceb --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_base/Person.java @@ -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 index 0000000000..cb33ce98bc --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_misc/Misc.java @@ -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 index 0000000000..9a08fc99dd --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub1/NoAnnotation.java @@ -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 index 0000000000..1eadad61fa --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub1/Zoo.java @@ -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 index 0000000000..2b982706c3 --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/Agent.java @@ -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 index 0000000000..a3f5dcaa53 --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/Customer.java @@ -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 phoneNumbers; + @XmlElementRef + @XmlElementWrapper + private final List agents = new ArrayList(); + + @XmlTransient + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @XmlElement(name = "phone-number") + public List getPhoneNumbers() { + return phoneNumbers; + } + + public void setPhoneNumbers(List 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 index 0000000000..430f10b8db --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/bundle_sub2/NoAnnotation.java @@ -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 index 0000000000..0d7d7da1f0 --- /dev/null +++ b/opendaylight/northbound/bundlescanner/implementation/src/test/java/org/opendaylight/controller/northbound/bundlescanner/internal/BundleScannerTest.java @@ -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 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> 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> 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> 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> 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 makeMockBundles() throws Exception { + List result = new ArrayList(); + 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 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 result = new ArrayList(); + 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 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 makeHeaders( + String name, String imports, String exports) + { + Dictionary headers = new Hashtable(); + 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(); + } + } +} diff --git a/opendaylight/northbound/commons/pom.xml b/opendaylight/northbound/commons/pom.xml index b88375ffdc..692b17a9a6 100644 --- a/opendaylight/northbound/commons/pom.xml +++ b/opendaylight/northbound/commons/pom.xml @@ -34,13 +34,21 @@ 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 @@ -60,5 +68,10 @@ usermanager 0.4.0-SNAPSHOT + + org.opendaylight.controller + bundlescanner + 0.4.0-SNAPSHOT + 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 index 0000000000..1d3919f4ea --- /dev/null +++ b/opendaylight/northbound/commons/src/main/java/org/opendaylight/controller/northbound/commons/NorthboundApplication.java @@ -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 getSingletons() { + Set singletons = new HashSet(); + singletons.add(new ContextResolver() { + @Override + public JAXBContext getContext(Class type) { + return newJAXBContext(); + } + + } ); + singletons.add(new JacksonJaxbJsonProvider()); + return singletons; + } + + @Override + public Set> getClasses() { + Set> result = new HashSet>(); + 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> 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> findJAXRSResourceClasses() { + BundleContext ctx = getBundleContext(); + String bundleName = ctx.getBundle().getSymbolicName(); + Set> result = new HashSet>(); + 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 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; + } + +} diff --git a/opendaylight/northbound/containermanager/pom.xml b/opendaylight/northbound/containermanager/pom.xml index 067e5f19b9..75341a6fdf 100644 --- a/opendaylight/northbound/containermanager/pom.xml +++ b/opendaylight/northbound/containermanager/pom.xml @@ -49,6 +49,7 @@ !org.codehaus.enunciate.jaxrs /controller/nb/v2/containermanager + ,${classes;ANNOTATION;javax.ws.rs.Path} diff --git a/opendaylight/northbound/flowprogrammer/pom.xml b/opendaylight/northbound/flowprogrammer/pom.xml index 2c729ba115..addf857aa1 100644 --- a/opendaylight/northbound/flowprogrammer/pom.xml +++ b/opendaylight/northbound/flowprogrammer/pom.xml @@ -61,6 +61,7 @@ /controller/nb/v2/flowprogrammer + ,${classes;ANNOTATION;javax.ws.rs.Path} ${project.basedir}/src/main/resources/META-INF 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 index 68c0ec1e5b..0000000000 --- a/opendaylight/northbound/flowprogrammer/src/main/java/org/opendaylight/controller/flowprogrammer/northbound/FlowProgrammerNorthboundRSApplication.java +++ /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> getClasses() { - Set> classes = new HashSet>(); - classes.add(FlowProgrammerNorthbound.class); - classes.add(JacksonJaxbJsonProvider.class); - return classes; - } -} diff --git a/opendaylight/northbound/flowprogrammer/src/main/resources/WEB-INF/web.xml b/opendaylight/northbound/flowprogrammer/src/main/resources/WEB-INF/web.xml index 5b3cec2292..f5c1ae5f92 100644 --- a/opendaylight/northbound/flowprogrammer/src/main/resources/WEB-INF/web.xml +++ b/opendaylight/northbound/flowprogrammer/src/main/resources/WEB-INF/web.xml @@ -8,7 +8,7 @@ com.sun.jersey.spi.container.servlet.ServletContainer javax.ws.rs.Application - org.opendaylight.controller.flowprogrammer.northbound.FlowProgrammerNorthboundRSApplication + org.opendaylight.controller.northbound.commons.NorthboundApplication 1 diff --git a/opendaylight/northbound/hosttracker/pom.xml b/opendaylight/northbound/hosttracker/pom.xml index c1f6598a8a..e0315fb1ee 100644 --- a/opendaylight/northbound/hosttracker/pom.xml +++ b/opendaylight/northbound/hosttracker/pom.xml @@ -62,6 +62,7 @@ !org.codehaus.enunciate.jaxrs /controller/nb/v2/hosttracker + ,${classes;ANNOTATION;javax.ws.rs.Path} ${project.basedir}/src/main/resources/META-INF 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 index 5d50dbf0b2..0000000000 --- a/opendaylight/northbound/hosttracker/src/main/java/org/opendaylight/controller/hosttracker/northbound/HostTrackerNorthboundRSApplication.java +++ /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> getClasses() { - Set> classes = new HashSet>(); - classes.add(HostTrackerNorthbound.class); - classes.add(JacksonJaxbJsonProvider.class); - return classes; - } -} diff --git a/opendaylight/northbound/hosttracker/src/main/resources/WEB-INF/web.xml b/opendaylight/northbound/hosttracker/src/main/resources/WEB-INF/web.xml index 01b8fedce1..d4ace8d89d 100644 --- a/opendaylight/northbound/hosttracker/src/main/resources/WEB-INF/web.xml +++ b/opendaylight/northbound/hosttracker/src/main/resources/WEB-INF/web.xml @@ -7,7 +7,7 @@ com.sun.jersey.spi.container.servlet.ServletContainer javax.ws.rs.Application - org.opendaylight.controller.hosttracker.northbound.HostTrackerNorthboundRSApplication + org.opendaylight.controller.northbound.commons.NorthboundApplication 1 diff --git a/opendaylight/northbound/integrationtest/pom.xml b/opendaylight/northbound/integrationtest/pom.xml index f0c04ae5e2..9f4e05dff0 100644 --- a/opendaylight/northbound/integrationtest/pom.xml +++ b/opendaylight/northbound/integrationtest/pom.xml @@ -232,6 +232,17 @@ 0.4.0-SNAPSHOT + + org.opendaylight.controller + bundlescanner + 0.4.0-SNAPSHOT + + + + org.opendaylight.controller + bundlescanner.implementation + 0.4.0-SNAPSHOT + org.codehaus.enunciate @@ -565,6 +576,11 @@ ${url.version} test + + org.ow2.asm + asm-all + 4.1 + org.springframework org.springframework.asm diff --git a/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java b/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java index b0a5f201b2..4404951135 100644 --- a/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java +++ b/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java @@ -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(), diff --git a/opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml b/opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml index a8d81a7367..c1eb56aa29 100644 --- a/opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml +++ b/opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml @@ -64,6 +64,7 @@ /controller/nb/v2/networkconfig/bridgedomain + ,${classes;ANNOTATION;javax.ws.rs.Path} ${project.basedir}/src/main/resources/META-INF 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 index 0d98d2282d..0000000000 --- a/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthboundApplication.java +++ /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> getClasses() { - Set> classes = new HashSet>(); - classes.add(BridgeDomainNorthbound.class); - classes.add(JacksonJaxbJsonProvider.class); - return classes; - } -} diff --git a/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/resources/WEB-INF/web.xml b/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/resources/WEB-INF/web.xml index f4de222acc..bb871a1a77 100644 --- a/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/resources/WEB-INF/web.xml +++ b/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/resources/WEB-INF/web.xml @@ -7,7 +7,7 @@ com.sun.jersey.spi.container.servlet.ServletContainer javax.ws.rs.Application - org.opendaylight.controller.networkconfig.bridgedomain.northbound.BridgeDomainNorthboundApplication + org.opendaylight.controller.northbound.commons.NorthboundApplication 1 diff --git a/opendaylight/northbound/staticrouting/pom.xml b/opendaylight/northbound/staticrouting/pom.xml index eb285f2e81..1028ca59ab 100644 --- a/opendaylight/northbound/staticrouting/pom.xml +++ b/opendaylight/northbound/staticrouting/pom.xml @@ -62,6 +62,7 @@ /controller/nb/v2/staticroute + ,${classes;ANNOTATION;javax.ws.rs.Path} ${project.basedir}/src/main/resources/META-INF 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 index a98a124524..0000000000 --- a/opendaylight/northbound/staticrouting/src/main/java/org/opendaylight/controller/forwarding/staticrouting/northbound/StaticRoutingNorthboundRSApplication.java +++ /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> getClasses() { - Set> classes = new HashSet>(); - classes.add(StaticRoutingNorthbound.class); - classes.add(JacksonJaxbJsonProvider.class); - return classes; - } -} diff --git a/opendaylight/northbound/staticrouting/src/main/resources/WEB-INF/web.xml b/opendaylight/northbound/staticrouting/src/main/resources/WEB-INF/web.xml index 0bf186b50e..9f7c21a8b0 100644 --- a/opendaylight/northbound/staticrouting/src/main/resources/WEB-INF/web.xml +++ b/opendaylight/northbound/staticrouting/src/main/resources/WEB-INF/web.xml @@ -7,7 +7,7 @@ com.sun.jersey.spi.container.servlet.ServletContainer javax.ws.rs.Application - org.opendaylight.controller.forwarding.staticrouting.northbound.StaticRoutingNorthboundRSApplication + org.opendaylight.controller.northbound.commons.NorthboundApplication 1 diff --git a/opendaylight/northbound/statistics/pom.xml b/opendaylight/northbound/statistics/pom.xml index 76129f4cb1..74f64c2457 100644 --- a/opendaylight/northbound/statistics/pom.xml +++ b/opendaylight/northbound/statistics/pom.xml @@ -70,6 +70,7 @@ /controller/nb/v2/statistics + ,${classes;ANNOTATION;javax.ws.rs.Path} ${project.basedir}/src/main/resources/META-INF 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 index 2b6dccc8b9..0000000000 --- a/opendaylight/northbound/statistics/src/main/java/org/opendaylight/controller/statistics/northbound/StatisticsNorthboundRSApplication.java +++ /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> getClasses() { - Set> classes = new HashSet>(); - classes.add(StatisticsNorthbound.class); - classes.add(JacksonJaxbJsonProvider.class); - return classes; - } -} diff --git a/opendaylight/northbound/statistics/src/main/resources/WEB-INF/web.xml b/opendaylight/northbound/statistics/src/main/resources/WEB-INF/web.xml index db0460ba56..179db608ad 100644 --- a/opendaylight/northbound/statistics/src/main/resources/WEB-INF/web.xml +++ b/opendaylight/northbound/statistics/src/main/resources/WEB-INF/web.xml @@ -7,7 +7,7 @@ com.sun.jersey.spi.container.servlet.ServletContainer javax.ws.rs.Application - org.opendaylight.controller.statistics.northbound.StatisticsNorthboundRSApplication + org.opendaylight.controller.northbound.commons.NorthboundApplication 1 diff --git a/opendaylight/northbound/subnets/pom.xml b/opendaylight/northbound/subnets/pom.xml index c3ad783773..ba58e06ecb 100644 --- a/opendaylight/northbound/subnets/pom.xml +++ b/opendaylight/northbound/subnets/pom.xml @@ -77,6 +77,7 @@ /controller/nb/v2/subnetservice + ,${classes;ANNOTATION;javax.ws.rs.Path} ${project.basedir}/src/main/resources/META-INF 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 index e03cac6562..0000000000 --- a/opendaylight/northbound/subnets/src/main/java/org/opendaylight/controller/subnets/northbound/SubnetsNorthboundRSApplication.java +++ /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> getClasses() { - Set> classes = new HashSet>(); - classes.add(SubnetsNorthbound.class); - classes.add(JacksonJaxbJsonProvider.class); - return classes; - } -} diff --git a/opendaylight/northbound/subnets/src/main/resources/WEB-INF/web.xml b/opendaylight/northbound/subnets/src/main/resources/WEB-INF/web.xml index a5c70ee9d8..2300b8da40 100644 --- a/opendaylight/northbound/subnets/src/main/resources/WEB-INF/web.xml +++ b/opendaylight/northbound/subnets/src/main/resources/WEB-INF/web.xml @@ -7,7 +7,7 @@ com.sun.jersey.spi.container.servlet.ServletContainer javax.ws.rs.Application - org.opendaylight.controller.subnets.northbound.SubnetsNorthboundRSApplication + org.opendaylight.controller.northbound.commons.NorthboundApplication 1 diff --git a/opendaylight/northbound/switchmanager/pom.xml b/opendaylight/northbound/switchmanager/pom.xml index a416414d06..d46b17e4f7 100644 --- a/opendaylight/northbound/switchmanager/pom.xml +++ b/opendaylight/northbound/switchmanager/pom.xml @@ -61,6 +61,7 @@ !org.codehaus.enunciate.jaxrs /controller/nb/v2/switchmanager + ,${classes;ANNOTATION;javax.ws.rs.Path} ${project.basedir}/src/main/resources/META-INF 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 index 1cdfd31e3f..0000000000 --- a/opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthboundRSApplication.java +++ /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> getClasses() { - Set> classes = new HashSet>(); - classes.add(SwitchNorthbound.class); - classes.add(JacksonJaxbJsonProvider.class); - return classes; - } -} diff --git a/opendaylight/northbound/switchmanager/src/main/resources/WEB-INF/web.xml b/opendaylight/northbound/switchmanager/src/main/resources/WEB-INF/web.xml index ea6fcc99b2..cce0dfb259 100644 --- a/opendaylight/northbound/switchmanager/src/main/resources/WEB-INF/web.xml +++ b/opendaylight/northbound/switchmanager/src/main/resources/WEB-INF/web.xml @@ -7,7 +7,7 @@ com.sun.jersey.spi.container.servlet.ServletContainer javax.ws.rs.Application - org.opendaylight.controller.switchmanager.northbound.SwitchNorthboundRSApplication + org.opendaylight.controller.northbound.commons.NorthboundApplication 1 diff --git a/opendaylight/northbound/topology/pom.xml b/opendaylight/northbound/topology/pom.xml index 803a28079e..ec3489aca2 100644 --- a/opendaylight/northbound/topology/pom.xml +++ b/opendaylight/northbound/topology/pom.xml @@ -64,6 +64,7 @@ !org.codehaus.enunciate.jaxrs /controller/nb/v2/topology + ,${classes;ANNOTATION;javax.ws.rs.Path} ${project.basedir}/src/main/resources/META-INF 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 index 992cf6ac52..0000000000 --- a/opendaylight/northbound/topology/src/main/java/org/opendaylight/controller/topology/northbound/TopologyNorthboundRSApplication.java +++ /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> getClasses() { - Set> classes = new HashSet>(); - classes.add(TopologyNorthboundJAXRS.class); - classes.add(JacksonJaxbJsonProvider.class); - return classes; - } -} diff --git a/opendaylight/northbound/topology/src/main/resources/WEB-INF/web.xml b/opendaylight/northbound/topology/src/main/resources/WEB-INF/web.xml index bc818c8c6d..29f2049c27 100644 --- a/opendaylight/northbound/topology/src/main/resources/WEB-INF/web.xml +++ b/opendaylight/northbound/topology/src/main/resources/WEB-INF/web.xml @@ -7,7 +7,7 @@ com.sun.jersey.spi.container.servlet.ServletContainer javax.ws.rs.Application - org.opendaylight.controller.topology.northbound.TopologyNorthboundRSApplication + org.opendaylight.controller.northbound.commons.NorthboundApplication 1 diff --git a/opendaylight/samples/northbound/loadbalancer/pom.xml b/opendaylight/samples/northbound/loadbalancer/pom.xml index 2ccb440952..e001dc8e58 100644 --- a/opendaylight/samples/northbound/loadbalancer/pom.xml +++ b/opendaylight/samples/northbound/loadbalancer/pom.xml @@ -62,6 +62,7 @@ !org.codehaus.enunciate.jaxrs /one/nb/v2/lb + ,${classes;ANNOTATION;javax.ws.rs.Path} ${project.basedir}/src/main/resources/META-INF 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 index 2f5aacab67..0000000000 --- a/opendaylight/samples/northbound/loadbalancer/src/main/java/org/opendaylight/controller/samples/loadbalancer/northbound/LoadBalancerNorthboundRSApplication.java +++ /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> getClasses() { - Set> classes = new HashSet>(); - classes.add(LoadBalancerNorthbound.class); - classes.add(JacksonJaxbJsonProvider.class); - return classes; - } -} diff --git a/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/web.xml b/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/web.xml index 0e8f8c1b56..ae2fce582a 100644 --- a/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/web.xml +++ b/opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/web.xml @@ -7,7 +7,7 @@ com.sun.jersey.spi.container.servlet.ServletContainer javax.ws.rs.Application - org.opendaylight.controller.samples.loadbalancer.northbound.LoadBalancerNorthboundRSApplication + org.opendaylight.controller.northbound.commons.NorthboundApplication 1