View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.xml.internal.ws.model;
27  
28  import com.oracle.xmlns.internal.webservices.jaxws_databinding.JavaMethod;
29  import com.oracle.xmlns.internal.webservices.jaxws_databinding.JavaParam;
30  import com.oracle.xmlns.internal.webservices.jaxws_databinding.JavaWsdlMappingType;
31  import com.oracle.xmlns.internal.webservices.jaxws_databinding.ObjectFactory;
32  import com.sun.xml.internal.bind.api.JAXBRIContext;
33  import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
34  import com.sun.xml.internal.ws.util.xml.XmlUtil;
35  import org.w3c.dom.Element;
36  import org.xml.sax.SAXException;
37  
38  import javax.xml.bind.JAXBContext;
39  import javax.xml.bind.JAXBElement;
40  import javax.xml.bind.JAXBException;
41  import javax.xml.bind.Unmarshaller;
42  import javax.xml.bind.util.JAXBResult;
43  import javax.xml.stream.XMLInputFactory;
44  import javax.xml.stream.XMLStreamException;
45  import javax.xml.stream.XMLStreamReader;
46  import javax.xml.transform.Source;
47  import javax.xml.transform.Transformer;
48  import javax.xml.transform.TransformerException;
49  import javax.xml.transform.TransformerFactory;
50  import javax.xml.transform.stream.StreamSource;
51  import javax.xml.validation.Schema;
52  import javax.xml.validation.SchemaFactory;
53  import java.io.*;
54  import java.lang.annotation.Annotation;
55  import java.lang.reflect.Method;
56  import java.net.URL;
57  import java.util.*;
58  
59  import static com.oracle.xmlns.internal.webservices.jaxws_databinding.ExistingAnnotationsType.MERGE;
60  
61  /**
62   * Metadata Reader able to read from either class annotations or external metadata files or combine both,
63   * depending on configuration provided in xml file itself.
64   *
65   * @author shih-chang.chen@oracle.com, miroslav.kos@oracle.com
66   */
67  public class ExternalMetadataReader extends ReflectAnnotationReader {
68  
69      private static final String NAMESPACE_WEBLOGIC_WSEE_DATABINDING = "http://xmlns.oracle.com/weblogic/weblogic-wsee-databinding";
70      private static final String NAMESPACE_JAXWS_RI_EXTERNAL_METADATA = "http://xmlns.oracle.com/webservices/jaxws-databinding";
71  
72      /**
73       * map of readers for defined java types
74       */
75      private Map<String, JavaWsdlMappingType> readers = new HashMap<String, JavaWsdlMappingType>();
76  
77      public ExternalMetadataReader(Collection<File> files, Collection<String> resourcePaths, ClassLoader classLoader,
78                                    boolean xsdValidation, boolean disableXmlSecurity) {
79  
80          if (files != null) {
81              for (File file : files) {
82                  try {
83                      String namespace = Util.documentRootNamespace(newSource(file), disableXmlSecurity);
84                      JavaWsdlMappingType externalMapping = parseMetadata(xsdValidation, newSource(file), namespace, disableXmlSecurity);
85                      readers.put(externalMapping.getJavaTypeName(), externalMapping);
86                  } catch (Exception e) {
87                      throw new RuntimeModelerException("runtime.modeler.external.metadata.unable.to.read", file.getAbsolutePath());
88                  }
89              }
90          }
91  
92          if (resourcePaths != null) {
93              for (String resourcePath : resourcePaths) {
94                  try {
95                      String namespace = Util.documentRootNamespace(newSource(resourcePath, classLoader), disableXmlSecurity);
96                      JavaWsdlMappingType externalMapping = parseMetadata(xsdValidation, newSource(resourcePath, classLoader), namespace, disableXmlSecurity);
97                      readers.put(externalMapping.getJavaTypeName(), externalMapping);
98                  } catch (Exception e) {
99                      throw new RuntimeModelerException("runtime.modeler.external.metadata.unable.to.read", resourcePath);
100                 }
101             }
102         }
103     }
104 
105     private StreamSource newSource(String resourcePath, ClassLoader classLoader) {
106         InputStream is = classLoader.getResourceAsStream(resourcePath);
107         return new StreamSource(is);
108     }
109 
110     private JavaWsdlMappingType parseMetadata(boolean xsdValidation, StreamSource source, String namespace, boolean disableXmlSecurity) throws JAXBException, IOException, TransformerException {
111         if (NAMESPACE_WEBLOGIC_WSEE_DATABINDING.equals(namespace)) {
112             return Util.transformAndRead(source, disableXmlSecurity);
113         } if (NAMESPACE_JAXWS_RI_EXTERNAL_METADATA.equals(namespace)) {
114             return Util.read(source, xsdValidation, disableXmlSecurity);
115         } else {
116             throw new RuntimeModelerException("runtime.modeler.external.metadata.unsupported.schema", namespace, Arrays.asList(NAMESPACE_WEBLOGIC_WSEE_DATABINDING, NAMESPACE_JAXWS_RI_EXTERNAL_METADATA).toString());
117         }
118     }
119 
120     private StreamSource newSource(File file) {
121         try {
122             return new StreamSource(new FileInputStream(file));
123         } catch (FileNotFoundException e) {
124             throw new RuntimeModelerException("runtime.modeler.external.metadata.unable.to.read", file.getAbsolutePath());
125         }
126     }
127 
128     public <A extends Annotation> A getAnnotation(Class<A> annType, Class<?> cls) {
129         JavaWsdlMappingType r = reader(cls);
130         return r == null ? super.getAnnotation(annType, cls) : Util.annotation(r, annType);
131     }
132 
133     private JavaWsdlMappingType reader(Class<?> cls) {
134         return readers.get(cls.getName());
135     }
136 
137     Annotation[] getAnnotations(List<Object> objects) {
138         ArrayList<Annotation> list = new ArrayList<Annotation>();
139         for (Object a : objects) {
140             if (Annotation.class.isInstance(a)) {
141                 list.add(Annotation.class.cast(a));
142             }
143         }
144         return list.toArray(new Annotation[list.size()]);
145     }
146 
147     public Annotation[] getAnnotations(final Class<?> c) {
148 
149         Merger<Annotation[]> merger = new Merger<Annotation[]>(reader(c)) {
150             Annotation[] reflection() {
151                 return ExternalMetadataReader.super.getAnnotations(c);
152             }
153 
154             Annotation[] external() {
155                 return getAnnotations(reader.getClassAnnotation());
156             }
157         };
158         return merger.merge();
159     }
160 
161     public Annotation[] getAnnotations(final Method m) {
162         Merger<Annotation[]> merger = new Merger<Annotation[]>(reader(m.getDeclaringClass())) {
163             Annotation[] reflection() {
164                 return ExternalMetadataReader.super.getAnnotations(m);
165             }
166 
167             Annotation[] external() {
168                 JavaMethod jm = getJavaMethod(m, reader);
169                 return (jm == null) ? new Annotation[0] : getAnnotations(jm.getMethodAnnotation());
170             }
171         };
172         return merger.merge();
173     }
174 
175     @SuppressWarnings("unchecked")
176     public <A extends Annotation> A getAnnotation(final Class<A> annType, final Method m) {
177         Merger<Annotation> merger = new Merger<Annotation>(reader(m.getDeclaringClass())) {
178             Annotation reflection() {
179                 return ExternalMetadataReader.super.getAnnotation(annType, m);
180             }
181 
182             Annotation external() {
183                 JavaMethod jm = getJavaMethod(m, reader);
184                 return Util.annotation(jm, annType);
185             }
186         };
187         return (A) merger.merge();
188     }
189 
190     public Annotation[][] getParameterAnnotations(final Method m) {
191         Merger<Annotation[][]> merger = new Merger<Annotation[][]>(reader(m.getDeclaringClass())) {
192             Annotation[][] reflection() {
193                 return ExternalMetadataReader.super.getParameterAnnotations(m);
194             }
195 
196             Annotation[][] external() {
197                 JavaMethod jm = getJavaMethod(m, reader);
198                 Annotation[][] a = m.getParameterAnnotations();
199                 for (int i = 0; i < m.getParameterTypes().length; i++) {
200                     if (jm == null) continue;
201                     JavaParam jp = jm.getJavaParams().getJavaParam().get(i);
202                     a[i] = getAnnotations(jp.getParamAnnotation());
203                 }
204                 return a;
205             }
206         };
207         return merger.merge();
208     }
209 
210     public void getProperties(final Map<String, Object> prop, final Class<?> cls) {
211 
212         JavaWsdlMappingType r = reader(cls);
213 
214         // no external reader or it requires annotations merging ...
215         if (r == null || MERGE.equals(r.getExistingAnnotations())) {
216             super.getProperties(prop, cls);
217         }
218 
219     }
220 
221     public void getProperties(final Map<String, Object> prop, final Method m) {
222 
223         JavaWsdlMappingType r = reader(m.getDeclaringClass());
224 
225         // no external reader or it requires annotations merging ...
226         if (r == null || MERGE.equals(r.getExistingAnnotations())) {
227             super.getProperties(prop, m);
228         }
229 
230         if (r != null) {
231             JavaMethod jm = getJavaMethod(m, r);
232             Element[] e = Util.annotation(jm);
233             prop.put("eclipselink-oxm-xml.xml-element", findXmlElement(e));
234         }
235 
236     }
237 
238     public void getProperties(final Map<String, Object> prop, final Method m, int pos) {
239 
240         JavaWsdlMappingType r = reader(m.getDeclaringClass());
241 
242         // no external reader or it requires annotations merging ...
243         if (r == null || MERGE.equals(r.getExistingAnnotations())) {
244             super.getProperties(prop, m, pos);
245         }
246 
247         if (r != null) {
248             JavaMethod jm = getJavaMethod(m, r);
249             if (jm == null) return;
250             JavaParam jp = jm.getJavaParams().getJavaParam().get(pos);
251             Element[] e = Util.annotation(jp);
252             prop.put("eclipselink-oxm-xml.xml-element", findXmlElement(e));
253         }
254     }
255 
256     JavaMethod getJavaMethod(Method method, JavaWsdlMappingType r) {
257 
258         JavaWsdlMappingType.JavaMethods javaMethods = r.getJavaMethods();
259         if (javaMethods == null) {
260             return null;
261         }
262 
263         List<JavaMethod> sameName = new ArrayList<JavaMethod>();
264         for (JavaMethod jm : javaMethods.getJavaMethod()) {
265             if (method.getName().equals(jm.getName())) {
266                 sameName.add(jm);
267             }
268         }
269 
270         if (sameName.isEmpty()) {
271             return null;
272         } else {
273             if (sameName.size() == 1) {
274                 return sameName.get(0);
275             } else {
276                 Class<?>[] argCls = method.getParameterTypes();
277                 for (JavaMethod jm : sameName) {
278                     JavaMethod.JavaParams params = jm.getJavaParams();
279                     if (params != null && params.getJavaParam() != null && params.getJavaParam().size() == argCls.length) {
280                         int count = 0;
281                         for (int i = 0; i < argCls.length; i++) {
282                             JavaParam jp = params.getJavaParam().get(i);
283                             if (argCls[i].getName().equals(jp.getJavaType())) {
284                                 count++;
285                             }
286                         }
287                         if (count == argCls.length) {
288                             return jm;
289                         }
290                     }
291                 }
292             }
293         }
294         return null;
295     }
296 
297     Element findXmlElement(Element[] xa) {
298         if (xa == null) return null;
299         for (Element e : xa) {
300             if (e.getLocalName().equals("java-type")) return e;
301             if (e.getLocalName().equals("xml-element")) return e;
302         }
303         return null;
304     }
305 
306     /**
307      * Helper class to merge two different arrays of annotation objects. It merges annotations based on attribute
308      * <code>existing-annotations</code> in external customization file.
309      * <p/>
310      * We suppose that in the result array there wouldn't be two annotations of same type:
311      * annotation.annotationType().getName(); if there are found such annotations the one from reflection is
312      * considered overriden and is thrown away.
313      * <p/>
314      * The helper can work either with one and two dimensional array, but it can be used for two single Annotation
315      * objects;
316      */
317     static abstract class Merger<T> {
318 
319         JavaWsdlMappingType reader;
320 
321         Merger(JavaWsdlMappingType r) {
322             this.reader = r;
323         }
324 
325         abstract T reflection();
326 
327         abstract T external();
328 
329         @SuppressWarnings("unchecked")
330         T merge() {
331             T reflection = reflection();
332             if (reader == null) {
333                 return reflection;
334             }
335 
336             T external = external();
337             if (!MERGE.equals(reader.getExistingAnnotations())) {
338                 return external;
339             }
340 
341             if (reflection instanceof Annotation) {
342                 return (T) doMerge((Annotation) reflection, (Annotation) external);
343             } else if (reflection instanceof Annotation[][]) {
344                 return (T) doMerge((Annotation[][]) reflection, (Annotation[][]) external);
345             } else {
346                 return (T) doMerge((Annotation[]) reflection, (Annotation[]) external);
347             }
348         }
349 
350         private Annotation doMerge(Annotation reflection, Annotation external) {
351             return external != null ? external : reflection;
352         }
353 
354         private Annotation[][] doMerge(Annotation[][] reflection, Annotation[][] external) {
355             for (int i = 0; i < reflection.length; i++) {
356                 reflection[i] = doMerge(reflection[i], external.length > i ? external[i] : null);
357             }
358             return reflection;
359         }
360 
361         private Annotation[] doMerge(Annotation[] annotations, Annotation[] externalAnnotations) {
362             HashMap<String, Annotation> mergeMap = new HashMap<String, Annotation>();
363             if (annotations != null) {
364                 for (Annotation reflectionAnnotation : annotations) {
365                     mergeMap.put(reflectionAnnotation.annotationType().getName(), reflectionAnnotation);
366                 }
367             }
368 
369             // overriding happens here, based on annotationType().getName() ...
370             if (externalAnnotations != null) {
371                 for (Annotation externalAnnotation : externalAnnotations) {
372                     mergeMap.put(externalAnnotation.annotationType().getName(), externalAnnotation);
373                 }
374             }
375             Collection<Annotation> values = mergeMap.values();
376             int size = values.size();
377             return size == 0 ? null : values.toArray(new Annotation[size]);
378         }
379 
380     }
381 
382     static class Util {
383 
384         //private static final String DATABINDING_XSD = "com/sun/xml/internal/ws/model/jaxws-databinding.xsd";
385         private static final String DATABINDING_XSD = "jaxws-databinding.xsd";
386         //private static final String TRANSLATE_NAMESPACES_XSL = "/com/sun/xml/internal/ws/model/jaxws-databinding-translate-namespaces.xml";
387         private static final String TRANSLATE_NAMESPACES_XSL = "jaxws-databinding-translate-namespaces.xml";
388 
389         static Schema schema;
390         static JAXBContext jaxbContext;
391 
392         static {
393             SchemaFactory sf = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
394             try {
395                 URL xsdUrl = getResource();
396                 if (xsdUrl != null) {
397                     schema = sf.newSchema(xsdUrl);
398                 }
399             } catch (SAXException e1) {
400                 //      e1.printStackTrace();
401             }
402 
403             jaxbContext = createJaxbContext(false);
404         }
405 
406         private static URL getResource() {
407             ClassLoader classLoader = Util.class.getClassLoader();
408             return classLoader != null ? classLoader.getResource(DATABINDING_XSD) : ClassLoader.getSystemResource(DATABINDING_XSD);
409         }
410 
411         private static JAXBContext createJaxbContext(boolean disableXmlSecurity) {
412             Class[] cls = {ObjectFactory.class};
413             try {
414                 if (disableXmlSecurity) {
415                     Map<String, Object> properties = new HashMap<String, Object>();
416                     properties.put(JAXBRIContext.DISABLE_XML_SECURITY, disableXmlSecurity);
417                     return JAXBContext.newInstance(cls, properties);
418                 } else {
419                     return JAXBContext.newInstance(cls);
420                 }
421             } catch (JAXBException e) {
422                 e.printStackTrace();
423                 return null;
424             }
425         }
426 
427         @SuppressWarnings("unchecked")
428         public static JavaWsdlMappingType read(Source src, boolean xsdValidation, boolean disableXmlSecurity) throws IOException, JAXBException {
429             JAXBContext ctx = jaxbContext(disableXmlSecurity);
430             try {
431                 Unmarshaller um = ctx.createUnmarshaller();
432                 if (xsdValidation) {
433                     if (schema == null) {
434                         //TODO 0 warning for schema == null
435                     }
436                     um.setSchema(schema);
437                 }
438                 Object o = um.unmarshal(src);
439                 return getJavaWsdlMapping(o);
440             } catch (JAXBException e) {
441                 // throw new
442                 // WebServiceException(WsDatabindingMessages.mappingFileCannotRead
443                 // (src.getSystemId()), e);
444                 URL url = new URL(src.getSystemId());
445                 Source s = new StreamSource(url.openStream());
446                 Unmarshaller um = ctx.createUnmarshaller();
447                 if (xsdValidation) {
448                     if (schema == null) {
449                         //TODO 0 warning for schema == null
450                     }
451                     um.setSchema(schema);
452                 }
453                 Object o = um.unmarshal(s);
454                 return getJavaWsdlMapping(o);
455             }
456         }
457 
458         private static JAXBContext jaxbContext(boolean disableXmlSecurity) {
459             // as it is supposed to have security enabled in most cases, we create and don't cache
460             // "insecure" JAXBContext - these should be corner cases
461             return disableXmlSecurity ? createJaxbContext(true) : jaxbContext;
462         }
463 
464         public static JavaWsdlMappingType transformAndRead(Source src, boolean disableXmlSecurity) throws TransformerException, JAXBException {
465             Source xsl = new StreamSource(Util.class.getResourceAsStream(TRANSLATE_NAMESPACES_XSL));
466             JAXBResult result = new JAXBResult(jaxbContext(disableXmlSecurity));
467             TransformerFactory tf = XmlUtil.newTransformerFactory(!disableXmlSecurity);
468             Transformer transformer = tf.newTemplates(xsl).newTransformer();
469             transformer.transform(src, result);
470             return getJavaWsdlMapping(result.getResult());
471         }
472 
473 
474         static JavaWsdlMappingType getJavaWsdlMapping(Object o) {
475             Object val = (o instanceof JAXBElement) ? ((JAXBElement) o).getValue() : o;
476             if (val instanceof JavaWsdlMappingType) return (JavaWsdlMappingType) val;
477             //    else if (val instanceof JavaWsdlMappings)
478             //      for (JavaWsdlMappingType m: ((JavaWsdlMappings) val).getJavaWsdlMapping())
479             //        if (seiName.equals(m.javaTypeName)) return m;
480             return null;
481         }
482 
483         static <T> T findInstanceOf(Class<T> type, List<Object> objects) {
484             for (Object o : objects) {
485                 if (type.isInstance(o)) {
486                     return type.cast(o);
487                 }
488             }
489             return null;
490         }
491 
492         static public <T> T annotation(JavaWsdlMappingType jwse, Class<T> anntype) {
493             if (jwse == null || jwse.getClassAnnotation() == null) {
494                 return null;
495             }
496             return findInstanceOf(anntype, jwse.getClassAnnotation());
497         }
498 
499         static public <T> T annotation(JavaMethod jm, Class<T> anntype) {
500             if (jm == null || jm.getMethodAnnotation() == null) {
501                 return null;
502             }
503             return findInstanceOf(anntype, jm.getMethodAnnotation());
504         }
505 
506         static public <T> T annotation(JavaParam jp, Class<T> anntype) {
507             if (jp == null || jp.getParamAnnotation() == null) {
508                 return null;
509             }
510             return findInstanceOf(anntype, jp.getParamAnnotation());
511         }
512 
513         static public Element[] annotation(JavaMethod jm) {
514             if (jm == null || jm.getMethodAnnotation() == null) {
515                 return null;
516             }
517             return findElements(jm.getMethodAnnotation());
518         }
519 
520         static public Element[] annotation(JavaParam jp) {
521             if (jp == null || jp.getParamAnnotation() == null) {
522                 return null;
523             }
524             return findElements(jp.getParamAnnotation());
525         }
526 
527         private static Element[] findElements(List<Object> objects) {
528             List<Element> elems = new ArrayList<Element>();
529             for (Object o : objects) {
530                 if (o instanceof Element) {
531                     elems.add((Element) o);
532                 }
533             }
534             return elems.toArray(new Element[elems.size()]);
535         }
536 
537         static String documentRootNamespace(Source src, boolean disableXmlSecurity) throws XMLStreamException {
538             XMLInputFactory factory;
539             factory = XmlUtil.newXMLInputFactory(!disableXmlSecurity);
540             XMLStreamReader streamReader = factory.createXMLStreamReader(src);
541             XMLStreamReaderUtil.nextElementContent(streamReader);
542             String namespaceURI = streamReader.getName().getNamespaceURI();
543             XMLStreamReaderUtil.close(streamReader);
544             return namespaceURI;
545         }
546     }
547 
548 
549 }