/** * Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org> * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.seedstack.seed.core.internal.dependency; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import org.seedstack.seed.SeedException; import org.seedstack.seed.core.internal.CoreErrorCode; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Proxy to implement abstract class. Override methods to proxy.<br> * For example: to create a proxy for a RatioGauge abstract class * * <pre> * DependencyClassProxy<RatioGauge> ratio = new DependencyClassProxy<RatioGauge>(RatioGauge.class, new ProxyMethodReplacer() { * public Ratio getRatio() { * return Ratio.of(2, 1); * } * }); * * </pre> * * @param <T> class to proxy */ public class DependencyClassProxy<T> implements MethodHandler { private final T proxy; private final Object substitute; /** * Create a proxy replacing methods of a class. * * @param clazz the class to be proxied. * @param substitute the substitute implementation. */ @SuppressWarnings("unchecked") public DependencyClassProxy(Class<T> clazz, final Object substitute) { this.substitute = substitute; try { ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(clazz); factory.setFilter(method -> { for (Method m : substitute.getClass().getDeclaredMethods()) { if (m.getName().equals(method.getName())) return true; } return false; }); this.proxy = (T) factory.create(new Class<?>[0], new Object[0], this); } catch (Exception e) { throw SeedException.wrap(e, CoreErrorCode.UNABLE_TO_CREATE_PROXY).put("class", clazz.getName()); } } public T getProxy() { return proxy; } @Override public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { try { Method m = substitute.getClass().getMethod(thisMethod.getName(), thisMethod.getParameterTypes()); m.setAccessible(true); return m.invoke(substitute, args); } catch (InvocationTargetException e) { throw e.getCause(); } } }