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.FaultDescription;
025: import org.apache.axis2.jaxws.description.OperationDescription;
026: import org.apache.axis2.jaxws.description.ServiceDescription;
027: import org.apache.axis2.jaxws.runtime.description.marshal.AnnotationDesc;
028: import org.apache.axis2.jaxws.runtime.description.marshal.FaultBeanDesc;
029: import org.apache.axis2.jaxws.utility.ClassUtils;
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032:
033: import java.security.PrivilegedActionException;
034: import java.security.PrivilegedExceptionAction;
035: import java.util.HashMap;
036: import java.util.Map;
037:
038: /**
039: * Examines a ServiceDesc and locates and/or builds the JAX-WS artifacts. The JAX-WS artifacts are:
040: * - request wrapper classes - response wrapper classes - fault beans for non-JAX-WS compliant
041: * exceptions
042: */
043: class ArtifactProcessor {
044:
045: private static final Log log = LogFactory
046: .getLog(ArtifactProcessor.class);
047:
048: private ServiceDescription serviceDesc;
049: private Map<OperationDescription, String> requestWrapperMap = new HashMap<OperationDescription, String>();
050: private Map<OperationDescription, String> responseWrapperMap = new HashMap<OperationDescription, String>();
051: private Map<FaultDescription, FaultBeanDesc> faultBeanDescMap = new HashMap<FaultDescription, FaultBeanDesc>();
052:
053: /**
054: * Artifact Processor
055: *
056: * @param serviceDesc
057: */
058: ArtifactProcessor(ServiceDescription serviceDesc) {
059: this .serviceDesc = serviceDesc;
060: }
061:
062: Map<OperationDescription, String> getRequestWrapperMap() {
063: return requestWrapperMap;
064: }
065:
066: Map<OperationDescription, String> getResponseWrapperMap() {
067: return responseWrapperMap;
068: }
069:
070: Map<FaultDescription, FaultBeanDesc> getFaultBeanDescMap() {
071: return faultBeanDescMap;
072: }
073:
074: void build() {
075: for (EndpointDescription ed : serviceDesc
076: .getEndpointDescriptions()) {
077: if (ed.getEndpointInterfaceDescription() != null) {
078: for (OperationDescription opDesc : ed
079: .getEndpointInterfaceDescription()
080: .getOperations()) {
081:
082: String declaringClassName = opDesc
083: .getJavaDeclaringClassName();
084: String packageName = getPackageName(declaringClassName);
085: String simpleName = getSimpleClassName(declaringClassName);
086: String methodName = opDesc.getJavaMethodName();
087:
088: // There is no default for @RequestWrapper/@ResponseWrapper classname None is listed in Sec. 7.3 on p. 80 of
089: // the JAX-WS spec, BUT Conformance(Using javax.xml.ws.RequestWrapper) in Sec 2.3.1.2 on p. 13
090: // says the entire annotation "...MAY be omitted if all its properties would have default vaules."
091: // We will assume that this statement gives us the liberty to find a wrapper class/build a wrapper class or
092: // implement an engine w/o the wrapper class.
093:
094: // @RequestWrapper className processing
095: String requestWrapperName = opDesc
096: .getRequestWrapperClassName();
097: if (requestWrapperName == null) {
098: if (packageName.length() > 0) {
099: requestWrapperName = packageName + "."
100: + javaMethodToClassName(methodName);
101: } else {
102: requestWrapperName = javaMethodToClassName(methodName);
103: }
104: }
105: String foundRequestWrapperName = findArtifact(requestWrapperName);
106: if (foundRequestWrapperName == null) {
107: foundRequestWrapperName = missingArtifact(requestWrapperName);
108: }
109: if (foundRequestWrapperName != null) {
110: requestWrapperMap.put(opDesc,
111: foundRequestWrapperName);
112: }
113:
114: // @ResponseWrapper className processing
115: String responseWrapperName = opDesc
116: .getResponseWrapperClassName();
117: if (responseWrapperName == null) {
118: if (packageName.length() > 0) {
119: responseWrapperName = packageName + "."
120: + javaMethodToClassName(methodName)
121: + "Response";
122: } else {
123: responseWrapperName = javaMethodToClassName(methodName)
124: + "Response";
125: }
126: }
127: String foundResponseWrapperName = findArtifact(responseWrapperName);
128: if (foundResponseWrapperName == null) {
129: foundResponseWrapperName = missingArtifact(responseWrapperName);
130: }
131: if (foundResponseWrapperName != null) {
132: responseWrapperMap.put(opDesc,
133: foundResponseWrapperName);
134: }
135:
136: for (FaultDescription faultDesc : opDesc
137: .getFaultDescriptions()) {
138: FaultBeanDesc faultBeanDesc = create(faultDesc,
139: opDesc);
140: faultBeanDescMap.put(faultDesc, faultBeanDesc);
141: }
142: }
143: }
144: }
145: }
146:
147: private FaultBeanDesc create(FaultDescription faultDesc,
148: OperationDescription opDesc) {
149: /* FaultBeanClass algorithm
150: * 1) The class defined on @WebFault of the exception
151: * 2) If not present or invalid, the class defined by getFaultInfo.
152: * 3) If not present, the class is found by looking for the
153: * a class named <exceptionName>Bean in the interface's package.
154: * 4) If not present, the class is found by looking for the
155: * a class named <exceptionName>Bean in the interface + jaxws package
156: */
157: String faultBeanClassName = faultDesc.getFaultBean();
158: if (faultBeanClassName == null
159: || faultBeanClassName.length() == 0) {
160: faultBeanClassName = faultDesc.getFaultInfo();
161: }
162: if (faultBeanClassName == null
163: || faultBeanClassName.length() == 0) {
164: String declaringClassName = opDesc
165: .getJavaDeclaringClassName();
166: String packageName = getPackageName(declaringClassName);
167: String simpleName = getSimpleClassName(faultDesc
168: .getExceptionClassName());
169: if (packageName.length() > 0) {
170: faultBeanClassName = packageName + "." + simpleName
171: + "Bean";
172: } else {
173: faultBeanClassName = simpleName + "Bean";
174: }
175: }
176: String foundClassName = findArtifact(faultBeanClassName);
177: if (foundClassName == null) {
178: faultBeanClassName = missingArtifact(faultBeanClassName);
179: }
180: if (foundClassName != null) {
181: faultBeanClassName = foundClassName;
182: }
183:
184: /* Local NameAlgorithm:
185: * 1) The name defined on the @WebFault of the exception.
186: * 2) If not present, the name defined via the @XmlRootElement of the fault bean class.
187: * 3) If not present, the <exceptionName>Bean
188: */
189: String faultBeanLocalName = faultDesc.getName();
190: if (faultBeanLocalName == null
191: || faultBeanLocalName.length() == 0) {
192: if (faultBeanClassName != null
193: && faultBeanClassName.length() > 0) {
194: try {
195: Class faultBean = loadClass(faultBeanClassName);
196: AnnotationDesc aDesc = AnnotationDescImpl
197: .create(faultBean);
198: if (aDesc.hasXmlRootElement()) {
199: faultBeanLocalName = aDesc
200: .getXmlRootElementName();
201: }
202: } catch (Throwable t) {
203: ExceptionFactory.makeWebServiceException(t);
204: }
205: }
206: }
207: if (faultBeanLocalName == null
208: || faultBeanLocalName.length() == 0) {
209: faultBeanLocalName = getSimpleClassName(faultDesc
210: .getExceptionClassName())
211: + "Bean";
212: }
213:
214: /* Algorithm for fault bean namespace
215: * 1) The namespace defined on the @WebFault of the exception.
216: * 2) If not present, the namespace defined via the @XmlRootElement of the class name.
217: * 3) If not present, the namespace of the method's declared class + "/jaxws"
218: */
219: String faultBeanNamespace = faultDesc.getTargetNamespace();
220: if (faultBeanNamespace == null
221: || faultBeanNamespace.length() == 0) {
222: if (faultBeanClassName != null
223: && faultBeanClassName.length() > 0) {
224: try {
225: Class faultBean = loadClass(faultBeanClassName);
226: AnnotationDesc aDesc = AnnotationDescImpl
227: .create(faultBean);
228: if (aDesc.hasXmlRootElement()) {
229: faultBeanNamespace = aDesc
230: .getXmlRootElementNamespace();
231: }
232: } catch (Throwable t) {
233: ExceptionFactory.makeWebServiceException(t);
234: }
235: }
236: }
237: if (faultBeanNamespace == null
238: || faultBeanNamespace.length() == 0) {
239: faultBeanNamespace = opDesc
240: .getEndpointInterfaceDescription()
241: .getTargetNamespace();
242: }
243:
244: return new FaultBeanDescImpl(faultBeanClassName,
245: faultBeanLocalName, faultBeanNamespace);
246: }
247:
248: /**
249: * @param className
250: * @return package name
251: */
252: private static String getPackageName(String className) {
253: int index = className.lastIndexOf(".");
254: if (index == 0) {
255: return "";
256: } else {
257: return className.substring(0, index);
258: }
259: }
260:
261: /**
262: * @param className
263: * @return simple class name
264: */
265: private static String getSimpleClassName(String className) {
266: int index = className.lastIndexOf(".");
267: if (index == 0) {
268: return className;
269: } else {
270: return className.substring(index + 1);
271: }
272: }
273:
274: /**
275: * @param methodName
276: * @return method name converted into a class name
277: */
278: private static String javaMethodToClassName(String methodName) {
279: String className = null;
280: if (methodName != null) {
281: StringBuffer buildClassName = new StringBuffer(methodName);
282: buildClassName.replace(0, 1, methodName.substring(0, 1)
283: .toUpperCase());
284: className = buildClassName.toString();
285: }
286: return className;
287: }
288:
289: /**
290: * This method is invoked if the artifact is missing
291: *
292: * @param artifactName
293: * @return newly constructed name or null
294: */
295: private String missingArtifact(String artifactName) {
296:
297: // TODO Could we contstruct a proxy of the artifact at this point ?
298: if (log.isDebugEnabled()) {
299: log.debug("The following class is missing: " + artifactName
300: + " Processing continues.");
301: }
302: return null;
303: }
304:
305: /**
306: * Determine the actual packager name for the generated artifacts by trying to load the class
307: * from one of two packages. This is necessary because the RI implementations of WSGen and
308: * WSImport generate the artifacts in different packages: - WSImport generates the artifacts in
309: * the same package as the SEI - WSGen generates the artifacts in a "jaxws" sub package under
310: * the SEI package. Note that from reading the JAX-WS spec, it seems that WSGen is doing that
311: * correctly; See the conformance requirement in JAX-WS 2.0 Spec Section 3.6.2.1 Document
312: * Wrapped on page 36: Conformance (Default wrapper bean package): In the absence of
313: * customizations, the wrapper beans package MUST be a generated jaxws subpackage of the SEI
314: * package. ^^^^^^^^^^^^^^^^
315: *
316: * @param artifactClassName
317: * @return
318: */
319: static final String JAXWS_SUBPACKAGE = "jaxws";
320:
321: private static String findArtifact(String artifactClassName) {
322: String returnArtifactClassName = null;
323: if (artifactClassName == null) {
324: return returnArtifactClassName;
325: }
326:
327: // Try to load the class that was passed in
328: try {
329: loadClass(artifactClassName);
330: returnArtifactClassName = artifactClassName;
331: } catch (ClassNotFoundException e) {
332: // Couldn't load the class; we'll try another one below.
333: }
334:
335: // If the original class couldn't be loaded, try adding ".jaxws." to the package
336: if (returnArtifactClassName == null) {
337: String originalPackage = getPackageName(artifactClassName);
338: if (originalPackage.length() > 0) {
339: String alternatePackage = originalPackage + "."
340: + JAXWS_SUBPACKAGE;
341: String className = getSimpleClassName(artifactClassName);
342: String alternateWrapperClass = alternatePackage + "."
343: + className;
344: try {
345: loadClass(alternateWrapperClass);
346: returnArtifactClassName = alternateWrapperClass;
347: } catch (ClassNotFoundException e) {
348: // Couldn't load the class
349: }
350: }
351: }
352:
353: return returnArtifactClassName;
354: }
355:
356: private static Class loadClass(String className)
357: throws ClassNotFoundException {
358: // Don't make this public, its a security exposure
359: return forName(className, true, getContextClassLoader());
360: }
361:
362: /**
363: * Return the class for this name
364: *
365: * @return Class
366: */
367: private static Class forName(final String className,
368: final boolean initialize, final ClassLoader classloader)
369: throws ClassNotFoundException {
370: // NOTE: This method must remain protected because it uses AccessController
371: Class cl = null;
372: try {
373: cl = (Class) AccessController
374: .doPrivileged(new PrivilegedExceptionAction() {
375: public Object run()
376: throws ClassNotFoundException {
377: // Class.forName does not support primitives
378: Class cls = ClassUtils
379: .getPrimitiveClass(className);
380: try {
381: if (cls == null) {
382: cls = Class.forName(className,
383: initialize, classloader);
384: }
385: return cls;
386: //Lets catch NoClassDefFoundError as its part of Throwable
387: //Any Exception that extends Exception will be handled by doPriv method.
388: } catch (NoClassDefFoundError e) {
389: // TODO Should the exception be swallowed ?
390: if (log.isDebugEnabled()) {
391: log
392: .debug("ArtifactProcessor cannot load the following class NoClassDefFoundError:"
393: + className);
394: }
395: }
396: return cls;
397: }
398: });
399: } catch (PrivilegedActionException e) {
400: if (log.isDebugEnabled()) {
401: log.debug("Exception thrown from AccessController: "
402: + e);
403: }
404: throw (ClassNotFoundException) e.getException();
405: }
406:
407: return cl;
408: }
409:
410: /** @return ClassLoader */
411: private static ClassLoader getContextClassLoader() {
412: // NOTE: This method must remain private because it uses AccessController
413: ClassLoader cl = null;
414: try {
415: cl = (ClassLoader) AccessController
416: .doPrivileged(new PrivilegedExceptionAction() {
417: public Object run()
418: throws ClassNotFoundException {
419: return Thread.currentThread()
420: .getContextClassLoader();
421: }
422: });
423: } catch (PrivilegedActionException e) {
424: if (log.isDebugEnabled()) {
425: log.debug("Exception thrown from AccessController: "
426: + e);
427: }
428: throw (RuntimeException) e.getException();
429: }
430:
431: return cl;
432: }
433: }
|