| // Copyright (C) 2020 The Android Open Source Project |
| // |
| // 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.googlesource.gerrit.plugins.replication; |
| |
| import com.google.gerrit.common.Nullable; |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| |
| /** |
| * A ForwardingProxy creates a Proxy which forwards all method calls to its delegate except for |
| * calls to methods which are implemented by its overrider's class. |
| * |
| * <p>Using this Proxy class makes it possible to use the delegate pattern on any interface without |
| * having to implement any of the interface's methods which directly forward their calls to the |
| * delegate. Using this is intended to make forwarding automated, easy, and less error prone by |
| * making it possible to implement the delegate pattern with an overrider object which only |
| * implements those methods which need overridden functionality and which will not directly forward |
| * their calls to the delegate. |
| * |
| * <p>The overrider object will be assumed to not implement any default java Object methods which |
| * are not overridden, as that would likely not be desirable behavior, and thus the Proxy will not |
| * forward those methods to the overrider unless the overrider overrides them. |
| * |
| * <p>If an overrider needs to make calls to the delegate, this can be achieved by passing the |
| * delegate into the overrider during construction. |
| */ |
| public class ForwardingProxy { |
| protected static class Handler<T> implements InvocationHandler { |
| protected T delegate; |
| protected Object overrider; |
| |
| protected Handler(T delegate, Object overrider) { |
| this.delegate = delegate; |
| this.overrider = overrider; |
| } |
| |
| @Override |
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
| Method overriden = getOverriden(method); |
| if (overriden != null) { |
| return overriden.invoke(overrider, args); |
| } |
| return method.invoke(delegate, args); |
| } |
| |
| @Nullable |
| protected Method getOverriden(Method method) { |
| try { |
| Method implementedByOverrider = |
| overrider.getClass().getMethod(method.getName(), method.getParameterTypes()); |
| |
| // Only allow defined (non java defaulted) methods to actually be overridden |
| if (Object.class != implementedByOverrider.getDeclaringClass()) { |
| return implementedByOverrider; |
| } |
| } catch (NoSuchMethodException | SecurityException e) { |
| return null; |
| } |
| return null; |
| } |
| } |
| |
| @SuppressWarnings("unchecked") // newProxyInstance returns Object |
| public static <T> T create(Class<T> toProxy, T delegate, Object overrider) { |
| return (T) |
| Proxy.newProxyInstance( |
| delegate.getClass().getClassLoader(), |
| new Class[] {toProxy}, |
| new Handler<>(delegate, overrider)); |
| } |
| } |