package org.opendaylight.controller.northbound.bundlescanner;
import java.util.List;
+import java.util.Set;
import org.osgi.framework.BundleContext;
public List<Class<?>> getAnnotatedClasses(
BundleContext context,
String[] annotations,
+ Set<String> excludes,
boolean includeDependentBundleClasses);
}
return bundle.getBundleId();
}
- public List<Class<?>> getAnnotatedClasses(Pattern pattern) {
+ public List<Class<?>> getAnnotatedClasses(Pattern pattern, Set<String> excludes) {
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(result, bundle);
+ return BundleScanner.loadClasses(result, bundle, excludes);
}
private boolean matches(Pattern pattern, Set<String> values) {
* @param allbundles - all bundles
* @param pattern - annotation pattern to match
* @param initBundle - the bundle which initiated this call
+ * @param excludes - set of class names to be excluded
*
* @return list of annotated classes matching the pattern
*/
public List<Class<?>> getAnnotatedClasses(
Collection<BundleInfo> allbundles,
- Pattern pattern, Bundle initBundle)
+ Pattern pattern, Bundle initBundle,
+ Set<String> excludes)
{
- List<Class<?>> classes = getAnnotatedClasses(pattern);
+ List<Class<?>> classes = getAnnotatedClasses(pattern, excludes);
processAnnotatedClassesInternal(this, allbundles, pattern,
- new HashSet<BundleInfo>(), classes, initBundle);
+ new HashSet<BundleInfo>(), classes, initBundle, excludes);
return classes;
}
Pattern pattern,
Collection<BundleInfo> visited,
List<Class<?>> classes,
- Bundle initBundle)
+ Bundle initBundle, Set<String> excludes)
{
for (BundleInfo other : bundlesToScan) {
if (other.getId() == target.getId()) continue;
if (target.isDependantOn(other)) {
if (!visited.contains(other)) {
classes.addAll(BundleScanner.loadClasses(
- other.getExportedAnnotatedClasses(pattern), initBundle));
+ other.getExportedAnnotatedClasses(pattern),
+ initBundle, excludes));
visited.add(other);
processAnnotatedClassesInternal(other, bundlesToScan,
- pattern, visited, classes, initBundle);
+ pattern, visited, classes, initBundle, excludes);
}
}
}
package org.opendaylight.controller.northbound.bundlescanner.internal;
import java.util.List;
+import java.util.Set;
import org.opendaylight.controller.northbound.bundlescanner.IBundleScanService;
import org.osgi.framework.BundleContext;
@Override
public List<Class<?>> getAnnotatedClasses(BundleContext context,
String[] annotations,
+ Set<String> excludes,
boolean includeDependentBundleClasses)
{
return BundleScanner.getInstance().getAnnotatedClasses(
- context, annotations, includeDependentBundleClasses);
+ context, annotations, excludes, includeDependentBundleClasses);
}
}
import java.util.Set;
import java.util.regex.Pattern;
+import javax.xml.bind.annotation.XmlRootElement;
+
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
public List<Class<?>> getAnnotatedClasses(BundleContext context,
String[] annotations,
+ Set<String> excludes,
boolean includeDependentBundleClasses)
{
BundleInfo info = bundleAnnotations.get(context.getBundle().getBundleId());
List<Class<?>> result = null;
if (includeDependentBundleClasses) {
result = info.getAnnotatedClasses(bundleAnnotations.values(),
- pattern, context.getBundle());
+ pattern, context.getBundle(), excludes);
+ // reverse the list to give precedence to the types loaded from the
+ // initiating bundle
+ Collections.reverse(result);
+ // validate for conflicts only when searching dependencies
+ validate(result);
} else {
- result = info.getAnnotatedClasses(pattern);
+ result = info.getAnnotatedClasses(pattern, excludes);
}
LOGGER.debug("Annotated classes detected: {} matching: {}", result, pattern);
return result;
public static List<Class<?>> loadClasses(
Collection<String> annotatedClasses,
- Bundle initBundle)
+ Bundle initBundle, Set<String> excludes)
{
List<Class<?>> result = new ArrayList<Class<?>>();
StringBuilder errors = new StringBuilder();
for (String name : annotatedClasses) {
try {
+ if (excludes != null && excludes.contains(name)) continue;
result.add(initBundle.loadClass(name));
} catch (ClassNotFoundException e) {
errors.append(name).append(", ");
return Pattern.compile(regex.toString());
}
+ private void validate(List<Class<?>> classes) {
+ if (classes == null || classes.size() == 0) return;
+ Map<String,String> names = new HashMap<String,String>();
+ StringBuilder conflictsMsg = new StringBuilder();
+ for (Class c : classes) {
+ XmlRootElement root = (XmlRootElement) c.getAnnotation(XmlRootElement.class);
+ if (root == null) continue;
+ String rootName = root.name();
+ if ("##default".equals(rootName)) {
+ String clsName = c.getSimpleName();
+ rootName = Character.toLowerCase(clsName.charAt(0)) + clsName.substring(1);
+ }
+ String other = names.get(rootName);
+ if (other != null && !other.equals(c.getName())) {
+ conflictsMsg.append(System.lineSeparator())
+ .append("[").append(rootName).append(":")
+ .append(c.getName()).append(",").append(other)
+ .append("]");
+ } else {
+ names.put(rootName, c.getName());
+ }
+ }
+ if (conflictsMsg.length() > 0) {
+ LOGGER.info("JAXB type conflicts detected : {}", conflictsMsg.toString());
+ }
+ }
+
}
import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
+import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
+import java.util.Set;
import java.util.regex.Pattern;
import org.junit.AfterClass;
public void testBundleEvents() throws Exception {
MockBundle newBundle = new TestMockBundle("misc", "", "bundle_misc");
assertTrue(bundleScanner.getAnnotatedClasses(
- newBundle.getBundleContext(), null, false).size() == 0);
+ newBundle.getBundleContext(), null, null, false).size() == 0);
BundleEvent event = new BundleEvent(BundleEvent.RESOLVED, newBundle);
bundleScanner.bundleChanged(event);
assertTrue(bundleScanner.getAnnotatedClasses(
- newBundle.getBundleContext(), null, false).size() == 1);
+ newBundle.getBundleContext(), null, null, false).size() == 1);
}
@Test
public void testAnnotatedClassesWithDependencies() throws Exception {
for (Bundle bundle : bundles) {
List<Class<?>> classes = bundleScanner.getAnnotatedClasses(
- bundle.getBundleContext(), null, true);
+ bundle.getBundleContext(), null, null, true);
String name = bundle.getSymbolicName();
System.out.println("name:" + name + " classes:" + classes.size());
if ("misc".equals(name)) {
Bundle bundle = findBundle("sub1");
String[] annos = { "javax.xml.bind.annotation.XmlTransient" };
List<Class<?>> classes = bundleScanner.getAnnotatedClasses(
- bundle.getBundleContext(), annos, true);
+ bundle.getBundleContext(), annos, null, true);
assertTrue(classes.size() == 1);
}
Bundle bundle = findBundle("sub1");
String[] annos = { "javax.xml.bind.annotation.*" };
List<Class<?>> classes = bundleScanner.getAnnotatedClasses(
- bundle.getBundleContext(), annos, true);
+ bundle.getBundleContext(), annos, null, true);
assertTrue(classes.size() == 6);
}
Bundle bundle = findBundle("sub1");
String[] annos = { "non.existent.pkg" };
List<Class<?>> classes = bundleScanner.getAnnotatedClasses(
- bundle.getBundleContext(), annos, true);
+ bundle.getBundleContext(), annos, null, true);
assertTrue(classes.size() == 0);
}
assertFalse(pattern.matcher("Ljavax/servlet/FOO;").find());
}
+ @Test
+ public void testExclude() {
+ Set<String> excludes = new HashSet<String>();
+ excludes.add("bundle_base.Animal");
+ Bundle bundle = findBundle("sub1");
+ String[] annos = { "javax.xml.bind.annotation.*" };
+ List<Class<?>> classes = bundleScanner.getAnnotatedClasses(
+ bundle.getBundleContext(), annos, excludes, true);
+ assertTrue(classes.size() == 5);
+ }
+
private static Bundle findBundle(String symName) {
for (Bundle bundle : bundles) {
if (bundle.getSymbolicName().equals(symName)) return bundle;
@SuppressWarnings("unchecked")
public class NorthboundApplication extends Application {
public static final String JAXRS_RESOURCES_MANIFEST_NAME = "Jaxrs-Resources";
+ public static final String JAXRS_EXCLUDES_MANIFEST_NAME = "Jaxrs-Exclude-Types";
private static final Logger LOGGER = LoggerFactory.getLogger(NorthboundApplication.class);
+ private final Set<Object> _singletons;
+
+ public NorthboundApplication() {
+ _singletons = new HashSet<Object>();
+ _singletons.add(new ContextResolver<JAXBContext>() {
+ JAXBContext jaxbContext = newJAXBContext();
+ @Override
+ public JAXBContext getContext(Class<?> type) {
+ return jaxbContext;
+ }
+
+ } );
+ _singletons.add(getJsonProvider());
+ _singletons.add(new JacksonJsonProcessingExceptionMapper());
+ }
////////////////////////////////////////////////////////////////
// Application overrides
@Override
public Set<Object> getSingletons() {
- Set<Object> singletons = new HashSet<Object>();
- singletons.add(new ContextResolver<JAXBContext>() {
- @Override
- public JAXBContext getContext(Class<?> type) {
- return newJAXBContext();
- }
-
- } );
- singletons.add(getJsonProvider());
- singletons.add(new JacksonJsonProcessingExceptionMapper());
- return singletons;
+ return _singletons;
}
@Override
try {
List<Class<?>> cls = svc.getAnnotatedClasses(ctx,
new String[] { XmlRootElement.class.getPackage().getName() },
+ parseManifestEntry(ctx, JAXRS_EXCLUDES_MANIFEST_NAME),
true);
return JAXBContext.newInstance(cls.toArray(new Class[cls.size()]));
} catch (JAXBException je) {
try {
IBundleScanService svc = lookupBundleScanner(ctx);
result.addAll(svc.getAnnotatedClasses(ctx,
- new String[] { javax.ws.rs.Path.class.getName() }, false));
+ new String[] { javax.ws.rs.Path.class.getName() },
+ null, 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);
- }
- }
+ for (String c : parseManifestEntry(ctx, JAXRS_RESOURCES_MANIFEST_NAME)) {
+ try {
+ result.add(ctx.getBundle().loadClass(c));
+ } catch (ClassNotFoundException cnfe) {
+ LOGGER.error("Cannot load class: {} in bundle: {} " +
+ "defined as MANIFEST JAX-RS resource", c, bundleName, cnfe);
}
}
-
}
if (result.size() == 0) {
return result;
}
+ private final Set<String> parseManifestEntry(BundleContext ctx, String name) {
+ Set<String> result = new HashSet<String>();
+ Dictionary<String,String> headers = ctx.getBundle().getHeaders();
+ String header = headers.get(name);
+ if (header != null) {
+ for (String s : header.split(",")) {
+ s = s.trim();
+ if (s.length() > 0) {
+ result.add(s);
+ }
+ }
+ }
+ return result;
+ }
+
}