2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.northbound.commons;
11 import java.util.Dictionary;
12 import java.util.HashSet;
13 import java.util.List;
16 import javax.ws.rs.core.Application;
17 import javax.ws.rs.ext.ContextResolver;
18 import javax.xml.bind.JAXBContext;
19 import javax.xml.bind.JAXBException;
20 import javax.xml.bind.annotation.XmlRootElement;
22 import org.opendaylight.controller.northbound.bundlescanner.IBundleScanService;
23 import org.opendaylight.controller.northbound.commons.exception.GenericExceptionMapper;
24 import org.opendaylight.controller.northbound.commons.query.QueryContextProvider;
25 import org.osgi.framework.Bundle;
26 import org.osgi.framework.BundleContext;
27 import org.osgi.framework.BundleReference;
28 import org.osgi.framework.FrameworkUtil;
29 import org.osgi.framework.ServiceException;
30 import org.osgi.framework.ServiceReference;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 import com.fasterxml.jackson.databind.DeserializationFeature;
35 import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
38 * Instance of javax.ws.rs.core.Application used to return the classes
39 * that will be instantiated for JAXRS processing. This hooks onto the
40 * bundle scanner service to provide JAXB classes to JAX-RS for prorcessing.
42 public class NorthboundApplication extends Application {
43 public static final String JAXRS_RESOURCES_MANIFEST_NAME = "Jaxrs-Resources";
44 public static final String JAXRS_EXCLUDES_MANIFEST_NAME = "Jaxrs-Exclude-Types";
45 private static final Logger LOGGER = LoggerFactory.getLogger(NorthboundApplication.class);
46 private final Set<Object> _singletons;
48 public NorthboundApplication() {
49 _singletons = new HashSet<Object>();
50 _singletons.add(new ContextResolver<JAXBContext>() {
51 JAXBContext jaxbContext;
53 public synchronized JAXBContext getContext(Class<?> type) {
54 if (jaxbContext == null) {
55 jaxbContext = newJAXBContext();
61 _singletons.add(getJsonProvider());
62 _singletons.add(new JacksonJsonProcessingExceptionMapper());
63 _singletons.add(new QueryContextProvider());
64 _singletons.add(new GenericExceptionMapper());
67 ////////////////////////////////////////////////////////////////
68 // Application overrides
69 ////////////////////////////////////////////////////////////////
72 public Set<Object> getSingletons() {
77 public Set<Class<?>> getClasses() {
78 Set<Class<?>> result = new HashSet<Class<?>>();
79 result.addAll(findJAXRSResourceClasses());
83 private static final JacksonJaxbJsonProvider getJsonProvider() {
84 JacksonJaxbJsonProvider jsonProvider = new JacksonJaxbJsonProvider();
85 jsonProvider.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
90 private BundleContext getBundleContext() {
91 ClassLoader tlcl = Thread.currentThread().getContextClassLoader();
94 if (tlcl instanceof BundleReference) {
95 bundle = ((BundleReference) tlcl).getBundle();
97 LOGGER.warn("Unable to determine the bundle context based on " +
98 "thread context classloader.");
99 bundle = FrameworkUtil.getBundle(this.getClass());
101 return (bundle == null ? null : bundle.getBundleContext());
104 private static final IBundleScanService lookupBundleScanner(BundleContext ctx) {
105 ServiceReference<?> svcRef = ctx.getServiceReference(IBundleScanService.class);
106 if (svcRef == null) {
107 throw new ServiceException("Unable to lookup IBundleScanService");
109 return IBundleScanService.class.cast(ctx.getService(svcRef));
112 private final JAXBContext newJAXBContext() {
113 BundleContext ctx = getBundleContext();
114 IBundleScanService svc = lookupBundleScanner(ctx);
116 List<Class<?>> cls = svc.getAnnotatedClasses(ctx,
117 new String[] { XmlRootElement.class.getPackage().getName() },
118 parseManifestEntry(ctx, JAXRS_EXCLUDES_MANIFEST_NAME),
120 return JAXBContext.newInstance(cls.toArray(new Class[cls.size()]));
121 } catch (JAXBException je) {
122 LOGGER.error("Error creating JAXBContext", je);
127 private final Set<Class<?>> findJAXRSResourceClasses() {
128 BundleContext ctx = getBundleContext();
129 String bundleName = ctx.getBundle().getSymbolicName();
130 Set<Class<?>> result = new HashSet<Class<?>>();
131 ServiceException recordException = null;
133 IBundleScanService svc = lookupBundleScanner(ctx);
134 result.addAll(svc.getAnnotatedClasses(ctx,
135 new String[] { javax.ws.rs.Path.class.getName() },
137 } catch (ServiceException se) {
138 recordException = se;
139 LOGGER.debug("Error finding JAXRS resource annotated classes in " +
140 "bundle: {} error: {}.", bundleName, se.getMessage());
141 // the bundle scan service cannot be lookedup. Lets attempt to
142 // lookup the resources from the bundle manifest header
143 for (String c : parseManifestEntry(ctx, JAXRS_RESOURCES_MANIFEST_NAME)) {
145 result.add(ctx.getBundle().loadClass(c));
146 } catch (ClassNotFoundException cnfe) {
147 LOGGER.error("Cannot load class: {} in bundle: {} " +
148 "defined as MANIFEST JAX-RS resource", c, bundleName, cnfe);
153 if (result.size() == 0) {
154 if (recordException != null) {
155 throw recordException;
157 throw new ServiceException("No resource classes found in bundle:" +
158 ctx.getBundle().getSymbolicName());
164 private final Set<String> parseManifestEntry(BundleContext ctx, String name) {
165 Set<String> result = new HashSet<String>();
166 Dictionary<String,String> headers = ctx.getBundle().getHeaders();
167 String header = headers.get(name);
168 if (header != null) {
169 for (String s : header.split(",")) {
171 if (s.length() > 0) {