// 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; import com.google.api.ads.common.lib.exception.AuthenticationException; import com.google.api.ads.common.lib.exception.ServiceException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * The {@code SoapServiceClient} class pairs together the {@code soapClient} and * {@code soapClientHandler} so that calls made through the * {@link #invoke(Object, Method, Object[])} method can be dispatched and * handled correctly in the SOAP layer. This class is not aware of what API it * uses and relies on the {@code soapClientHandler} to determine how the SOAP * framework should be used. * * @param <T> the type of SOAP client */ public abstract class SoapServiceClient<T> implements InvocationHandler { private final SoapClientHandlerInterface<T> soapClientHandler; private final T soapClient; /** * Protected constructor. * * @param soapClientHandler the SOAP client handler * @param soapClient the SOAP client */ protected SoapServiceClient(SoapClientHandlerInterface<T> soapClientHandler, T soapClient) { this.soapClient = soapClient; this.soapClientHandler = soapClientHandler; } /** * Called from {@link #invoke(Object, Method, Object[])} if the method is * intended for the SOAP client. Extending classes should override this method * if they wish to wrap the call, such in cases of reauthentication or * exception handling. The actual SOAP call is synchronized on the SOAP client * so that only one request to the SOAP client can be made with without * interruption, useful for logging and exception handling. * * @param soapCall the call to send to the SOAP client * @return the return value from the {@code soapCall} */ protected synchronized SoapCallReturn callSoapClient(SoapCall<T> soapCall) { return soapClientHandler.invokeSoapCall(soapCall); } /** * Wraps the underlying SOAP RPC such that first the method, by its name, * is applied to the runtime class. If no such method exists, it is assumed * that the call is meant for the SOAP client. In this case, the SOAP client * handler will invoke the SOAP client method with provided arguments. * * @param proxy the proxy class that invoke was called on * @param method the method to apply to the proxy class or the underlying SOAP * client * @param args the method arguments * @return the return from the {@code SoapServiceClient} or a * {@link SoapCallReturn} object containing the result from the SOAP call * @see InvocationHandler#invoke(Object, Method, Object[]) * @throws Throwable thrown if the SOAP call passed into this method results * in an exception. The exception thrown will be not be wrapped - it will * adhere to the "throws" clause of the passed in {@code Method}. */ @Override public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable { try { return getClass().getMethod(method.getName(), method.getParameterTypes()) .invoke(this, args); } catch (NoSuchMethodException e) { // Ignore and let the SOAP client handler take over. } setHeaders(); SoapCallReturn soapCallReturn = callSoapClient( createSoapCall(soapClientHandler.getSoapClientMethod(soapClient, method), args)); logSoapCall(soapCallReturn); return unwrapSoapCallReturn(soapCallReturn); } /** * Creates the {@link SoapCall} from the {@code soapClientMethod} and its * {@code args}. */ protected SoapCall<T> createSoapCall(Method soapClientMethod, Object[] args) { return new SoapCall<T>(soapClientMethod, soapClient, args); } /** * Sets the endpoint address of the underlying SOAP client. */ public void setEndpointAddress(String endpointAddress) { soapClientHandler.setEndpointAddress(soapClient, endpointAddress); } /** * Returns the underlying SOAP client. */ public Object getSoapClient() { return soapClient; } /** * Returns the SOAP client handler. */ protected SoapClientHandlerInterface<T> getSoapClientHandler() { return soapClientHandler; } /** * Logs a SOAP call. * * @param soapCallReturn */ protected abstract void logSoapCall(SoapCallReturn soapCallReturn); /** * Sets the headers for the service client. * * @throws ServiceException if there was a problem setting the headers * @throws AuthenticationException if there was a problem authenticating while * setting headers */ protected abstract void setHeaders() throws ServiceException, AuthenticationException; /** * Handles the exception. * <p> * Default behavior is to return the exception unchanged. * </p> * * @param e the exception to handle * @return the exception which may have been modified (wrapped/unwrapped) */ protected Throwable handleException(Throwable e) { return e; } /** * Unwraps a SOAP call return such that if there was an exception, it is * thrown and if it was a successful call, the return value of the SOAP call * is returned. * * @param soapCallReturn the {@code SoapCallReturn} to unwrap * @return the {@link SoapCallReturn#getReturnValue()} if the call was * successful * @throws Throwable the exception captured in the * {@link SoapCallReturn#getException()} if present */ protected Object unwrapSoapCallReturn(SoapCallReturn soapCallReturn) throws Throwable { if (soapCallReturn.getException() != null) { throw handleException(soapCallReturn.getException()); } else { return soapCallReturn.getReturnValue(); } } }