001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.jaxb;
019:
020: import java.io.BufferedReader;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.InputStreamReader;
024: import java.io.OutputStream;
025: import java.util.ArrayList;
026: import java.util.Arrays;
027: import java.util.Collection;
028: import java.util.HashMap;
029: import java.util.HashSet;
030: import java.util.Iterator;
031: import java.util.List;
032: import java.util.Map;
033: import java.util.ResourceBundle;
034: import java.util.Set;
035:
036: import javax.xml.bind.JAXBContext;
037: import javax.xml.bind.JAXBException;
038: import javax.xml.bind.SchemaOutputResolver;
039: import javax.xml.stream.XMLEventReader;
040: import javax.xml.stream.XMLEventWriter;
041: import javax.xml.stream.XMLStreamReader;
042: import javax.xml.stream.XMLStreamWriter;
043: import javax.xml.transform.Result;
044: import javax.xml.transform.dom.DOMResult;
045: import javax.xml.transform.dom.DOMSource;
046:
047: import org.w3c.dom.Document;
048: import org.w3c.dom.Node;
049:
050: import com.sun.xml.bind.v2.ContextFactory;
051: import com.sun.xml.bind.v2.runtime.JAXBContextImpl;
052:
053: import org.apache.cxf.common.i18n.BundleUtils;
054: import org.apache.cxf.common.i18n.Message;
055: import org.apache.cxf.common.util.CacheMap;
056: import org.apache.cxf.common.util.PackageUtils;
057: import org.apache.cxf.common.util.StringUtils;
058: import org.apache.cxf.databinding.DataBinding;
059: import org.apache.cxf.databinding.DataReader;
060: import org.apache.cxf.databinding.DataWriter;
061: import org.apache.cxf.databinding.source.AbstractDataBinding;
062: import org.apache.cxf.jaxb.io.DataReaderImpl;
063: import org.apache.cxf.jaxb.io.DataWriterImpl;
064: import org.apache.cxf.service.Service;
065: import org.apache.cxf.service.factory.ServiceConstructionException;
066: import org.apache.cxf.service.model.ServiceInfo;
067: import org.apache.cxf.ws.addressing.ObjectFactory;
068: import org.apache.cxf.wsdl11.WSDLServiceBuilder;
069: import org.apache.ws.commons.schema.XmlSchemaCollection;
070:
071: public final class JAXBDataBinding extends AbstractDataBinding
072: implements DataBinding {
073:
074: public static final String SCHEMA_RESOURCE = "SCHEMRESOURCE";
075:
076: public static final String UNWRAP_JAXB_ELEMENT = "unwrap.jaxb.element";
077:
078: private static final ResourceBundle BUNDLE = BundleUtils
079: .getBundle(JAXBDataBinding.class);
080:
081: private static final Class<?> SUPPORTED_READER_FORMATS[] = new Class<?>[] {
082: Node.class, XMLEventReader.class, XMLStreamReader.class };
083: private static final Class<?> SUPPORTED_WRITER_FORMATS[] = new Class<?>[] {
084: OutputStream.class, Node.class, XMLEventWriter.class,
085: XMLStreamWriter.class };
086:
087: private static final Map<Set<Class<?>>, JAXBContext> JAXBCONTEXT_CACHE = new CacheMap<Set<Class<?>>, JAXBContext>();
088:
089: Class[] extraClass;
090:
091: JAXBContext context;
092: Set<Class<?>> contextClasses;
093:
094: Class<?> cls;
095:
096: public JAXBDataBinding() {
097: }
098:
099: public JAXBDataBinding(Class<?>... classes) throws JAXBException {
100: contextClasses = new HashSet<Class<?>>();
101: contextClasses.addAll(Arrays.asList(classes));
102: setContext(createJAXBContext(contextClasses));
103: }
104:
105: public JAXBDataBinding(JAXBContext context) {
106: this ();
107: setContext(context);
108: }
109:
110: public JAXBContext getContext() {
111: return context;
112: }
113:
114: public void setContext(JAXBContext ctx) {
115: context = ctx;
116: }
117:
118: @SuppressWarnings("unchecked")
119: public <T> DataWriter<T> createWriter(Class<T> c) {
120: if (c == XMLStreamWriter.class) {
121: return (DataWriter<T>) new DataWriterImpl<XMLStreamWriter>(
122: context);
123: } else if (c == OutputStream.class) {
124: return (DataWriter<T>) new DataWriterImpl<OutputStream>(
125: context);
126: } else if (c == XMLEventWriter.class) {
127: return (DataWriter<T>) new DataWriterImpl<XMLEventWriter>(
128: context);
129: } else if (c == Node.class) {
130: return (DataWriter<T>) new DataWriterImpl<Node>(context);
131: }
132:
133: return null;
134: }
135:
136: public Class<?>[] getSupportedWriterFormats() {
137: return SUPPORTED_WRITER_FORMATS;
138: }
139:
140: @SuppressWarnings("unchecked")
141: public <T> DataReader<T> createReader(Class<T> c) {
142: DataReader<T> dr = null;
143: if (c == XMLStreamReader.class) {
144: dr = (DataReader<T>) new DataReaderImpl<XMLStreamReader>(
145: context);
146: } else if (c == XMLEventReader.class) {
147: dr = (DataReader<T>) new DataReaderImpl<XMLEventReader>(
148: context);
149: } else if (c == Node.class) {
150: dr = (DataReader<T>) new DataReaderImpl<Node>(context);
151: }
152:
153: return dr;
154: }
155:
156: public Class<?>[] getSupportedReaderFormats() {
157: return SUPPORTED_READER_FORMATS;
158: }
159:
160: public void initialize(Service service) {
161: //context is already set, don't redo it
162: if (context != null) {
163: return;
164: }
165:
166: contextClasses = new HashSet<Class<?>>();
167: for (ServiceInfo serviceInfo : service.getServiceInfos()) {
168: JAXBContextInitializer initializer = new JAXBContextInitializer(
169: serviceInfo, contextClasses);
170: initializer.walk();
171:
172: }
173: try {
174: String tns = service.getName().getNamespaceURI();
175: if (service.getServiceInfos().size() > 0) {
176: tns = service.getServiceInfos().get(0).getInterface()
177: .getName().getNamespaceURI();
178: }
179: setContext(createJAXBContext(contextClasses, tns));
180: } catch (JAXBException e1) {
181: throw new ServiceConstructionException(e1);
182: }
183:
184: for (ServiceInfo serviceInfo : service.getServiceInfos()) {
185: XmlSchemaCollection col = (XmlSchemaCollection) serviceInfo
186: .getProperty(WSDLServiceBuilder.WSDL_SCHEMA_LIST);
187:
188: if (col != null) {
189: // someone has already filled in the types
190: continue;
191: }
192:
193: col = new XmlSchemaCollection();
194:
195: Collection<DOMSource> schemas = getSchemas();
196: if (schemas != null) {
197: for (DOMSource r : schemas) {
198: addSchemaDocument(serviceInfo, col, (Document) r
199: .getNode(), r.getSystemId());
200: }
201: } else {
202: try {
203: for (DOMResult r : generateJaxbSchemas()) {
204: addSchemaDocument(serviceInfo, col,
205: (Document) r.getNode(), r.getSystemId());
206: }
207: } catch (IOException e) {
208: throw new ServiceConstructionException(new Message(
209: "SCHEMA_GEN_EXC", BUNDLE), e);
210: }
211: }
212:
213: serviceInfo.setProperty(
214: WSDLServiceBuilder.WSDL_SCHEMA_LIST, col);
215: JAXBContextImpl riContext;
216: if (context instanceof JAXBContextImpl) {
217: riContext = (JAXBContextImpl) context;
218: } else {
219: // fall back if we're using another jaxb implementation
220: try {
221: riContext = (JAXBContextImpl) ContextFactory
222: .createContext(contextClasses
223: .toArray(new Class[contextClasses
224: .size()]), null);
225: } catch (JAXBException e) {
226: throw new ServiceConstructionException(e);
227: }
228: }
229:
230: JAXBSchemaInitializer schemaInit = new JAXBSchemaInitializer(
231: serviceInfo, col, riContext);
232: schemaInit.walk();
233: }
234: }
235:
236: public void setExtraClass(Class[] userExtraClass) {
237: extraClass = userExtraClass;
238: }
239:
240: public Class[] getExtraClass() {
241: return extraClass;
242: }
243:
244: private List<DOMResult> generateJaxbSchemas() throws IOException {
245: final List<DOMResult> results = new ArrayList<DOMResult>();
246:
247: context.generateSchema(new SchemaOutputResolver() {
248: @Override
249: public Result createOutput(String ns, String file)
250: throws IOException {
251: DOMResult result = new DOMResult();
252: result.setSystemId(file);
253: // Don't include WS-Addressing bits
254: if ("http://www.w3.org/2005/02/addressing/wsdl"
255: .equals(ns)) {
256: return result;
257: }
258: results.add(result);
259: return result;
260: }
261: });
262:
263: return results;
264: }
265:
266: public JAXBContext createJAXBContext(Set<Class<?>> classes)
267: throws JAXBException {
268: return createJAXBContext(classes, null);
269: }
270:
271: public JAXBContext createJAXBContext(Set<Class<?>> classes,
272: String defaultNs) throws JAXBException {
273: Iterator it = classes.iterator();
274: String className = "";
275: Object remoteExceptionObject = null;
276: while (it.hasNext()) {
277: remoteExceptionObject = (Class) it.next();
278: className = remoteExceptionObject.toString();
279: if (!("".equals(className))
280: && className.contains("RemoteException")) {
281: classes.remove(remoteExceptionObject);
282: }
283: }
284: // add user extra class into jaxb context
285: if (extraClass != null && extraClass.length > 0) {
286: for (Class clz : extraClass) {
287: classes.add(clz);
288: }
289: }
290:
291: for (Class<?> clz : classes) {
292: if (clz.getName().endsWith("ObjectFactory")) {
293: //kind of a hack, but ObjectFactories may be created with empty namespaces
294: defaultNs = null;
295: }
296: }
297:
298: Map<String, Object> map = new HashMap<String, Object>();
299: if (defaultNs != null) {
300: map
301: .put("com.sun.xml.bind.defaultNamespaceRemap",
302: defaultNs);
303: }
304:
305: //try and read any jaxb.index files that are with the other classes. This should
306: //allow loading of extra classes (such as subclasses for inheritance reasons)
307: //that are in the same package.
308: Map<String, InputStream> packages = new HashMap<String, InputStream>();
309: Map<String, ClassLoader> packageLoaders = new HashMap<String, ClassLoader>();
310: for (Class<?> jcls : classes) {
311: if (!packages
312: .containsKey(PackageUtils.getPackageName(jcls))) {
313: packages.put(PackageUtils.getPackageName(jcls), jcls
314: .getResourceAsStream("jaxb.index"));
315: packageLoaders.put(PackageUtils.getPackageName(jcls),
316: jcls.getClassLoader());
317: }
318: }
319: for (Map.Entry<String, InputStream> entry : packages.entrySet()) {
320: if (entry.getValue() != null) {
321: try {
322: BufferedReader reader = new BufferedReader(
323: new InputStreamReader(entry.getValue(),
324: "UTF-8"));
325: String pkg = entry.getKey();
326: ClassLoader loader = packageLoaders.get(pkg);
327: if (!StringUtils.isEmpty(pkg)) {
328: pkg += ".";
329: }
330:
331: String line = reader.readLine();
332: while (line != null) {
333: line = line.trim();
334: if (line.indexOf("#") != -1) {
335: line = line.substring(0, line.indexOf("#"));
336: }
337: if (!StringUtils.isEmpty(line)) {
338: try {
339: Class<?> ncls = Class.forName(pkg
340: + line, false, loader);
341: classes.add(ncls);
342: } catch (Exception e) {
343: //ignore
344: }
345: }
346: line = reader.readLine();
347: }
348: } catch (Exception e) {
349: //ignore
350: } finally {
351: try {
352: entry.getValue().close();
353: } catch (Exception e) {
354: //ignore
355: }
356: }
357: }
358: }
359:
360: addWsAddressingTypes(classes);
361:
362: synchronized (JAXBCONTEXT_CACHE) {
363: if (!JAXBCONTEXT_CACHE.containsKey(classes)) {
364: JAXBContext ctx = JAXBContext.newInstance(classes
365: .toArray(new Class[classes.size()]), map);
366: JAXBCONTEXT_CACHE.put(classes, ctx);
367: }
368: }
369:
370: return JAXBCONTEXT_CACHE.get(classes);
371: }
372:
373: private void addWsAddressingTypes(Set<Class<?>> classes) {
374: if (classes.contains(ObjectFactory.class)) {
375: // ws-addressing is used, lets add the specific types
376: try {
377: classes
378: .add(Class
379: .forName("org.apache.cxf.ws.addressing.wsdl.ObjectFactory"));
380: classes
381: .add(Class
382: .forName("org.apache.cxf.ws.addressing.wsdl.AttributedQNameType"));
383: classes
384: .add(Class
385: .forName("org.apache.cxf.ws.addressing.wsdl.ServiceNameType"));
386: } catch (ClassNotFoundException unused) {
387: // REVISIT - ignorable if WS-ADDRESSING not available?
388: // maybe add a way to allow interceptors to add stuff to the
389: // context?
390: }
391: }
392: }
393: }
|