Merge "BUG-2329 Add test for anyxmls inside rpc resonse for netcfon-connector"
[controller.git] / opendaylight / adsal / 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.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;
33
34 import com.fasterxml.jackson.databind.DeserializationFeature;
35 import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
36
37 /**
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.
41  */
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;
47
48     public NorthboundApplication() {
49         _singletons = new HashSet<Object>();
50         _singletons.add(new ContextResolver<JAXBContext>() {
51             JAXBContext jaxbContext;
52             @Override
53             public synchronized JAXBContext getContext(Class<?> type) {
54                 if (jaxbContext == null) {
55                     jaxbContext = newJAXBContext();
56                 }
57                 return jaxbContext;
58             }
59
60         } );
61         _singletons.add(getJsonProvider());
62         _singletons.add(new JacksonJsonProcessingExceptionMapper());
63         _singletons.add(new QueryContextProvider());
64         _singletons.add(new GenericExceptionMapper());
65     }
66
67     ////////////////////////////////////////////////////////////////
68     //  Application overrides
69     ////////////////////////////////////////////////////////////////
70
71     @Override
72     public Set<Object> getSingletons() {
73         return _singletons;
74     }
75
76     @Override
77     public Set<Class<?>> getClasses() {
78         Set<Class<?>> result = new HashSet<Class<?>>();
79         result.addAll(findJAXRSResourceClasses());
80         return result;
81     }
82
83     private static final JacksonJaxbJsonProvider getJsonProvider() {
84         JacksonJaxbJsonProvider jsonProvider = new JacksonJaxbJsonProvider();
85         jsonProvider.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
86                 false);
87         return jsonProvider;
88     }
89
90     private BundleContext getBundleContext() {
91         ClassLoader tlcl = Thread.currentThread().getContextClassLoader();
92         Bundle bundle = null;
93
94         if (tlcl instanceof BundleReference) {
95             bundle = ((BundleReference) tlcl).getBundle();
96         } else {
97             LOGGER.warn("Unable to determine the bundle context based on " +
98                         "thread context classloader.");
99             bundle = FrameworkUtil.getBundle(this.getClass());
100         }
101         return (bundle == null ? null : bundle.getBundleContext());
102     }
103
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");
108         }
109         return IBundleScanService.class.cast(ctx.getService(svcRef));
110     }
111
112     private final JAXBContext newJAXBContext() {
113         BundleContext ctx = getBundleContext();
114         IBundleScanService svc = lookupBundleScanner(ctx);
115         try {
116             List<Class<?>> cls = svc.getAnnotatedClasses(ctx,
117                     new String[] { XmlRootElement.class.getPackage().getName() },
118                     parseManifestEntry(ctx, JAXRS_EXCLUDES_MANIFEST_NAME),
119                     true);
120             return JAXBContext.newInstance(cls.toArray(new Class[cls.size()]));
121         } catch (JAXBException je) {
122             LOGGER.error("Error creating JAXBContext", je);
123             return null;
124         }
125     }
126
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;
132         try {
133             IBundleScanService svc = lookupBundleScanner(ctx);
134             result.addAll(svc.getAnnotatedClasses(ctx,
135                     new String[] { javax.ws.rs.Path.class.getName() },
136                     null, false));
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)) {
144                 try {
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);
149                 }
150             }
151         }
152
153         if (result.size() == 0) {
154             if (recordException != null) {
155                 throw recordException;
156             } else {
157                 throw new ServiceException("No resource classes found in bundle:" +
158                         ctx.getBundle().getSymbolicName());
159             }
160         }
161         return result;
162     }
163
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(",")) {
170                 s = s.trim();
171                 if (s.length() > 0) {
172                     result.add(s);
173                 }
174             }
175         }
176         return result;
177     }
178
179 }