blob: ce0e30a1320e363bb1759ad52b728d83c3cef904 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.axis2.jaxws.description.impl;
import org.apache.axis2.AxisFault;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.AxisOperationFactory;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.java.security.AccessController;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.description.EndpointDescription;
import org.apache.axis2.jaxws.description.EndpointInterfaceDescription;
import org.apache.axis2.jaxws.description.EndpointInterfaceDescriptionJava;
import org.apache.axis2.jaxws.description.EndpointInterfaceDescriptionWSDL;
import org.apache.axis2.jaxws.description.OperationDescription;
import org.apache.axis2.jaxws.description.ServiceDescriptionWSDL;
import org.apache.axis2.jaxws.description.builder.DescriptionBuilderComposite;
import org.apache.axis2.jaxws.description.builder.MDQConstants;
import org.apache.axis2.jaxws.description.builder.MethodDescriptionComposite;
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.wsdl.Definition;
import javax.wsdl.PortType;
import javax.xml.namespace.QName;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/** @see ../EndpointInterfaceDescription */
class EndpointInterfaceDescriptionImpl
implements EndpointInterfaceDescription, EndpointInterfaceDescriptionJava,
EndpointInterfaceDescriptionWSDL {
private EndpointDescriptionImpl parentEndpointDescription;
private ArrayList<OperationDescription> operationDescriptions =
new ArrayList<OperationDescription>();
private Map<QName, List<OperationDescription>> dispatchableOperations;
private DescriptionBuilderComposite dbc;
//Logging setup
private static final Log log = LogFactory.getLog(EndpointInterfaceDescriptionImpl.class);
// ===========================================
// ANNOTATION related information
// ===========================================
// ANNOTATION: @WebService
private WebService webServiceAnnotation;
private String webServiceTargetNamespace;
private String webService_Name;
// ANNOTATION: @SOAPBinding
// Note this is the Type-level annotation. See OperationDescription for the Method-level annotation
private SOAPBinding soapBindingAnnotation;
private javax.jws.soap.SOAPBinding.Style soapBindingStyle;
// Default value per JSR-181 MR Sec 4.7 "Annotation: javax.jws.soap.SOAPBinding" pg 28
public static final javax.jws.soap.SOAPBinding.Style SOAPBinding_Style_DEFAULT =
javax.jws.soap.SOAPBinding.Style.DOCUMENT;
private javax.jws.soap.SOAPBinding.Use soapBindingUse;
// Default value per JSR-181 MR Sec 4.7 "Annotation: javax.jws.soap.SOAPBinding" pg 28
public static final javax.jws.soap.SOAPBinding.Use SOAPBinding_Use_DEFAULT =
javax.jws.soap.SOAPBinding.Use.LITERAL;
private javax.jws.soap.SOAPBinding.ParameterStyle soapParameterStyle;
// Default value per JSR-181 MR Sec 4.7 "Annotation: javax.jws.soap.SOAPBinding" pg 28
public static final javax.jws.soap.SOAPBinding.ParameterStyle SOAPBinding_ParameterStyle_DEFAULT =
javax.jws.soap.SOAPBinding.ParameterStyle.WRAPPED;
/**
* Add the operationDescription to the list of operations. Note that we can not create the
* list of dispatchable operations at this points.
* @see #initializeDispatchableOperationsList()
*
* @param operation The operation description to add to this endpoint interface
*/
void addOperation(OperationDescription operation) {
operationDescriptions.add(operation);
}
/**
* Construct a service requester (aka client-side) EndpointInterfaceDescription for the
* given SEI class. This constructor is used if hierachy is being built fully from annotations
* and not WSDL.
* @param sei
* @param parent
*/
EndpointInterfaceDescriptionImpl(Class sei, EndpointDescriptionImpl parent) {
parentEndpointDescription = parent;
dbc = new DescriptionBuilderComposite();
dbc.setCorrespondingClass(sei);
// Per JSR-181 all methods on the SEI are mapped to operations regardless
// of whether they include an @WebMethod annotation. That annotation may
// be present to customize the mapping, but is not required (p14)
for (Method method : getSEIMethods(dbc.getCorrespondingClass())) {
OperationDescription operation = new OperationDescriptionImpl(method, this);
addOperation(operation);
}
}
/**
* Construct a service requester (aka client-side) EndpointInterfaceDescrption for
* an SEI represented by an AxisService. This constructor is used if the hierachy is
* being built fully from WSDL. The AxisService and underlying AxisOperations were built
* based on the WSDL, so we will use them to create the necessary objects.
*
* @param parent
*/
EndpointInterfaceDescriptionImpl(EndpointDescriptionImpl parent) {
parentEndpointDescription = parent;
dbc = new DescriptionBuilderComposite();
AxisService axisService = parentEndpointDescription.getAxisService();
if (axisService != null) {
ArrayList publishedOperations = axisService.getPublishedOperations();
Iterator operationsIterator = publishedOperations.iterator();
while (operationsIterator.hasNext()) {
AxisOperation axisOperation = (AxisOperation)operationsIterator.next();
addOperation(new OperationDescriptionImpl(axisOperation, this));
}
}
}
/**
* Construct as Provider-based endpoint which does not have specific WSDL operations. Since there
* are no specific WSDL operations in this case, there will be a single generic operation that
* will accept any incoming operation.
*
* @param dbc
* @param parent
*/
EndpointInterfaceDescriptionImpl(DescriptionBuilderComposite dbc,
EndpointDescriptionImpl parent) {
if (log.isDebugEnabled()) {
log.debug("Creating a EndpointInterfaceDescription for a generic WSDL-less provider");
}
parentEndpointDescription = parent;
this.dbc = dbc;
// Construct the generic provider AxisOperation to use then construct
// an OperactionDescription for it.
AxisOperation genericProviderAxisOp = null;
try {
genericProviderAxisOp =
AxisOperationFactory.getOperationDescription(WSDLConstants.WSDL20_2006Constants.MEP_URI_IN_OUT);
} catch (AxisFault e) {
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("eiDescrImplErr"),e);
}
genericProviderAxisOp.setName(new QName(JAXWS_NOWSDL_PROVIDER_OPERATION_NAME));
OperationDescription opDesc = new OperationDescriptionImpl(genericProviderAxisOp, this);
addOperation(opDesc);
AxisService axisService = getEndpointDescription().getAxisService();
axisService.addOperation(genericProviderAxisOp);
}
/**
* Build an EndpointInterfaceDescription from a DescriptionBuilderComposite. This EID has
* WSDL operations associated with it. It could represent an SEI-based endpoint built from
* WSDL or annotations, OR it could represent a Provider-based enpoint built from WSDL. It will
* not represent a Provider-based endpoint built without WSDL (which does not know about
* specific WSDL operations). For that type of EID, see:
* @see #EndpointInterfaceDescriptionImpl(DescriptionBuilderComposite dbc, EndpointDescriptionImpl parent)
* @param dbc
* @param isClass
* @param parent
*/
EndpointInterfaceDescriptionImpl(DescriptionBuilderComposite dbc,
boolean isClass,
EndpointDescriptionImpl parent) {
parentEndpointDescription = parent;
this.dbc = dbc;
getEndpointDescription().getAxisService()
.setTargetNamespace(getEndpointDescriptionImpl().getTargetNamespace());
// Per JSR-181 all methods on the SEI are mapped to operations regardless
// of whether they include an @WebMethod annotation. That annotation may
// be present to customize the mapping, but is not required (p14)
//We are processing the SEI composite
//For every MethodDescriptionComposite in this list, call OperationDescription
//constructor for it, then add this operation
//Retrieve the relevent method composites for this dbc (and those in the superclass chain)
Iterator<MethodDescriptionComposite> iter = retrieveReleventMethods(dbc);
if (log.isDebugEnabled())
log.debug("EndpointInterfaceDescriptionImpl: Finished retrieving methods");
MethodDescriptionComposite mdc = null;
while (iter.hasNext()) {
mdc = iter.next();
mdc.setDeclaringClass(dbc.getClassName());
// Only add if it is a method that would be or is in the WSDL i.e.
// don't create an OperationDescriptor for the MDC representing the
// constructor
if (DescriptionUtils.createOperationDescription(mdc.getMethodName())) {
//First check if this operation already exists on the AxisService, if so
//then use that in the description hierarchy
AxisService axisService = getEndpointDescription().getAxisService();
AxisOperation axisOperation = axisService
.getOperation(OperationDescriptionImpl.determineOperationQName(mdc));
OperationDescription operation =
new OperationDescriptionImpl(mdc, this, axisOperation);
if (axisOperation == null) {
// This axisOperation did not already exist on the AxisService, and so was created
// with the OperationDescription, so we need to add the operation to the service
((OperationDescriptionImpl)operation).addToAxisService(axisService);
}
if (log.isDebugEnabled())
log.debug("EID: Just added operation= " + operation.getOperationName());
addOperation(operation);
}
}
if (log.isDebugEnabled())
log.debug("EndpointInterfaceDescriptionImpl: Finished Adding operations");
}
private static Method[] getSEIMethods(Class sei) {
// Per JSR-181 all methods on the SEI are mapped to operations regardless
// of whether they include an @WebMethod annotation. That annotation may
// be present to customize the mapping, but is not required (p14)
Method[] seiMethods = sei.getMethods();
ArrayList methodList = new ArrayList();
if (sei != null) {
for (Method method : seiMethods) {
if (method.getDeclaringClass().getName().equals("java.lang.Object")) {
continue;
}
methodList.add(method);
if (!Modifier.isPublic(method.getModifiers())) {
// JSR-181 says methods must be public (p14)
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("seiMethodsErr"));
}
// TODO: other validation per JSR-181
}
}
return (Method[])methodList.toArray(new Method[methodList.size()]);
// return seiMethods;
}
/**
* Update a previously created EndpointInterfaceDescription with information from an annotated
* SEI. This should only be necessary when the this was created with WSDL. In this case, the
* information from the WSDL is augmented based on the annotated SEI.
*
* @param sei
*/
void updateWithSEI(Class sei) {
Class seiClass = dbc.getCorrespondingClass();
if (seiClass != null && seiClass != sei) {
throw ExceptionFactory.makeWebServiceException(new UnsupportedOperationException(Messages.getMessage("seiProcessingErr")));
}
else if (seiClass != null && seiClass == sei) {
// We've already done the necessary updates for this SEI
return;
}
else if (sei != null) {
seiClass = sei;
dbc.setCorrespondingClass(sei);
// Update (or possibly add) the OperationDescription for each of the methods on the SEI.
for (Method seiMethod : getSEIMethods(seiClass)) {
if (getOperation(seiMethod) != null) {
// If an OpDesc already exists with this java method set on it, then the OpDesc has already
// been updated for this method, so skip it.
continue;
}
// At this point (for now at least) the operations were created with WSDL previously.
// If they had been created from an annotated class and no WSDL, then the seiClass would have
// already been set so we would have taken other branches in this if test. (Note this could
// change once AxisServices can be built from annotations by the ServiceDescription class).
// Since the operations were created from WSDL, they will not have a java method, which
// comes from the SEI, set on them yet.
//
// Another consideration is that currently Axis2 does not support overloaded WSDL operations.
// That means there will only be one OperationDesc build from WSDL. Still another consideration is
// that the JAXWS async methods which may exist on the SEI will NOT exist in the WSDL. An example
// of these methods for the WSDL operation:
// String echo(String)
// optionally generated JAX-WS SEI methods from the tooling; take note of the annotation specifying the
// operation name
// @WebMethod(operationName="echo" ...)
// Response<String> echoStringAsync(String)
// @WebMethod(operationName="echo" ...)
// Future<?> echoStringAsync(String, AsyncHandler)
//
// So given all the above, the code does the following based on the operation QName
// (which might also be the java method name; see determineOperationQName for details)
// (1) If an operationDesc does not exist, add it.
// (2) If an operationDesc does exist but does not have a java method set on it, set it
// (3) If an operationDesc does exist and has a java method set on it already, add a new one.
//
// TODO: May need to change when Axis2 supports overloaded WSDL operations
// Get the QName for this java method and then update (or add) the appropriate OperationDescription
// See comments below for imporant notes about the current implementation.
// NOTE ON OVERLOADED OPERATIONS
// Axis2 does NOT currently support overloading WSDL operations.
QName seiOperationQName =
OperationDescriptionImpl.determineOperationQName(seiMethod);
OperationDescription[] updateOpDesc = getOperation(seiOperationQName);
if (updateOpDesc == null || updateOpDesc.length == 0) {
// This operation wasn't defined in the WSDL. Note that the JAX-WS async methods
// which are defined on the SEI are not defined as operations in the WSDL.
// Although they usually specific the same OperationName as the WSDL operation,
// there may be cases where they do not.
OperationDescription operation = new OperationDescriptionImpl(seiMethod, this);
addOperation(operation);
} else {
// Currently Axis2 does not support overloaded operations. That means that even if the WSDL
// defined overloaded operations, there would still only be a single AxisOperation, and it
// would be the last operation encounterd.
// HOWEVER the generated JAX-WS async methods (see above) may (will always?) have the same
// operation name and so will come down this path; they need to be added.
// TODO: When Axis2 starts supporting overloaded operations, then this logic will need to be changed
// Loop through all the opdescs; if one doesn't currently have a java method set, set it
// If all have java methods set, then add a new one. Assume we'll need to add a new one.
boolean addOpDesc = true;
for (OperationDescription checkOpDesc : updateOpDesc) {
if (checkOpDesc.getSEIMethod() == null) {
// TODO: Should this be checking (somehow) that the signature matches? Probably not an issue until overloaded WSDL ops are supported.
//Make sure that this is not one of the 'async' methods associated with
//this operation. If it is, let it be created as its own opDesc.
if (!DescriptionUtils.isAsync(seiMethod)) {
((OperationDescriptionImpl) checkOpDesc).setSEIMethod(seiMethod);
addOpDesc = false;
break;
}
}
}
if (addOpDesc) {
OperationDescription operation =
new OperationDescriptionImpl(seiMethod, this);
addOperation(operation);
}
}
}
}
}
/**
* Return the OperationDescriptions corresponding to a particular Java method name. Note that an
* array is returned because a method could be overloaded.
*
* @param javaMethodName String representing a Java Method Name
* @return
*/
public OperationDescription[] getOperationForJavaMethod(String javaMethodName) {
if (DescriptionUtils.isEmpty(javaMethodName)) {
return null;
}
ArrayList<OperationDescription> matchingOperations = new ArrayList<OperationDescription>();
for (OperationDescription operation : getOperations()) {
if (javaMethodName.equals(operation.getJavaMethodName())) {
matchingOperations.add(operation);
}
}
if (matchingOperations.size() == 0)
return null;
else
return matchingOperations.toArray(new OperationDescription[0]);
}
/**
* Return the OperationDesription (only one) corresponding to the OperationName passed in.
*
* @param operationName
* @return
*/
public OperationDescription getOperation(String operationName) {
if (DescriptionUtils.isEmpty(operationName)) {
return null;
}
OperationDescription matchingOperation = null;
for (OperationDescription operation : getOperations()) {
if (operationName.equals(operation.getOperationName())) {
matchingOperation = operation;
break;
}
}
return matchingOperation;
}
public OperationDescription[] getOperations() {
return operationDescriptions.toArray(new OperationDescription[0]);
}
EndpointDescriptionImpl getEndpointDescriptionImpl() {
return (EndpointDescriptionImpl)parentEndpointDescription;
}
public EndpointDescription getEndpointDescription() {
return parentEndpointDescription;
}
/**
* Return an array of Operations given an operation QName. Note that an array is returned since
* a WSDL operation may be overloaded per JAX-WS.
*
* @param operationQName
* @return
*/
public OperationDescription[] getOperation(QName operationQName) {
OperationDescription[] returnOperations = null;
if (!DescriptionUtils.isEmpty(operationQName)) {
ArrayList<OperationDescription> matchingOperations =
new ArrayList<OperationDescription>();
OperationDescription[] allOperations = getOperations();
for (OperationDescription operation : allOperations) {
if (operation.getName().getLocalPart().equals(operationQName.getLocalPart())) {
matchingOperations.add(operation);
}
}
// Only return an array if there's anything in it
if (matchingOperations.size() > 0) {
returnOperations = matchingOperations.toArray(new OperationDescription[0]);
}
}
return returnOperations;
}
/* (non-Javadoc)
* @see org.apache.axis2.jaxws.description.EndpointInterfaceDescription#getDispatchableOperation(QName operationQName)
*/
public OperationDescription[] getDispatchableOperation(QName operationQName) {
// REVIEW: Can this be synced at a more granular level? Can't sync on dispatchableOperations because
// it may be null, but also the initialization must finish before next thread sees
// dispatachableOperations != null
synchronized(this) {
if (dispatchableOperations == null) {
initializeDispatchableOperationsList();
}
}
// Note that OperationDescriptionImpl creates operation qname with empty namespace. Thus
// using only the localPart to get dispatchable operations.
QName key = new QName("",operationQName.getLocalPart());
List<OperationDescription> operations = dispatchableOperations.get(key);
if(operations!=null){
return operations.toArray(new OperationDescription[operations.size()]);
}
return new OperationDescription[0];
}
/* (non-Javadoc)
* @see org.apache.axis2.jaxws.description.EndpointInterfaceDescription#getDispatchableOperations()
*/
public OperationDescription[] getDispatchableOperations() {
OperationDescription[] returnOperations = null;
// REVIEW: Can this be synced at a more granular level? Can't sync on dispatchableOperations because
// it may be null, but also the initialization must finish before next thread sees
// dispatachableOperations != null
synchronized(this) {
if (dispatchableOperations == null) {
initializeDispatchableOperationsList();
}
}
Collection<List<OperationDescription>> dispatchableValues = dispatchableOperations.values();
Iterator<List<OperationDescription>> iteratorValues = dispatchableValues.iterator();
ArrayList<OperationDescription> allDispatchableOperations = new ArrayList<OperationDescription>();
while (iteratorValues.hasNext()) {
List<OperationDescription> opDescList = iteratorValues.next();
allDispatchableOperations.addAll(opDescList);
}
if (allDispatchableOperations.size() > 0) {
returnOperations = allDispatchableOperations.toArray(new OperationDescription[allDispatchableOperations.size()]);
}
return returnOperations;
}
/**
* Create the list of dispatchable operations from the list of all the operations. A
* dispatchable operation is one that can be invoked on the endpoint, so it DOES NOT include:
* - JAXWS Client Async methods
* - Methods that have been excluded via WebMethod.exclude annotation
*
* Note: We have to create the list of dispatchable operations in a lazy way; we can't
* create it as the operations are added via addOperations() because on the client
* that list is built in two parts; first using AxisOperations from the WSDL, which will
* not have any annotation information (such as WebMethod.exclude). That list will then
* be updated with SEI information, which is the point annotation information becomes
* available.
*/
private void initializeDispatchableOperationsList() {
dispatchableOperations = new HashMap<QName, List<OperationDescription>>();
OperationDescription[] opDescs = getOperations();
for (OperationDescription opDesc : opDescs) {
if (!opDesc.isJAXWSAsyncClientMethod() && !opDesc.isExcluded()) {
List<OperationDescription> dispatchableOperationsWithName = dispatchableOperations.get(opDesc.getName());
if(dispatchableOperationsWithName == null) {
dispatchableOperationsWithName = new ArrayList<OperationDescription>();
dispatchableOperations.put(opDesc.getName(), dispatchableOperationsWithName);
}
dispatchableOperationsWithName.add(opDesc);
}
}
}
/**
* Return an OperationDescription for the corresponding SEI method. Note that this ONLY works
* if the OperationDescriptions were created from introspecting an SEI. If the were created
* with a WSDL then use the getOperation(QName) method, which can return > 1 operation.
*
* @param seiMethod The java.lang.Method from the SEI for which an OperationDescription is
* wanted
* @return
*/
public OperationDescription getOperation(Method seiMethod) {
OperationDescription returnOperation = null;
if (seiMethod != null) {
OperationDescription[] allOperations = getOperations();
for (OperationDescription operation : allOperations) {
if (operation.getSEIMethod() != null && operation.getSEIMethod().equals(seiMethod))
{
returnOperation = operation;
}
}
}
return returnOperation;
}
public Class getSEIClass() {
return dbc.getCorrespondingClass();
}
// Annotation-realted getters
// ========================================
// SOAP Binding annotation realted methods
// ========================================
public SOAPBinding getAnnoSoapBinding() {
if (soapBindingAnnotation == null) {
soapBindingAnnotation = dbc.getSoapBindingAnnot();
}
return soapBindingAnnotation;
}
public javax.jws.soap.SOAPBinding.Style getSoapBindingStyle() {
return getAnnoSoapBindingStyle();
}
public javax.jws.soap.SOAPBinding.Style getAnnoSoapBindingStyle() {
if (soapBindingStyle == null) {
if (getAnnoSoapBinding() != null && getAnnoSoapBinding().style() != null) {
soapBindingStyle = getAnnoSoapBinding().style();
} else {
soapBindingStyle = SOAPBinding_Style_DEFAULT;
}
}
return soapBindingStyle;
}
public javax.jws.soap.SOAPBinding.Use getSoapBindingUse() {
return getAnnoSoapBindingUse();
}
public javax.jws.soap.SOAPBinding.Use getAnnoSoapBindingUse() {
if (soapBindingUse == null) {
if (getAnnoSoapBinding() != null && getAnnoSoapBinding().use() != null) {
soapBindingUse = getAnnoSoapBinding().use();
} else {
soapBindingUse = SOAPBinding_Use_DEFAULT;
}
}
return soapBindingUse;
}
public javax.jws.soap.SOAPBinding.ParameterStyle getSoapBindingParameterStyle() {
return getAnnoSoapBindingParameterStyle();
}
public javax.jws.soap.SOAPBinding.ParameterStyle getAnnoSoapBindingParameterStyle() {
if (soapParameterStyle == null) {
if (getAnnoSoapBinding() != null && getAnnoSoapBinding().parameterStyle() != null) {
soapParameterStyle = getAnnoSoapBinding().parameterStyle();
} else {
soapParameterStyle = SOAPBinding_ParameterStyle_DEFAULT;
}
}
return soapParameterStyle;
}
/*
* Returns a non-null (possibly empty) list of MethodDescriptionComposites
*/
Iterator<MethodDescriptionComposite> retrieveReleventMethods(DescriptionBuilderComposite dbc) {
/*
* Depending on whether this is an implicit SEI or an actual SEI, Gather up and build a
* list of MDC's. If this is an actual SEI, then starting with this DBC, build a list of all
* MDC's that are public methods in the chain of extended classes.
* If this is an implicit SEI, then starting with this DBC,
* 1. If a false exclude is found, then take only those that have false excludes
* 2. Assuming no false excludes, take all public methods that don't have exclude == true
* 3. For each super class, if 'WebService' present, take all MDC's according to rules 1&2
* But, if WebService not present, grab only MDC's that are annotated.
*/
if (log.isTraceEnabled()) {
log.trace("retrieveReleventMethods: Enter");
}
ArrayList<MethodDescriptionComposite> retrieveList =
new ArrayList<MethodDescriptionComposite>();
if (dbc.isInterface()) {
if(log.isDebugEnabled()) {
log.debug("Removing overridden methods for interface: " + dbc.getClassName() +
" with super interface: " + dbc.getSuperClassName());
}
// make sure we retrieve all the methods, then remove the overridden
// methods that exist in the base interface
retrieveList = retrieveSEIMethodsChain(dbc);
retrieveList = removeOverriddenMethods(retrieveList, dbc);
} else {
//this is an implied SEI...rules are more complicated
retrieveList = retrieveImplicitSEIMethods(dbc);
//Now, continue to build this list with relevent methods in the chain of
//superclasses. If the logic for processing superclasses is the same as for
//the original SEI, then we can combine this code with above code. But, its possible
//the logic is different for superclasses...keeping separate for now.
DescriptionBuilderComposite tempDBC = dbc;
while (!DescriptionUtils.isEmpty(tempDBC.getSuperClassName())) {
//verify that this superclass name is not
// java.lang.object, if so, then we're done processing
if (DescriptionUtils.javifyClassName(tempDBC.getSuperClassName())
.equals(MDQConstants.OBJECT_CLASS_NAME))
break;
DescriptionBuilderComposite superDBC =
getEndpointDescriptionImpl().getServiceDescriptionImpl().getDBCMap()
.get(tempDBC.getSuperClassName());
if (log.isTraceEnabled())
log.trace("superclass name for this DBC is:" + tempDBC.getSuperClassName());
//Verify that we can find the SEI in the composite list
if (superDBC == null) {
throw ExceptionFactory.makeWebServiceException(Messages.getMessage("seiNotFoundErr"));
}
//If the superclass contains a WebService annotation then retrieve its methods
//as we would for the impl class, otherwise ignore the methods of this
//superclass
if (superDBC.getWebServiceAnnot() != null) {
//Now, gather the list of Methods just like we do for the lowest subclass
retrieveList.addAll(retrieveImplicitSEIMethods(superDBC));
}
tempDBC = superDBC;
} //Done with implied SEI's superclasses
retrieveList = removeOverriddenMethods(retrieveList, dbc);
}//Done with implied SEI's
return retrieveList.iterator();
}
/**
* This method will establish a <code>HashMap</code> that represents a class name of a composite
* and an integer value for the entry. The integer represents the classes level in the Java
* hierarchy. 0 represents the most basic class with n representing the highest level class.
*
* @param dbc - <code>DescriptionBuilderComposite</code>
* @return - <code>HashMap</code>
*/
private HashMap<String, Integer> getClassHierarchy(DescriptionBuilderComposite dbc) {
HashMap<String, DescriptionBuilderComposite> dbcMap = getEndpointDescriptionImpl().
getServiceDescriptionImpl().getDBCMap();
HashMap<String, Integer> hierarchyMap = new HashMap<String, Integer>();
if (log.isDebugEnabled()) {
log.debug("Putting class at base level: " + dbc.getClassName());
}
hierarchyMap.put(dbc.getClassName(), Integer.valueOf(0));
DescriptionBuilderComposite superDBC = dbcMap.get((dbc.getSuperClassName()));
int i = 1;
while (superDBC != null && !superDBC.getClassName().equals("java.lang.Object")) {
hierarchyMap.put(superDBC.getClassName(), Integer.valueOf(i));
if (log.isDebugEnabled()) {
log.debug("Putting class: " + superDBC.getClassName() + " at hierarchy rank: " +
i);
}
i++;
superDBC = dbcMap.get(superDBC.getSuperClassName());
}
return hierarchyMap;
}
/**
* This method drives the establishment of the hierarchy of interfaces for an SEI.
*/
private Map<String, Integer> getInterfaceHierarchy(DescriptionBuilderComposite dbc) {
if(log.isDebugEnabled()) {
log.debug("Getting interface hierarchy for: " + dbc.getClassName());
}
Map<String, Integer> hierarchyMap = new HashMap<String, Integer>();
hierarchyMap.put(dbc.getClassName(), 0);
return getInterfaceHierarchy(dbc.getInterfacesList(),
hierarchyMap,
1);
}
/**
* Recursive method that builds the hierarchy of interfaces. This begins with an
* SEI and walks all of its super interfaces.
*/
private Map<String, Integer> getInterfaceHierarchy(List<String> interfaces,
Map<String, Integer> hierarchyMap,
int level) {
HashMap<String, DescriptionBuilderComposite> dbcMap = getEndpointDescriptionImpl().
getServiceDescriptionImpl().getDBCMap();
// walk through all of the interfaces
if(interfaces != null
&&
!interfaces.isEmpty()) {
for(String interfaze : interfaces) {
DescriptionBuilderComposite interDBC = dbcMap.get(interfaze);
if(interDBC != null) {
if(log.isDebugEnabled()) {
log.debug("Inserting super interface " + interDBC.getClassName() +
" at level " + level);
}
hierarchyMap.put(interDBC.getClassName(), level);
return getInterfaceHierarchy(interDBC.getInterfacesList(), hierarchyMap, level++);
}
}
}
return hierarchyMap;
}
/**
* This method will loop through each method that was previously determined as being relevant to
* the current composite. It will then drive the call to determine if this represents a method
* that has been overridden. If it represents an overriding method declaration it will remove
* the inherited methods from the list leaving only the most basic method declaration.
*
* @param methodList - <code>ArrayList</code> list of relevant methods
* @param dbc - <code>DescriptionBuilderComposite</code> current composite
* @return - <code>ArrayList</code>
*/
private ArrayList<MethodDescriptionComposite> removeOverriddenMethods(
ArrayList<MethodDescriptionComposite>
methodList, DescriptionBuilderComposite dbc) {
Map<String, Integer> hierarchyMap = dbc.isInterface() ? getInterfaceHierarchy(dbc) :
getClassHierarchy(dbc);
ArrayList<MethodDescriptionComposite> returnMethods =
new ArrayList<MethodDescriptionComposite>();
for (int i = 0; i < methodList.size(); i++) {
if (notFound(returnMethods, methodList.get(i))) {
returnMethods.add(getBaseMethod(methodList.get(i), i, methodList, hierarchyMap));
}
}
return returnMethods;
}
/**
* This method will loop through each method we have already identified as a base method and
* compare the current method.
*
* @param mdcList - <code>ArrayList</code> identified base methods
* @param mdc - <code>MethodDescriptionComposite</code> current method
* @return - boolean
*/
private boolean notFound(ArrayList<MethodDescriptionComposite> mdcList,
MethodDescriptionComposite mdc) {
for (MethodDescriptionComposite method : mdcList) {
if (mdc.compare(method)) {
return false;
}
}
return true;
}
/**
* This method is responsible for determining the most basic level of a method declaration in
* the <code>DescriptionBuilderComposite</code> hierarchy.
*
* @param mdc - <code>MethodDescriptionComposite</code> current method
* @param index - <code>int</code> current location in method list
* @param methodList - <code>List</code> list of methods available on this composite
* @param hierarchyMap - <code>HashMap</code> map that represents the hierarchy of the current
* <code>DescriptionBuilderComposite</code>
* @return - <code>MethodDescriptionComposite</code> most basic method declaration
*/
private static MethodDescriptionComposite getBaseMethod(MethodDescriptionComposite mdc,
int index,
ArrayList<MethodDescriptionComposite> methodList,
Map<String, Integer>
hierarchyMap) {
int baseLevel = hierarchyMap.get(mdc.getDeclaringClass());
if (log.isDebugEnabled()) {
log.debug("Base method: " + mdc.getMethodName() + " initial level: " + baseLevel);
}
for (; index < methodList.size(); index++) {
MethodDescriptionComposite compareMDC = methodList.get(index);
// If the two methods are the same method that means we have found an inherited
// overridden case
if (mdc.equals(compareMDC)) {
if (log.isDebugEnabled()) {
log.debug("Found equivalent methods: " + mdc.getMethodName());
}
// get the declaration level of the method we are comparing to
int compareLevel = hierarchyMap.get(compareMDC.getDeclaringClass());
// if the method was declared by a class in a lower level of the hierarchy it
// becomes the method that we will compare other methods to
if (compareLevel < baseLevel) {
if (log.isDebugEnabled()) {
log.debug("Found method lower in hierarchy chain: " +
compareMDC.getMethodName()
+ " of class: " + compareMDC.getMethodName());
}
mdc = compareMDC;
baseLevel = compareLevel;
}
}
}
return mdc;
}
/*
* This is called when we know that this DBC is an implicit SEI
*/
private ArrayList<MethodDescriptionComposite> retrieveImplicitSEIMethods(
DescriptionBuilderComposite dbc) {
ArrayList<MethodDescriptionComposite> retrieveList =
new ArrayList<MethodDescriptionComposite>();
retrieveList = DescriptionUtils.getMethodsWithFalseExclusions(dbc);
//If this list is empty, then there are no false exclusions, so gather
//all composites that don't have exclude == true
//If the list is not empty, then it means we found at least one method with 'exclude==false'
//so the list should contain only those methods
if (retrieveList == null || retrieveList.size() == 0) {
Iterator<MethodDescriptionComposite> iter = null;
List<MethodDescriptionComposite> mdcList = dbc.getMethodDescriptionsList();
if (mdcList != null) {
iter = dbc.getMethodDescriptionsList().iterator();
while (iter.hasNext()) {
MethodDescriptionComposite mdc = iter.next();
if (!DescriptionUtils.isExcludeTrue(mdc)) {
mdc.setDeclaringClass(dbc.getClassName());
retrieveList.add(mdc);
}
}
}
}
return retrieveList;
}
private ArrayList<MethodDescriptionComposite> retrieveSEIMethods(
DescriptionBuilderComposite dbc) {
//Rules for retrieving Methods on an SEI (or a superclass of an SEI) are simple
//Just retrieve all methods regardless of WebMethod annotations
ArrayList<MethodDescriptionComposite> retrieveList =
new ArrayList<MethodDescriptionComposite>();
Iterator<MethodDescriptionComposite> iter = null;
List<MethodDescriptionComposite> mdcList = dbc.getMethodDescriptionsList();
if (mdcList != null) {
iter = dbc.getMethodDescriptionsList().iterator();
while (iter.hasNext()) {
MethodDescriptionComposite mdc = iter.next();
mdc.setDeclaringClass(dbc.getClassName());
retrieveList.add(mdc);
}
}
return retrieveList;
}
private ArrayList<MethodDescriptionComposite> retrieveSEIMethodsChain(
DescriptionBuilderComposite tmpDBC) {
DescriptionBuilderComposite dbc = tmpDBC;
ArrayList<MethodDescriptionComposite> retrieveList =
new ArrayList<MethodDescriptionComposite>();
retrieveList = retrieveSEIMethods(dbc);
//Since this is an interface, anything that is in the extends clause will actually appear
// in the interfaces list instead.
Iterator<String> iter = null;
List<String> interfacesList = dbc.getInterfacesList();
if (interfacesList != null) {
iter = dbc.getInterfacesList().iterator();
while (iter.hasNext()) {
String interfaceName = iter.next();
DescriptionBuilderComposite superInterface =
getEndpointDescriptionImpl().getServiceDescriptionImpl().getDBCMap()
.get(interfaceName);
retrieveList.addAll(retrieveSEIMethodsChain(superInterface));
}
}
return retrieveList;
}
private Definition getWSDLDefinition() {
return ((ServiceDescriptionWSDL)getEndpointDescription().getServiceDescription())
.getWSDLDefinition();
}
public PortType getWSDLPortType() {
PortType portType = null;
// EndpointDescriptionWSDL endpointDescWSDL = (EndpointDescriptionWSDL) getEndpointDescription();
// Binding wsdlBinding = endpointDescWSDL.getWSDLBinding();
// if (wsdlBinding != null) {
// portType = wsdlBinding.getPortType();
// }
Definition wsdlDefn = getWSDLDefinition();
if (wsdlDefn != null) {
String tns = getEndpointDescription().getTargetNamespace();
String localPart = getEndpointDescription().getName();
if (localPart != null) {
portType = wsdlDefn.getPortType(new QName(tns, localPart));
}
}
return portType;
}
public String getTargetNamespace() {
return getAnnoWebServiceTargetNamespace();
}
public WebService getAnnoWebService() {
if (webServiceAnnotation == null) {
webServiceAnnotation = dbc.getWebServiceAnnot();
}
return webServiceAnnotation;
}
public String getAnnoWebServiceTargetNamespace() {
if (webServiceTargetNamespace == null) {
if (getAnnoWebService() != null
&& !DescriptionUtils.isEmpty(getAnnoWebService().targetNamespace())) {
webServiceTargetNamespace = getAnnoWebService().targetNamespace();
} else {
// Default value per JSR-181 MR Sec 4.1 pg 15 defers to "Implementation defined,
// as described in JAX-WS 2.0, section 3.2" which is JAX-WS 2.0 Sec 3.2, pg 29.
webServiceTargetNamespace =
DescriptionUtils.makeNamespaceFromPackageName(
DescriptionUtils.getJavaPackageName(dbc.getClassName()),
"http");
}
}
return webServiceTargetNamespace;
}
public String getAnnoWebServiceName() {
if (webService_Name == null) {
if (getAnnoWebService() != null
&& !DescriptionUtils.isEmpty(getAnnoWebService().name())) {
webService_Name = getAnnoWebService().name();
} else {
// Per the JSR 181 Specification, the default
// is the simple name of the class.
webService_Name = DescriptionUtils.getSimpleJavaClassName(dbc.getClassName());
}
}
return webService_Name;
}
public String getName() {
return getAnnoWebServiceName();
}
public QName getPortType() {
String name = getName();
String tns = getTargetNamespace();
return new QName(tns, name);
}
public String toString() {
final String newline = "\n";
final String sameline = "; ";
StringBuffer string = new StringBuffer();
try {
string.append(super.toString());
string.append(newline);
string.append("Name: " + getName());
string.append(sameline);
string.append("PortType: " + getPortType());
//
string.append(newline);
string.append("SOAP Style: " + getSoapBindingStyle());
string.append(sameline);
string.append("SOAP Use: " + getSoapBindingUse());
string.append(sameline);
string.append("SOAP Paramater Style: " + getSoapBindingParameterStyle());
//
string.append(newline);
OperationDescription[] operations = getOperations();
if (operations != null && operations.length > 0) {
string.append("Number of operations: " + operations.length);
for (OperationDescription operation : operations) {
string.append(newline);
string.append("Operation: " + operation.toString());
}
} else {
string.append("OperationDescription array is null");
}
}
catch (Throwable t) {
string.append(newline);
string.append("Complete debug information not currently available for " +
"EndpointInterfaceDescription");
return string.toString();
}
return string.toString();
}
/**
* Get an annotation. This is wrappered to avoid a Java2Security violation.
* @param cls Class that contains annotation
* @param annotation Class of requrested Annotation
* @return annotation or null
*/
private static Annotation getAnnotation(final Class cls, final Class annotation) {
return (Annotation) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return cls.getAnnotation(annotation);
}
});
}
}