blob: c8dcae0c02c79f3f5a5c883490421bc7e756e82a [file] [log] [blame]
// 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));
}
}