0001: /*
0002: * Copyright 2001-2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: /*
0018: * Modified by Nabh Information Systems, Inc. for Stringbeans Web Services
0019: * Framework.
0020: *
0021: * Modifications (c) 2005 Nabh Information Systems, Inc.
0022: */
0023: package com.nabhinc.ws.soap;
0024:
0025: import org.apache.axis.AxisServiceConfig;
0026: import org.apache.axis.Constants;
0027: import org.apache.axis.InternalException;
0028: import org.apache.axis.AxisProperties;
0029: import org.apache.axis.components.logger.LogFactory;
0030: import org.apache.axis.encoding.*;
0031: import org.apache.axis.constants.Style;
0032: import org.apache.axis.constants.Use;
0033: import org.apache.axis.description.FaultDesc;
0034: import org.apache.axis.description.OperationDesc;
0035: import org.apache.axis.description.ParameterDesc;
0036: import org.apache.axis.description.ServiceDesc;
0037: import org.apache.axis.message.SOAPBodyElement;
0038: import org.apache.axis.message.SOAPEnvelope;
0039: import org.apache.axis.utils.JavaUtils;
0040: import org.apache.axis.utils.Messages;
0041: import org.apache.axis.utils.bytecode.ParamNameExtractor;
0042: import org.apache.axis.wsdl.Skeleton;
0043: import org.apache.axis.wsdl.fromJava.Namespaces;
0044: import org.apache.commons.logging.Log;
0045: import org.w3c.dom.Document;
0046: import org.w3c.dom.Element;
0047:
0048: import com.nabhinc.ws.core.PropertyInfo;
0049: import com.nabhinc.ws.server.RequestInfo;
0050: import com.nabhinc.ws.server.ServerObjectConfig;
0051: import com.nabhinc.ws.server.WebService;
0052:
0053: import javax.xml.namespace.QName;
0054: import javax.xml.rpc.holders.Holder;
0055: import java.lang.reflect.InvocationTargetException;
0056: import java.lang.reflect.Method;
0057: import java.lang.reflect.Modifier;
0058: import java.util.ArrayList;
0059: import java.util.Collection;
0060: import java.util.Collections;
0061: import java.util.Comparator;
0062: import java.util.HashMap;
0063: import java.util.Iterator;
0064: import java.util.List;
0065: import java.util.StringTokenizer;
0066:
0067: /**
0068: * A ServiceDesc is an abstract description of a service.
0069: *
0070: * ServiceDescs contain OperationDescs, which are descriptions of operations.
0071: * The information about a service's operations comes from one of two places:
0072: * 1) deployment, or 2) introspection.
0073: *
0074: * @author Glen Daniels (gdaniels@apache.org)
0075: */
0076: public class JavaServiceDesc implements ServiceDesc {
0077: /**
0078: * Comment for <code>serialVersionUID</code>
0079: */
0080: private static final long serialVersionUID = 3256445789579456566L;
0081:
0082: protected static Log log = LogFactory.getLog(JavaServiceDesc.class
0083: .getName());
0084:
0085: /** The name of this service */
0086: private String name = null;
0087:
0088: /** The documentation of this service */
0089: private String documentation = null;
0090:
0091: /** Style/Use */
0092: private Style style = Style.RPC;
0093: private Use use = Use.ENCODED;
0094:
0095: // Style and Use are related. By default, if Style==RPC, Use should be
0096: // ENCODED. But if Style==DOCUMENT, Use should be LITERAL. So we want
0097: // to keep the defaults synced until someone explicitly sets the Use.
0098: private boolean useSet = false;
0099:
0100: /** Our operations - a list of OperationDescs */
0101: private ArrayList operations = new ArrayList();
0102:
0103: /** A collection of namespaces which will map to this service */
0104: private List namespaceMappings = null;
0105:
0106: /**
0107: * Where does our WSDL document live? If this is non-null, the "?WSDL"
0108: * generation will automatically return this file instead of dynamically
0109: * creating a WSDL. BE CAREFUL because this means that Handlers will
0110: * not be able to add to the WSDL for extensions/headers....
0111: */
0112: private String wsdlFileName = null;
0113:
0114: /**
0115: * An endpoint URL which someone has specified for this service. If
0116: * this is set, WSDL generation will pick it up instead of defaulting
0117: * to the transport URL.
0118: */
0119: private String endpointURL = null;
0120:
0121: /** Place to store user-extensible service-related properties */
0122: private HashMap properties = null;
0123:
0124: /** Lookup caches */
0125: private HashMap name2OperationsMap = null;
0126: private HashMap qname2OperationsMap = null;
0127: private transient HashMap method2OperationMap = new HashMap();
0128:
0129: // THE FOLLOWING STUFF IS ALL JAVA-SPECIFIC, AND WILL BE FACTORED INTO
0130: // A JAVA-SPECIFIC SUBCLASS. --Glen
0131:
0132: /** List of allowed methods */
0133: /** null allows everything, an empty ArrayList allows nothing */
0134: private List allowedMethods = null;
0135:
0136: /** List if disallowed methods */
0137: private List disallowedMethods = null;
0138:
0139: /** Implementation class */
0140: private Class implClass = null;
0141:
0142: /**
0143: * Is the implementation a Skeleton? If this is true, it will generate
0144: * a Fault to provide OperationDescs via WSDD.
0145: */
0146: private boolean isSkeletonClass = false;
0147:
0148: /** Cached copy of the skeleton "getOperationDescByName" method */
0149: private transient Method skelMethod = null;
0150:
0151: /** Classes at which we should stop looking up the inheritance chain
0152: * when introspecting
0153: */
0154: private ArrayList stopClasses = null;
0155:
0156: /** Lookup caches */
0157: private transient HashMap method2ParamsMap = new HashMap();
0158: private OperationDesc messageServiceDefaultOp = null;
0159:
0160: /** Method names for which we have completed any introspection necessary */
0161: private ArrayList completedNames = new ArrayList();
0162:
0163: /** Our typemapping for resolving Java<->XML type issues */
0164: private TypeMapping tm = null;
0165: private TypeMappingRegistry tmr = null;
0166:
0167: private boolean haveAllSkeletonMethods = false;
0168: private boolean introspectionComplete = false;
0169:
0170: /**
0171: * Default constructor
0172: */
0173: public JavaServiceDesc() {
0174: }
0175:
0176: /**
0177: * What kind of service is this?
0178: * @return
0179: */
0180: public Style getStyle() {
0181: return style;
0182: }
0183:
0184: public void setStyle(Style style) {
0185: this .style = style;
0186: if (!useSet) {
0187: // Use hasn't been explicitly set, so track style
0188: use = style == Style.RPC ? Use.ENCODED : Use.LITERAL;
0189: }
0190: }
0191:
0192: /**
0193: * What kind of use is this?
0194: * @return
0195: */
0196: public Use getUse() {
0197: return use;
0198: }
0199:
0200: public void setUse(Use use) {
0201: useSet = true;
0202: this .use = use;
0203: }
0204:
0205: /**
0206: * Determine whether or not this is a "wrapped" invocation, i.e. whether
0207: * the outermost XML element of the "main" body element represents a
0208: * method call, with the immediate children of that element representing
0209: * arguments to the method.
0210: *
0211: * @return true if this is wrapped (i.e. RPC or WRAPPED style),
0212: * false otherwise
0213: */
0214: public boolean isWrapped() {
0215: return ((style == Style.RPC) || (style == Style.WRAPPED));
0216: }
0217:
0218: /**
0219: * the wsdl file of the service.
0220: * When null, it means that the wsdl should be autogenerated
0221: * @return filename or null
0222: */
0223: public String getWSDLFile() {
0224: return wsdlFileName;
0225: }
0226:
0227: /**
0228: * set the wsdl file of the service; this causes the named
0229: * file to be returned on a ?wsdl, probe, not introspection
0230: * generated wsdl.
0231: * @param wsdlFileName filename or null to re-enable introspection
0232: */
0233: public void setWSDLFile(String wsdlFileName) {
0234: this .wsdlFileName = wsdlFileName;
0235: }
0236:
0237: public List getAllowedMethods() {
0238: return allowedMethods;
0239: }
0240:
0241: public void setAllowedMethods(List allowedMethods) {
0242: this .allowedMethods = allowedMethods;
0243: }
0244:
0245: public Class getImplClass() {
0246: return implClass;
0247: }
0248:
0249: /**
0250: * set the implementation class
0251: * <p>
0252: * Warning: You cannot call getInitializedServiceDesc() after setting this
0253: * as it uses this to indicate its work has already been done.
0254: *
0255: * @param implClass
0256: * @throws IllegalArgumentException if the implementation class is already
0257: * set
0258: */
0259: public void setImplClass(Class implClass) {
0260: if (this .implClass != null)
0261: throw new IllegalArgumentException(Messages
0262: .getMessage("implAlreadySet"));
0263:
0264: this .implClass = implClass;
0265: if (Skeleton.class.isAssignableFrom(implClass)) {
0266: isSkeletonClass = true;
0267: loadSkeletonOperations();
0268: }
0269: }
0270:
0271: private void loadSkeletonOperations() {
0272: Method method = null;
0273: try {
0274: method = implClass.getDeclaredMethod("getOperationDescs",
0275: new Class[] {});
0276: } catch (NoSuchMethodException e) {
0277: } catch (SecurityException e) {
0278: }
0279: if (method == null) {
0280: // FIXME : Throw an error?
0281: return;
0282: }
0283:
0284: try {
0285: Collection opers = (Collection) method.invoke(implClass,
0286: (Object[]) null);
0287: for (Iterator i = opers.iterator(); i.hasNext();) {
0288: OperationDesc skelDesc = (OperationDesc) i.next();
0289: addOperationDesc(skelDesc);
0290: }
0291: } catch (IllegalAccessException e) {
0292: if (log.isDebugEnabled()) {
0293: log.debug(Messages.getMessage("exception00"), e);
0294: }
0295: return;
0296: } catch (IllegalArgumentException e) {
0297: if (log.isDebugEnabled()) {
0298: log.debug(Messages.getMessage("exception00"), e);
0299: }
0300: return;
0301: } catch (InvocationTargetException e) {
0302: if (log.isDebugEnabled()) {
0303: log.debug(Messages.getMessage("exception00"), e);
0304: }
0305: return;
0306: }
0307: haveAllSkeletonMethods = true;
0308: }
0309:
0310: public TypeMapping getTypeMapping() {
0311: if (tm == null) {
0312: return DefaultTypeMappingImpl.getSingletonDelegate();
0313: // throw new RuntimeException(Messages.getMessage("noDefaultTypeMapping00"));
0314: }
0315: return tm;
0316: }
0317:
0318: public void setTypeMapping(TypeMapping tm) {
0319: this .tm = tm;
0320: }
0321:
0322: /**
0323: * the name of the service
0324: */
0325: public String getName() {
0326: return name;
0327: }
0328:
0329: /**
0330: * the name of the service
0331: * @param name
0332: */
0333: public void setName(String name) {
0334: this .name = name;
0335: }
0336:
0337: /**
0338: * get the documentation for the service
0339: */
0340: public String getDocumentation() {
0341: return documentation;
0342: }
0343:
0344: /**
0345: * set the documentation for the service
0346: */
0347: public void setDocumentation(String documentation) {
0348: this .documentation = documentation;
0349: }
0350:
0351: public ArrayList getStopClasses() {
0352: return stopClasses;
0353: }
0354:
0355: public void setStopClasses(ArrayList stopClasses) {
0356: this .stopClasses = stopClasses;
0357: }
0358:
0359: public List getDisallowedMethods() {
0360: return disallowedMethods;
0361: }
0362:
0363: public void setDisallowedMethods(List disallowedMethods) {
0364: this .disallowedMethods = disallowedMethods;
0365: }
0366:
0367: public void removeOperationDesc(OperationDesc operation) {
0368: operations.remove(operation);
0369: operation.setParent(null);
0370:
0371: if (name2OperationsMap != null) {
0372: String name = operation.getName();
0373: ArrayList overloads = (ArrayList) name2OperationsMap
0374: .get(name);
0375: if (overloads != null) {
0376: overloads.remove(operation);
0377: if (overloads.size() == 0) {
0378: name2OperationsMap.remove(name);
0379: }
0380: }
0381: }
0382:
0383: if (qname2OperationsMap != null) {
0384: QName qname = operation.getElementQName();
0385: ArrayList list = (ArrayList) qname2OperationsMap.get(qname);
0386: if (list != null) {
0387: list.remove(operation);
0388: }
0389: }
0390:
0391: if (method2OperationMap != null) {
0392: Method method = operation.getMethod();
0393: if (method != null) {
0394: method2OperationMap.remove(method);
0395: }
0396: }
0397: }
0398:
0399: public void addOperationDesc(OperationDesc operation) {
0400: operations.add(operation);
0401: operation.setParent(this );
0402: if (name2OperationsMap == null) {
0403: name2OperationsMap = new HashMap();
0404: }
0405:
0406: // Add name to name2Operations Map
0407: String name = operation.getName();
0408: ArrayList overloads = (ArrayList) name2OperationsMap.get(name);
0409: if (overloads == null) {
0410: overloads = new ArrayList();
0411: name2OperationsMap.put(name, overloads);
0412: } else if (JavaUtils.isTrue(AxisProperties
0413: .getProperty(Constants.WSIBP11_COMPAT_PROPERTY))
0414: && overloads.size() > 0) {
0415: throw new RuntimeException(Messages.getMessage(
0416: "noOverloadedOperations", name));
0417: }
0418: overloads.add(operation);
0419: }
0420:
0421: /**
0422: * get all the operations as a list of OperationDescs.
0423: * this method triggers an evaluation of the valid operations by
0424: * introspection, so use sparingly
0425: * @return reference to the operations array. This is not a copy
0426: */
0427: public ArrayList getOperations() {
0428: loadServiceDescByIntrospection(); // Just in case...
0429: return operations;
0430: }
0431:
0432: /**
0433: * get all overloaded operations by name
0434: * @param methodName
0435: * @return null for no match, or an array of OperationDesc objects
0436: */
0437: public OperationDesc[] getOperationsByName(String methodName) {
0438: getSyncedOperationsForName(implClass, methodName);
0439:
0440: if (name2OperationsMap == null)
0441: return null;
0442:
0443: ArrayList overloads = (ArrayList) name2OperationsMap
0444: .get(methodName);
0445: if (overloads == null) {
0446: return null;
0447: }
0448:
0449: OperationDesc[] array = new OperationDesc[overloads.size()];
0450: return (OperationDesc[]) overloads.toArray(array);
0451: }
0452:
0453: /**
0454: * Return an operation matching the given method name. Note that if we
0455: * have multiple overloads for this method, we will return the first one.
0456: * @return null for no match
0457: */
0458: public OperationDesc getOperationByName(String methodName) {
0459: // If we need to load up operations from introspection data, do it.
0460: // This returns fast if we don't need to do anything, so it's not very
0461: // expensive.
0462: getSyncedOperationsForName(implClass, methodName);
0463:
0464: if (name2OperationsMap == null)
0465: return null;
0466:
0467: ArrayList overloads = (ArrayList) name2OperationsMap
0468: .get(methodName);
0469: if (overloads == null) {
0470: return null;
0471: }
0472:
0473: return (OperationDesc) overloads.get(0);
0474: }
0475:
0476: /**
0477: * Map an XML QName to an operation. Returns the first one it finds
0478: * in the case of mulitple matches.
0479: * @return null for no match
0480: */
0481: public OperationDesc getOperationByElementQName(QName qname) {
0482: OperationDesc[] overloads = getOperationsByQName(qname);
0483:
0484: // Return the first one....
0485: if ((overloads != null) && overloads.length > 0)
0486: return overloads[0];
0487:
0488: return null;
0489: }
0490:
0491: /**
0492: * Return all operations which match this QName (i.e. get all the
0493: * overloads)
0494: * @return null for no match
0495: */
0496: public OperationDesc[] getOperationsByQName(QName qname) {
0497: // Look in our mapping of QNames -> operations.
0498:
0499: // But first, let's make sure we've initialized said mapping....
0500: initQNameMap();
0501:
0502: ArrayList overloads = (ArrayList) qname2OperationsMap
0503: .get(qname);
0504: if (overloads == null) {
0505: // Nothing specifically matching this QName.
0506: if (name2OperationsMap != null) {
0507: if ((isWrapped() || ((style == Style.MESSAGE) && (getDefaultNamespace() == null)))) {
0508: // Try ignoring the namespace....?
0509: overloads = (ArrayList) name2OperationsMap
0510: .get(qname.getLocalPart());
0511: } else {
0512: // TODO the above code is weird: a JavaServiceDesc can be document or rpc and
0513: // still define a WSDL operation using a wrapper style mapping.
0514: // The following code handles this case.
0515: Object ops = name2OperationsMap.get(qname
0516: .getLocalPart());
0517: if (ops != null) {
0518: overloads = new ArrayList((Collection) ops);
0519: for (Iterator iter = overloads.iterator(); iter
0520: .hasNext();) {
0521: OperationDesc operationDesc = (OperationDesc) iter
0522: .next();
0523: if (Style.WRAPPED != operationDesc
0524: .getStyle()) {
0525: iter.remove();
0526: }
0527: }
0528: }
0529: }
0530: }
0531: // Handle the case where a single Message-style operation wants
0532: // to accept anything.
0533: if ((style == Style.MESSAGE)
0534: && (messageServiceDefaultOp != null))
0535: return new OperationDesc[] { messageServiceDefaultOp };
0536:
0537: if (overloads == null)
0538: return null;
0539: }
0540:
0541: getSyncedOperationsForName(implClass,
0542: ((OperationDesc) overloads.get(0)).getName());
0543:
0544: // Sort the overloads by number of arguments - prevents us calling methods
0545: // with more parameters than supplied in the request (with missing parameters
0546: // defaulted to null) when a perfectly good method exists with exactly the
0547: // supplied parameters.
0548: Collections.sort(overloads, new Comparator() {
0549: public int compare(Object o1, Object o2) {
0550: Method meth1 = ((OperationDesc) o1).getMethod();
0551: Method meth2 = ((OperationDesc) o2).getMethod();
0552: return (meth1.getParameterTypes().length - meth2
0553: .getParameterTypes().length);
0554: }
0555: });
0556:
0557: OperationDesc[] array = new OperationDesc[overloads.size()];
0558: return (OperationDesc[]) overloads.toArray(array);
0559: }
0560:
0561: private synchronized void initQNameMap() {
0562: if (qname2OperationsMap == null) {
0563: loadServiceDescByIntrospection();
0564:
0565: qname2OperationsMap = new HashMap();
0566: for (Iterator i = operations.iterator(); i.hasNext();) {
0567: OperationDesc operationDesc = (OperationDesc) i.next();
0568: QName qname = operationDesc.getElementQName();
0569: ArrayList list = (ArrayList) qname2OperationsMap
0570: .get(qname);
0571: if (list == null) {
0572: list = new ArrayList();
0573: qname2OperationsMap.put(qname, list);
0574: }
0575: list.add(operationDesc);
0576: }
0577: }
0578: }
0579:
0580: /**
0581: * Synchronize an existing OperationDesc to a java.lang.Method.
0582: *
0583: * This method is used when the deployer has specified operation metadata
0584: * and we want to match that up with a real java Method so that the
0585: * Operation-level dispatch carries us all the way to the implementation.
0586: * Search the declared methods on the implementation class to find one
0587: * with an argument list which matches our parameter list.
0588: */
0589: private void syncOperationToClass(OperationDesc oper,
0590: Class implClass) {
0591: // ------------------------------------------------
0592: // Developer Note:
0593: //
0594: // The goal of the sync code is to associate
0595: // the OperationDesc/ParamterDesc with the
0596: // target Method. There are a number of ways to get to this
0597: // point depending on what information
0598: // is available. Here are the main scenarios:
0599: //
0600: // A) Deployment with wsdd (non-skeleton):
0601: // * OperationDesc/ParameterDesc loaded from deploy.wsdd
0602: // * Loaded ParameterDesc does not have javaType,
0603: // so it is discovered using the TypeMappingRegistry
0604: // (also loaded via deploy.wsdd) and the
0605: // typeQName specified by the ParameterDesc.
0606: // * Sync occurs using the discovered
0607: // javaTypes and the javaTypes of the Method
0608: // parameters
0609: //
0610: // B) Deployment with no wsdd OperationDesc info (non-skeleton):
0611: // * Implementation Class introspected to build
0612: // OperationDesc/ParameterDesc.
0613: // * ParameterDesc is known via introspection.
0614: // * ParameterDesc are discovered using javaType
0615: // and TypeMappingRegistry.
0616: // * Sync occurs using the introspected
0617: // javaTypes and the javaTypes of the Method
0618: // parameters
0619: //
0620: // C) Deployment with wsdd (skeleton):
0621: // * OperationDesc/ParameterDesc loaded from the Skeleton
0622: // * In this scenario the ParameterDescs' already
0623: // have javaTypes (see E below).
0624: // * Sync occurs using the ParameterDesc
0625: // javaTypes and the javaTypes of the Method
0626: // parameters.
0627: //
0628: // D) Commandline Java2WSDL loading non-Skeleton Class/Interface
0629: // * Class/Interface introspected to build
0630: // OperationDesc/ParameterDesc.
0631: // * The javaTypes of the ParameterDesc are set using introspection.
0632: // * typeQNames are determined for built-in types using
0633: // from the default TypeMappingRegistry. Other
0634: // typeQNames are guessed from the javaType. Note
0635: // that there is no loaded TypeMappingRegistry.
0636: // * Sync occurs using the ParameterDesc
0637: // javaTypes and the javaTypes of the Method
0638: // parameters.
0639: //
0640: // E) Commandline Java2WSDL loading Skeleton Class
0641: // * OperationDesc/ParameterDesc loaded from Skeleton
0642: // * Each ParameterDesc has an appropriate typeQName
0643: // * Each ParameterDesc also has a javaType, which is
0644: // essential for sync'ing up with the
0645: // method since there is no loaded TypeMappingRegistry.
0646: // * Syncronization occurs using the ParameterDesc
0647: // javaTypes and the javaTypes of the Method
0648: // parameters.
0649: //
0650: // So in each scenario, the ultimate sync'ing occurs
0651: // using the javaTypes of the ParameterDescs and the
0652: // javaTypes of the Method parameters.
0653: //
0654: // ------------------------------------------------
0655:
0656: // If we're already mapped to a Java method, no need to do anything.
0657: if (oper.getMethod() != null)
0658: return;
0659:
0660: // Find the method. We do this once for each Operation.
0661:
0662: Method[] methods = getMethods(implClass);
0663: // A place to keep track of possible matches
0664: Method possibleMatch = null;
0665:
0666: for (int i = 0; i < methods.length; i++) {
0667: Method method = methods[i];
0668: if (Modifier.isPublic(method.getModifiers())
0669: && method.getName().equals(oper.getName())
0670: && method2OperationMap.get(method) == null) {
0671:
0672: if (style == Style.MESSAGE) {
0673: int messageOperType = checkMessageMethod(method);
0674: if (messageOperType == OperationDesc.MSG_METHOD_NONCONFORMING)
0675: continue;
0676: if (messageOperType == -1) {
0677: throw new InternalException(
0678: "Couldn't match method to any of the allowable message-style patterns!");
0679: }
0680: oper.setMessageOperationStyle(messageOperType);
0681:
0682: // Don't bother checking params if we're message style
0683: possibleMatch = method;
0684: break;
0685: }
0686:
0687: // Check params
0688: Class[] paramTypes = method.getParameterTypes();
0689: if (paramTypes.length != oper.getNumParams())
0690: continue;
0691:
0692: int j;
0693: boolean conversionNecessary = false;
0694: for (j = 0; j < paramTypes.length; j++) {
0695: Class type = paramTypes[j];
0696: Class actualType = type;
0697: if (Holder.class.isAssignableFrom(type)) {
0698: actualType = JavaUtils.getHolderValueType(type);
0699: }
0700: ParameterDesc param = oper.getParameter(j);
0701: QName typeQName = param.getTypeQName();
0702: if (typeQName == null) {
0703: // No typeQName is available. Set it using
0704: // information from the actual type.
0705: // (Scenarios B and D)
0706: // There is no need to try and match with
0707: // the Method parameter javaType because
0708: // the ParameterDesc is being constructed
0709: // by introspecting the Method.
0710: typeQName = getTypeMapping().getTypeQName(
0711: actualType);
0712: param.setTypeQName(typeQName);
0713: } else {
0714: // A type qname is available.
0715: // Ensure that the ParameterDesc javaType
0716: // is convertable to the Method parameter type
0717: //
0718: // Use the available javaType (Scenarios C and E)
0719: // or get one from the TMR (Scenario A).
0720: Class paramClass = param.getJavaType();
0721: if (paramClass != null
0722: && JavaUtils
0723: .getHolderValueType(paramClass) != null) {
0724: paramClass = JavaUtils
0725: .getHolderValueType(paramClass);
0726: }
0727: if (paramClass == null) {
0728: paramClass = getTypeMapping()
0729: .getClassForQName(
0730: param.getTypeQName(), type);
0731: }
0732:
0733: if (paramClass != null) {
0734: // This is a match if the paramClass is somehow
0735: // convertable to the "real" parameter type. If not,
0736: // break out of this loop.
0737: if (!JavaUtils.isConvertable(paramClass,
0738: actualType)) {
0739: break;
0740: }
0741:
0742: if (!actualType
0743: .isAssignableFrom(paramClass)) {
0744: // This doesn't fit without conversion
0745: conversionNecessary = true;
0746: }
0747: }
0748: }
0749: // In all scenarios the ParameterDesc javaType is set to
0750: // match the javaType in the corresponding parameter.
0751: // This is essential.
0752: param.setJavaType(type);
0753: }
0754:
0755: if (j != paramTypes.length) {
0756: // failed.
0757: continue;
0758: }
0759:
0760: // This is our latest possibility
0761: possibleMatch = method;
0762:
0763: // If this is exactly it, stop now. Otherwise keep looking
0764: // just in case we find a better match.
0765: if (!conversionNecessary) {
0766: break;
0767: }
0768:
0769: }
0770: }
0771:
0772: // At this point, we may or may not have a possible match.
0773: // FIXME : Should we prefer an exact match from a base class over
0774: // a with-conversion match from the target class? If so,
0775: // we'll need to change the logic below.
0776: if (possibleMatch != null) {
0777: Class returnClass = possibleMatch.getReturnType();
0778: oper.setReturnClass(returnClass);
0779:
0780: QName returnType = oper.getReturnType();
0781: if (returnType == null) {
0782: oper.setReturnType(getTypeMapping().getTypeQName(
0783: returnClass));
0784: }
0785:
0786: // Do the faults
0787: createFaultMetadata(possibleMatch, oper);
0788:
0789: oper.setMethod(possibleMatch);
0790: method2OperationMap.put(possibleMatch, oper);
0791: return;
0792: }
0793:
0794: // Didn't find a match. Try the superclass, if appropriate
0795: Class super Class = implClass.getSuperclass();
0796: if (super Class != null
0797: && !super Class.getName().startsWith("java.")
0798: && !super Class.getName().startsWith("javax.")
0799: && (stopClasses == null || !stopClasses
0800: .contains(super Class.getName()))) {
0801: syncOperationToClass(oper, super Class);
0802: }
0803:
0804: // Exception if sync fails to find method for operation
0805: if (oper.getMethod() == null) {
0806: InternalException ie = new InternalException(Messages
0807: .getMessage("serviceDescOperSync00",
0808: oper.getName(), implClass.getName()));
0809: throw ie;
0810: }
0811: }
0812:
0813: private Method[] getMethods(Class implClass) {
0814: if (implClass.isInterface()) {
0815: // Returns all methods incl inherited
0816: return implClass.getMethods();
0817: } else {
0818: return implClass.getDeclaredMethods();
0819: }
0820: }
0821:
0822: private int checkMessageMethod(Method method) {
0823: // Collect the types so we know what we're dealing with in the target
0824: // method.
0825: Class[] params = method.getParameterTypes();
0826:
0827: if (params.length == 1) {
0828: if ((params[0] == Element[].class)
0829: && (method.getReturnType() == Element[].class)) {
0830: return OperationDesc.MSG_METHOD_ELEMENTARRAY;
0831: }
0832:
0833: if ((params[0] == SOAPBodyElement[].class)
0834: && (method.getReturnType() == SOAPBodyElement[].class)) {
0835: return OperationDesc.MSG_METHOD_BODYARRAY;
0836: }
0837:
0838: if ((params[0] == Document.class)
0839: && (method.getReturnType() == Document.class)) {
0840: return OperationDesc.MSG_METHOD_DOCUMENT;
0841: }
0842: } else if (params.length == 2) {
0843: if (((params[0] == SOAPEnvelope.class) && (params[1] == SOAPEnvelope.class))
0844: || ((params[0] == javax.xml.soap.SOAPEnvelope.class) && (params[1] == javax.xml.soap.SOAPEnvelope.class))
0845: && (method.getReturnType() == void.class)) {
0846: return OperationDesc.MSG_METHOD_SOAPENVELOPE;
0847: }
0848: }
0849: if (null != allowedMethods && !allowedMethods.isEmpty())
0850: throw new InternalException(Messages.getMessage(
0851: "badMsgMethodParams", method.getName()));
0852: return OperationDesc.MSG_METHOD_NONCONFORMING;
0853: }
0854:
0855: /**
0856: * Fill in a service description by introspecting the implementation
0857: * class.
0858: */
0859: public void loadServiceDescByIntrospection() {
0860: loadServiceDescByIntrospection(implClass);
0861:
0862: // Setting this to null means there is nothing more to do, and it
0863: // avoids future string compares.
0864: completedNames = null;
0865: }
0866:
0867: /**
0868: * Fill in a service description by introspecting the implementation
0869: * class.
0870: */
0871: public void loadServiceDescByIntrospection(Class implClass) {
0872: if (introspectionComplete || implClass == null) {
0873: return;
0874: }
0875:
0876: // set the implementation class for the service description
0877: this .implClass = implClass;
0878: if (Skeleton.class.isAssignableFrom(implClass)) {
0879: isSkeletonClass = true;
0880: loadSkeletonOperations();
0881: }
0882:
0883: /** If the class knows what it should be exporting,
0884: * respect its wishes.
0885: */
0886: AxisServiceConfig axisConfig = null;
0887: try {
0888: Method method = implClass.getDeclaredMethod(
0889: "getAxisServiceConfig", new Class[] {});
0890: if (method != null
0891: && Modifier.isStatic(method.getModifiers())) {
0892: axisConfig = (AxisServiceConfig) method.invoke(null,
0893: (Object[]) null);
0894: }
0895: } catch (Exception e) {
0896: // No problem, just continue without...
0897: }
0898:
0899: if (axisConfig != null) {
0900: String allowedMethodsStr = axisConfig.getAllowedMethods();
0901: if (allowedMethodsStr != null
0902: && !"*".equals(allowedMethodsStr)) {
0903: ArrayList methodList = new ArrayList();
0904: StringTokenizer tokenizer = new StringTokenizer(
0905: allowedMethodsStr, " ,");
0906: while (tokenizer.hasMoreTokens()) {
0907: methodList.add(tokenizer.nextToken());
0908: }
0909: setAllowedMethods(methodList);
0910: }
0911: }
0912:
0913: loadServiceDescByIntrospectionRecursive(implClass);
0914:
0915: // All operations should now be synchronized. Check it.
0916: for (Iterator iterator = operations.iterator(); iterator
0917: .hasNext();) {
0918: OperationDesc operation = (OperationDesc) iterator.next();
0919: if (operation.getMethod() == null) {
0920: throw new InternalException(Messages.getMessage(
0921: "badWSDDOperation", operation.getName(), ""
0922: + operation.getNumParams()));
0923: }
0924: }
0925:
0926: if ((style == Style.MESSAGE) && operations.size() == 1) {
0927: messageServiceDefaultOp = (OperationDesc) operations.get(0);
0928: }
0929:
0930: introspectionComplete = true;
0931: }
0932:
0933: /**
0934: * Is this method from ServiceLifeCycle interface?
0935: * @param m
0936: * @return true if this method is from ServiceLifeCycle interface
0937: */
0938: private boolean isServiceLifeCycleMethod(Class implClass, Method m) {
0939: if (WebService.class.isAssignableFrom(implClass)) {
0940: String methodName = m.getName();
0941:
0942: if (methodName.equals("init")) {
0943: // Check if the method signature is
0944: // "public abstract void init(Object context) throws ServiceException;"
0945: Class[] classes = m.getParameterTypes();
0946: if (classes != null && classes.length == 1
0947: && classes[0] == ServerObjectConfig.class
0948: && m.getReturnType() == Void.TYPE) {
0949: return true;
0950: }
0951: } else if (methodName.equals("destroy")) {
0952: // Check if the method signature is
0953: // "public abstract void destroy();"
0954: Class[] classes = m.getParameterTypes();
0955: if (classes != null && classes.length == 0
0956: && m.getReturnType() == Void.TYPE) {
0957: return true;
0958: }
0959: } else if (methodName.equals("invoke")) {
0960: // Check if the method signature is
0961: // "public abstract void init(Object context) throws ServiceException;"
0962: Class[] classes = m.getParameterTypes();
0963: if (classes != null && classes.length == 1
0964: && classes[0] == RequestInfo.class
0965: && m.getReturnType() == Object.class) {
0966: return true;
0967: }
0968: } else if (methodName.equals("start")) {
0969: // Check if the method signature is
0970: // "public abstract void init(Object context) throws ServiceException;"
0971: Class[] classes = m.getParameterTypes();
0972: if (classes != null && classes.length == 0) {
0973: return true;
0974: }
0975: } else if (methodName.equals("stop")) {
0976: // Check if the method signature is
0977: // "public abstract void init(Object context) throws ServiceException;"
0978: Class[] classes = m.getParameterTypes();
0979: if (classes != null && classes.length == 0) {
0980: return true;
0981: }
0982: } else if (methodName.equals("setProperties")) {
0983: // Check if the method signature is
0984: // "public abstract void init(Object context) throws ServiceException;"
0985: Class[] classes = m.getParameterTypes();
0986: if (classes != null
0987: && classes.length == 1
0988: && classes[0].isArray()
0989: && classes[0].getComponentType() == PropertyInfo.class
0990: && m.getReturnType() == Void.TYPE) {
0991: return true;
0992: }
0993: }
0994: }
0995: return false;
0996: }
0997:
0998: /**
0999: * Recursive helper class for loadServiceDescByIntrospection
1000: */
1001: private void loadServiceDescByIntrospectionRecursive(Class implClass) {
1002: if (Skeleton.class.equals(implClass)) {
1003: return;
1004: }
1005:
1006: Method[] methods = getMethods(implClass);
1007:
1008: for (int i = 0; i < methods.length; i++) {
1009: if (Modifier.isPublic(methods[i].getModifiers())
1010: && !isServiceLifeCycleMethod(implClass, methods[i])) {
1011: getSyncedOperationsForName(implClass, methods[i]
1012: .getName());
1013: }
1014: }
1015:
1016: if (implClass.isInterface()) {
1017: Class[] super Classes = implClass.getInterfaces();
1018: for (int i = 0; i < super Classes.length; i++) {
1019: Class super Class = super Classes[i];
1020: if (!super Class.getName().startsWith("java.")
1021: && !super Class.getName().startsWith("javax.")
1022: && (stopClasses == null || !stopClasses
1023: .contains(super Class.getName()))) {
1024: loadServiceDescByIntrospectionRecursive(super Class);
1025: }
1026: }
1027: } else {
1028: Class super Class = implClass.getSuperclass();
1029: if (super Class != null
1030: && !super Class.getName().startsWith("java.")
1031: && !super Class.getName().startsWith("javax.")
1032: && (stopClasses == null || !stopClasses
1033: .contains(super Class.getName()))) {
1034: loadServiceDescByIntrospectionRecursive(super Class);
1035: }
1036: }
1037: }
1038:
1039: /**
1040: * Fill in a service description by introspecting the implementation
1041: * class. This version takes the implementation class and the in-scope
1042: * TypeMapping.
1043: */
1044: public void loadServiceDescByIntrospection(Class cls, TypeMapping tm) {
1045: // Should we complain if the implClass changes???
1046: implClass = cls;
1047: this .tm = tm;
1048:
1049: if (Skeleton.class.isAssignableFrom(implClass)) {
1050: isSkeletonClass = true;
1051: loadSkeletonOperations();
1052: }
1053:
1054: loadServiceDescByIntrospection();
1055: }
1056:
1057: /**
1058: * Makes sure we have completely synchronized OperationDescs with
1059: * the implementation class.
1060: */
1061: private void getSyncedOperationsForName(Class implClass,
1062: String methodName) {
1063: // If we're a Skeleton deployment, skip the statics.
1064: if (isSkeletonClass) {
1065: if (methodName.equals("getOperationDescByName")
1066: || methodName.equals("getOperationDescs"))
1067: return;
1068: }
1069:
1070: // If we have no implementation class, don't worry about it (we're
1071: // probably on the client)
1072: if (implClass == null)
1073: return;
1074:
1075: // If we're done introspecting, or have completed this method, return
1076: if (completedNames == null
1077: || completedNames.contains(methodName))
1078: return;
1079:
1080: // Skip it if it's not a sanctioned method name
1081: if ((allowedMethods != null)
1082: && !allowedMethods.contains(methodName))
1083: return;
1084:
1085: if ((disallowedMethods != null)
1086: && disallowedMethods.contains(methodName))
1087: return;
1088:
1089: // If we're a skeleton class, make sure we don't already have any
1090: // OperationDescs for this name (as that might cause conflicts),
1091: // then load them up from the Skeleton class.
1092: if (isSkeletonClass && !haveAllSkeletonMethods) {
1093: // FIXME : Check for existing ones and fault if found
1094:
1095: if (skelMethod == null) {
1096: // Grab metadata from the Skeleton for parameter info
1097: try {
1098: skelMethod = implClass.getDeclaredMethod(
1099: "getOperationDescByName",
1100: new Class[] { String.class });
1101: } catch (NoSuchMethodException e) {
1102: } catch (SecurityException e) {
1103: }
1104: if (skelMethod == null) {
1105: // FIXME : Throw an error?
1106: return;
1107: }
1108: }
1109: try {
1110: List skelList = (List) skelMethod.invoke(implClass,
1111: new Object[] { methodName });
1112: if (skelList != null) {
1113: Iterator i = skelList.iterator();
1114: while (i.hasNext()) {
1115: addOperationDesc((OperationDesc) i.next());
1116: }
1117: }
1118: } catch (IllegalAccessException e) {
1119: if (log.isDebugEnabled()) {
1120: log.debug(Messages.getMessage("exception00"), e);
1121: }
1122: return;
1123: } catch (IllegalArgumentException e) {
1124: if (log.isDebugEnabled()) {
1125: log.debug(Messages.getMessage("exception00"), e);
1126: }
1127: return;
1128: } catch (InvocationTargetException e) {
1129: if (log.isDebugEnabled()) {
1130: log.debug(Messages.getMessage("exception00"), e);
1131: }
1132: return;
1133: }
1134: }
1135:
1136: // OK, go find any current OperationDescs for this method name and
1137: // make sure they're synced with the actual class.
1138: if (name2OperationsMap != null) {
1139: ArrayList currentOverloads = (ArrayList) name2OperationsMap
1140: .get(methodName);
1141: if (currentOverloads != null) {
1142: // For each one, sync it to the implementation class' methods
1143: for (Iterator i = currentOverloads.iterator(); i
1144: .hasNext();) {
1145: OperationDesc oper = (OperationDesc) i.next();
1146: if (oper.getMethod() == null) {
1147: syncOperationToClass(oper, implClass);
1148: }
1149: }
1150: }
1151: }
1152:
1153: // Now all OperationDescs from deployment data have been completely
1154: // filled in. So we now make new OperationDescs for any method
1155: // overloads which were not covered above.
1156: // NOTE : This is the "lenient" approach, which allows you to
1157: // specify one overload and still get the others by introspection.
1158: // We could equally well return above if we found OperationDescs,
1159: // and have a rule that if you specify any overloads, you must specify
1160: // all the ones you want accessible.
1161:
1162: createOperationsForName(implClass, methodName);
1163:
1164: // Note that we never have to look at this method name again.
1165: completedNames.add(methodName);
1166: }
1167:
1168: private String getUniqueOperationName(String name) {
1169: int i = 1;
1170: String candidate;
1171: do {
1172: candidate = name + i++;
1173: } while (name2OperationsMap.get(candidate) != null);
1174:
1175: return candidate;
1176: }
1177:
1178: /**
1179: * Look for methods matching this name, and for each one, create an
1180: * OperationDesc (if it's not already in our list).
1181: *
1182: * TODO: Make this more efficient
1183: */
1184: private void createOperationsForName(Class implClass,
1185: String methodName) {
1186: // If we're a Skeleton deployment, skip the statics.
1187: if (isSkeletonClass) {
1188: if (methodName.equals("getOperationDescByName")
1189: || methodName.equals("getOperationDescs"))
1190: return;
1191: }
1192:
1193: Method[] methods = getMethods(implClass);
1194:
1195: for (int i = 0; i < methods.length; i++) {
1196: Method method = methods[i];
1197: if (Modifier.isPublic(method.getModifiers())
1198: && method.getName().equals(methodName)
1199: && !isServiceLifeCycleMethod(implClass, method)) {
1200: createOperationForMethod(method);
1201: }
1202: }
1203:
1204: Class super Class = implClass.getSuperclass();
1205: if (super Class != null
1206: && !super Class.getName().startsWith("java.")
1207: && !super Class.getName().startsWith("javax.")
1208: && (stopClasses == null || !stopClasses
1209: .contains(super Class.getName()))) {
1210: createOperationsForName(super Class, methodName);
1211: }
1212: }
1213:
1214: /**
1215: * Make an OperationDesc from a Java method.
1216: *
1217: * In the absence of deployment metadata, this code will introspect a
1218: * Method and create an appropriate OperationDesc. If the class
1219: * implements the Skeleton interface, we will use the metadata from there
1220: * in constructing the OperationDesc. If not, we use parameter names
1221: * from the bytecode debugging info if available, or "in0", "in1", etc.
1222: * if not.
1223: */
1224: private void createOperationForMethod(Method method) {
1225: // If we've already got it, never mind
1226: if (method2OperationMap.get(method) != null) {
1227: return;
1228: }
1229:
1230: Class[] paramTypes = method.getParameterTypes();
1231:
1232: // And if we've already got an exact match (i.e. an override),
1233: // never mind
1234:
1235: ArrayList overloads = name2OperationsMap == null ? null
1236: : (ArrayList) name2OperationsMap.get(method.getName());
1237: if (overloads != null && !overloads.isEmpty()) {
1238: // Search each OperationDesc that already has a Method
1239: // associated with it, and check for parameter type equivalence.
1240: for (int i = 0; i < overloads.size(); i++) {
1241: OperationDesc op = (OperationDesc) overloads.get(i);
1242: Method checkMethod = op.getMethod();
1243: if (checkMethod != null) {
1244: Class[] others = checkMethod.getParameterTypes();
1245: if (paramTypes.length == others.length) {
1246: int j = 0;
1247: for (; j < others.length; j++) {
1248: if (!others[j].equals(paramTypes[j]))
1249: break;
1250: }
1251: // If we got all the way through, we have a match.
1252: if (j == others.length)
1253: return;
1254: }
1255: }
1256: }
1257: }
1258:
1259: boolean isWSICompliant = JavaUtils.isTrue(AxisProperties
1260: .getProperty(Constants.WSIBP11_COMPAT_PROPERTY));
1261:
1262: // Make an OperationDesc, fill in common stuff
1263: OperationDesc operation = new OperationDesc();
1264:
1265: // If we're WS-I compliant, we can't have overloaded operation names.
1266: // If we find duplicates, we generate unique names for them and map
1267: // those names to the correct Method.
1268: String name = method.getName();
1269: if (isWSICompliant && name2OperationsMap != null) {
1270: Collection methodNames = name2OperationsMap.keySet();
1271: name = JavaUtils.getUniqueValue(methodNames, name);
1272: }
1273: operation.setName(name);
1274: String defaultNS = "";
1275: if (namespaceMappings != null && !namespaceMappings.isEmpty()) {
1276: // If we have a default namespace mapping, require callers to
1277: // use that namespace.
1278: defaultNS = (String) namespaceMappings.get(0);
1279: }
1280: if (defaultNS.length() == 0) {
1281: defaultNS = Namespaces.makeNamespace(method
1282: .getDeclaringClass().getName());
1283: }
1284: operation.setElementQName(new QName(defaultNS, name));
1285: operation.setMethod(method);
1286:
1287: // If this is a MESSAGE style service, set up the OperationDesc
1288: // appropriately.
1289: if (style == Style.MESSAGE) {
1290: int messageOperType = checkMessageMethod(method);
1291: if (messageOperType == OperationDesc.MSG_METHOD_NONCONFORMING)
1292: return;
1293: if (messageOperType == -1) {
1294: throw new InternalException(
1295: "Couldn't match method to any of the allowable message-style patterns!");
1296: }
1297: operation.setMessageOperationStyle(messageOperType);
1298: operation.setReturnClass(Object.class);
1299: operation.setReturnType(Constants.XSD_ANYTYPE);
1300: } else {
1301: // For other styles, continue here.
1302: Class retClass = method.getReturnType();
1303: operation.setReturnClass(retClass);
1304: QName typeQName = getTypeQName(retClass);
1305: operation.setReturnType(typeQName);
1306:
1307: String[] paramNames = getParamNames(method);
1308:
1309: for (int k = 0; k < paramTypes.length; k++) {
1310: Class type = paramTypes[k];
1311: ParameterDesc paramDesc = new ParameterDesc();
1312: // param should be unqualified if we're using rpc style,
1313: // or should use the operation's namespace if its document style
1314: String paramNamespace = (this .style == Style.RPC ? ""
1315: : operation.getElementQName().getNamespaceURI());
1316:
1317: // If we have a name for this param, use it, otherwise call
1318: // it "in*"
1319: if (paramNames != null && paramNames[k] != null
1320: && paramNames[k].length() > 0) {
1321: paramDesc.setQName(new QName(paramNamespace,
1322: paramNames[k]));
1323: } else {
1324: paramDesc.setQName(new QName(paramNamespace, "in"
1325: + k));
1326: }
1327:
1328: // If it's a Holder, mark it INOUT, and set the XML type QName
1329: // to the held type. Otherwise it's IN.
1330:
1331: Class heldClass = JavaUtils.getHolderValueType(type);
1332: if (heldClass != null) {
1333: paramDesc.setMode(ParameterDesc.INOUT);
1334: paramDesc.setTypeQName(getTypeQName(heldClass));
1335: } else {
1336: paramDesc.setMode(ParameterDesc.IN);
1337: paramDesc.setTypeQName(getTypeQName(type));
1338: }
1339: paramDesc.setJavaType(type);
1340: operation.addParameter(paramDesc);
1341: }
1342: }
1343:
1344: createFaultMetadata(method, operation);
1345:
1346: addOperationDesc(operation);
1347: method2OperationMap.put(method, operation);
1348: }
1349:
1350: private QName getTypeQName(Class javaClass) {
1351: QName typeQName;
1352: TypeMapping tm = getTypeMapping();
1353: if (style == Style.RPC) {
1354: typeQName = tm.getTypeQName(javaClass);
1355: } else {
1356: typeQName = tm.getTypeQNameExact(javaClass);
1357: if (typeQName == null && javaClass.isArray()) {
1358: typeQName = tm.getTypeQName(javaClass
1359: .getComponentType());
1360: } else {
1361: typeQName = tm.getTypeQName(javaClass);
1362: }
1363: }
1364: return typeQName;
1365: }
1366:
1367: private void createFaultMetadata(Method method,
1368: OperationDesc operation) {
1369: // Create Exception Types
1370: Class[] exceptionTypes = method.getExceptionTypes();
1371:
1372: for (int i = 0; i < exceptionTypes.length; i++) {
1373: // Every remote method declares a java.rmi.RemoteException
1374: // Only interested in application specific exceptions.
1375: // Ignore java and javax package exceptions.
1376: Class ex = exceptionTypes[i];
1377: if (ex != java.rmi.RemoteException.class
1378: && ex != org.apache.axis.AxisFault.class
1379: && !ex.getName().startsWith("java.")
1380: && !ex.getName().startsWith("javax.")) {
1381:
1382: // For JSR 101 v.1.0, there is a simple fault mapping
1383: // and a complexType fault mapping...both mappings
1384: // generate a class that extends (directly or indirectly)
1385: // Exception.
1386: // When converting java back to wsdl it is not possible
1387: // to determine which way to do the mapping,
1388: // so it is always mapped back using the complexType
1389: // fault mapping because it is more useful (i.e. it
1390: // establishes a hierarchy of exceptions). Note that this
1391: // will not cause any roundtripping problems.
1392: // Rich
1393:
1394: /* Old Simple Type Mode
1395: Field[] f = ex.getDeclaredFields();
1396: ArrayList exceptionParams = new ArrayList();
1397: for (int j = 0; j < f.length; j++) {
1398: int mod = f[j].getModifiers();
1399: if (Modifier.isPublic(mod) &&
1400: !Modifier.isStatic(mod)) {
1401: QName qname = new QName("", f[j].getName());
1402: QName typeQName = tm.getTypeQName(f[j].getType());
1403: ParameterDesc param = new ParameterDesc(qname,
1404: ParameterDesc.IN,
1405: typeQName);
1406: param.setJavaType(f[j].getType());
1407: exceptionParams.add(param);
1408: }
1409: }
1410: String pkgAndClsName = ex.getName();
1411: FaultDesc fault = new FaultDesc();
1412: fault.setName(pkgAndClsName);
1413: fault.setParameters(exceptionParams);
1414: operation.addFault(fault);
1415: */
1416:
1417: FaultDesc fault = operation.getFaultByClass(ex, false);
1418: boolean isNew;
1419:
1420: // If we didn't find one, create a new one
1421: if (fault == null) {
1422: fault = new FaultDesc();
1423: isNew = true;
1424: } else {
1425: isNew = false;
1426: }
1427:
1428: // Try to fil in any parts of the faultDesc that aren't there
1429:
1430: // XMLType
1431: QName xmlType = fault.getXmlType();
1432: if (xmlType == null) {
1433: fault.setXmlType(getTypeMapping().getTypeQName(ex));
1434: }
1435:
1436: // Name and Class Name
1437: String pkgAndClsName = ex.getName();
1438: if (fault.getClassName() == null) {
1439: fault.setClassName(pkgAndClsName);
1440: }
1441: if (fault.getName() == null) {
1442: String name = pkgAndClsName.substring(pkgAndClsName
1443: .lastIndexOf('.') + 1, pkgAndClsName
1444: .length());
1445: fault.setName(name);
1446: }
1447:
1448: // Parameters
1449: // We add a single parameter which points to the type
1450: if (fault.getParameters() == null) {
1451: if (xmlType == null) {
1452: xmlType = getTypeMapping().getTypeQName(ex);
1453: }
1454: QName qname = fault.getQName();
1455: if (qname == null) {
1456: qname = new QName("", "fault");
1457: }
1458: ParameterDesc param = new ParameterDesc(qname,
1459: ParameterDesc.IN, xmlType);
1460: param.setJavaType(ex);
1461: ArrayList exceptionParams = new ArrayList();
1462: exceptionParams.add(param);
1463: fault.setParameters(exceptionParams);
1464: }
1465:
1466: // QName
1467: if (fault.getQName() == null) {
1468: fault.setQName(new QName(pkgAndClsName));
1469: }
1470:
1471: if (isNew) {
1472: // Add the fault to the operation
1473: operation.addFault(fault);
1474: }
1475: }
1476: }
1477: }
1478:
1479: private String[] getParamNames(Method method) {
1480: synchronized (method2ParamsMap) {
1481: String[] paramNames = (String[]) method2ParamsMap
1482: .get(method);
1483: if (paramNames != null)
1484: return paramNames;
1485: paramNames = ParamNameExtractor
1486: .getParameterNamesFromDebugInfo(method);
1487: method2ParamsMap.put(method, paramNames);
1488: return paramNames;
1489: }
1490: }
1491:
1492: public void setNamespaceMappings(List namespaces) {
1493: namespaceMappings = namespaces;
1494: }
1495:
1496: public String getDefaultNamespace() {
1497: if (namespaceMappings == null || namespaceMappings.isEmpty())
1498: return null;
1499: return (String) namespaceMappings.get(0);
1500: }
1501:
1502: public void setDefaultNamespace(String namespace) {
1503: if (namespaceMappings == null)
1504: namespaceMappings = new ArrayList();
1505: namespaceMappings.add(0, namespace);
1506: }
1507:
1508: public void setProperty(String name, Object value) {
1509: if (properties == null) {
1510: properties = new HashMap();
1511: }
1512: properties.put(name, value);
1513: }
1514:
1515: public Object getProperty(String name) {
1516: if (properties == null)
1517: return null;
1518:
1519: return properties.get(name);
1520: }
1521:
1522: public String getEndpointURL() {
1523: return endpointURL;
1524: }
1525:
1526: public void setEndpointURL(String endpointURL) {
1527: this .endpointURL = endpointURL;
1528: }
1529:
1530: public TypeMappingRegistry getTypeMappingRegistry() {
1531: if (tmr == null) {
1532: tmr = new TypeMappingRegistryImpl(false);
1533: }
1534: return tmr;
1535: }
1536:
1537: public void setTypeMappingRegistry(TypeMappingRegistry tmr) {
1538: this .tmr = tmr;
1539: }
1540:
1541: public boolean isInitialized() {
1542: return implClass != null;
1543: }
1544: }
|