0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: */
0019: package org.apache.axis2.jaxws.description.impl;
0020:
0021: import static org.apache.axis2.jaxws.description.builder.MDQConstants.RETURN_TYPE_FUTURE;
0022: import static org.apache.axis2.jaxws.description.builder.MDQConstants.RETURN_TYPE_RESPONSE;
0023:
0024: import org.apache.axis2.client.ServiceClient;
0025: import org.apache.axis2.context.ConfigurationContext;
0026: import org.apache.axis2.description.AxisService;
0027: import org.apache.axis2.jaxws.ClientConfigurationFactory;
0028: import org.apache.axis2.jaxws.ExceptionFactory;
0029: import org.apache.axis2.jaxws.description.DescriptionFactory;
0030: import org.apache.axis2.jaxws.description.EndpointDescription;
0031: import org.apache.axis2.jaxws.description.EndpointInterfaceDescription;
0032: import org.apache.axis2.jaxws.description.ServiceDescription;
0033: import org.apache.axis2.jaxws.description.ServiceDescriptionJava;
0034: import org.apache.axis2.jaxws.description.ServiceDescriptionWSDL;
0035: import org.apache.axis2.jaxws.description.ServiceRuntimeDescription;
0036: import org.apache.axis2.jaxws.description.builder.DescriptionBuilderComposite;
0037: import org.apache.axis2.jaxws.description.builder.MDQConstants;
0038: import org.apache.axis2.jaxws.description.builder.MethodDescriptionComposite;
0039: import org.apache.axis2.jaxws.description.builder.ParameterDescriptionComposite;
0040: import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
0041: import org.apache.axis2.jaxws.i18n.Messages;
0042: import org.apache.axis2.jaxws.util.WSDL4JWrapper;
0043: import org.apache.axis2.jaxws.util.WSDLWrapper;
0044: import org.apache.commons.logging.Log;
0045: import org.apache.commons.logging.LogFactory;
0046:
0047: import javax.jws.HandlerChain;
0048: import javax.wsdl.Definition;
0049: import javax.wsdl.Port;
0050: import javax.wsdl.PortType;
0051: import javax.wsdl.Service;
0052: import javax.wsdl.WSDLException;
0053: import javax.wsdl.extensions.ExtensibilityElement;
0054: import javax.xml.bind.JAXBContext;
0055: import javax.xml.bind.JAXBElement;
0056: import javax.xml.bind.Unmarshaller;
0057: import javax.xml.namespace.QName;
0058: import javax.xml.ws.soap.SOAPBinding;
0059:
0060: import java.io.FileNotFoundException;
0061: import java.io.IOException;
0062: import java.io.InputStream;
0063: import java.net.ConnectException;
0064: import java.net.URL;
0065: import java.net.UnknownHostException;
0066: import java.util.ArrayList;
0067: import java.util.Collection;
0068: import java.util.Collections;
0069: import java.util.HashMap;
0070: import java.util.Iterator;
0071: import java.util.List;
0072: import java.util.Map;
0073:
0074: /** @see ../ServiceDescription */
0075: class ServiceDescriptionImpl implements ServiceDescription,
0076: ServiceDescriptionWSDL, ServiceDescriptionJava {
0077: private ClientConfigurationFactory clientConfigFactory;
0078: private ConfigurationContext configContext;
0079:
0080: private URL wsdlURL;
0081: private QName serviceQName;
0082:
0083: // Only ONE of the following will be set in a ServiceDescription, depending on whether this Description
0084: // was created from a service-requester or service-provider flow.
0085: private Class serviceClass; // A service-requester generated service or generic service class
0086:
0087: // TODO: Possibly remove Definition and delegate to the Defn on the AxisSerivce set as a paramater by WSDLtoAxisServicBuilder?
0088: private WSDLWrapper wsdlWrapper;
0089: private WSDLWrapper generatedWsdlWrapper;
0090:
0091: //ANNOTATION: @HandlerChain
0092: private HandlerChain handlerChainAnnotation;
0093: private HandlerChainsType handlerChainsType;
0094:
0095: private Map<QName, EndpointDescription> endpointDescriptions = new HashMap<QName, EndpointDescription>();
0096:
0097: private static final Log log = LogFactory
0098: .getLog(ServiceDescriptionImpl.class);
0099:
0100: private HashMap<String, DescriptionBuilderComposite> dbcMap = null;
0101:
0102: private DescriptionBuilderComposite composite = null;
0103: private boolean isServerSide = false;
0104:
0105: // RUNTIME INFORMATION
0106: Map<String, ServiceRuntimeDescription> runtimeDescMap = Collections
0107: .synchronizedMap(new HashMap<String, ServiceRuntimeDescription>());
0108:
0109: /**
0110: * This is (currently) the client-side-only constructor Construct a service description hierachy
0111: * based on WSDL (may be null), the Service class, and a service QName.
0112: *
0113: * @param wsdlURL The WSDL file (this may be null).
0114: * @param serviceQName The name of the service in the WSDL. This can not be null since a
0115: * javax.xml.ws.Service can not be created with a null service QName.
0116: * @param serviceClass The JAX-WS service class. This could be an instance of
0117: * javax.xml.ws.Service or a generated service subclass thereof. This will
0118: * not be null.
0119: */
0120: ServiceDescriptionImpl(URL wsdlURL, QName serviceQName,
0121: Class serviceClass) {
0122: if (serviceQName == null) {
0123: throw ExceptionFactory.makeWebServiceException(Messages
0124: .getMessage("serviceDescErr0"));
0125: }
0126: if (serviceClass == null) {
0127: throw ExceptionFactory.makeWebServiceException(Messages
0128: .getMessage("serviceDescErr1", "null"));
0129: }
0130: if (!javax.xml.ws.Service.class.isAssignableFrom(serviceClass)) {
0131: throw ExceptionFactory.makeWebServiceException(Messages
0132: .getMessage("serviceDescErr1", serviceClass
0133: .getName()));
0134: }
0135:
0136: // TODO: On the client side, we should not support partial WSDL; i.e. if the WSDL is specified it must be
0137: // complete and must contain the ServiceQName. This is how the Sun RI behaves on the client.
0138: // When this is fixed, the check in ServiceDelegate(URL, QName, Class) should be removed
0139: this .wsdlURL = wsdlURL;
0140: // TODO: The serviceQName needs to be verified between the argument/WSDL/Annotation
0141: this .serviceQName = serviceQName;
0142: this .serviceClass = serviceClass;
0143:
0144: setupWsdlDefinition();
0145: }
0146:
0147: /**
0148: * This is (currently) the service-provider-side-only constructor. Create a service Description
0149: * based on a service implementation class
0150: *
0151: * @param serviceImplClass
0152: */
0153: ServiceDescriptionImpl(Class serviceImplClass,
0154: AxisService axisService) {
0155: isServerSide = true;
0156: // Create the EndpointDescription hierachy from the service impl annotations; Since the PortQName is null,
0157: // it will be set to the annotation value.
0158: EndpointDescriptionImpl endpointDescription = new EndpointDescriptionImpl(
0159: serviceImplClass, null, axisService, this );
0160: addEndpointDescription(endpointDescription);
0161:
0162: // TODO: The ServiceQName instance variable should be set based on annotation or default
0163: }
0164:
0165: /**
0166: * This is (currently) the service-provider-side-only constructor. Create a service Description
0167: * based on a service implementation class
0168: *
0169: * @param serviceImplClass
0170: */
0171: ServiceDescriptionImpl(
0172: HashMap<String, DescriptionBuilderComposite> dbcMap,
0173: DescriptionBuilderComposite composite) {
0174: this .composite = composite;
0175:
0176: String serviceImplName = this .composite.getClassName();
0177:
0178: this .dbcMap = dbcMap;
0179: //TODO: How to we get this when called from server side, create here for now
0180: //REVIEW: The value being set here is used later in validation checking to
0181: // validation that should occur separately on server and client. If
0182: // at some point this constructor is ever called by the client side,
0183: // then we'll have to get smarter about how we determine server/client
0184: // validation
0185: this .isServerSide = true;
0186:
0187: //capture the WSDL, if there is any...to be used for later processing
0188: setupWsdlDefinition();
0189:
0190: // Do a first pass validation for this DescriptionBuilderComposite.
0191: // This is not intended to be a full integrity check, but rather a fail-fast mechanism
0192: // TODO: Refactor this to a seperate validator class?
0193: validateDBCLIntegrity();
0194:
0195: // The ServiceQName instance variable is set based on annotation or default
0196: // It will be set by the EndpointDescriptionImpl since it is the one that knows
0197: // how to process the annotations and the defaults.
0198: //TODO: When we get this, need to consider verifying service name between WSDL
0199: // and annotations, so
0200:
0201: // Create the EndpointDescription hierachy from the service impl annotations; Since the PortQName is null,
0202: // it will be set to the annotation value.
0203: //EndpointDescription endpointDescription = new EndpointDescription(null, this, serviceImplName);
0204: EndpointDescriptionImpl endpointDescription = new EndpointDescriptionImpl(
0205: this , serviceImplName);
0206: addEndpointDescription(endpointDescription);
0207: }
0208:
0209: /*=======================================================================*/
0210: /*=======================================================================*/
0211: // START of public accessor methods
0212: /**
0213: * Update or create an EndpointDescription. Updates to existing EndpointDescriptons will be
0214: * based on the SEI class and its annotations. Both declared ports and dynamic ports can be
0215: * updated. A declared port is one that is defined (e.g. in WSDL or via annotations); a dyamic
0216: * port is one that is not defined (e.g. not via WSDL or annotations) and has been added via
0217: * Serivce.addPort.
0218: * <p/>
0219: * Notes on how an EndpointDescription can be updated or created: 1) Service.createDispatch can
0220: * create a Dispatch client for either a declared or dynamic port 2) Note that creating a
0221: * Dispatch does not associate an SEI with an endpoint 3) Service.getPort will associate an SEI
0222: * with a port 4) A getPort on an endpoint which was originally created for a Distpatch will
0223: * update that EndpointDescription with the SEI provided on the getPort 5) Service.getPort can
0224: * not be called on a dynamic port (per the JAX-WS spec) 6) Service.addPort can not be called
0225: * for a declared port
0226: *
0227: * @param sei This will be non-null if the update is of type GET_PORT; it will be null if
0228: * the update is ADD_PORT or CREATE_DISPATCH
0229: * @param portQName
0230: * @param updateType Indicates what is causing the update GET_PORT is an attempt to get a
0231: * declared SEI-based port ADD_PORT is an attempt to add a previously
0232: * non-existent dynamic port CREATE_DISPATCH is an attempt to create a
0233: * Dispatch-based client to either a declared port or a pre-existing dynamic
0234: * port.
0235: */
0236:
0237: EndpointDescription updateEndpointDescription(Class sei,
0238: QName portQName, DescriptionFactory.UpdateType updateType) {
0239:
0240: EndpointDescriptionImpl endpointDescription = getEndpointDescriptionImpl(portQName);
0241: boolean isPortDeclared = isPortDeclared(portQName);
0242:
0243: switch (updateType) {
0244:
0245: case ADD_PORT:
0246: // Port must NOT be declared (e.g. can not already exist in WSDL)
0247: // If an EndpointDesc doesn't exist; create it as long as it doesn't exist in the WSDL
0248: // TODO: This test can be simplified once isPortDeclared(QName) understands annotations and WSDL as ways to declare a port.
0249: if (DescriptionUtils.isEmpty(portQName)) {
0250: throw ExceptionFactory.makeWebServiceException(Messages
0251: .getMessage("addPortErr2"));
0252: }
0253: if (getWSDLWrapper() != null && isPortDeclared) {
0254: // TODO: RAS & NLS
0255: throw ExceptionFactory
0256: .makeWebServiceException(Messages.getMessage(
0257: "addPortDup", portQName.toString()));
0258: } else if (endpointDescription == null) {
0259: // Use the SEI Class and its annotations to finish creating the Description hierachy. Note that EndpointInterface, Operations, Parameters, etc.
0260: // are not created for dynamic ports. It would be an error to later do a getPort against a dynamic port (per the JAX-WS spec)
0261: endpointDescription = new EndpointDescriptionImpl(sei,
0262: portQName, true, this );
0263: addEndpointDescription(endpointDescription);
0264: } else {
0265: // All error check above passed, the EndpointDescription already exists and needs no updating
0266: }
0267: break;
0268:
0269: case GET_PORT:
0270:
0271: // try to find existing endpointDesc by SEI class if portQName was not specified
0272: if (endpointDescription == null && portQName == null
0273: && sei != null) {
0274: endpointDescription = getEndpointDescriptionImpl(sei);
0275: }
0276:
0277: // If an endpointDesc doesn't exist, and the port exists in the WSDL, create it
0278: // If an endpointDesc already exists and has an associated SEI already, make sure they match
0279: // If an endpointDesc already exists and was created for Dispatch (no SEI), update that with the SEI provided on the getPort
0280:
0281: // Port must be declared (e.g. in WSDL or via annotations)
0282: // TODO: Once isPortDeclared understands annotations and not just WSDL, the 2nd part of this check can possibly be removed.
0283: // Although consider the check below that updates an existing EndpointDescritpion with an SEI.
0284: if (!isPortDeclared
0285: || (endpointDescription != null && endpointDescription
0286: .isDynamicPort())) {
0287: // This guards against the case where an addPort was done previously and now a getPort is done on it.
0288: // TODO: RAS & NLS
0289: throw ExceptionFactory
0290: .makeWebServiceException("ServiceDescription.updateEndpointDescription: Can not do a getPort on a port added via addPort(). PortQN: "
0291: + (portQName != null ? portQName
0292: .toString() : "not specified"));
0293: } else if (sei == null) {
0294: // TODO: RAS & NLS
0295: throw ExceptionFactory
0296: .makeWebServiceException("ServiceDescription.updateEndpointDescription: Can not do a getPort with a null SEI. PortQN: "
0297: + (portQName != null ? portQName
0298: .toString() : "not specified"));
0299: } else if (endpointDescription == null) {
0300: // Use the SEI Class and its annotations to finish creating the Description hierachy: Endpoint, EndpointInterface, Operations, Parameters, etc.
0301: // TODO: Need to create the Axis Description objects after we have all the config info (i.e. from this SEI)
0302: endpointDescription = new EndpointDescriptionImpl(sei,
0303: portQName, this );
0304: addEndpointDescription(endpointDescription);
0305: /*
0306: * We must reset the service runtime description after adding a new endpoint
0307: * since the runtime description information could have references to old
0308: * information. See MarshalServiceRuntimeDescriptionFactory and
0309: * MarshalServiceRuntimeDescription.
0310: */
0311: resetServiceRuntimeDescription();
0312: } else if (getEndpointSEI(portQName) == null
0313: && !endpointDescription.isDynamicPort()) {
0314: // Existing endpointDesc from a declared port needs to be updated with an SEI
0315: // Note that an EndpointDescritption created from an addPort (i.e. a dynamic port) can not do this.
0316: endpointDescription.updateWithSEI(sei);
0317: } else if (getEndpointSEI(portQName) != sei) {
0318: // TODO: RAS & NLS
0319: throw ExceptionFactory
0320: .makeWebServiceException("ServiceDescription.updateEndpointDescription: Can't do a getPort() specifiying a different SEI than the previous getPort(). PortQN: "
0321: + portQName
0322: + "; current SEI: "
0323: + sei
0324: + "; previous SEI: "
0325: + getEndpointSEI(portQName));
0326: } else {
0327: // All error check above passed, the EndpointDescription already exists and needs no updating
0328: }
0329: break;
0330:
0331: case CREATE_DISPATCH:
0332: // Port may or may not exist in WSDL.
0333: // If an endpointDesc doesn't exist and it is in the WSDL, it can be created
0334: // Otherwise, it is an error.
0335: if (DescriptionUtils.isEmpty(portQName)) {
0336: throw ExceptionFactory.makeWebServiceException(Messages
0337: .getMessage("createDispatchFail0"));
0338: } else if (endpointDescription != null) {
0339: // The EndpoingDescription already exists; nothing needs to be done
0340: } else if (sei != null) {
0341: // The Dispatch should not have an SEI associated with it on the update call.
0342: // REVIEW: Is this a valid check?
0343: throw ExceptionFactory
0344: .makeWebServiceException("ServiceDescription.updateEndpointDescription: Can not specify an SEI when creating a Dispatch. PortQN: "
0345: + portQName);
0346: } else if (getWSDLWrapper() != null && isPortDeclared) {
0347: // EndpointDescription doesn't exist and this is a declared Port, so create one
0348: // Use the SEI Class and its annotations to finish creating the Description hierachy. Note that EndpointInterface, Operations, Parameters, etc.
0349: // are not created for Dipsatch-based ports, but might be updated later if a getPort is done against the same declared port.
0350: // TODO: Need to create the Axis Description objects after we have all the config info (i.e. from this SEI)
0351: endpointDescription = new EndpointDescriptionImpl(sei,
0352: portQName, this );
0353: addEndpointDescription(endpointDescription);
0354: } else {
0355: // The port is not a declared port and it does not have an EndpointDescription, meaning an addPort has not been done for it
0356: // This is an error.
0357: throw ExceptionFactory.makeWebServiceException(Messages
0358: .getMessage("createDispatchFail1", portQName
0359: .toString()));
0360: }
0361: break;
0362: }
0363: return endpointDescription;
0364: }
0365:
0366: private Class getEndpointSEI(QName portQName) {
0367: Class endpointSEI = null;
0368: EndpointDescription endpointDesc = getEndpointDescription(portQName);
0369: if (endpointDesc != null) {
0370: EndpointInterfaceDescription endpointInterfaceDesc = endpointDesc
0371: .getEndpointInterfaceDescription();
0372: if (endpointInterfaceDesc != null) {
0373: endpointSEI = endpointInterfaceDesc.getSEIClass();
0374: }
0375: }
0376: return endpointSEI;
0377: }
0378:
0379: private boolean isPortDeclared(QName portQName) {
0380: // TODO: This needs to account for declaration of the port via annotations in addition to just WSDL
0381: // TODO: Add logic to check the portQN namespace against the WSDL Definition NS
0382: boolean portIsDeclared = false;
0383: if (!DescriptionUtils.isEmpty(portQName)) {
0384: if (getWSDLWrapper() != null) {
0385: Definition wsdlDefn = getWSDLWrapper().getDefinition();
0386: Service wsdlService = wsdlDefn.getService(serviceQName);
0387: Port wsdlPort = wsdlService.getPort(portQName
0388: .getLocalPart());
0389: portIsDeclared = (wsdlPort != null);
0390: } else {
0391: // TODO: Add logic to determine if port is declared via annotations when no WSDL is present. For now, we have to assume it is declared
0392: // so getPort(...) and createDispatch(...) calls work when there is no WSDL.
0393: portIsDeclared = true;
0394: }
0395: } else {
0396: // PortQName is null, so the runtime gets to choose which one to use. Since there's no WSDL
0397: // we'll use annotations, so it is implicitly declared
0398: portIsDeclared = true;
0399: }
0400: return portIsDeclared;
0401: }
0402:
0403: /* (non-Javadoc)
0404: * @see org.apache.axis2.jaxws.description.ServiceDescription#getEndpointDescriptions()
0405: */
0406: public EndpointDescription[] getEndpointDescriptions() {
0407: return endpointDescriptions.values().toArray(
0408: new EndpointDescriptionImpl[0]);
0409: }
0410:
0411: public Collection<EndpointDescription> getEndpointDescriptions_AsCollection() {
0412: return endpointDescriptions.values();
0413: }
0414:
0415: /* (non-Javadoc)
0416: * @see org.apache.axis2.jaxws.description.ServiceDescription#getEndpointDescription(javax.xml.namespace.QName)
0417: */
0418: public EndpointDescription getEndpointDescription(QName portQName) {
0419: EndpointDescription returnDesc = null;
0420: if (!DescriptionUtils.isEmpty(portQName)) {
0421: returnDesc = endpointDescriptions.get(portQName);
0422: }
0423: return returnDesc;
0424: }
0425:
0426: EndpointDescriptionImpl getEndpointDescriptionImpl(QName portQName) {
0427: return (EndpointDescriptionImpl) getEndpointDescription(portQName);
0428: }
0429:
0430: EndpointDescriptionImpl getEndpointDescriptionImpl(Class seiClass) {
0431: for (EndpointDescription endpointDescription : endpointDescriptions
0432: .values()) {
0433: EndpointInterfaceDescription endpointInterfaceDesc = endpointDescription
0434: .getEndpointInterfaceDescription();
0435: // Note that Dispatch endpoints will not have an endpointInterface because the do not have an associated SEI
0436: if (endpointInterfaceDesc != null) {
0437: Class endpointSEIClass = endpointInterfaceDesc
0438: .getSEIClass();
0439: if (endpointSEIClass != null
0440: && endpointSEIClass.equals(seiClass)) {
0441: return (EndpointDescriptionImpl) endpointDescription;
0442: }
0443: }
0444: }
0445: return null;
0446: }
0447:
0448: DescriptionBuilderComposite getDescriptionBuilderComposite() {
0449: return composite;
0450: }
0451:
0452: /* (non-Javadoc)
0453: * @see org.apache.axis2.jaxws.description.ServiceDescription#getEndpointDescription(java.lang.Class)
0454: */
0455: public EndpointDescription[] getEndpointDescription(Class seiClass) {
0456: EndpointDescription[] returnEndpointDesc = null;
0457: ArrayList<EndpointDescriptionImpl> matchingEndpoints = new ArrayList<EndpointDescriptionImpl>();
0458: for (EndpointDescription endpointDescription : endpointDescriptions
0459: .values()) {
0460: EndpointInterfaceDescription endpointInterfaceDesc = endpointDescription
0461: .getEndpointInterfaceDescription();
0462: // Note that Dispatch endpoints will not have an endpointInterface because the do not have an associated SEI
0463: if (endpointInterfaceDesc != null) {
0464: Class endpointSEIClass = endpointInterfaceDesc
0465: .getSEIClass();
0466: if (endpointSEIClass != null
0467: && endpointSEIClass.equals(seiClass)) {
0468: matchingEndpoints
0469: .add((EndpointDescriptionImpl) endpointDescription);
0470: }
0471: }
0472: }
0473: if (matchingEndpoints.size() > 0) {
0474: returnEndpointDesc = matchingEndpoints
0475: .toArray(new EndpointDescriptionImpl[0]);
0476: }
0477: return returnEndpointDesc;
0478: }
0479:
0480: /*
0481: * @return True - if we are processing with the DBC List instead of reflection
0482: */
0483: boolean isDBCMap() {
0484: if (dbcMap == null)
0485: return false;
0486: else
0487: return true;
0488: }
0489:
0490: // END of public accessor methods
0491: /*=======================================================================*/
0492: /*=======================================================================*/
0493: private void addEndpointDescription(EndpointDescriptionImpl endpoint) {
0494: endpointDescriptions.put(endpoint.getPortQName(), endpoint);
0495: }
0496:
0497: private void setupWsdlDefinition() {
0498: // Note that there may be no WSDL provided, for example when called from
0499: // Service.create(QName serviceName).
0500:
0501: if (isDBCMap()) {
0502:
0503: // Currently, there is a bug which allows the wsdlDefinition to be placed
0504: // on either the impl class composite or the sei composite, or both. We need to
0505: // look in both places and find the correct one, if it exists.
0506:
0507: if (((composite.getWebServiceAnnot() != null) && DescriptionUtils
0508: .isEmpty(composite.getWebServiceAnnot()
0509: .endpointInterface()))
0510: || (!(composite.getWebServiceProviderAnnot() == null))) {
0511: //This is either an implicit SEI, or a WebService Provider
0512: if (composite.getWsdlDefinition() != null) {
0513: this .wsdlURL = composite.getWsdlURL();
0514:
0515: try {
0516: this .wsdlWrapper = new WSDL4JWrapper(
0517: this .wsdlURL, composite
0518: .getWsdlDefinition());
0519: } catch (WSDLException e) {
0520: throw ExceptionFactory.makeWebServiceException(
0521: Messages.getMessage("wsdlException", e
0522: .getMessage()), e);
0523: }
0524: }
0525:
0526: } else if (composite.getWebServiceAnnot() != null) {
0527: //This impl class specifies an SEI...this is a special case. There is a bug
0528: //in the tooling that allows for the wsdllocation to be specifed on either the
0529: //impl. class, or the SEI, or both. So, we need to look for the wsdl as follows:
0530: // 1. If the Wsdl exists on the SEI, then check for it on the impl.
0531: // 2. If it is not found in either location, in that order, then generate
0532:
0533: DescriptionBuilderComposite seic = getDBCMap().get(
0534: composite.getWebServiceAnnot()
0535: .endpointInterface());
0536:
0537: try {
0538: if (seic.getWsdlDefinition() != null) {
0539: //set the sdimpl from the SEI composite
0540: this .wsdlURL = seic.getWsdlURL();
0541: this .wsdlWrapper = new WSDL4JWrapper(seic
0542: .getWsdlURL(), seic.getWsdlDefinition());
0543: } else if (composite.getWsdlDefinition() != null) {
0544: //set the sdimpl from the impl. class composite
0545: this .wsdlURL = composite.getWsdlURL();
0546: this .wsdlWrapper = new WSDL4JWrapper(composite
0547: .getWsdlURL(), composite
0548: .getWsdlDefinition());
0549: }
0550: } catch (WSDLException e) {
0551: throw ExceptionFactory.makeWebServiceException(
0552: Messages.getMessage("wsdlException", e
0553: .getMessage()), e);
0554: }
0555: }
0556:
0557: //Deprecate this code block when MDQ is fully integrated
0558: } else if (wsdlURL != null) {
0559: try {
0560: this .wsdlWrapper = new WSDL4JWrapper(this .wsdlURL);
0561: } catch (FileNotFoundException e) {
0562: throw ExceptionFactory.makeWebServiceException(Messages
0563: .getMessage("wsdlNotFoundErr", e.getMessage()),
0564: e);
0565: } catch (UnknownHostException e) {
0566: throw ExceptionFactory.makeWebServiceException(Messages
0567: .getMessage("unknownHost", e.getMessage()), e);
0568: } catch (ConnectException e) {
0569: throw ExceptionFactory.makeWebServiceException(
0570: Messages.getMessage("connectionRefused", e
0571: .getMessage()), e);
0572: } catch (IOException e) {
0573: throw ExceptionFactory.makeWebServiceException(Messages
0574: .getMessage("urlStream", e.getMessage()), e);
0575: } catch (WSDLException e) {
0576: throw ExceptionFactory
0577: .makeWebServiceException(Messages.getMessage(
0578: "wsdlException", e.getMessage()), e);
0579: }
0580: }
0581: }
0582:
0583: // TODO: Remove these and replace with appropraite get* methods for WSDL information
0584: /* (non-Javadoc)
0585: * @see org.apache.axis2.jaxws.description.ServiceDescriptionWSDL#getWSDLWrapper()
0586: */
0587: public WSDLWrapper getWSDLWrapper() {
0588: return wsdlWrapper;
0589: }
0590:
0591: /* (non-Javadoc)
0592: * @see org.apache.axis2.jaxws.description.ServiceDescriptionWSDL#getWSDLLocation()
0593: */
0594: public URL getWSDLLocation() {
0595: return wsdlURL;
0596: }
0597:
0598: /**
0599: * TODO: This method should be replaced with specific methods for getWSDLGenerated... similar to
0600: * how getWsdlWrapper should be replaced.
0601: */
0602: public WSDLWrapper getGeneratedWsdlWrapper() {
0603: return this .generatedWsdlWrapper;
0604: }
0605:
0606: void setAxisConfigContext(ConfigurationContext config) {
0607: this .configContext = config;
0608: }
0609:
0610: /* (non-Javadoc)
0611: * @see org.apache.axis2.jaxws.description.ServiceDescription#getAxisConfigContext()
0612: */
0613: public ConfigurationContext getAxisConfigContext() {
0614: if (configContext == null) {
0615: configContext = getClientConfigurationFactory()
0616: .getClientConfigurationContext();
0617: }
0618: return configContext;
0619:
0620: }
0621:
0622: ClientConfigurationFactory getClientConfigurationFactory() {
0623:
0624: if (clientConfigFactory == null) {
0625: clientConfigFactory = DescriptionFactory
0626: .createClientConfigurationFactory();
0627: }
0628: return clientConfigFactory;
0629: }
0630:
0631: /* (non-Javadoc)
0632: * @see org.apache.axis2.jaxws.description.ServiceDescription#getServiceClient(javax.xml.namespace.QName)
0633: */
0634: public ServiceClient getServiceClient(QName portQName) {
0635: ServiceClient returnServiceClient = null;
0636: if (!DescriptionUtils.isEmpty(portQName)) {
0637: EndpointDescription endpointDesc = getEndpointDescription(portQName);
0638: if (endpointDesc != null) {
0639: returnServiceClient = endpointDesc.getServiceClient();
0640: } else {
0641: // Couldn't find Endpoint Description for port QName
0642: if (log.isDebugEnabled()) {
0643: log.debug("Could not find portQName: " + portQName
0644: + " under ServiceDescription: "
0645: + toString());
0646: }
0647: }
0648: } else {
0649: // PortQName is empty
0650: if (log.isDebugEnabled()) {
0651: log
0652: .debug("PortQName agrument is invalid; it can not be null or an empty string: "
0653: + portQName);
0654: }
0655: }
0656:
0657: return returnServiceClient;
0658: }
0659:
0660: /* (non-Javadoc)
0661: * @see org.apache.axis2.jaxws.description.ServiceDescription#getServiceQName()
0662: */
0663: public QName getServiceQName() {
0664: //It is assumed that this will always be set in the constructor rather than
0665: //built up from the class or DBC
0666: return serviceQName;
0667: }
0668:
0669: void setServiceQName(QName theName) {
0670: serviceQName = theName;
0671: }
0672:
0673: public boolean isServerSide() {
0674: return isServerSide;
0675: }
0676:
0677: HashMap<String, DescriptionBuilderComposite> getDBCMap() {
0678: return dbcMap;
0679: }
0680:
0681: void setGeneratedWsdlWrapper(WSDL4JWrapper wrapper) {
0682: this .generatedWsdlWrapper = wrapper;
0683: }
0684:
0685: void setWsdlWrapper(WSDL4JWrapper wrapper) {
0686: this .wsdlWrapper = wrapper;
0687: }
0688:
0689: private void validateDBCLIntegrity() {
0690:
0691: //First, check the integrity of this input composite
0692: //and retrieve
0693: //the composite that represents this impl
0694:
0695: //TODO: Currently, we are calling this method on the DBC. However, the DBC
0696: //will eventually need access to to the whole DBC map to do proper validation.
0697: //We don't want to pass the map of DBC's back into a single DBC.
0698: //So, for starters, this method and all the privates that it calls should be
0699: // moved to here. At some point, we should consider using a new class that we
0700: //can implement scenarios of, like validateServiceImpl implements validator
0701:
0702: try {
0703: validateIntegrity();
0704: } catch (Exception ex) {
0705: if (log.isDebugEnabled()) {
0706: log.debug("Validation phase 1 failure: "
0707: + ex.toString(), ex);
0708: log.debug("Failing composite: " + composite.toString());
0709: }
0710: throw ExceptionFactory.makeWebServiceException(
0711: "Validation Exception " + ex, ex);
0712: }
0713: }
0714:
0715: /*
0716: * Validates the integrity of an impl. class. This should not be called directly for an SEI composite
0717: */
0718: void validateIntegrity() {
0719: //TODO: Consider moving this to a utils area, do we really want a public
0720: // method that checks integrity...possibly
0721:
0722: //In General, this integrity checker should do gross level checking
0723: //It should not be setting spec-defined default values, but can look
0724: //at things like empty strings or null values
0725:
0726: //TODO: This method will validate the integrity of this object. Basically, if
0727: //consumer set this up improperly, then we should fail fast, should consider placing
0728: //this method in a utils class within the 'description' package
0729:
0730: //Verify that, if this implements a strongly typed provider interface, that it
0731: // also contain a WebServiceProvider annotation per JAXWS Sec. 5.1
0732: Iterator<String> iter = composite.getInterfacesList()
0733: .iterator();
0734:
0735: // Remember if we've validated the Provider interface. Later we'll make sure that if we have an
0736: // WebServiceProvider annotation, we found a valid interface here.
0737: boolean providerInterfaceValid = false;
0738: while (iter.hasNext()) {
0739: String interfaceString = iter.next();
0740: if (interfaceString.equals(MDQConstants.PROVIDER_SOURCE)
0741: || interfaceString
0742: .equals(MDQConstants.PROVIDER_SOAP)
0743: || interfaceString
0744: .equals(MDQConstants.PROVIDER_DATASOURCE)
0745: || interfaceString
0746: .equals(MDQConstants.PROVIDER_STRING)) {
0747: providerInterfaceValid = true;
0748: //This is a provider based endpoint, make sure the annotation exists
0749: if (composite.getWebServiceProviderAnnot() == null) {
0750: // TODO: RAS/NLS
0751: throw ExceptionFactory
0752: .makeWebServiceException("Validation error: This is a Provider based endpoint that does not contain a WebServiceProvider annotation. Provider class: "
0753: + composite.getClassName());
0754: }
0755: }
0756: }
0757:
0758: //Verify that WebService and WebServiceProvider are not both specified
0759: //per JAXWS - Sec. 7.7
0760: if (composite.getWebServiceAnnot() != null
0761: && composite.getWebServiceProviderAnnot() != null) {
0762: // TODO: RAS/NLS
0763: throw ExceptionFactory
0764: .makeWebServiceException("Validation error: WebService annotation and WebServiceProvider annotation cannot coexist. Implementation class: "
0765: + composite.getClassName());
0766: }
0767:
0768: if (composite.getWebServiceProviderAnnot() != null) {
0769: if (!providerInterfaceValid) {
0770: // TODO: RAS/NLS
0771: throw ExceptionFactory
0772: .makeWebServiceException("Validation error: This is a Provider that does not specify a valid Provider interface. Implementation class: "
0773: + composite.getClassName());
0774: }
0775: // There must be a public default constructor per JAXWS - Sec 5.1
0776: if (!validateDefaultConstructor()) {
0777: // TODO: RAS/NLS
0778: throw ExceptionFactory
0779: .makeWebServiceException("Validation error: Provider must have a public default constructor. Implementation class: "
0780: + composite.getClassName());
0781: }
0782: // There must be an invoke method per JAXWS - Sec 5.1.1
0783: if (!validateInvokeMethod()) {
0784: // TODO: RAS/NLS
0785: throw ExceptionFactory
0786: .makeWebServiceException("Validation error: Provider must have a public invoke method. Implementation class: "
0787: + composite.getClassName());
0788: }
0789:
0790: //If ServiceMode annotation specifies 'payload', then make sure that it is not typed with
0791: // SOAPMessage or DataSource
0792: validateProviderInterfaces();
0793:
0794: } else if (composite.getWebServiceAnnot() != null) {
0795:
0796: if (composite.getServiceModeAnnot() != null) {
0797: // TODO: RAS/NLS
0798: throw ExceptionFactory
0799: .makeWebServiceException("Validation error: ServiceMode annotation can only be specified for WebServiceProvider. Implementation class: "
0800: + composite.getClassName());
0801: }
0802:
0803: //TODO: hmmm, will we ever actually validate an interface directly...don't think so
0804: if (!composite.isInterface()) {
0805: // TODO: Validate on the class that this.classModifiers Array does not contain the strings
0806: // FINAL or ABSTRACT, but does contain PUBLIC
0807: // TODO: Validate on the class that a public constructor exists
0808: // TODO: Validate on the class that a finalize() method does not exist
0809: if (!DescriptionUtils.isEmpty(composite
0810: .getWebServiceAnnot().wsdlLocation())) {
0811: if (composite.getWsdlDefinition() == null
0812: && composite.getWsdlURL() == null) {
0813: // TODO: RAS/NLS
0814: throw ExceptionFactory
0815: .makeWebServiceException("Validation error: cannot find WSDL Definition specified by this WebService annotation. Implementation class: "
0816: + composite.getClassName()
0817: + "; WSDL location: "
0818: + composite
0819: .getWebServiceAnnot()
0820: .wsdlLocation());
0821: }
0822: }
0823:
0824: // setWebServiceAnnotDefaults(true=impl); Must happen before we start checking annot
0825: if (!DescriptionUtils.isEmpty(composite
0826: .getWebServiceAnnot().endpointInterface())) {
0827:
0828: DescriptionBuilderComposite seic = dbcMap
0829: .get(composite.getWebServiceAnnot()
0830: .endpointInterface());
0831:
0832: //Verify that we can find the SEI in the composite list
0833: if (seic == null) {
0834: // TODO: RAS/NLS
0835: throw ExceptionFactory
0836: .makeWebServiceException("Validation error: cannot find SEI specified by the WebService.endpointInterface. Implementaiton class: "
0837: + composite.getClassName()
0838: + "; EndpointInterface: "
0839: + composite
0840: .getWebServiceAnnot()
0841: .endpointInterface());
0842: }
0843:
0844: // Verify that the only class annotations are WebService and HandlerChain
0845: // (per JSR181 Sec. 3.1). Note that this applies to JSR-181 annotations; the restriction
0846: // does not apply to JSR-224 annotations such as BindingType
0847: if (composite.getSoapBindingAnnot() != null
0848: || composite.getWebFaultAnnot() != null
0849: || composite.getWebServiceClientAnnot() != null
0850: || composite.getWebServiceContextAnnot() != null
0851: || !composite.getAllWebServiceRefAnnots()
0852: .isEmpty()) {
0853: // TODO: RAS/NLS
0854: throw ExceptionFactory
0855: .makeWebServiceException("Validation error: invalid annotations specified when WebService annotation specifies an endpoint interface. Implemntation class: "
0856: + composite.getClassName());
0857: }
0858:
0859: //Verify that WebService annotation does not contain a name attribute
0860: //(per JSR181 Sec. 3.1)
0861: if (!DescriptionUtils.isEmpty(composite
0862: .getWebServiceAnnot().name())) {
0863: // TODO: RAS/NLS
0864: throw ExceptionFactory
0865: .makeWebServiceException("Validation error: WebService.name must not be specified when the bean specifies an endpoint interface. Implentation class: "
0866: + composite.getClassName()
0867: + "; WebService.name: "
0868: + composite
0869: .getWebServiceAnnot()
0870: .name());
0871: }
0872:
0873: validateSEI(seic);
0874: //Verify that that this implementation class implements all methods in the interface
0875: validateImplementation(seic);
0876:
0877: //Verify that this impl. class does not contain any @WebMethod annotations
0878: if (webMethodAnnotationsExist()) {
0879: // TODO: RAS/NLS
0880: throw ExceptionFactory
0881: .makeWebServiceException("Validation error: WebMethod annotations cannot exist on implentation when WebService.endpointInterface is set. Implementation class: "
0882: + composite.getClassName());
0883: }
0884:
0885: } else { //this is an implicit SEI (i.e. impl w/out endpointInterface
0886:
0887: checkImplicitSEIAgainstWSDL();
0888: // TODO: Call ValidateWebMethodAnnots()
0889: // - this method will check that all methods are public - ???
0890: //
0891: }
0892: } else { //this is an interface...we should not be processing interfaces here
0893: // TODO: RAS/NLS
0894: throw ExceptionFactory
0895: .makeWebServiceException("Validation error: Improper usage: cannot invoke this method with an interface. Implementation class: "
0896: + composite.getClassName());
0897: }
0898:
0899: // Verify that the SOAPBinding annotations are supported.
0900: //REVIEW: The assumption here is that SOAPBinding annotation can be place on an impl. class
0901: // (implicit SEI) or a WebServiceProvider
0902: if (composite.getSoapBindingAnnot() != null) {
0903: if (composite.getSoapBindingAnnot().use() == javax.jws.soap.SOAPBinding.Use.ENCODED) {
0904: throw ExceptionFactory
0905: .makeWebServiceException("Validation error: Unsupported SOAPBinding annotation value. The ENCODED setting is not supported for SOAPBinding.Use. Implementation class: "
0906: + composite.getClassName());
0907: }
0908: }
0909:
0910: //TODO: don't think this is necessary
0911: checkMethodsAgainstWSDL();
0912: }
0913: }
0914:
0915: /**
0916: * Validate there is an invoke method on the composite.
0917: *
0918: * @return
0919: */
0920: private boolean validateInvokeMethod() {
0921: boolean validInvokeMethod = false;
0922: List<MethodDescriptionComposite> invokeMethodList = composite
0923: .getMethodDescriptionComposite("invoke");
0924: if (invokeMethodList != null && !invokeMethodList.isEmpty()) {
0925: validInvokeMethod = true;
0926: }
0927: return validInvokeMethod;
0928: }
0929:
0930: /**
0931: * Validate that, if using PAYLOAD mode, then interfaces list cannot contain SOAPMessage or
0932: * DataSource
0933: *
0934: * @return
0935: */
0936: private void validateProviderInterfaces() {
0937:
0938: // Default for ServiceMode is 'PAYLOAD'. So, if it is specified (explicitly or
0939: // implicitly) then verify that we are not implementing improper interfaces)
0940: if ((composite.getServiceModeAnnot() == null)
0941: || composite.getServiceModeAnnot().value() == javax.xml.ws.Service.Mode.PAYLOAD) {
0942:
0943: Iterator<String> iter = composite.getInterfacesList()
0944: .iterator();
0945:
0946: while (iter.hasNext()) {
0947: String interfaceString = iter.next();
0948: if (interfaceString.equals(MDQConstants.PROVIDER_SOAP)
0949: || interfaceString
0950: .equals(MDQConstants.PROVIDER_DATASOURCE)) {
0951:
0952: throw ExceptionFactory
0953: .makeWebServiceException("Validation error: SOAPMessage and DataSource objects cannot be used when ServiceMode specifies PAYLOAD. Implementation class: "
0954: + composite.getClassName());
0955: }
0956: }
0957:
0958: } else {
0959: // We are in MESSAGE mode
0960: // Conformance: JAXWS Spec.- Sec. 4.3 (javax.activation.DataSource)
0961:
0962: // REVIEW: Should the provider interface validation be moved to post-construction validation,
0963: // since it seems that the logic to understand the default values for binding type
0964: // (see comment below) should be left to the creation of the Description objects.
0965: String bindingType = null;
0966: if (composite.getBindingTypeAnnot() != null) {
0967: bindingType = composite.getBindingTypeAnnot().value();
0968: }
0969:
0970: Iterator<String> iter = composite.getInterfacesList()
0971: .iterator();
0972:
0973: while (iter.hasNext()) {
0974: String interfaceString = iter.next();
0975:
0976: if (interfaceString.equals(MDQConstants.PROVIDER_SOAP)) {
0977:
0978: // Make sure BindingType is SOAP/HTTP with SOAPMessage
0979: // object, Default for Binding Type is SOAP/HTTP
0980: if (!DescriptionUtils.isEmpty(bindingType)
0981: && !bindingType
0982: .equals(SOAPBinding.SOAP11HTTP_BINDING)
0983: && !bindingType
0984: .equals(SOAPBinding.SOAP11HTTP_MTOM_BINDING)
0985: && !bindingType
0986: .equals(SOAPBinding.SOAP12HTTP_BINDING)
0987: && !bindingType
0988: .equals(SOAPBinding.SOAP12HTTP_MTOM_BINDING))
0989:
0990: throw ExceptionFactory
0991: .makeWebServiceException("Validation error: SOAPMessage objects cannot be used with HTTP binding type. Implementation class: "
0992: + composite.getClassName());
0993:
0994: } else if (interfaceString
0995: .equals(MDQConstants.PROVIDER_DATASOURCE)) {
0996:
0997: // Make sure BindingType is XML/HTTP with DataSource object
0998: if (DescriptionUtils.isEmpty(bindingType)
0999: || !bindingType
1000: .equals(javax.xml.ws.http.HTTPBinding.HTTP_BINDING))
1001:
1002: throw ExceptionFactory
1003: .makeWebServiceException("Validation error: DataSource objects must be used with HTTP binding type. Implementation class: "
1004: + composite.getClassName());
1005: }
1006: }
1007: }
1008: }
1009:
1010: /**
1011: * Validate there is a default no-argument constructor on the composite.
1012: *
1013: * @return
1014: */
1015: private boolean validateDefaultConstructor() {
1016: boolean validDefaultCtor = false;
1017: List<MethodDescriptionComposite> constructorList = composite
1018: .getMethodDescriptionComposite("<init>");
1019: if (constructorList != null && !constructorList.isEmpty()) {
1020: // There are public constructors; make sure there is one that takes no arguments.
1021: for (MethodDescriptionComposite checkCtor : constructorList) {
1022: List<ParameterDescriptionComposite> paramList = checkCtor
1023: .getParameterDescriptionCompositeList();
1024: if (paramList == null || paramList.isEmpty()) {
1025: validDefaultCtor = true;
1026: break;
1027: }
1028: }
1029: }
1030:
1031: return validDefaultCtor;
1032: }
1033:
1034: private void validateImplementation(DescriptionBuilderComposite seic) {
1035: /*
1036: * Verify that an impl class implements all the methods of the SEI. We
1037: * have to verify this because an impl class is not required to actually use
1038: * the 'implements' clause. So, if it doesn't, the Java compiler won't
1039: * catch it. Don't need to worry about chaining because only one EndpointInterface
1040: * can be specified, and the SEI cannot specify an EndpointInterface, so the Java
1041: * compiler will take care of everything else.
1042: */
1043:
1044: HashMap<String, MethodDescriptionComposite> compositeHashMap = new HashMap<String, MethodDescriptionComposite>();
1045: Iterator<MethodDescriptionComposite> compIterator = composite
1046: .getMethodDescriptionsList().iterator();
1047: while (compIterator.hasNext()) {
1048: MethodDescriptionComposite mdc = compIterator.next();
1049: compositeHashMap.put(mdc.getMethodName(), mdc);
1050: }
1051: // Add methods declared in the implementation's superclass
1052: addSuperClassMethods(compositeHashMap, composite);
1053:
1054: HashMap<String, MethodDescriptionComposite> seiMethodHashMap = new HashMap<String, MethodDescriptionComposite>();
1055: Iterator<MethodDescriptionComposite> seiMethodIterator = seic
1056: .getMethodDescriptionsList().iterator();
1057: while (seiMethodIterator.hasNext()) {
1058: MethodDescriptionComposite mdc = seiMethodIterator.next();
1059: seiMethodHashMap.put(mdc.getMethodName(), mdc);
1060: }
1061: // Add any methods declared in superinterfaces of the SEI
1062: addSuperClassMethods(seiMethodHashMap, seic);
1063:
1064: // Make sure all the methods in the SEI (including any inherited from superinterfaces) are
1065: // implemented by the bean (including inherited methods on the bean).
1066: Iterator<MethodDescriptionComposite> verifySEIIterator = seiMethodHashMap
1067: .values().iterator();
1068: while (verifySEIIterator.hasNext()) {
1069: MethodDescriptionComposite mdc = verifySEIIterator.next();
1070: // TODO: This does not take into consideration overloaded java methods!
1071: MethodDescriptionComposite implMDC = compositeHashMap
1072: .get(mdc.getMethodName());
1073:
1074: if (implMDC == null) {
1075: // TODO: RAS/NLS
1076: throw ExceptionFactory
1077: .makeWebServiceException("Validation error: Implementation subclass does not implement method on specified interface. Implementation class: "
1078: + composite.getClassName()
1079: + "; missing method name: "
1080: + mdc.getMethodName()
1081: + "; endpointInterface: "
1082: + seic.getClassName());
1083: } else {
1084: //At least we found it, now make sure that signatures match up
1085:
1086: //Check for exception and signature matching
1087: validateMethodExceptions(mdc, implMDC, seic
1088: .getClassName());
1089: validateMethodReturnValue(mdc, implMDC, seic
1090: .getClassName());
1091: validateMethodParameters(mdc, implMDC, seic
1092: .getClassName());
1093: }
1094: }
1095: }
1096:
1097: private void validateMethodParameters(
1098: MethodDescriptionComposite seiMDC,
1099: MethodDescriptionComposite implMDC, String className) {
1100: List<ParameterDescriptionComposite> seiPDCList = seiMDC
1101: .getParameterDescriptionCompositeList();
1102: List<ParameterDescriptionComposite> implPDCList = implMDC
1103: .getParameterDescriptionCompositeList();
1104: if ((seiPDCList == null || seiPDCList.isEmpty())
1105: && (implPDCList == null || implPDCList.isEmpty())) {
1106: // There are no parameters on the SEI or the impl; all is well
1107: } else if ((seiPDCList == null || seiPDCList.isEmpty())
1108: && !(implPDCList == null || implPDCList.isEmpty())) {
1109: String message = "Validation error: SEI indicates no parameters but implementation method specifies parameters: "
1110: + implPDCList
1111: + "; Implementation class: "
1112: + composite.getClassName()
1113: + "; Method name: "
1114: + seiMDC.getMethodName()
1115: + "; Endpoint Interface: "
1116: + className;
1117: throw ExceptionFactory.makeWebServiceException(message);
1118: } else if ((seiPDCList != null && !seiPDCList.isEmpty())
1119: && !(implPDCList != null && !implPDCList.isEmpty())) {
1120: String message = "Validation error: SEI indicates parameters "
1121: + seiPDCList
1122: + " but implementation method specifies no parameters; Implementation class: "
1123: + composite.getClassName()
1124: + "; Method name: "
1125: + seiMDC.getMethodName()
1126: + "; Endpoint Interface: "
1127: + className;
1128: throw ExceptionFactory.makeWebServiceException(message);
1129: } else if (seiPDCList.size() != implPDCList.size()) {
1130: String message = "Validation error: The number of parameters on the SEI method ("
1131: + seiPDCList.size()
1132: + ") does not match the number of parameters on the implementation ( "
1133: + implPDCList.size()
1134: + "); Implementation class: "
1135: + composite.getClassName()
1136: + "; Method name: "
1137: + seiMDC.getMethodName()
1138: + "; Endpoint Interface: "
1139: + className;
1140: throw ExceptionFactory.makeWebServiceException(message);
1141:
1142: } else {
1143: // Make sure the order and type of parameters match
1144: // REVIEW: This checks for strict equality of the fully qualified
1145: // type. It does not
1146: // take into consideration object hierachy. For example foo(Animal)
1147: // will not equal bar(Zebra)
1148: boolean parametersMatch = true;
1149: String failingMessage = null;
1150: for (int paramNumber = 0; paramNumber < seiPDCList.size(); paramNumber++) {
1151: String seiParamType = seiPDCList.get(paramNumber)
1152: .getParameterType();
1153: String implParamType = implPDCList.get(paramNumber)
1154: .getParameterType();
1155: if (!seiParamType.equals(implParamType)) {
1156: parametersMatch = false;
1157: failingMessage = "Validation error: SEI and implementation parameters do not match. Parameter number "
1158: + paramNumber
1159: + " on the SEI is "
1160: + seiParamType
1161: + "; on the implementation it is "
1162: + implParamType
1163: + "; Implementation class: "
1164: + composite.getClassName()
1165: + "; Method name: "
1166: + seiMDC.getMethodName()
1167: + "; Endpoint Interface: " + className;
1168: break;
1169: }
1170: }
1171: if (!parametersMatch) {
1172: throw ExceptionFactory
1173: .makeWebServiceException(failingMessage);
1174: }
1175: }
1176: }
1177:
1178: private void validateMethodReturnValue(
1179: MethodDescriptionComposite seiMDC,
1180: MethodDescriptionComposite implMDC, String className) {
1181: String seiReturnValue = seiMDC.getReturnType();
1182: String implReturnValue = implMDC.getReturnType();
1183:
1184: if (seiReturnValue == null && implReturnValue == null) {
1185: // Neither specify a return value; all is well
1186: } else if (seiReturnValue == null && implReturnValue != null) {
1187: String message = "Validation error: SEI indicates no return value but implementation method specifies return value: "
1188: + implReturnValue
1189: + "; Implementation class: "
1190: + composite.getClassName()
1191: + "; Method name: "
1192: + seiMDC.getMethodName()
1193: + "; Endpoint Interface: "
1194: + className;
1195: throw ExceptionFactory.makeWebServiceException(message);
1196: } else if (seiReturnValue != null && implReturnValue == null) {
1197: String message = "Validation error: SEI indicates return value "
1198: + seiReturnValue
1199: + " but implementation method specifies no return value; Implementation class: "
1200: + composite.getClassName()
1201: + "; Method name: "
1202: + seiMDC.getMethodName()
1203: + "; Endpoint Interface: "
1204: + className;
1205: throw ExceptionFactory.makeWebServiceException(message);
1206: } else if (!seiReturnValue.equals(implReturnValue)) {
1207: String message = "Validation error: SEI return value "
1208: + seiReturnValue
1209: + " does not match implementation method return value "
1210: + implReturnValue + "; Implementation class: "
1211: + composite.getClassName() + "; Method name: "
1212: + seiMDC.getMethodName() + "; Endpoint Interface: "
1213: + className;
1214: throw ExceptionFactory.makeWebServiceException(message);
1215: }
1216:
1217: }
1218:
1219: private void validateMethodExceptions(
1220: MethodDescriptionComposite seiMDC,
1221: MethodDescriptionComposite implMDC, String className) {
1222:
1223: String[] seiExceptions = seiMDC.getExceptions();
1224: String[] implExceptions = implMDC.getExceptions();
1225:
1226: // An impl can choose to throw fewer checked exceptions than declared on the SEI, but not more.
1227: // This is analagous to the Java rules for interfaces.
1228: if (seiExceptions == null) {
1229: if (implExceptions == null) {
1230: return;
1231: } else {
1232: // SEI delcares no checked exceptions, but the implementation has checked exceptions, which is an error
1233: throw ExceptionFactory
1234: .makeWebServiceException("Validation error: Implementation method signature has more checked exceptions than SEI method signature (0): Implementation class: "
1235: + composite.getClassName()
1236: + "; method name: "
1237: + seiMDC.getMethodName()
1238: + "; endpointInterface: " + className);
1239: }
1240: } else if (implExceptions == null) {
1241: // Implementation throws fewer checked exceptions than SEI, which is OK.
1242: return;
1243: }
1244:
1245: // Check the list length; An implementation can not declare more exceptions than the SEI
1246: if (seiExceptions.length < implExceptions.length) {
1247: throw ExceptionFactory
1248: .makeWebServiceException("Validation error: Implementation method signature has more checked exceptions ("
1249: + implExceptions.length
1250: + ") than SEI method signature ("
1251: + seiExceptions.length
1252: + "): Implementation class: "
1253: + composite.getClassName()
1254: + "; method name: "
1255: + seiMDC.getMethodName()
1256: + "; endpointInterface: " + className);
1257: }
1258:
1259: // Make sure that each checked exception declared by the
1260: // implementation is on the SEI also
1261: if (implExceptions.length > 0) {
1262: for (String implException : implExceptions) {
1263: boolean foundIt = false;
1264: if (seiExceptions.length > 0) {
1265: for (String seiException : seiExceptions) {
1266: if (seiException.equals(implException)) {
1267: foundIt = true;
1268: break;
1269: }
1270: }
1271: }
1272:
1273: if (!foundIt) {
1274: throw ExceptionFactory
1275: .makeWebServiceException("Validation error: Implementation method signature throws exception "
1276: + implException
1277: + "which is not declared on the SEI method signature: Implementation class: "
1278: + composite.getClassName()
1279: + "; method name: "
1280: + seiMDC.getMethodName()
1281: + "; endpointInterface: "
1282: + className);
1283: }
1284: }
1285: }
1286:
1287: }
1288:
1289: /**
1290: * Adds any methods declared in superclasses to the HashMap. The hierachy starting with the DBC
1291: * will be walked up recursively, adding methods from each parent DBC encountered.
1292: * <p/>
1293: * Note that this can be used for either classes or interfaces.
1294: *
1295: * @param methodMap
1296: * @param dbc
1297: */
1298: private void addSuperClassMethods(HashMap methodMap,
1299: DescriptionBuilderComposite dbc) {
1300: DescriptionBuilderComposite super DBC = dbcMap.get(dbc
1301: .getSuperClassName());
1302: if (super DBC != null) {
1303: Iterator<MethodDescriptionComposite> mIter = super DBC
1304: .getMethodDescriptionsList().iterator();
1305: while (mIter.hasNext()) {
1306: MethodDescriptionComposite mdc = mIter.next();
1307: methodMap.put(mdc.getMethodName(), mdc);
1308: }
1309: addSuperClassMethods(methodMap, super DBC);
1310: }
1311: }
1312:
1313: /*
1314: * This method verifies that, if there are any WebMethod with exclude == false, then
1315: * make sure that we find all of those methods represented in the wsdl. However, if
1316: * there are no exclusions == false, or there are no WebMethod annotations, then verify
1317: * that all the public methods are in the wsdl
1318: */
1319: private void checkMethodsAgainstWSDL() {
1320: //Verify that, for ImplicitSEI, that all methods that should exist(if one false found, then
1321: //only look for WebMethods w/ False, else take all public methods but ignore those with
1322: //exclude == true
1323: if (webMethodAnnotationsExist()) {
1324: if (DescriptionUtils.falseExclusionsExist(composite))
1325: verifyFalseExclusionsWithWSDL();
1326: else
1327: verifyPublicMethodsWithWSDL();
1328: } else {
1329: verifyPublicMethodsWithWSDL();
1330: }
1331: }
1332:
1333: private void checkImplicitSEIAgainstWSDL() {
1334:
1335: //TODO: If there is a WSDL, then verify that all WebMethods on this class and in the
1336: // superclasses chain are represented in the WSDL...Look at logic below to make
1337: // sure this really happening
1338:
1339: if (webMethodAnnotationsExist()) {
1340: if (DescriptionUtils.falseExclusionsExist(composite))
1341: verifyFalseExclusionsWithWSDL();
1342: else
1343: verifyPublicMethodsWithWSDL();
1344: } else {
1345: verifyPublicMethodsWithWSDL();
1346: }
1347:
1348: }
1349:
1350: private void checkSEIAgainstWSDL() {
1351: //TODO: Place logic here to verify that each publicMethod with WebMethod annot
1352: // is contained in the WSDL (If there is a WSDL) If we find
1353: // a WebMethod annotation, use its values for looking in the WSDL
1354:
1355: }
1356:
1357: private void validateSEI(DescriptionBuilderComposite seic) {
1358:
1359: //TODO: Validate SEI superclasses -- hmmm, may be doing this below
1360: //
1361: if (seic.getWebServiceAnnot() == null) {
1362: // TODO: RAS & NLS
1363: throw ExceptionFactory
1364: .makeWebServiceException("Validation error: SEI does not contain a WebService annotation. Implementation class: "
1365: + composite.getClassName()
1366: + "; SEI: "
1367: + seic.getClassName());
1368: }
1369: if (!seic.getWebServiceAnnot().endpointInterface().equals("")) {
1370: // TODO: RAS & NLS
1371: throw ExceptionFactory
1372: .makeWebServiceException("Validation error: SEI must not set a value for @WebService.endpointInterface. Implementation class: "
1373: + composite.getClassName()
1374: + "; SEI: "
1375: + seic.getClassName()
1376: + "; Invalid endpointInterface value: "
1377: + seic.getWebServiceAnnot()
1378: .endpointInterface());
1379: }
1380: // Verify that the SOAPBinding annotations are supported.
1381: if (seic.getSoapBindingAnnot() != null) {
1382: if (seic.getSoapBindingAnnot().use() == javax.jws.soap.SOAPBinding.Use.ENCODED) {
1383: throw ExceptionFactory
1384: .makeWebServiceException("Validation error: Unsupported SOAPBinding annotation value. The ENCODED setting is not supported for SOAPBinding.Use. Implementation class: "
1385: + seic.getClassName());
1386: }
1387: }
1388:
1389: checkSEIAgainstWSDL();
1390:
1391: //TODO: More validation here
1392:
1393: //TODO: Make sure we don't find any WebMethod annotations with exclude == true
1394: // anywhere in the superclasses chain
1395:
1396: //TODO: Check that all WebMethod annotations in the superclass chain are represented in
1397: // WSDL, assuming there is WSDL
1398:
1399: //TODO: Validate that the interface is public
1400:
1401: // Call ValidateWebMethodAnnots()
1402: //
1403:
1404: //This will perform validation for all methods, regardless of WebMethod annotations
1405: //It is called for the SEI, and an impl. class that does not specify an endpointInterface
1406: validateMethods(seic.getMethodDescriptionsList());
1407: }
1408:
1409: /** @return Returns TRUE if we find just one WebMethod Annotation */
1410: private boolean webMethodAnnotationsExist() {
1411: MethodDescriptionComposite mdc = null;
1412: Iterator<MethodDescriptionComposite> iter = composite
1413: .getMethodDescriptionsList().iterator();
1414:
1415: while (iter.hasNext()) {
1416: mdc = iter.next();
1417:
1418: if (mdc.getWebMethodAnnot() != null)
1419: return true;
1420: }
1421:
1422: return false;
1423: }
1424:
1425: private void verifyFalseExclusionsWithWSDL() {
1426: //TODO: Place logic here to verify that each exclude==false WebMethod annot we find
1427: // is contained in the WSDL
1428: }
1429:
1430: private void verifyPublicMethodsWithWSDL() {
1431: //TODO: Place logic here to verify that each publicMethod with no WebMethod annot
1432: // is contained in the WSDL
1433:
1434: }
1435:
1436: private void validateMethods(
1437: List<MethodDescriptionComposite> mdcList) {
1438: if (mdcList != null && !mdcList.isEmpty()) {
1439: for (MethodDescriptionComposite mdc : mdcList) {
1440: String returnType = mdc.getReturnType();
1441: if (returnType != null
1442: && (returnType.equals(RETURN_TYPE_FUTURE) || returnType
1443: .equals(RETURN_TYPE_RESPONSE))) {
1444: throw ExceptionFactory
1445: .makeWebServiceException(Messages
1446: .getMessage("serverSideAsync", mdc
1447: .getDeclaringClass(), mdc
1448: .getMethodName()));
1449: }
1450: }
1451: }
1452: // TODO: Fill this out to validate all MethodDescriptionComposite (and
1453: // their inclusive
1454: // annotations on this SEI (SEI is assumed here)
1455: //check oneway
1456: //
1457:
1458: //This could be an SEI, or an impl. class that doesn' specify an EndpointInterface (so, it
1459: //is implicitly an SEI...need to consider this
1460: //
1461:
1462: //TODO: Verify that, if this is an interface...that there are no Methods with WebMethod
1463: // annotations that contain exclude == true
1464:
1465: //TODO: Verify that, if a SOAPBinding annotation exists, that its style be set to
1466: // only DOCUMENT JSR181-Sec 4.7.1
1467:
1468: }
1469:
1470: private void validateWSDLOperations() {
1471: //Verifies that all operations on the wsdl are found in the impl/sei class
1472: }
1473:
1474: public boolean isWSDLSpecified() {
1475: boolean wsdlSpecified = false;
1476: if (getWSDLWrapper() != null) {
1477: wsdlSpecified = (getWSDLWrapper().getDefinition() != null);
1478: }
1479: return wsdlSpecified;
1480: }
1481:
1482: // ===========================================
1483: // ANNOTATION: HandlerChain
1484: // ===========================================
1485:
1486: /**
1487: * Returns a schema derived java class containing the the handler configuration filel
1488: *
1489: * @return HandlerChainsType This is the top-level element for the Handler configuration file
1490: *
1491: */
1492: public HandlerChainsType getHandlerChain() {
1493:
1494: if (handlerChainsType == null) {
1495:
1496: getAnnoHandlerChainAnnotation();
1497: if (handlerChainAnnotation != null) {
1498:
1499: String handlerFileName = handlerChainAnnotation.file();
1500:
1501: // TODO RAS & NLS
1502: if (log.isDebugEnabled()) {
1503: if (composite != null) {
1504: log
1505: .debug("EndpointDescriptionImpl.getHandlerChain: fileName: "
1506: + handlerFileName
1507: + " className: "
1508: + composite.getClassName());
1509: } else {
1510: log
1511: .debug("EndpointDescriptionImpl.getHandlerChain: fileName: "
1512: + handlerFileName
1513: + " className: "
1514: + serviceClass.getName());
1515: }
1516: }
1517:
1518: String className = (composite != null) ? composite
1519: .getClassName() : serviceClass.getName();
1520:
1521: ClassLoader classLoader = (composite != null) ? composite
1522: .getClassLoader()
1523: : this .getClass().getClassLoader();
1524:
1525: InputStream is = DescriptionUtils
1526: .openHandlerConfigStream(handlerFileName,
1527: className, classLoader);
1528:
1529: try {
1530:
1531: // All the classes we need should be part of this package
1532: JAXBContext jc = JAXBContext
1533: .newInstance(
1534: "org.apache.axis2.jaxws.description.xml.handler",
1535: this .getClass().getClassLoader());
1536:
1537: Unmarshaller u = jc.createUnmarshaller();
1538:
1539: JAXBElement<?> o = (JAXBElement<?>) u.unmarshal(is);
1540: handlerChainsType = (HandlerChainsType) o
1541: .getValue();
1542:
1543: } catch (Exception e) {
1544: throw ExceptionFactory
1545: .makeWebServiceException("EndpointDescriptionImpl: getHandlerChain: thrown when attempting to unmarshall JAXB content");
1546: }
1547: }
1548: }
1549: return handlerChainsType;
1550: }
1551:
1552: /*
1553: * This is a client side only method. The generated service class may contain
1554: * handler chain annotations
1555: */
1556: public HandlerChain getAnnoHandlerChainAnnotation() {
1557: if (this .handlerChainAnnotation == null) {
1558: if (serviceClass != null) {
1559: handlerChainAnnotation = (HandlerChain) serviceClass
1560: .getAnnotation(HandlerChain.class);
1561: }
1562: }
1563:
1564: return handlerChainAnnotation;
1565: }
1566:
1567: /* Returns the WSDL definiton as specified in the metadata. Note that this WSDL may not be
1568: * complete.
1569: */
1570: public Definition getWSDLDefinition() {
1571: Definition defn = null;
1572: if (getWSDLWrapper() != null) {
1573: defn = getWSDLWrapper().getDefinition();
1574: }
1575: return defn;
1576: }
1577:
1578: /**
1579: * Returns the WSDL definiton as created by calling the WSDL generator. This will be null
1580: * unless the WSDL definition provided by the metadata is incomplete
1581: */
1582: public Definition getWSDLGeneratedDefinition() {
1583: Definition defn = null;
1584: if (getGeneratedWsdlWrapper() != null) {
1585: defn = getGeneratedWsdlWrapper().getDefinition();
1586: }
1587: return defn;
1588: }
1589:
1590: public Service getWSDLService() {
1591: Service returnWSDLService = null;
1592: Definition defn = getWSDLDefinition();
1593: if (defn != null) {
1594: returnWSDLService = defn.getService(getServiceQName());
1595: }
1596: return returnWSDLService;
1597: }
1598:
1599: public Map getWSDLPorts() {
1600: Service wsdlService = getWSDLService();
1601: if (wsdlService != null) {
1602: return wsdlService.getPorts();
1603: } else {
1604: return null;
1605: }
1606: }
1607:
1608: public List<QName> getPorts() {
1609: ArrayList<QName> portList = new ArrayList<QName>();
1610: // Note that we don't cache these results because the list of ports can be added
1611: // to via getPort(...) and addPort(...).
1612:
1613: // If the WSDL is specified, get the list of ports under this service
1614: Map wsdlPortsMap = getWSDLPorts();
1615: if (wsdlPortsMap != null) {
1616: Iterator wsdlPortsIterator = wsdlPortsMap.values()
1617: .iterator();
1618: // Note that the WSDL Ports do not have a target namespace associated with them.
1619: // JAXWS says to use the TNS from the Service.
1620: String serviceTNS = getServiceQName().getNamespaceURI();
1621: for (Port wsdlPort = null; wsdlPortsIterator.hasNext();) {
1622: wsdlPort = (Port) wsdlPortsIterator.next();
1623: String wsdlPortLocalPart = wsdlPort.getName();
1624: portList.add(new QName(serviceTNS, wsdlPortLocalPart));
1625: }
1626: }
1627:
1628: // Go through the list of Endpoints that have been created and add any
1629: // not already in the list. This will include ports added to the Service
1630: // via getPort(...) and addPort(...)
1631: Collection<EndpointDescription> endpointDescs = getEndpointDescriptions_AsCollection();
1632: for (EndpointDescription endpointDesc : endpointDescs) {
1633: QName endpointPortQName = endpointDesc.getPortQName();
1634: if (!portList.contains(endpointPortQName)) {
1635: portList.add(endpointPortQName);
1636: }
1637: }
1638: return portList;
1639: }
1640:
1641: public List<Port> getWSDLPortsUsingPortType(QName portTypeQN) {
1642: ArrayList<Port> portList = new ArrayList<Port>();
1643: if (!DescriptionUtils.isEmpty(portTypeQN)) {
1644: Map wsdlPortMap = getWSDLPorts();
1645: if (wsdlPortMap != null && !wsdlPortMap.isEmpty()) {
1646: for (Object mapElement : wsdlPortMap.values()) {
1647: Port wsdlPort = (Port) mapElement;
1648: PortType wsdlPortType = wsdlPort.getBinding()
1649: .getPortType();
1650: QName wsdlPortTypeQN = wsdlPortType.getQName();
1651: if (portTypeQN.equals(wsdlPortTypeQN)) {
1652: portList.add(wsdlPort);
1653: }
1654: }
1655: }
1656: }
1657: return portList;
1658: }
1659:
1660: public List<Port> getWSDLPortsUsingSOAPAddress(List<Port> wsdlPorts) {
1661: ArrayList<Port> portsUsingAddress = new ArrayList<Port>();
1662: if (wsdlPorts != null && !wsdlPorts.isEmpty()) {
1663: for (Port checkPort : wsdlPorts) {
1664: List extensibilityElementList = checkPort
1665: .getExtensibilityElements();
1666: for (Object checkElement : extensibilityElementList) {
1667: if (EndpointDescriptionImpl
1668: .isSOAPAddressElement((ExtensibilityElement) checkElement)) {
1669: portsUsingAddress.add(checkPort);
1670: }
1671: }
1672: }
1673: }
1674: return portsUsingAddress;
1675: }
1676:
1677: public ServiceRuntimeDescription getServiceRuntimeDesc(String name) {
1678: // TODO Add toString support
1679: return runtimeDescMap.get(name);
1680: }
1681:
1682: public void setServiceRuntimeDesc(ServiceRuntimeDescription srd) {
1683: // TODO Add toString support
1684: runtimeDescMap.put(srd.getKey(), srd);
1685: }
1686:
1687: private void resetServiceRuntimeDescription() {
1688: runtimeDescMap.clear();
1689: }
1690:
1691: /** Return a string representing this Description object and all the objects it contains. */
1692: public String toString() {
1693: final String newline = "\n";
1694: final String sameline = "; ";
1695: // This produces a TREMENDOUS amount of output if we have the WSDL Definition objects
1696: // do a toString on themselves.
1697: boolean dumpWSDLContents = false;
1698: StringBuffer string = new StringBuffer();
1699: try {
1700: // Basic information
1701: string.append(super .toString());
1702: string.append(newline);
1703: string.append("ServiceQName: " + getServiceQName());
1704: // WSDL information
1705: string.append(newline);
1706: string.append("isWSDLSpecified: " + isWSDLSpecified());
1707: string.append(sameline);
1708: string.append("WSDL Location: " + getWSDLLocation());
1709: string.append(newline);
1710: if (dumpWSDLContents) {
1711: string
1712: .append("WSDL Definition: "
1713: + getWSDLDefinition());
1714: string.append(newline);
1715: string.append("Generated WSDL Definition: "
1716: + getWSDLGeneratedDefinition());
1717: } else {
1718: string.append("WSDL Definition available: "
1719: + (getWSDLDefinition() != null));
1720: string.append(sameline);
1721: string.append("Generated WSDL Definition available: "
1722: + (getWSDLGeneratedDefinition() != null));
1723: }
1724: // Ports
1725: string.append(newline);
1726: List<QName> ports = getPorts();
1727: string.append("Number of ports: " + ports.size());
1728: string.append(newline);
1729: string.append("Port QNames: ");
1730: for (QName port : ports) {
1731: string.append(port + sameline);
1732: }
1733: // Axis Config information
1734: // We don't print out the config context because it will force one to be created
1735: // if it doesn't already exist.
1736: // string.append(newline);
1737: // string.append("ConfigurationContext: " + getAxisConfigContext());
1738: // EndpointDescriptions
1739: string.append(newline);
1740: Collection<EndpointDescription> endpointDescs = getEndpointDescriptions_AsCollection();
1741: if (endpointDescs == null) {
1742: string.append("EndpointDescription array is null");
1743: } else {
1744: string.append("Number of EndpointDescrptions: "
1745: + endpointDescs.size());
1746: string.append(newline);
1747: for (EndpointDescription endpointDesc : endpointDescs) {
1748: string.append(endpointDesc.toString());
1749: string.append(newline);
1750: }
1751: }
1752: string.append("RuntimeDescriptions:"
1753: + this .runtimeDescMap.size());
1754: string.append(newline);
1755: for (ServiceRuntimeDescription runtimeDesc : runtimeDescMap
1756: .values()) {
1757: string.append(runtimeDesc.toString());
1758: string.append(newline);
1759: }
1760: } catch (Throwable t) {
1761: string.append(newline);
1762: string
1763: .append("Complete debug information not currently available for "
1764: + "ServiceDescription");
1765: return string.toString();
1766: }
1767: return string.toString();
1768:
1769: }
1770: }
|