| /* |
| * 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; |
| |
| import org.apache.axiom.om.OMElement; |
| import org.apache.axis2.addressing.AddressingConstants; |
| import org.apache.axis2.addressing.AddressingHelper; |
| import org.apache.axis2.description.AxisEndpoint; |
| import org.apache.axis2.description.AxisService; |
| import org.apache.axis2.jaxws.addressing.util.EndpointReferenceUtils; |
| import org.apache.axis2.jaxws.binding.BindingUtils; |
| import org.apache.axis2.jaxws.binding.SOAPBinding; |
| import org.apache.axis2.jaxws.client.PropertyValidator; |
| import org.apache.axis2.jaxws.core.InvocationContext; |
| import org.apache.axis2.jaxws.core.MessageContext; |
| import org.apache.axis2.jaxws.description.EndpointDescription; |
| import org.apache.axis2.jaxws.description.ServiceDescription; |
| import org.apache.axis2.jaxws.handler.HandlerResolverImpl; |
| import org.apache.axis2.jaxws.i18n.Messages; |
| import org.apache.axis2.jaxws.spi.ServiceDelegate; |
| import org.apache.axis2.transport.http.HTTPConstants; |
| import org.apache.axis2.util.LoggingControl; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.ws.Binding; |
| import javax.xml.ws.EndpointReference; |
| import javax.xml.ws.WebServiceException; |
| import javax.xml.ws.WebServiceFeature; |
| import javax.xml.ws.handler.HandlerResolver; |
| import javax.xml.ws.soap.AddressingFeature.Responses; |
| import javax.xml.ws.wsaddressing.W3CEndpointReference; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| public class BindingProvider implements org.apache.axis2.jaxws.spi.BindingProvider { |
| private static final Log log = LogFactory.getLog(BindingProvider.class); |
| |
| protected Map<String, Object> requestContext; |
| |
| protected Map<String, Object> responseContext; |
| |
| protected EndpointDescription endpointDesc; |
| |
| // NOTE this reference to the ServiceDelegate MUST be a strong reference to keep the delegate |
| // from being GC'd when the Service instance in the client goes out of scope but ports under |
| // that service are still in use. |
| protected ServiceDelegate serviceDelegate; |
| |
| private org.apache.axis2.jaxws.spi.Binding binding; |
| |
| public static final String BINDING_PROVIDER = "org.apache.axis2.jaxws.BindingProvider"; |
| |
| public BindingProvider(ServiceDelegate svcDelegate, |
| EndpointDescription epDesc, |
| org.apache.axis2.addressing.EndpointReference epr, |
| String addressingNamespace, |
| WebServiceFeature... features) { |
| this.endpointDesc = epDesc; |
| this.serviceDelegate = svcDelegate; |
| |
| initialize(epr, addressingNamespace, features); |
| } |
| |
| /* |
| * Initialize any objects needed by the BindingProvider |
| */ |
| private void initialize(org.apache.axis2.addressing.EndpointReference epr, |
| String addressingNamespace, |
| WebServiceFeature... features) { |
| requestContext = new ValidatingClientContext(); |
| responseContext = new ValidatingClientContext(); |
| |
| // Setting standard property defaults for the request context |
| requestContext.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, Boolean.FALSE); |
| requestContext.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE); |
| |
| // Addressing is disabled by default unless it is turned on in the WSDL |
| String addressingFlagFromWSDL = AddressingHelper.getAddressingRequirementParemeterValue(endpointDesc.getAxisService()); |
| if(AddressingConstants.ADDRESSING_UNSPECIFIED.equals(addressingFlagFromWSDL)){ |
| requestContext.put(AddressingConstants.DISABLE_ADDRESSING_FOR_OUT_MESSAGES, Boolean.TRUE); |
| } |
| |
| // Set the endpoint address |
| String endpointAddress = (epr != null ) ? epr.getAddress() : endpointDesc.getEndpointAddress(); |
| if (endpointAddress != null && !"".equals(endpointAddress)) { |
| requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress); |
| } |
| |
| // JAXWS 9.2.1.1 requires that we go ahead and create the binding object |
| // so we can also set the handlerchain |
| binding = (org.apache.axis2.jaxws.spi.Binding) BindingUtils.createBinding(endpointDesc); |
| if(log.isDebugEnabled()){ |
| log.debug("Lookign for Handler Resolver"); |
| } |
| // TODO should we allow the ServiceDelegate to figure out the default handlerresolver? Probably yes, since a client app may look for one there. |
| HandlerResolver handlerResolver = null; |
| if(serviceDelegate.getHandlerResolver() != null){ |
| if(log.isDebugEnabled()){ |
| log.debug("Reading default Handler Resolver "); |
| } |
| handlerResolver = serviceDelegate.getHandlerResolver(); |
| } |
| else{ |
| handlerResolver = new HandlerResolverImpl(endpointDesc.getServiceDescription(), serviceDelegate); |
| if(log.isDebugEnabled()){ |
| log.debug("Creating new Handler Resolver using HandlerResolverImpl"); |
| } |
| } |
| |
| // See if the metadata from creating the service indicates that MTOM, Addressing and/or RespectBinding should be enabled |
| if (binding instanceof SOAPBinding) { |
| configureBindingFromMetadata(); |
| } |
| |
| // check for properties that need to be set on the BindingProvider |
| String seiName = null; |
| if(endpointDesc.getEndpointInterfaceDescription() != null |
| && |
| endpointDesc.getEndpointInterfaceDescription().getSEIClass() != null) { |
| seiName = endpointDesc.getEndpointInterfaceDescription().getSEIClass().getName(); |
| } |
| String portQNameString = endpointDesc.getPortQName().toString(); |
| String key = seiName + ":" + portQNameString; |
| Map<String, Object> bProps = endpointDesc.getServiceDescription().getBindingProperties(serviceDelegate, key); |
| if(bProps != null) { |
| if(log.isDebugEnabled()) { |
| log.debug("Setting binding props with size: " + bProps.size() + " on " + |
| "BindingProvider RequestContext"); |
| } |
| requestContext.putAll(bProps); |
| } |
| |
| binding.setHandlerChain(handlerResolver.getHandlerChain(endpointDesc.getPortInfo())); |
| |
| //Set JAX-WS 2.1 related properties. |
| try { |
| binding.setAxis2EndpointReference(epr); |
| binding.setAddressingNamespace(addressingNamespace); |
| binding.setFeatures(features); |
| } |
| catch (Exception e) { |
| throw ExceptionFactory.makeWebServiceException(e); |
| } |
| } |
| |
| /** |
| * Configure the binding from the Metadata for WebService Features. |
| */ |
| private void configureBindingFromMetadata() { |
| // MTOM can be enabled either at the ServiceDescription level (via the WSDL binding type) or |
| // at the EndpointDescription level via the binding type used to create a Dispatch. |
| boolean enableMTOMFromMetadata = false; |
| int mtomThreshold = 0; |
| boolean isAddressingConfiguredViaMetadata = false; |
| boolean enableRespectBindingdFromMetadata = false; |
| boolean enableAddressingFromMetadata = false; |
| boolean requireAddressingFromMetadata = false; |
| Responses addressingResponses = null; |
| |
| // if we have an SEI for the port, then we'll use it in order to search for WebService Feature configuration |
| if(endpointDesc.getEndpointInterfaceDescription() != null |
| && |
| endpointDesc.getEndpointInterfaceDescription().getSEIClass() != null) { |
| enableMTOMFromMetadata = endpointDesc.getServiceDescription().isMTOMEnabled(serviceDelegate, |
| endpointDesc.getEndpointInterfaceDescription().getSEIClass()); |
| mtomThreshold = getMTOMThreshold(endpointDesc.getServiceDescription(), serviceDelegate, |
| endpointDesc.getEndpointInterfaceDescription().getSEIClass()); |
| |
| enableRespectBindingdFromMetadata = isRespectBindingEnabled(endpointDesc.getServiceDescription(), serviceDelegate, |
| endpointDesc.getEndpointInterfaceDescription().getSEIClass()); |
| |
| isAddressingConfiguredViaMetadata = isAddressingConfigured(endpointDesc.getServiceDescription(), serviceDelegate, |
| endpointDesc.getEndpointInterfaceDescription().getSEIClass()); |
| if (isAddressingConfiguredViaMetadata) { |
| enableAddressingFromMetadata = isAddressingEnabled(endpointDesc.getServiceDescription(), serviceDelegate, |
| endpointDesc.getEndpointInterfaceDescription().getSEIClass()); |
| requireAddressingFromMetadata = isAddressingRequired(endpointDesc.getServiceDescription(), serviceDelegate, |
| endpointDesc.getEndpointInterfaceDescription().getSEIClass()); |
| addressingResponses = getAddressingResponses(endpointDesc.getServiceDescription(), serviceDelegate, |
| endpointDesc.getEndpointInterfaceDescription().getSEIClass()); |
| } |
| |
| |
| } |
| else { |
| enableMTOMFromMetadata = endpointDesc.getServiceDescription().isMTOMEnabled(serviceDelegate); |
| // MTOM.Threshold, RespectBinding, and Addressing does not need to be set here based on the sparse composite |
| // (i.e. depolyment descriptor) since it can only be applied to a port injection (i.e. an SEI) using a DD. |
| } |
| if (!enableMTOMFromMetadata) { |
| String bindingType = endpointDesc.getClientBindingID(); |
| enableMTOMFromMetadata = (bindingType.equals(SOAPBinding.SOAP11HTTP_MTOM_BINDING) || |
| bindingType.equals(SOAPBinding.SOAP12HTTP_MTOM_BINDING)); |
| } |
| |
| if (enableMTOMFromMetadata) { |
| ((SOAPBinding) binding).setMTOMEnabled(true); |
| ((SOAPBinding) binding).setMTOMThreshold(mtomThreshold); |
| } |
| |
| if (enableRespectBindingdFromMetadata) { |
| ((SOAPBinding) binding).setRespectBindingEnabled(true); |
| } |
| |
| if (isAddressingConfiguredViaMetadata) { |
| ((SOAPBinding) binding).setAddressingConfigured(true); |
| ((SOAPBinding) binding).setAddressingEnabled(enableAddressingFromMetadata); |
| ((SOAPBinding) binding).setAddressingRequired(requireAddressingFromMetadata); |
| ((SOAPBinding) binding).setAddressingResponses(addressingResponses); |
| } |
| } |
| |
| private boolean isRespectBindingEnabled(ServiceDescription serviceDescription, ServiceDelegate serviceDelegateKey, |
| Class seiClass) { |
| boolean isEnabled = serviceDescription.isRespectBindingEnabled(serviceDelegateKey, seiClass); |
| return isEnabled; |
| } |
| |
| |
| /** |
| * Answer if addressing was explicitly configured via metadata. Note that if Addressing was not explicitly configured, |
| * then the related methods will return default values. If Addressing was explicitly configured, the related |
| * methods will return values based on whatever configuration was specified. |
| * @see #isAddressingEnabled(ServiceDescription, ServiceDelegate, Class) |
| * @see #isAddressingRequired(ServiceDescription, ServiceDelegate, Class) |
| * @see #getAddressingResponses(ServiceDescription, ServiceDelegate, Class) |
| * @param serviceDescription |
| * @param serviceDelegateKey |
| * @param seiClass |
| * @return true if addressing was explicitly configured via metadata, false otherwise. |
| */ |
| private boolean isAddressingConfigured(ServiceDescription serviceDescription, ServiceDelegate serviceDelegateKey, |
| Class seiClass) { |
| boolean isConfigured = serviceDescription.isAddressingConfigured(serviceDelegateKey, seiClass); |
| return isConfigured; |
| } |
| |
| private boolean isAddressingEnabled(ServiceDescription serviceDescription, ServiceDelegate serviceDelegateKey, |
| Class seiClass) { |
| boolean isEnabled = serviceDescription.isAddressingEnabled(serviceDelegateKey, seiClass); |
| return isEnabled; |
| } |
| |
| private boolean isAddressingRequired(ServiceDescription serviceDescription, ServiceDelegate serviceDelegateKey, |
| Class seiClass) { |
| boolean isRequired = serviceDescription.isAddressingRequired(serviceDelegateKey, seiClass); |
| return isRequired; |
| } |
| private Responses getAddressingResponses(ServiceDescription serviceDescription, ServiceDelegate serviceDelegateKey, |
| Class seiClass) { |
| Responses responses = serviceDescription.getAddressingResponses(serviceDelegateKey, seiClass); |
| return responses; |
| } |
| |
| private int getMTOMThreshold(ServiceDescription serviceDescription, ServiceDelegate serviceDelegate, Class seiClass) { |
| int threshold = serviceDescription.getMTOMThreshold(serviceDelegate, seiClass); |
| |
| return threshold; |
| } |
| |
| public ServiceDelegate getServiceDelegate() { |
| return serviceDelegate; |
| } |
| |
| public EndpointDescription getEndpointDescription() { |
| return endpointDesc; |
| } |
| |
| public Binding getBinding() { |
| return binding; |
| } |
| |
| public Map<String, Object> getRequestContext() { |
| return requestContext; |
| } |
| |
| public Map<String, Object> getResponseContext() { |
| return responseContext; |
| } |
| |
| /** |
| * Check for maintain session state enablement either in the |
| * MessageContext.isMaintainSession() or in the ServiceContext properties. |
| * |
| * @param mc |
| * @param ic |
| */ |
| protected void checkMaintainSessionState(MessageContext mc, InvocationContext ic) { |
| Map<String, Object> properties = ic.getServiceClient().getServiceContext().getProperties(); |
| boolean bValue = false; |
| |
| if (properties != null |
| && properties |
| .containsKey(javax.xml.ws.BindingProvider.SESSION_MAINTAIN_PROPERTY)) { |
| bValue = (Boolean) properties |
| .get(javax.xml.ws.BindingProvider.SESSION_MAINTAIN_PROPERTY); |
| } |
| if (mc.isMaintainSession() || bValue == true) { |
| setupSessionContext(properties); |
| } |
| } |
| |
| /* |
| * Ensure that the next request context contains the session value returned |
| * from previous request |
| */ |
| protected void setupSessionContext(Map<String, Object> properties) { |
| String sessionKey = null; |
| Object sessionValue = null; |
| |
| if (properties == null) { |
| throw ExceptionFactory.makeWebServiceException(Messages.getMessage("NoMaintainSessionProperty")); |
| } else if (properties.containsKey(HTTPConstants.HEADER_LOCATION)) { |
| sessionKey = HTTPConstants.HEADER_LOCATION; |
| sessionValue = properties.get(sessionKey); |
| if (sessionValue != null && !"".equals(sessionValue)) { |
| requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, sessionValue); |
| } |
| } else if (properties.containsKey(HTTPConstants.HEADER_COOKIE)) { |
| sessionKey = HTTPConstants.HEADER_COOKIE; |
| sessionValue = properties.get(sessionKey); |
| if (sessionValue != null && !"".equals(sessionValue)) { |
| requestContext.put(HTTPConstants.COOKIE_STRING, sessionValue); |
| } |
| } else if (properties.containsKey(HTTPConstants.HEADER_COOKIE2)) { |
| sessionKey = HTTPConstants.HEADER_COOKIE2; |
| sessionValue = properties.get(sessionKey); |
| if (sessionValue != null && !"".equals(sessionValue)) { |
| requestContext.put(HTTPConstants.COOKIE_STRING, sessionValue); |
| } |
| } else { |
| throw ExceptionFactory |
| .makeWebServiceException(Messages.getMessage("NoMaintainSessionProperty")); |
| } |
| |
| if (sessionValue == null) { |
| throw ExceptionFactory.makeWebServiceException( |
| Messages.getMessage("NullValueForMaintainSessionProperty", sessionKey)); |
| } |
| } |
| |
| /** |
| * Returns a boolean value representing whether or not a SOAPAction header should be sent with |
| * the request. |
| */ |
| protected boolean useSoapAction() { |
| //TODO: Add some bit of validation for this property so that we know |
| // it is actually a Boolean and not a String. |
| Boolean use = (Boolean)requestContext.get(BindingProvider.SOAPACTION_USE_PROPERTY); |
| if (use != null) { |
| if (use.booleanValue()) { |
| return true; |
| } else { |
| return false; |
| } |
| } else { |
| // If the value is not set, then just default to sending a SOAPAction |
| return true; |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see javax.xml.ws.BindingProvider#getEndpointReference() |
| */ |
| public EndpointReference getEndpointReference() { |
| return getEndpointReference(W3CEndpointReference.class); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see javax.xml.ws.BindingProvider#getEndpointReference(java.lang.Class) |
| */ |
| public <T extends EndpointReference> T getEndpointReference(Class<T> clazz) { |
| EndpointReference jaxwsEPR = null; |
| String addressingNamespace = EndpointReferenceUtils.getAddressingNamespace(clazz); |
| |
| try { |
| org.apache.axis2.addressing.EndpointReference epr = binding.getAxis2EndpointReference(); |
| |
| if (epr == null) { |
| String address = |
| (String) requestContext.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY); |
| if (address == null) |
| address = endpointDesc.getEndpointAddress(); |
| QName service = endpointDesc.getServiceQName(); |
| QName port = endpointDesc.getPortQName(); |
| String wsdlLocation = "?wsdl"; // let the webcontainer redirect us to the real WSDL URL; it knows where it is |
| |
| epr = EndpointReferenceUtils.createAxis2EndpointReference(address, service, port, wsdlLocation, addressingNamespace); |
| |
| // Add reference parameters from WSDL to the EPR |
| AxisService axisService = endpointDesc.getAxisService(); |
| if (axisService != null) { |
| AxisEndpoint axisEndpoint = axisService.getEndpoint(axisService.getEndpointName()); |
| |
| if(axisEndpoint != null){ |
| ArrayList referenceParameters = (ArrayList) axisEndpoint.getParameterValue(AddressingConstants.REFERENCE_PARAMETER_PARAMETER); |
| if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) { |
| log.trace("getEndpointReference: Adding reference parameters to EPR from WSDL: axisService = " + axisService + ", axisEndpoint = " + axisEndpoint.getName() + ", referenceParameters = " + referenceParameters); |
| } |
| if(referenceParameters!=null){ |
| Iterator iterator = referenceParameters.iterator(); |
| HashMap<QName, OMElement> refParamMap = new HashMap<QName, OMElement>(); |
| while (iterator.hasNext()) { |
| OMElement omElement = (OMElement)iterator.next(); |
| refParamMap.put(omElement.getQName(), omElement); |
| } |
| epr.setReferenceParameters(refParamMap); |
| } |
| } |
| } |
| } |
| else if (!addressingNamespace.equals(binding.getAddressingNamespace())) { |
| throw ExceptionFactory. |
| makeWebServiceException(Messages.getMessage("bindingProviderErr1", |
| binding.getAddressingNamespace(), |
| addressingNamespace)); |
| } |
| |
| jaxwsEPR = EndpointReferenceUtils.convertFromAxis2(epr, addressingNamespace); |
| } catch (UnsupportedOperationException e) { |
| throw e; |
| } catch (WebServiceException e) { |
| throw e; |
| } catch (Exception e) { |
| throw ExceptionFactory. |
| makeWebServiceException(Messages.getMessage("endpointRefConstructionFailure3", |
| e.toString())); |
| } |
| |
| return clazz.cast(jaxwsEPR); |
| } |
| |
| /* |
| * An inner class used to validate properties as they are set by the client. |
| */ |
| class ValidatingClientContext extends Hashtable<String, Object> { |
| private static final long serialVersionUID = 3485112205801917858L; |
| |
| @Override |
| public synchronized Object put(String key, Object value) { |
| // super.put rightly throws a NullPointerException if key or value is null, so don't continue if that's the case |
| if (value == null) |
| return null; |
| if (PropertyValidator.validate(key, value)) { |
| return super.put(key, value); |
| } else { |
| throw ExceptionFactory.makeWebServiceException( |
| Messages.getMessage("invalidPropValue", key, value.getClass().getName(), |
| PropertyValidator.getExpectedValue(key).getName())); |
| } |
| } |
| } |
| |
| |
| } |