5b8219126b8abbb7c82124b22f502505c2791e8c
[controller.git] / opendaylight / northbound / commons / src / main / java / org / opendaylight / controller / northbound / commons / NorthboundApplication.java
1 /**
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.northbound.commons;
10
11 import java.util.Dictionary;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Set;
15
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;
21
22 import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
23 import org.codehaus.jackson.map.DeserializationConfig;
24 import org.opendaylight.controller.northbound.bundlescanner.IBundleScanService;
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;
33
34 /**
35  * Instance of javax.ws.rs.core.Application used to return the classes
36  * that will be instantiated for JAXRS processing. This hooks onto the
37  * bundle scanner service to provide JAXB classes to JAX-RS for prorcessing.
38  */
39 @SuppressWarnings("unchecked")
40 public class NorthboundApplication extends Application {
41     public static final String JAXRS_RESOURCES_MANIFEST_NAME = "Jaxrs-Resources";
42     private static final Logger LOGGER = LoggerFactory.getLogger(NorthboundApplication.class);
43
44     ////////////////////////////////////////////////////////////////
45     //  Application overrides
46     ////////////////////////////////////////////////////////////////
47
48     @Override
49     public Set<Object> getSingletons() {
50         Set<Object> singletons = new HashSet<Object>();
51         singletons.add(new ContextResolver<JAXBContext>() {
52             @Override
53             public JAXBContext getContext(Class<?> type) {
54                 return newJAXBContext();
55             }
56
57         } );
58         singletons.add(getJsonProvider());
59         singletons.add(new JacksonJsonProcessingExceptionMapper());
60         return singletons;
61     }
62
63     @Override
64     public Set<Class<?>> getClasses() {
65         Set<Class<?>> result = new HashSet<Class<?>>();
66         result.addAll(findJAXRSResourceClasses());
67         return result;
68     }
69
70     private static final JacksonJaxbJsonProvider getJsonProvider() {
71         JacksonJaxbJsonProvider jsonProvider = new JacksonJaxbJsonProvider();
72         jsonProvider.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,
73                 false);
74         return jsonProvider;
75     }
76
77     private BundleContext getBundleContext() {
78         ClassLoader tlcl = Thread.currentThread().getContextClassLoader();
79         Bundle bundle = null;
80
81         if (tlcl instanceof BundleReference) {
82             bundle = ((BundleReference) tlcl).getBundle();
83         } else {
84             LOGGER.warn("Unable to determine the bundle context based on " +
85                         "thread context classloader.");
86             bundle = FrameworkUtil.getBundle(this.getClass());
87         }
88         return (bundle == null ? null : bundle.getBundleContext());
89     }
90
91     private static final IBundleScanService lookupBundleScanner(BundleContext ctx) {
92         ServiceReference svcRef = ctx.getServiceReference(IBundleScanService.class);
93         if (svcRef == null) {
94             throw new ServiceException("Unable to lookup IBundleScanService");
95         }
96         return IBundleScanService.class.cast(ctx.getService(svcRef));
97     }
98
99     private final JAXBContext newJAXBContext() {
100         BundleContext ctx = getBundleContext();
101         IBundleScanService svc = lookupBundleScanner(ctx);
102         try {
103             List<Class<?>> cls = svc.getAnnotatedClasses(ctx,
104                     new String[] { XmlRootElement.class.getPackage().getName() },
105                     true);
106             return JAXBContext.newInstance(cls.toArray(new Class[cls.size()]));
107         } catch (JAXBException je) {
108             LOGGER.error("Error creating JAXBContext", je);
109             return null;
110         }
111     }
112
113     private final Set<Class<?>> findJAXRSResourceClasses() {
114         BundleContext ctx = getBundleContext();
115         String bundleName = ctx.getBundle().getSymbolicName();
116         Set<Class<?>> result = new HashSet<Class<?>>();
117         ServiceException recordException = null;
118         try {
119             IBundleScanService svc = lookupBundleScanner(ctx);
120             result.addAll(svc.getAnnotatedClasses(ctx,
121                     new String[] { javax.ws.rs.Path.class.getName() }, false));
122         } catch (ServiceException se) {
123             recordException = se;
124             LOGGER.debug("Error finding JAXRS resource annotated classes in " +
125                     "bundle: {} error: {}.", bundleName, se.getMessage());
126             // the bundle scan service cannot be lookedup. Lets attempt to
127             // lookup the resources from the bundle manifest header
128             Dictionary<String,String> headers = ctx.getBundle().getHeaders();
129             String header = headers.get(JAXRS_RESOURCES_MANIFEST_NAME);
130             if (header != null) {
131                 for (String s : header.split(",")) {
132                     s = s.trim();
133                     if (s.length() > 0) {
134                         try {
135                             result.add(ctx.getBundle().loadClass(s));
136                         } catch (ClassNotFoundException cnfe) {
137                             LOGGER.error("Cannot load class: {} in bundle: {} " +
138                                     "defined as MANIFEST JAX-RS resource", s, bundleName, cnfe);
139                         }
140                     }
141                 }
142             }
143
144         }
145
146         if (result.size() == 0) {
147             if (recordException != null) {
148                 throw recordException;
149             } else {
150                 throw new ServiceException("No resource classes found in bundle:" +
151                         ctx.getBundle().getSymbolicName());
152             }
153         }
154         return result;
155     }
156
157 }