/* * * * Copyright 2010, Unitils.org * * * * 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 org.unitils.mock.core.proxy; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import org.unitils.core.UnitilsException; import java.lang.reflect.Method; import java.util.List; import static java.lang.reflect.Modifier.isAbstract; import static java.util.Arrays.asList; import static org.unitils.mock.core.proxy.ProxyUtils.getProxiedMethodStackTrace; import static org.unitils.util.MethodUtils.*; /** * A cglib method intercepter that will delegate the invocations to the given invocation hanlder. */ public class CglibProxyMethodInterceptor<T> implements MethodInterceptor { private String mockName; private Class<T> proxiedType; /* The invocation handler */ private ProxyInvocationHandler invocationHandler; /** * Creates an interceptor. * * @param mockName The name of the mock, not null * @param proxiedType The proxied type, not null * @param invocationHandler The handler to delegate the invocations to, not null */ public CglibProxyMethodInterceptor(String mockName, Class<T> proxiedType, ProxyInvocationHandler invocationHandler) { this.mockName = mockName; this.proxiedType = proxiedType; this.invocationHandler = invocationHandler; } /** * Intercepts the method call by wrapping the invocation in a {@link CglibProxyInvocation} and delegating the * handling to the invocation handler. * * @param proxy The proxy, not null * @param method The method that was called, not null * @param arguments The arguments that were used, not null * @param methodProxy The cglib method proxy, not null * @return The value to return for the method call, ignored for void methods */ public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy) throws Throwable { if (isFinalizeMethod(method)) { return null; } else if (isEqualsMethod(method)) { return proxy == arguments[0]; } else if (isHashCodeMethod(method)) { return super.hashCode(); } else if (isCloneMethod(method)) { return proxy; } else if (isToStringMethod(method)) { return getProxiedType().getSimpleName() + "@" + Integer.toHexString(super.hashCode()); } ProxyInvocation invocation = new CglibProxyInvocation(mockName, method, asList(arguments), getProxiedMethodStackTrace(), proxy, methodProxy); return invocationHandler.handleInvocation(invocation); } public String getMockName() { return mockName; } /** * @return The proxied type, not null */ public Class<?> getProxiedType() { return proxiedType; } /** * An invocation implementation that uses the cglib method proxy to be able to invoke the original behavior. */ public static class CglibProxyInvocation extends ProxyInvocation { /* The cglib method proxy */ private MethodProxy methodProxy; /** * Creates an invocation. * * @param mockName The name of the mock, not null * @param method The method that was called, not null * @param arguments The arguments that were used, not null * @param invokedAt The location of the invocation, not null * @param proxy The proxy, not null * @param methodProxy The cglib method proxy, not null */ public CglibProxyInvocation(String mockName, Method method, List<Object> arguments, StackTraceElement[] invokedAt, Object proxy, MethodProxy methodProxy) { super(mockName, proxy, method, arguments, invokedAt); this.methodProxy = methodProxy; } /** * Invokes the original behavior by calling the method proxy. * If there is no original behavior, e.g. an interface or abstract method, an exception is raised. * * @return The result value */ @Override public Object invokeOriginalBehavior() throws Throwable { Method method = getMethod(); if (isAbstract(method.getModifiers())) { throw new UnitilsException("Unable to invoke original behavior. The method is abstract, it does not have any behavior defined: " + getMethod()); } return methodProxy.invokeSuper(getProxy(), getArguments().toArray()); } } }