/** * JBoss, Home of Professional Open Source * Copyright ${year}, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.arquillian.rusheye.suite.utils; import static java.lang.reflect.Modifier.isFinal; import static java.lang.reflect.Modifier.isPrivate; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import javassist.util.proxy.ProxyObject; import org.apache.commons.lang.ArrayUtils; import org.arquillian.rusheye.suite.annotations.Nullify; public final class NullingProxy { private NullingProxy() { } public static <T> T handle(T instance, Class<? extends Annotation> nullified) throws InstantiationException, IllegalAccessException { Class<?> originalClass = (Class<?>) instance.getClass(); if (instance instanceof ProxyObject) { originalClass = (Class<?>) originalClass.getSuperclass(); } ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setSuperclass(originalClass); Class<?> proxyClass = proxyFactory.createClass(); MethodHandler handler = new NullingHandler(instance, nullified); @SuppressWarnings("unchecked") T proxyInstance = (T) proxyClass.newInstance(); ((ProxyObject) proxyInstance).setHandler(handler); return proxyInstance; } public static class NullingHandler implements InvocationHandler, MethodHandler { Object object; Class<? extends Annotation> nullified; public NullingHandler(Object object, Class<? extends Annotation> nullified) { this.object = object; this.nullified = nullified; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Nullify nullify = method.getAnnotation(Nullify.class); if (nullify != null) { if (ArrayUtils.contains(nullify.value(), nullified)) { return null; } } Object result = method.invoke(object, args); if (result == null) { return result; } else if (method.getReturnType().isInterface()) { return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { method.getReturnType() }, new NullingHandler(result, nullified)); } else if (canBeProxied(result)) { return NullingProxy.handle(result, nullified); } else { return result; } } private boolean canBeProxied(Object object) { Class<?> type = object.getClass(); int mod = type.getModifiers(); return !(isFinal(mod) || isPrivate(mod) || !hasNoParametricConstructor(type)); } private boolean hasNoParametricConstructor(Class<?> type) { try { type.getConstructor(); return true; } catch (SecurityException e) { throw new IllegalStateException(e); } catch (NoSuchMethodException e) { return false; } } @Override public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { return invoke(self, thisMethod, args); } } }