/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.container.api; import static java.lang.reflect.Proxy.getInvocationHandler; import static java.lang.reflect.Proxy.isProxyClass; import static java.util.Arrays.asList; import static org.mule.runtime.api.util.Preconditions.checkArgument; import org.mule.runtime.api.service.Service; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.LinkedList; import java.util.List; /** * Extends {@link InvocationHandler} to provide and expose metadata about the inner {@link Service} implementation. * <p> * This allows for nested {@link Service} {@link Proxy}ies to work as expected. * * @since 4.0 */ public abstract class ServiceInvocationHandler implements InvocationHandler { private final Service service; /** * Creates a new proxy for the provided service instance. * * @param service service instance to wrap. Non null. */ protected ServiceInvocationHandler(Service service) { checkArgument(service != null, "service cannot be null"); this.service = service; } /** * @return the methods declared in the implementation of the proxied service. */ protected Method[] getServiceImplementationDeclaredMethods() { if (isProxyClass(getService().getClass()) && getInvocationHandler(getService()) instanceof ServiceInvocationHandler) { return ((ServiceInvocationHandler) getInvocationHandler(getService())).getServiceImplementationDeclaredMethods(); } else { List<Method> methods = new LinkedList<>(); Class<?> clazz = getService().getClass(); while (clazz != Object.class) { methods.addAll(asList(clazz.getDeclaredMethods())); clazz = clazz.getSuperclass(); } return methods.toArray(new Method[methods.size()]); } } /** * Performs the actual invocation on the proxied {@link Service}, or delegates the call to an inner proxy. * * See {@link InvocationHandler#invoke(Object, Method, Object[])} */ protected Object doInvoke(Object proxy, Method method, Object[] args) throws Throwable { if (isProxyClass(getService().getClass()) && getInvocationHandler(getService()) instanceof ServiceInvocationHandler) { return ((ServiceInvocationHandler) getInvocationHandler(getService())).invoke(getService(), method, args); } else { try { return method.invoke(getService(), args); } catch (InvocationTargetException ite) { // Unwrap target exception to ensure InvocationTargetException (in case of unchecked exceptions) or // UndeclaredThrowableException (in case of checked exceptions) is not thrown by Service instead of target exception. throw ite.getTargetException(); } } } /** * The proxied {@link Service}. */ protected Service getService() { return service; } }