Upgrade jackson library to version 2.3.0
[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 import javax.ws.rs.core.Application;
16 import javax.ws.rs.ext.ContextResolver;
17 import javax.xml.bind.JAXBContext;
18 import javax.xml.bind.JAXBException;
19 import javax.xml.bind.annotation.XmlRootElement;
20
21 import com.fasterxml.jackson.databind.DeserializationFeature;
22 import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
23 import org.opendaylight.controller.northbound.bundlescanner.IBundleScanService;
24 import org.osgi.framework.Bundle;
25 import org.osgi.framework.BundleContext;
26 import org.osgi.framework.BundleReference;
27 import org.osgi.framework.FrameworkUtil;
28 import org.osgi.framework.ServiceException;
29 import org.osgi.framework.ServiceReference;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * Instance of javax.ws.rs.core.Application used to return the classes
35  * that will be instantiated for JAXRS processing. This hooks onto the
36  * bundle scanner service to provide JAXB classes to JAX-RS for prorcessing.
37  */
38 @SuppressWarnings("unchecked")
39 public class NorthboundApplication extends Application {
40     public static final String JAXRS_RESOURCES_MANIFEST_NAME = "Jaxrs-Resources";
41     public static final String JAXRS_EXCLUDES_MANIFEST_NAME = "Jaxrs-Exclude-Types";
42     private static final Logger LOGGER = LoggerFactory.getLogger(NorthboundApplication.class);
43     private final Set<Object> _singletons;
44
45     public NorthboundApplication() {
46         _singletons = new HashSet<Object>();
47         _singletons.add(new ContextResolver<JAXBContext>() {
48             JAXBContext jaxbContext;
49             @Override
50             public synchronized JAXBContext getContext(Class<?> type) {
51                 if (jaxbContext == null) {
52                     jaxbContext = newJAXBContext();
53                 }
54                 return jaxbContext;
55             }
56
57         } );
58         _singletons.add(getJsonProvider());
59         _singletons.add(new JacksonJsonProcessingExceptionMapper());
60     }
61
62     ////////////////////////////////////////////////////////////////
63     //  Application overrides
64     ////////////////////////////////////////////////////////////////
65
66     @Override
67     public Set<Object> getSingletons() {
68         return _singletons;
69     }
70
71     @Override
72     public Set<Class<?>> getClasses() {
73         Set<Class<?>> result = new HashSet<Class<?>>();
74         result.addAll(findJAXRSResourceClasses());
75         return result;
76     }
77
78     private static final JacksonJaxbJsonProvider getJsonProvider() {
79         JacksonJaxbJsonProvider jsonProvider = new JacksonJaxbJsonProvider();
80         jsonProvider.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
81                 false);
82         return jsonProvider;
83     }
84
85     private BundleContext getBundleContext() {
86         ClassLoader tlcl = Thread.currentThread().getContextClassLoader();
87         Bundle bundle = null;
88
89         if (tlcl instanceof BundleReference) {
90             bundle = ((BundleReference) tlcl).getBundle();
91         } else {
92             LOGGER.warn("Unable to determine the bundle context based on " +
93                         "thread context classloader.");
94             bundle = FrameworkUtil.getBundle(this.getClass());
95         }
96         return (bundle == null ? null : bundle.getBundleContext());
97     }
98
99     private static final IBundleScanService lookupBundleScanner(BundleContext ctx) {
100         ServiceReference svcRef = ctx.getServiceReference(IBundleScanService.class);
101         if (svcRef == null) {
102             throw new ServiceException("Unable to lookup IBundleScanService");
103         }
104         return IBundleScanService.class.cast(ctx.getService(svcRef));
105     }
106
107     private final JAXBContext newJAXBContext() {
108         BundleContext ctx = getBundleContext();
109         IBundleScanService svc = lookupBundleScanner(ctx);
110         try {
111             List<Class<?>> cls = svc.getAnnotatedClasses(ctx,
112                     new String[] { XmlRootElement.class.getPackage().getName() },
113                     parseManifestEntry(ctx, JAXRS_EXCLUDES_MANIFEST_NAME),
114                     true);
115             return JAXBContext.newInstance(cls.toArray(new Class[cls.size()]));
116         } catch (JAXBException je) {
117             LOGGER.error("Error creating JAXBContext", je);
118             return null;
119         }
120     }
121
122     private final Set<Class<?>> findJAXRSResourceClasses() {
123         BundleContext ctx = getBundleContext();
124         String bundleName = ctx.getBundle().getSymbolicName();
125         Set<Class<?>> result = new HashSet<Class<?>>();
126         ServiceException recordException = null;
127         try {
128             IBundleScanService svc = lookupBundleScanner(ctx);
129             result.addAll(svc.getAnnotatedClasses(ctx,
130                     new String[] { javax.ws.rs.Path.class.getName() },
131                     null, false));
132         } catch (ServiceException se) {
133             recordException = se;
134             LOGGER.debug("Error finding JAXRS resource annotated classes in " +
135                     "bundle: {} error: {}.", bundleName, se.getMessage());
136             // the bundle scan service cannot be lookedup. Lets attempt to
137             // lookup the resources from the bundle manifest header
138             for (String c : parseManifestEntry(ctx, JAXRS_RESOURCES_MANIFEST_NAME)) {
139                 try {
140                     result.add(ctx.getBundle().loadClass(c));
141                 } catch (ClassNotFoundException cnfe) {
142                     LOGGER.error("Cannot load class: {} in bundle: {} " +
143                             "defined as MANIFEST JAX-RS resource", c, bundleName, cnfe);
144                 }
145             }
146         }
147
148         if (result.size() == 0) {
149             if (recordException != null) {
150                 throw recordException;
151             } else {
152                 throw new ServiceException("No resource classes found in bundle:" +
153                         ctx.getBundle().getSymbolicName());
154             }
155         }
156         return result;
157     }
158
159     private final Set<String> parseManifestEntry(BundleContext ctx, String name) {
160         Set<String> result = new HashSet<String>();
161         Dictionary<String,String> headers = ctx.getBundle().getHeaders();
162         String header = headers.get(name);
163         if (header != null) {
164             for (String s : header.split(",")) {
165                 s = s.trim();
166                 if (s.length() > 0) {
167                     result.add(s);
168                 }
169             }
170         }
171         return result;
172     }
173
174 }