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: */
019: package org.apache.axis2.jaxws.runtime.description.marshal.impl;
020:
021: import org.apache.axis2.java.security.AccessController;
022: import org.apache.axis2.jaxws.ExceptionFactory;
023: import org.apache.axis2.jaxws.description.EndpointDescription;
024: import org.apache.axis2.jaxws.description.EndpointDescriptionJava;
025: import org.apache.axis2.jaxws.description.EndpointInterfaceDescription;
026: import org.apache.axis2.jaxws.description.FaultDescription;
027: import org.apache.axis2.jaxws.description.OperationDescription;
028: import org.apache.axis2.jaxws.description.ParameterDescription;
029: import org.apache.axis2.jaxws.description.ServiceDescription;
030: import org.apache.axis2.jaxws.description.ServiceDescriptionWSDL;
031: import org.apache.axis2.jaxws.runtime.description.marshal.AnnotationDesc;
032: import org.apache.axis2.jaxws.runtime.description.marshal.FaultBeanDesc;
033: import org.apache.axis2.jaxws.runtime.description.marshal.MarshalServiceRuntimeDescription;
034: import org.apache.axis2.jaxws.util.WSDL4JWrapper;
035: import org.apache.axis2.jaxws.util.WSDLWrapper;
036: import org.apache.axis2.jaxws.utility.ClassUtils;
037: import org.apache.axis2.jaxws.utility.JavaUtils;
038: import org.apache.axis2.jaxws.wsdl.SchemaReader;
039: import org.apache.axis2.jaxws.wsdl.SchemaReaderException;
040: import org.apache.axis2.jaxws.wsdl.impl.SchemaReaderImpl;
041: import org.apache.commons.logging.Log;
042: import org.apache.commons.logging.LogFactory;
043:
044: import javax.wsdl.Definition;
045: import javax.wsdl.WSDLException;
046: import javax.xml.bind.JAXBElement;
047: import java.io.File;
048: import java.io.IOException;
049: import java.lang.reflect.Method;
050: import java.net.MalformedURLException;
051: import java.net.URL;
052: import java.security.PrivilegedActionException;
053: import java.security.PrivilegedExceptionAction;
054: import java.util.Collection;
055: import java.util.Set;
056: import java.util.TreeSet;
057:
058: /**
059: * In order to marshal or unmarshal the user data, we need to know the set of packages involved.
060: * The set of packages is used to construct an appropriate JAXBContext object during the
061: * marshalling/unmarshalling.
062: * <p/>
063: * There are two ways to get this data.
064: * <p/>
065: * Schema Walk (preferred): Get the list of packages by walking the schemas that are referenced by
066: * the wsdl (or generated wsdl). Each schema represents a different package. The package is
067: * obtained using the jaxb customization or JAXB default ns<->package rule.
068: * <p/>
069: * Annotation Walk(secondary) : Walk the list of Endpoints, Operations, Parameters, etc. and build a
070: * list of packages by looking at the classes involved.
071: * <p/>
072: * The Schema Walk is faster and more complete, but relies on the presence of the schema or wsdl.
073: * <p/>
074: * The Annotation Walk is slower and is not complete. For example, the annotation walk may not
075: * discover the packages for derived types that are defined in a different schema than the formal
076: * parameter types.
077: */
078: public class PackageSetBuilder {
079:
080: private static Log log = LogFactory.getLog(PackageSetBuilder.class);
081:
082: /** This is a static utility class. The constructor is intentionally private */
083: private PackageSetBuilder() {
084: }
085:
086: /**
087: * Walks the schemas of the serviceDesc's wsdl (or generated wsdl) to determine the list of
088: * packages. This is the preferred algorithm for discovering the package set.
089: *
090: * @param serviceDesc ServiceDescription
091: * @return Set of Packages
092: */
093: public static TreeSet<String> getPackagesFromSchema(
094: ServiceDescription serviceDesc) {
095:
096: TreeSet<String> set = new TreeSet<String>();
097: //If we are on client side we will get wsdl definition from ServiceDescription. If we are on server side we will have to
098: //read wsdlLocation from @WebService Annotation.
099: ServiceDescriptionWSDL sdw = (ServiceDescriptionWSDL) serviceDesc;
100: Definition wsdlDefinition = sdw.getWSDLDefinition();
101: Collection<EndpointDescription> endpointDescs = serviceDesc
102: .getEndpointDescriptions_AsCollection();
103: if (endpointDescs != null) {
104: for (EndpointDescription ed : endpointDescs) {
105:
106: if (wsdlDefinition == null) {
107: // TODO I don't think we should be trying to load the wsdlDefinition here.
108:
109: //Let see if we can get wsdl definition from endpoint @WebService annotation.
110: if (ed instanceof EndpointDescriptionJava) {
111: String wsdlLocation = ((EndpointDescriptionJava) ed)
112: .getAnnoWebServiceWSDLLocation();
113: wsdlDefinition = getWSDLDefinition(wsdlLocation);
114: }
115: }
116: //So at this point either we got wsdl definition from ServiceDescription (which means we are running this code
117: //on client side) or we got it from the @WebService annotation (which means we are running this code on server side)
118: if (wsdlDefinition != null) {
119: SchemaReader sr = new SchemaReaderImpl();
120: try {
121: Set<String> pkgSet = sr
122: .readPackagesFromSchema(wsdlDefinition);
123: set.addAll(pkgSet);
124: } catch (SchemaReaderException e) {
125: ExceptionFactory.makeWebServiceException(e);
126: }
127: }
128: }
129: }
130: return set;
131: }
132:
133: /**
134: * @param serviceDescription ServiceDescription
135: * @return Set of Packages
136: */
137: public static TreeSet<String> getPackagesFromAnnotations(
138: ServiceDescription serviceDesc,
139: MarshalServiceRuntimeDescription msrd) {
140: TreeSet<String> set = new TreeSet<String>();
141: Collection<EndpointDescription> endpointDescs = serviceDesc
142: .getEndpointDescriptions_AsCollection();
143:
144: // Build a set of packages from all of the endpoints
145: if (endpointDescs != null) {
146: for (EndpointDescription endpointDesc : endpointDescs) {
147: set.addAll(getPackagesFromAnnotations(endpointDesc,
148: msrd));
149: }
150: }
151: return set;
152: }
153:
154: /**
155: * @param endpointDesc EndpointDescription
156: * @return Set of Packages
157: */
158: private static TreeSet<String> getPackagesFromAnnotations(
159: EndpointDescription endpointDesc,
160: MarshalServiceRuntimeDescription msrd) {
161: EndpointInterfaceDescription endpointInterfaceDesc = endpointDesc
162: .getEndpointInterfaceDescription();
163: if (endpointInterfaceDesc == null) {
164: return new TreeSet<String>();
165: } else {
166: return getPackagesFromAnnotations(endpointInterfaceDesc,
167: msrd);
168: }
169: }
170:
171: /**
172: * @param endpointInterfaceDescription EndpointInterfaceDescription
173: * @return Set of Packages
174: */
175: private static TreeSet<String> getPackagesFromAnnotations(
176: EndpointInterfaceDescription endpointInterfaceDesc,
177: MarshalServiceRuntimeDescription msrd) {
178: TreeSet<String> set = new TreeSet<String>();
179: OperationDescription[] opDescs = endpointInterfaceDesc
180: .getOperations();
181:
182: // Build a set of packages from all of the opertions
183: if (opDescs != null) {
184: for (int i = 0; i < opDescs.length; i++) {
185: getPackagesFromAnnotations(opDescs[i], set, msrd);
186: }
187: }
188: return set;
189: }
190:
191: /**
192: * Update the package set with the packages referenced by this OperationDesc
193: *
194: * @param opDesc OperationDescription
195: * @param set Set<Package> that is updated
196: */
197: private static void getPackagesFromAnnotations(
198: OperationDescription opDesc, TreeSet<String> set,
199: MarshalServiceRuntimeDescription msrd) {
200:
201: // Walk the parameter information
202: ParameterDescription[] parameterDescs = opDesc
203: .getParameterDescriptions();
204: if (parameterDescs != null) {
205: for (int i = 0; i < parameterDescs.length; i++) {
206: getPackagesFromAnnotations(parameterDescs[i], set, msrd);
207: }
208: }
209:
210: // Walk the fault information
211: FaultDescription[] faultDescs = opDesc.getFaultDescriptions();
212: if (faultDescs != null) {
213: for (int i = 0; i < faultDescs.length; i++) {
214: getPackagesFromAnnotations(faultDescs[i], set, msrd);
215: }
216: }
217:
218: // Also consider the request and response wrappers
219: String pkg = getPackageFromClassName(msrd
220: .getRequestWrapperClassName(opDesc));
221: if (log.isDebugEnabled()) {
222: log.debug("Package from Request Wrapper annotation = "
223: + pkg);
224: }
225: if (pkg != null) {
226: set.add(pkg);
227: }
228: pkg = getPackageFromClassName(msrd
229: .getResponseWrapperClassName(opDesc));
230: if (log.isDebugEnabled()) {
231: log.debug("Package from Response Wrapper annotation = "
232: + pkg);
233: }
234: if (pkg != null) {
235: set.add(pkg);
236: }
237:
238: // Finally consider the result type
239: Class cls = opDesc.getResultActualType();
240: if (cls != null && cls != void.class && cls != Void.class) {
241: Package returnTypePkg = cls.getPackage();
242: if (log.isDebugEnabled()) {
243: log.debug("Package from Return Type = " + pkg);
244: }
245: if (returnTypePkg != null) {
246: pkg = returnTypePkg.getName();
247: set.add(pkg);
248: }
249: }
250: }
251:
252: /**
253: * Update the package set with the packages referenced by this ParameterDescription
254: *
255: * @param paramDesc ParameterDesc
256: * @param set Set<Package> that is updated
257: */
258: private static void getPackagesFromAnnotations(
259: ParameterDescription paramDesc, TreeSet<String> set,
260: MarshalServiceRuntimeDescription msrd) {
261:
262: // Get the type that defines the actual data. (this is never a holder )
263: Class paramClass = paramDesc.getParameterActualType();
264:
265: if (paramClass != null) {
266: setTypeAndElementPackages(paramClass, paramDesc
267: .getTargetNamespace(), paramDesc.getPartName(),
268: set, msrd);
269: }
270:
271: }
272:
273: /**
274: * Update the package set with the packages referenced by this FaultDescription
275: *
276: * @param faultDesc FaultDescription
277: * @param set Set<Package> that is updated
278: */
279: private static void getPackagesFromAnnotations(
280: FaultDescription faultDesc, TreeSet<String> set,
281: MarshalServiceRuntimeDescription msrd) {
282:
283: FaultBeanDesc faultBeanDesc = msrd.getFaultBeanDesc(faultDesc);
284: if (faultBeanDesc == null) {
285: if (log.isDebugEnabled()) {
286: log
287: .debug("faultBeanDesc from MarshallServiceRuntimeDescription is null");
288: }
289: //NO FaultBeanDesc found nothing we can do.
290: return;
291: }
292: String faultBeanName = faultBeanDesc.getFaultBeanClassName();
293: if (faultBeanName == null) {
294: if (log.isDebugEnabled()) {
295: log.debug("FaultBeanName is null");
296: }
297: //We cannot load the faultBeanName
298: return;
299: }
300: Class faultBean = loadClass(faultBeanName);
301: if (faultBean != null) {
302: setTypeAndElementPackages(faultBean, faultBeanDesc
303: .getFaultBeanNamespace(), faultBeanDesc
304: .getFaultBeanLocalName(), set, msrd);
305: }
306: }
307:
308: /**
309: * For each data element, we need the package for both the element and its type.
310: *
311: * @param cls Class representing element, type or both
312: * @param namespace of the element
313: * @param localPart of the element
314: * @param set with both type and element packages set
315: */
316: private static void setTypeAndElementPackages(Class cls,
317: String namespace, String localPart, TreeSet<String> set,
318: MarshalServiceRuntimeDescription msrd) {
319:
320: // Get the element and type classes
321: Class eClass = getElement(cls, msrd);
322: Class tClass = getType(cls);
323:
324: // Set the package for the type
325: if (tClass != null) {
326: Package typePkg = tClass.getPackage();
327: //For primitive types there is no package
328: String pkg = (typePkg != null) ? typePkg.getName() : null;
329:
330: if (pkg != null) {
331: set.add(pkg);
332: }
333: }
334:
335: // Set the package for the element
336: if (tClass != eClass) {
337: if (eClass == null) {
338: // A null or empty namespace indicates that the element is
339: // unqualified. This can occur if the parameter is represented as a child element
340: // in doc/lit wrapped. The package is determined from the wrapper element in such casses.
341: if (namespace != null && namespace.length() > 0) {
342: // Use default namespace to package algorithm
343: String pkg = makePackage(namespace);
344: if (pkg != null) {
345: set.add(pkg);
346: }
347: }
348: } else {
349: Package elementPkg = eClass.getPackage();
350: String pkg = (elementPkg != null) ? elementPkg
351: .getName() : null;
352: if (pkg != null) {
353: set.add(pkg);
354: }
355: }
356: }
357: }
358:
359: /**
360: * If cls represents an xml element then cls is returned. Otherwise null is returned
361: *
362: * @param cls Class
363: * @return Class or null
364: */
365: private static Class getElement(Class cls,
366: MarshalServiceRuntimeDescription msrd) {
367: AnnotationDesc annotationDesc = msrd.getAnnotationDesc(cls);
368: if (annotationDesc == null) {
369: // This shouldn't happen
370: annotationDesc = AnnotationDescImpl.create(cls);
371: }
372: if (annotationDesc.hasXmlRootElement()) {
373: return cls;
374: }
375: return null;
376: }
377:
378: private final static Class[] noClass = new Class[] {};
379:
380: /**
381: * Returns the class that defines the type.
382: *
383: * @param cls
384: * @return
385: */
386: private static Class getType(Class cls) {
387: if (JAXBElement.class.isAssignableFrom(cls)) {
388: try {
389: Method m = cls.getMethod("getValue", noClass);
390: return m.getReturnType();
391: } catch (Exception e) {
392: // We should never get here
393: if (log.isDebugEnabled()) {
394: log
395: .debug("Cannot find JAXBElement.getValue method.");
396: }
397: return null;
398: }
399: } else {
400: return cls;
401: }
402: }
403:
404: /**
405: * Default Namespace to Package algorithm
406: *
407: * @param ns
408: * @return
409: */
410: private static String makePackage(String ns) {
411: String pkgName = JavaUtils.getPackageFromNamespace(ns);
412: return pkgName;
413: }
414:
415: /**
416: * Return the package associated with the class name. The className may not be specified (in
417: * which case a null Package is returned). if class has unnamed package return ""
418: *
419: * @param className String (may be null or empty)
420: * @return Package or null if problems occur
421: */
422: private static String getPackageFromClassName(String className) {
423: Class clz = loadClass(className);
424: String pkg = (clz == null) ? null
425: : (clz.getPackage() == null) ? "" : clz.getPackage()
426: .getName();
427: return pkg;
428: }
429:
430: /**
431: * Loads the class
432: *
433: * @param className
434: * @return Class (or null if the class cannot be loaded)
435: */
436: private static Class loadClass(String className) {
437: // Don't make this public, its a security exposure
438: if (className == null || className.length() == 0) {
439: return null;
440: }
441: try {
442: // Class.forName does not support primitives
443: Class cls = ClassUtils.getPrimitiveClass(className);
444: if (cls == null) {
445: cls = Class.forName(className, true,
446: getContextClassLoader());
447: }
448: return cls;
449: //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that
450: //does not extend Exception, so lets catch everything that extends Throwable
451: //rather than just Exception.
452: } catch (Throwable e) {
453: // TODO Should the exception be swallowed ?
454: if (log.isDebugEnabled()) {
455: log
456: .debug("PackageSetBuilder cannot load the following class:"
457: + className);
458: }
459: }
460: return null;
461: }
462:
463: private static Definition getWSDLDefinition(String wsdlLoc) {
464: Definition wsdlDefinition = null;
465: final String wsdlLocation = wsdlLoc;
466: if (wsdlLocation != null && wsdlLocation.trim().length() > 0) {
467: try {
468: wsdlDefinition = (Definition) AccessController
469: .doPrivileged(new PrivilegedExceptionAction() {
470: public Object run()
471: throws MalformedURLException,
472: IOException, WSDLException {
473: String baseDir = new File(System
474: .getProperty("basedir", "."))
475: .getCanonicalPath();
476: String wsdlLocationPath = new File(
477: baseDir + File.separator
478: + wsdlLocation)
479: .getAbsolutePath();
480: File file = new File(wsdlLocationPath);
481: URL url = file.toURL();
482: if (log.isDebugEnabled()) {
483: log.debug("Reading WSDL from URL:"
484: + url.toString());
485: }
486: WSDLWrapper wsdlWrapper = new WSDL4JWrapper(
487: url);
488: return wsdlWrapper.getDefinition();
489: }
490: });
491: } catch (PrivilegedActionException e) {
492: ExceptionFactory.makeWebServiceException(e
493: .getException());
494: }
495: }
496:
497: return wsdlDefinition;
498: }
499:
500: /**
501: * Return the class for this name
502: *
503: * @return Class
504: */
505: static Class forName(final String className,
506: final boolean initialize, final ClassLoader classloader)
507: throws ClassNotFoundException {
508: // NOTE: This method must remain protected because it uses AccessController
509: Class cl = null;
510: try {
511: cl = (Class) AccessController
512: .doPrivileged(new PrivilegedExceptionAction() {
513: public Object run()
514: throws ClassNotFoundException {
515: // Class.forName does not support primitives
516: Class cls = ClassUtils
517: .getPrimitiveClass(className);
518: if (cls == null) {
519: cls = Class.forName(className,
520: initialize, classloader);
521: }
522: return cls;
523: }
524: });
525: } catch (PrivilegedActionException e) {
526: if (log.isDebugEnabled()) {
527: log.debug("Exception thrown from AccessController: "
528: + e);
529: }
530: throw (ClassNotFoundException) e.getException();
531: }
532:
533: return cl;
534: }
535:
536: /** @return ClassLoader */
537: static ClassLoader getContextClassLoader() {
538: // NOTE: This method must remain private because it uses AccessController
539: ClassLoader cl = null;
540: try {
541: cl = (ClassLoader) AccessController
542: .doPrivileged(new PrivilegedExceptionAction() {
543: public Object run()
544: throws ClassNotFoundException {
545: return Thread.currentThread()
546: .getContextClassLoader();
547: }
548: });
549: } catch (PrivilegedActionException e) {
550: if (log.isDebugEnabled()) {
551: log.debug("Exception thrown from AccessController: "
552: + e);
553: }
554: throw (RuntimeException) e.getException();
555: }
556:
557: return cl;
558: }
559: }
|