// Copyright 2011, Google Inc. All Rights Reserved. // // Licensed 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 com.google.api.ads.common.lib.soap.axis; import com.google.api.ads.common.lib.conf.AdsApiConfiguration; import com.google.api.ads.common.lib.exception.ServiceException; import com.google.api.ads.common.lib.soap.RequestInfo; import com.google.api.ads.common.lib.soap.ResponseInfo; import com.google.api.ads.common.lib.soap.SoapCall; import com.google.api.ads.common.lib.soap.SoapCallReturn; import com.google.api.ads.common.lib.soap.SoapClientHandler; import com.google.api.ads.common.lib.soap.SoapClientHandlerInterface; import com.google.api.ads.common.lib.soap.SoapServiceDescriptor; import com.google.api.ads.common.lib.soap.compatability.AxisCompatible; import com.google.api.ads.common.lib.utils.NodeExtractor; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import java.lang.reflect.InvocationTargetException; import java.util.Hashtable; import java.util.Map; import javax.inject.Inject; import javax.xml.namespace.QName; import javax.xml.soap.SOAPException; import org.apache.axis.AxisFault; import org.apache.axis.EngineConfiguration; import org.apache.axis.EngineConfigurationFactory; import org.apache.axis.Message; import org.apache.axis.MessageContext; import org.apache.axis.client.Service; import org.apache.axis.client.Stub; import org.apache.axis.message.SOAPHeaderElement; import org.apache.axis.transport.http.HTTPConstants; import org.apache.commons.beanutils.BeanUtils; import org.w3c.dom.Node; /** * SOAP Client Handler implementation for use with Axis 1.x. */ public class AxisHandler extends SoapClientHandler<Stub> { private final EngineConfigurationFactory engineConfigurationFactory; private final NodeExtractor nodeExtractor; private final ImmutableList<String> requestIdXPathComponents; @Inject public AxisHandler(EngineConfigurationFactory engineConfigurationFactory, NodeExtractor nodeExtractor, AdsApiConfiguration adsApiConfiguration) { this.engineConfigurationFactory = engineConfigurationFactory; this.nodeExtractor = nodeExtractor; String requestIdXPath = adsApiConfiguration.getRequestIdXPath(); if (!Strings.isNullOrEmpty(requestIdXPath)) { requestIdXPathComponents = ImmutableList.<String>copyOf( Splitter.on('/').split(requestIdXPath)); } else { requestIdXPathComponents = ImmutableList.<String>of(); } } /** * Sets the endpoint address of the given SOAP client. * * @param soapClient the SOAP client to set the endpoint address for * @param endpointAddress the target endpoint address */ @Override public void setEndpointAddress(Stub soapClient, String endpointAddress) { soapClient._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, endpointAddress); } /** * Sets the read timeout of the given SOAP client. * * @param soapClient the SOAP client to set the read timeout for * @param timeout the timeout in milliseconds */ @Override public void setRequestTimeout(Stub soapClient, int timeout) { soapClient.setTimeout(timeout); } /** * Returns a SOAP header from the given SOAP client, if it exists. * * @param soapClient the SOAP client to check for the given header * @param headerName the name of the header being looked for * @return the header element, if it exists */ @Override public Object getHeader(Stub soapClient, String headerName) { SOAPHeaderElement[] soapHeaders = soapClient.getHeaders(); for (SOAPHeaderElement soapHeader : soapHeaders) { if (soapHeader.getName().equals(headerName)) { return soapHeader; } } return null; } /** * Clears all of the SOAP headers from the given SOAP client. * * @param soapClient the client to remove the headers from */ @Override public void clearHeaders(Stub soapClient) { soapClient._setProperty(HTTPConstants.REQUEST_HEADERS, new Hashtable<String, String>()); soapClient.clearHeaders(); } /** * @see SoapClientHandler#setHeader(Object, String, String, Object) */ @Override public void setHeader(Stub soapClient, String namespace, String headerName, Object headerValue) { try { QName qName = new QName(namespace, headerName); SOAPHeaderElement soapHeaderElement = new SOAPHeaderElement(qName); soapHeaderElement.setObjectValue(headerValue); soapHeaderElement.setActor(null); soapClient.setHeader(soapHeaderElement); } catch (SOAPException e) { throw new ServiceException("Could not set header.", e); } } /** * Updates the child attribute of headerName named childName to childValue. * * @param soapClient the stub * @param parentHeaderName the name of the parent header * @param childName the name of the child * @param childValue the value for the child * * @throws NullPointerException if no header exists named parentHeaderName */ public void setHeaderChild(Stub soapClient, String parentHeaderName, String childName, Object childValue) { SOAPHeaderElement headerElement = (SOAPHeaderElement) getHeader(soapClient, parentHeaderName); Object headerObject = Preconditions.checkNotNull(headerElement, "Parent header named %s does not exist", parentHeaderName).getObjectValue(); try { BeanUtils.setProperty(headerObject, childName, childValue); } catch (IllegalAccessException e) { throw new ServiceException("Failed to set header child " + childName, e); } catch (InvocationTargetException e) { throw new ServiceException("Failed to set header child " + childName, e); } } /** * @see SoapClientHandler#putAllHttpHeaders(Object, Map) */ @Override public void putAllHttpHeaders(Stub soapClient, Map<String, String> headersMap) { @SuppressWarnings("unchecked") Hashtable<String, String> headers = (Hashtable<String, String>) soapClient._getProperty(HTTPConstants.REQUEST_HEADERS); if (headers == null) { headers = new Hashtable<String, String>(); } headers.putAll(headersMap); soapClient._setProperty(HTTPConstants.REQUEST_HEADERS, headers); } /** * Set whether SOAP requests should use compression. * * @param soapClient the client to set compression settings for * @param compress whether or not to use compression */ @Override public void setCompression(Stub soapClient, boolean compress) { soapClient._setProperty(HTTPConstants.MC_ACCEPT_GZIP, compress); soapClient._setProperty(HTTPConstants.MC_GZIP_REQUEST, compress); } /** * Creates a SOAP client using a SOAP service descriptor. * * @param soapServiceDescriptor the descriptor to use for creating a client * @return the SOAP client for this descriptor * @throws ServiceException thrown if the SOAP client cannot be created */ @Override public Stub createSoapClient(SoapServiceDescriptor soapServiceDescriptor) throws ServiceException { try { if (soapServiceDescriptor instanceof AxisCompatible) { AxisCompatible axisCompatibleService = (AxisCompatible) soapServiceDescriptor; EngineConfiguration engineConfiguration = engineConfigurationFactory.getClientEngineConfig(); Service locator = (Service) axisCompatibleService.getLocatorClass() .getConstructor(new Class[] {EngineConfiguration.class}) .newInstance(new Object[] {engineConfiguration}); return (Stub) locator.getClass().getMethod("getPort", Class.class) .invoke(locator, soapServiceDescriptor.getInterfaceClass()); } throw new ServiceException( "Service [" + soapServiceDescriptor + "] not compatible with Axis", null); } catch (SecurityException e) { throw new ServiceException("Unexpected Exception.", e); } catch (NoSuchMethodException e) { throw new ServiceException("Unexpected Exception.", e); } catch (IllegalArgumentException e) { throw new ServiceException("Unexpected Exception.", e); } catch (IllegalAccessException e) { throw new ServiceException("Unexpected Exception.", e); } catch (InvocationTargetException e) { throw new ServiceException("Unexpected Exception.", e); } catch (ClassNotFoundException e) { throw new ServiceException("Unexpected Exception.", e); } catch (InstantiationException e) { throw new ServiceException("Unexpected Exception.", e); } } /** * Invoke a SOAP call. * * @param soapCall the call to make to a SOAP web service * @return information about the SOAP response */ @Override public SoapCallReturn invokeSoapCall(SoapCall<Stub> soapCall) { Stub stub = soapCall.getSoapClient(); SoapCallReturn.Builder builder = new SoapCallReturn.Builder(); synchronized (stub) { Object result = null; try { result = invoke(soapCall); } catch (InvocationTargetException e) { builder.withException(e.getTargetException()); } catch (Exception e) { builder.withException(e); } finally { MessageContext messageContext = stub._getCall().getMessageContext(); try { builder.withRequestInfo(new RequestInfo.Builder().withSoapRequestXml( messageContext.getRequestMessage().getSOAPPartAsString()) .withMethodName(stub._getCall().getOperationName().getLocalPart()) .withServiceName(stub.getPortName().getLocalPart()) .withUrl(stub._getCall().getTargetEndpointAddress()) .build()); } catch (AxisFault e) { builder.withException(e); } String requestId = null; Message responseMessage = messageContext.getResponseMessage(); try { if (!requestIdXPathComponents.isEmpty() && responseMessage != null) { Node requestIdNode = nodeExtractor.extractNode( responseMessage.getSOAPHeader(), requestIdXPathComponents); if (requestIdNode != null) { requestId = requestIdNode.getFirstChild().getNodeValue(); } } } catch (SOAPException e) { // Ignore, since capturing the requestId is not critical. } try { builder.withResponseInfo( new ResponseInfo.Builder() .withSoapResponseXml( responseMessage == null ? null : responseMessage.getSOAPPartAsString()) .withRequestId(requestId) .build()); } catch (AxisFault e) { builder.withException(e); } } return builder.withReturnValue(result).build(); } } /** * @see SoapClientHandlerInterface#getEndpointAddress(Object) */ @Override public String getEndpointAddress(Stub soapClient) { return (String) soapClient._getProperty(Stub.ENDPOINT_ADDRESS_PROPERTY); } /** * @see SoapClientHandlerInterface#createSoapHeaderElement(QName) */ @Override public javax.xml.soap.SOAPHeaderElement createSoapHeaderElement(QName qName) { return new SOAPHeaderElement(qName); } }