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.description.impl;
020:
021: import static org.apache.axis2.jaxws.description.builder.MDQConstants.CONSTRUCTOR_METHOD;
022:
023: import org.apache.axis2.AxisFault;
024: import org.apache.axis2.description.AxisService;
025: import org.apache.axis2.description.Parameter;
026: import org.apache.axis2.jaxws.ExceptionFactory;
027: import org.apache.axis2.jaxws.description.AttachmentDescription;
028: import org.apache.axis2.jaxws.description.AttachmentType;
029: import org.apache.axis2.jaxws.description.EndpointDescription;
030: import org.apache.axis2.jaxws.description.OperationDescription;
031: import org.apache.axis2.jaxws.description.builder.DescriptionBuilderComposite;
032: import org.apache.axis2.jaxws.description.builder.MethodDescriptionComposite;
033: import org.apache.axis2.jaxws.description.builder.WebMethodAnnot;
034: import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
035: import org.apache.axis2.jaxws.i18n.Messages;
036: import org.apache.commons.logging.Log;
037: import org.apache.commons.logging.LogFactory;
038:
039: import javax.wsdl.Binding;
040: import javax.wsdl.BindingOperation;
041: import javax.wsdl.Operation;
042: import javax.wsdl.extensions.mime.MIMEContent;
043: import javax.wsdl.extensions.mime.MIMEMultipartRelated;
044: import javax.wsdl.extensions.mime.MIMEPart;
045: import javax.wsdl.extensions.soap.SOAPBody;
046: import javax.wsdl.extensions.soap.SOAPHeader;
047: import javax.wsdl.extensions.soap12.SOAP12Body;
048: import javax.wsdl.extensions.soap12.SOAP12Header;
049: import javax.xml.bind.JAXBContext;
050: import javax.xml.bind.JAXBElement;
051: import javax.xml.bind.Unmarshaller;
052: import javax.xml.namespace.QName;
053: import javax.xml.ws.handler.Handler;
054: import javax.xml.ws.handler.soap.SOAPHandler;
055:
056: import java.io.IOException;
057: import java.io.InputStream;
058: import java.lang.reflect.Method;
059: import java.net.MalformedURLException;
060: import java.net.URI;
061: import java.net.URISyntaxException;
062: import java.net.URL;
063: import java.util.ArrayList;
064: import java.util.Iterator;
065: import java.util.List;
066: import java.util.Set;
067: import java.util.StringTokenizer;
068:
069: /** Utilities used throughout the Description package. */
070: public class DescriptionUtils {
071: private static final Log log = LogFactory
072: .getLog(DescriptionUtils.class);
073:
074: static boolean isEmpty(String string) {
075: return (string == null || "".equals(string));
076: }
077:
078: static boolean isEmpty(QName qname) {
079: return qname == null || isEmpty(qname.getLocalPart());
080: }
081:
082: /** @return Returns TRUE if we find just one WebMethod Annotation with exclude flag set to false */
083: static boolean falseExclusionsExist(DescriptionBuilderComposite dbc) {
084: MethodDescriptionComposite mdc = null;
085: Iterator<MethodDescriptionComposite> iter = dbc
086: .getMethodDescriptionsList().iterator();
087:
088: while (iter.hasNext()) {
089: mdc = iter.next();
090:
091: WebMethodAnnot wma = mdc.getWebMethodAnnot();
092: if (wma != null) {
093: if (wma.exclude() == false)
094: return true;
095: }
096: }
097:
098: return false;
099: }
100:
101: /**
102: * Gathers all MethodDescriptionCompsite's that contain a WebMethod Annotation with the exclude
103: * set to FALSE
104: *
105: * @return Returns List<MethodDescriptionComposite>
106: */
107: static ArrayList<MethodDescriptionComposite> getMethodsWithFalseExclusions(
108: DescriptionBuilderComposite dbc) {
109: ArrayList<MethodDescriptionComposite> mdcList = new ArrayList<MethodDescriptionComposite>();
110: Iterator<MethodDescriptionComposite> iter = dbc
111: .getMethodDescriptionsList().iterator();
112:
113: if (DescriptionUtils.falseExclusionsExist(dbc)) {
114: while (iter.hasNext()) {
115: MethodDescriptionComposite mdc = iter.next();
116: if (mdc.getWebMethodAnnot() != null) {
117: if (mdc.getWebMethodAnnot().exclude() == false) {
118: mdc.setDeclaringClass(dbc.getClassName());
119: mdcList.add(mdc);
120: }
121: }
122: }
123: }
124:
125: return mdcList;
126: }
127:
128: /*
129: * Check whether a MethodDescriptionComposite contains a WebMethod annotation with
130: * exlude set to true
131: */
132: static boolean isExcludeTrue(MethodDescriptionComposite mdc) {
133:
134: if (mdc.getWebMethodAnnot() != null) {
135: if (mdc.getWebMethodAnnot().exclude() == true) {
136: return true;
137: }
138: }
139:
140: return false;
141: }
142:
143: static String javifyClassName(String className) {
144: if (className.indexOf("/") != -1) {
145: return className.replaceAll("/", ".");
146: }
147: return className;
148: }
149:
150: /**
151: * Return the name of the class without any package qualifier. This method should be DEPRECATED
152: * when DBC support is complete
153: *
154: * @param theClass
155: * @return the name of the class sans package qualification.
156: */
157: static String getSimpleJavaClassName(Class theClass) {
158: String returnName = null;
159: if (theClass != null) {
160: String fqName = theClass.getName();
161: // We need the "simple name", so strip off any package information from the name
162: int endOfPackageIndex = fqName.lastIndexOf('.');
163: int startOfClassIndex = endOfPackageIndex + 1;
164: returnName = fqName.substring(startOfClassIndex);
165: }
166: return returnName;
167: }
168:
169: /**
170: * Return the name of the class without any package qualifier.
171: *
172: * @param theClass
173: * @return the name of the class sans package qualification.
174: */
175: static String getSimpleJavaClassName(String name) {
176: String returnName = null;
177:
178: if (name != null) {
179: String fqName = name;
180:
181: // We need the "simple name", so strip off any package information from the name
182: int endOfPackageIndex = fqName.lastIndexOf('.');
183: int startOfClassIndex = endOfPackageIndex + 1;
184: returnName = fqName.substring(startOfClassIndex);
185: }
186: return returnName;
187: }
188:
189: /**
190: * Returns the package name from the class. If no package, then returns null This method should
191: * be DEPRECATED when DBC support is complete
192: *
193: * @param theClass
194: * @return
195: */
196: static String getJavaPackageName(Class theClass) {
197: String returnPackage = null;
198: if (theClass != null) {
199: String fqName = theClass.getName();
200: // Get the package name, if there is one
201: int endOfPackageIndex = fqName.lastIndexOf('.');
202: if (endOfPackageIndex >= 0) {
203: returnPackage = fqName.substring(0, endOfPackageIndex);
204: }
205: }
206: return returnPackage;
207: }
208:
209: /**
210: * Returns the package name from the class. If no package, then returns null
211: *
212: * @param theClassName
213: * @return
214: */
215: static String getJavaPackageName(String theClassName) {
216: String returnPackage = null;
217: if (theClassName != null) {
218: String fqName = theClassName;
219: // Get the package name, if there is one
220: int endOfPackageIndex = fqName.lastIndexOf('.');
221: if (endOfPackageIndex >= 0) {
222: returnPackage = fqName.substring(0, endOfPackageIndex);
223: }
224: }
225: return returnPackage;
226: }
227:
228: /**
229: * Create a JAX-WS namespace based on the package name
230: *
231: * @param packageName
232: * @param protocol
233: * @return
234: */
235: static final String NO_PACKAGE_HOST_NAME = "DefaultNamespace";
236:
237: static String makeNamespaceFromPackageName(String packageName,
238: String protocol) {
239: if (DescriptionUtils.isEmpty(protocol)) {
240: protocol = "http";
241: }
242: if (DescriptionUtils.isEmpty(packageName)) {
243: return protocol + "://" + NO_PACKAGE_HOST_NAME;
244: }
245: StringTokenizer st = new StringTokenizer(packageName, ".");
246: String[] words = new String[st.countTokens()];
247: for (int i = 0; i < words.length; ++i)
248: words[i] = st.nextToken();
249:
250: StringBuffer sb = new StringBuffer(80);
251: for (int i = words.length - 1; i >= 0; --i) {
252: String word = words[i];
253: // seperate with dot
254: if (i != words.length - 1)
255: sb.append('.');
256: sb.append(word);
257: }
258: return protocol + "://" + sb.toString() + "/";
259: }
260:
261: /**
262: * Determines whether a method should have an OperationDescription created for it based on the
263: * name. This is a convenience method to allow us to exlude methods such as constructors.
264: *
265: * @param methodName
266: * @return
267: */
268: static boolean createOperationDescription(String methodName) {
269: if (methodName.equals(CONSTRUCTOR_METHOD)) {
270: return false;
271: }
272: return true;
273: }
274:
275: /**
276: * This is a helper method that will open a stream to an @HandlerChain configuration file.
277: *
278: * @param configFile - The path to the file
279: * @param className - The class in which the annotation was declared. This is used in case the
280: * file path is relative.
281: * @param classLoader - ClassLoader used to load relative file paths.
282: * @return
283: */
284: public static InputStream openHandlerConfigStream(
285: String configFile, String className, ClassLoader classLoader) {
286: InputStream configStream = null;
287: URL configURL;
288: if (log.isDebugEnabled()) {
289: log
290: .debug("Attempting to load @HandlerChain configuration file: "
291: + configFile
292: + " relative to class: "
293: + className);
294: }
295: try {
296: configURL = new URL(configFile);
297: if (configURL != null) {
298: if (log.isDebugEnabled()) {
299: log
300: .debug("Found absolute @HandlerChain configuration file: "
301: + configFile);
302: }
303: configStream = configURL.openStream();
304: }
305: } catch (MalformedURLException e) {
306: // try another method to obtain a stream to the configuration file
307: } catch (IOException e) {
308: // report this since it was a valid URL but the openStream caused a problem
309: ExceptionFactory.makeWebServiceException(Messages
310: .getMessage("hcConfigLoadFail", configFile,
311: className, e.toString()));
312: }
313: if (configStream == null) {
314: if (log.isDebugEnabled()) {
315: log
316: .debug("@HandlerChain.file attribute referes to a relative location: "
317: + configFile);
318: }
319: className = className.replace(".", "/");
320: try {
321: if (log.isDebugEnabled()) {
322: log
323: .debug("Resolving @HandlerChain configuration file: "
324: + configFile
325: + " relative to class file: "
326: + className);
327: }
328: URI uri = new URI(className);
329: uri = uri.resolve(configFile);
330: String resolvedPath = uri.toString();
331: if (log.isDebugEnabled()) {
332: log
333: .debug("@HandlerChain.file resolved file path location: "
334: + resolvedPath);
335: }
336: configStream = classLoader
337: .getResourceAsStream(resolvedPath);
338: } catch (URISyntaxException e) {
339: ExceptionFactory.makeWebServiceException(Messages
340: .getMessage("hcConfigLoadFail", configFile,
341: className, e.toString()));
342: }
343: }
344: if (configStream == null) {
345: ExceptionFactory
346: .makeWebServiceException(Messages.getMessage(
347: "handlerChainNS", configFile, className));
348: } else {
349: if (log.isDebugEnabled()) {
350: log.debug("@HandlerChain configuration file: "
351: + configFile + " in class: " + className
352: + " was successfully loaded.");
353: }
354: }
355: return configStream;
356: }
357:
358: /**
359: * Determine is this method is an async method
360: * @param method - The method to examine
361: * @return
362: */
363: public static boolean isAsync(Method method) {
364:
365: if (method == null) {
366: return false;
367: }
368:
369: String methodName = method.getName();
370: Class returnType = method.getReturnType();
371:
372: if (methodName.endsWith("Async")
373: && (returnType
374: .isAssignableFrom(javax.xml.ws.Response.class) || returnType
375: .isAssignableFrom(java.util.concurrent.Future.class))) {
376: return true;
377: } else {
378: return false;
379: }
380: }
381:
382: public static HandlerChainsType loadHandlerChains(InputStream is) {
383: try {
384: // All the classes we need should be part of this package
385: JAXBContext jc = JAXBContext.newInstance(
386: "org.apache.axis2.jaxws.description.xml.handler",
387: EndpointDescriptionImpl.class.getClassLoader());
388:
389: Unmarshaller u = jc.createUnmarshaller();
390:
391: JAXBElement<?> o = (JAXBElement<?>) u.unmarshal(is);
392: return (HandlerChainsType) o.getValue();
393:
394: } catch (Exception e) {
395: throw ExceptionFactory
396: .makeWebServiceException("EndpointDescriptionImpl: loadHandlerList: thrown when attempting to unmarshall JAXB content");
397: }
398: }
399:
400: /**
401: * This method will loop through a list of extensibility elements looking for one
402: * of four objects: SOAPBody, SOAP12Body, SOAPHeader, SOAP12Header. If any of these
403: * objects are found the namespace URI from this object will be returned.
404: */
405: public static String getNamespaceFromSOAPElement(List extElements) {
406: Iterator extIter = extElements.iterator();
407: while (extIter.hasNext()) {
408: Object extObj = extIter.next();
409: if (extObj instanceof SOAPBody) {
410: if (log.isDebugEnabled()) {
411: log.debug("Returning SOAPBody namespace: "
412: + ((SOAPBody) extObj).getNamespaceURI());
413: }
414: return ((SOAPBody) extObj).getNamespaceURI();
415: } else if (extObj instanceof SOAP12Body) {
416: if (log.isDebugEnabled()) {
417: log.debug("Returning SOAP12Body namespace: "
418: + ((SOAP12Body) extObj).getNamespaceURI());
419: }
420: return ((SOAP12Body) extObj).getNamespaceURI();
421: } else if (extObj instanceof SOAPHeader) {
422: if (log.isDebugEnabled()) {
423: log.debug("Returning SOAPHeader namespace: "
424: + ((SOAPHeader) extObj).getNamespaceURI());
425: }
426: return ((SOAPHeader) extObj).getNamespaceURI();
427: } else if (extObj instanceof SOAP12Header) {
428: if (log.isDebugEnabled()) {
429: log
430: .debug("Returning SOAP12Header namespace: "
431: + ((SOAP12Header) extObj)
432: .getNamespaceURI());
433: }
434: return ((SOAP12Header) extObj).getNamespaceURI();
435: }
436: }
437: return null;
438: }
439:
440: /**
441: * This method will process a WSDL Binding and build AttachmentDescription objects if the
442: * WSDL dicatates attachments.
443: */
444: public static void getAttachmentFromBinding(
445: OperationDescriptionImpl opDesc, Binding binding) {
446: if (binding != null) {
447: Iterator bindingOpIter = binding.getBindingOperations()
448: .iterator();
449: while (bindingOpIter.hasNext()) {
450: BindingOperation bindingOp = (BindingOperation) bindingOpIter
451: .next();
452: // found the BindingOperation that matches the current OperationDescription
453: if (bindingOp.getName().equals(
454: opDesc.getName().getLocalPart())) {
455: if (bindingOp.getBindingInput() != null) {
456: if (log.isDebugEnabled()) {
457: log.debug("Processing binding input");
458: }
459: processBindingForMIME(bindingOp
460: .getBindingInput()
461: .getExtensibilityElements(), opDesc,
462: bindingOp.getOperation());
463: }
464: if (bindingOp.getBindingOutput() != null) {
465: if (log.isDebugEnabled()) {
466: log.debug("Processing binding output");
467: }
468: processBindingForMIME(bindingOp
469: .getBindingOutput()
470: .getExtensibilityElements(), opDesc,
471: bindingOp.getOperation());
472: }
473: }
474: }
475: }
476: }
477:
478: /**
479: * This method will loop through the extensibility elements for a given BindingInput or
480: * BindingOutput element and determine if it has any MIMEMultipartRelated content. If it
481: * does it will build up the appropriate AttachmentDescription objects.
482: */
483: private static void processBindingForMIME(
484: List extensibilityElements,
485: OperationDescriptionImpl opDesc, Operation operation) {
486: Iterator extensibilityIter = extensibilityElements.iterator();
487: while (extensibilityIter.hasNext()) {
488: Object obj = extensibilityIter.next();
489: if (obj instanceof MIMEMultipartRelated) {
490: // Found mime information now process it and determine if we need to
491: // create an AttachmentDescription
492: MIMEMultipartRelated mime = (MIMEMultipartRelated) obj;
493: Iterator partIter = mime.getMIMEParts().iterator();
494: while (partIter.hasNext()) {
495: MIMEPart mimePart = (MIMEPart) partIter.next();
496: Iterator mExtIter = mimePart
497: .getExtensibilityElements().iterator();
498: // Process each mime part to determine if there is mime content
499: while (mExtIter.hasNext()) {
500: Object obj2 = mExtIter.next();
501: // For mime content we need to potentially create an AttachmentDescription
502: if (obj2 instanceof MIMEContent) {
503: MIMEContent mimeContent = (MIMEContent) obj2;
504: String part = mimeContent.getPart();
505: String type = mimeContent.getType();
506: // if we have not already processed this part for the operation
507: if (opDesc
508: .getPartAttachmentDescription(part) == null) {
509: if (log.isDebugEnabled()) {
510: log
511: .debug("Adding new AttachmentDescription for part: "
512: + part
513: + " on operation: "
514: + opDesc
515: .getOperationName());
516: }
517: AttachmentDescription attachmentDesc = new AttachmentDescriptionImpl(
518: AttachmentType.SWA,
519: new String[] { type });
520: opDesc.addPartAttachmentDescription(
521: part, attachmentDesc);
522: } else {
523: if (log.isDebugEnabled()) {
524: log
525: .debug("Already created AttachmentDescription for part: "
526: + part
527: + " of type: "
528: + type);
529: }
530: }
531: }
532: }
533: }
534: }
535: }
536: }
537:
538: public static void registerHandlerHeaders(AxisService axisService,
539: List<Handler> handlers) {
540: if (handlers == null || axisService == null) {
541: return;
542: }
543:
544: ArrayList<QName> understoodHeaderQNames = new ArrayList<QName>();
545: for (Handler handler : handlers) {
546: if (handler instanceof SOAPHandler) {
547: SOAPHandler soapHandler = (SOAPHandler) handler;
548:
549: Set<QName> headers = soapHandler.getHeaders();
550: if (headers != null) {
551: for (QName header : headers) {
552: if (!understoodHeaderQNames.contains(header)) {
553: understoodHeaderQNames.add(header);
554: }
555: }
556: }
557: }
558: }
559:
560: if (!understoodHeaderQNames.isEmpty()) {
561: Parameter headerQNParameter = new Parameter(
562: EndpointDescription.HANDLER_PARAMETER_QNAMES,
563: understoodHeaderQNames);
564: try {
565: axisService.addParameter(headerQNParameter);
566: } catch (AxisFault e) {
567: // TODO: RAS
568: log.warn(
569: "Unable to add Parameter for header QNames to AxisService "
570: + axisService, e);
571: }
572: }
573: }
574: }
|