/*
Copyright (c) 2007 Health Market Science, Inc.
This library 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.
This library 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 library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
You can contact Health Market Science at info@healthmarketscience.com
or at the following address:
Health Market Science
2700 Horizon Drive
Suite 200
King of Prussia, PA 19406
*/
package com.healthmarketscience.rmiio;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import java.lang.reflect.Proxy;
/**
* Base class for implementing remote stub wrappers with builtin retry
* policies. Providers of remote interfaces can generate simple wrappers for
* use by a remote client which handle the retry functionality under the hood,
* simplifying client code dramatically. Note that this should only be done
* for <b>idempotent</b> method calls. See {@link RemoteInputStreamWrapper}
* and {@link RemoteOutputStreamWrapper} for example usage.
* <p>
* While subclassing presents an ability to fine tune the wrapper
* implementation, it may be difficult and/or unnecessary for many interfaces.
* In that case, a much simpler wrapper can be created using the {@link #wrap}
* method, which uses the java {@code Proxy} functionality to generate the
* wrapper implementation at run time. This may be slightly less efficient
* than a custom implementation since reflection is used for the actual method
* invocations, but for remote method calls that overhead is probably
* meaningless.
*
* @author James Ahlborn
*/
public class RemoteWrapper<RemoteType>
implements InvocationHandler, RemoteClient
{
/** the handle to the remote interface which will do the real work of the
remote method calls */
protected final RemoteType _stub;
/** the retry policy we are using for our remote calls */
protected RemoteRetry _retry;
/** the log which will be used by the retry facility when making the remote
calls */
protected final Logger _log;
public RemoteWrapper(RemoteType stub, RemoteRetry retry, Logger log) {
_stub = stub;
_retry = retry;
_log = log;
}
/**
* Simple wrapper generator which creates a Proxy for the given remote
* interface. This proxy will make all the remote calls through the
* {@link #invoke} method which makes the actual method calls on the
* underlying stub within the retry logic.
*
* @param iface the remote interface to be implemented
* @param stub the underlying implementation of the remote interface which
* will actually make the remote calls
* @param retry the retry policy to use for the remote calls
* @param log the log to use during retry handling
*
* @return a proxy for the given interface using the given retry strategy
*/
public static <R> R wrap(Class<R> iface, R stub,
RemoteRetry retry, Logger log)
{
RemoteWrapper<R> wrapper = new RemoteWrapper<R>(stub, retry, log);
return iface.cast(Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[]{iface}, wrapper));
}
/**
* Gets the wrapper underlying a proxy created by a call to {@link #wrap}.
*/
public static RemoteWrapper<?> getWrapper(Object proxy)
{
return (RemoteWrapper<?>)Proxy.getInvocationHandler(proxy);
}
public RemoteType getStub() {
return _stub;
}
public Logger getLog() {
return _log;
}
public RemoteRetry getRemoteRetry() {
return _retry;
}
/**
* {@inheritDoc}
* <p>
* This may be useful for temporarily changing the retry policy for a
* specific set of calls (e.g. a startup/discovery sequence may be more
* forgiving than normal usage).
* <p>
* Note, this method is not thread-safe as this should only be used on a
* wrapper for which the caller has exclusive ownership (the retry policy
* will be changed for all users of the wrapper).
*/
public void setRemoteRetry(RemoteRetry retry) {
_retry = ((retry != null) ? retry : DEFAULT_RETRY);
}
public Object invoke(Object proxy, final Method method, final Object[] args)
throws Throwable
{
// make the method call on the actual remote stub within the retry handler
return _retry.call(new RemoteRetry.Caller<Object>()
{
@Override
public Object call() throws Exception {
return method.invoke(_stub, method, args);
}
}, _log, Exception.class);
}
}