package de.dal33t.powerfolder.clientserver;
/*
* Copyright 2004 - 2008 Christian Sprajc. All rights reserved.
*
* This file is part of PowerFolder.
*
* PowerFolder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* PowerFolder 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PowerFolder. If not, see <http://www.gnu.org/licenses/>.
*
* $Id$
*/
import java.awt.EventQueue;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import de.dal33t.powerfolder.ConfigurationEntry;
import de.dal33t.powerfolder.Controller;
import de.dal33t.powerfolder.Member;
import de.dal33t.powerfolder.message.Identity;
import de.dal33t.powerfolder.message.clientserver.RemoteMethodCallRequest;
import de.dal33t.powerfolder.message.clientserver.RemoteMethodCallResponse;
import de.dal33t.powerfolder.net.ConnectionException;
import de.dal33t.powerfolder.util.Reject;
import de.dal33t.powerfolder.util.Util;
public class RemoteServiceStubFactory {
private static final Logger LOG = Logger
.getLogger(RemoteServiceStubFactory.class.getName());
private RemoteServiceStubFactory() {
// No instance allowed
}
/**
* Constructs a stub implementing the given service interface. All calls are
* executed against the remote service repository of the remote site.
*
* @param <T>
* The interface class of the service
* @param controller
* the controller
* @param serviceInterface
* @param remoteSide
* @return the remote stub implementing the service interface
*/
public static <T> T createRemoteStub(Controller controller,
Class<? extends T> serviceInterface, Member remoteSide)
{
return createRemoteStub(controller, serviceInterface, remoteSide, null);
}
/**
* Constructs a stub implementing the given service interface. All calls are
* executed against the remote service repository of the remote site.
*
* @param <T>
* The interface class of the service
* @param controller
* the controller
* @param serviceInterface
* @param remoteSide
* @param throwableHandler
* @return the remote stub implementing the service interface
*/
@SuppressWarnings("unchecked")
public static <T> T createRemoteStub(Controller controller,
Class<? extends T> serviceInterface, Member remoteSide,
ThrowableHandler throwableHandler)
{
Reject.ifNull(controller, "Controller is null");
Reject.ifNull(remoteSide, "Remote site is null");
Reject.ifFalse(serviceInterface.isInterface(),
"Service interface class is not a interface! " + serviceInterface);
InvocationHandler handler = new RemoteInvocationHandler(controller,
serviceInterface.getName(), remoteSide, throwableHandler);
return (T) Proxy.newProxyInstance(RemoteServiceStubFactory.class
.getClassLoader(), new Class[]{serviceInterface}, handler);
}
private static class RemoteInvocationHandler implements InvocationHandler {
private Controller controller;
private Member remoteSide;
private String serviceId;
private ThrowableHandler throwableHandler;
private RemoteInvocationHandler(Controller controller,
String serviceId, Member remoteSide,
ThrowableHandler throwableHandler)
{
this.controller = controller;
this.remoteSide = remoteSide;
this.serviceId = serviceId;
this.throwableHandler = throwableHandler;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
if (Util.isAwtAvailable() && EventQueue.isDispatchThread()) {
LOG.log(Level.WARNING, "Call to remote service method ("
+ method + ") executed in EDT thread. Args: "
+ (args != null ? Arrays.asList(args) : "n/a"),
new RuntimeException("here"));
}
if (ServerClient.SERVER_HANDLE_MESSAGE_THREAD.get()) {
LOG.log(Level.WARNING, "Call to remote service method ("
+ method + ") executed Server/Member.handleMessage. Args: "
+ (args != null ? Arrays.asList(args) : "n/a"),
new RuntimeException("here"));
}
Identity id = remoteSide.getIdentity();
RequestExecutor executor = new RequestExecutor(controller,
remoteSide);
RemoteMethodCallRequest request = new RemoteMethodCallRequest(
serviceId, method, args);
if (id == null || id.isSupportsSerializedRequest()) {
request = request.toSerzializedForm();
} else {
if (ConfigurationEntry.SECURITY_PERMISSIONS_STRICT
.getValueBoolean(controller))
{
LOG
.severe("Using strict permission security setting while executing legacy type request."
+ "Please check program version of "
+ remoteSide
+ ": "
+ (id == null ? "" : id.getProgramVersion())
+ ". Request: " + request);
}
}
RemoteMethodCallResponse response;
try {
response = (RemoteMethodCallResponse) executor.execute(request);
} catch (ConnectionException e) {
throw new RemoteCallException(e);
}
if (response.isException()) {
StackTraceElement[] serverSte = response.getException()
.getStackTrace();
StackTraceElement[] clientSte = new RuntimeException()
.getStackTrace();
StackTraceElement[] fullSte = new StackTraceElement[serverSte.length
+ clientSte.length];
System.arraycopy(serverSte, 0, fullSte, 0, serverSte.length);
System.arraycopy(clientSte, 0, fullSte, serverSte.length,
clientSte.length);
response.getException().setStackTrace(fullSte);
boolean exceptionDeclared = Arrays.asList(
method.getExceptionTypes()).contains(
response.getException().getClass());
if (exceptionDeclared) {
throw response.getException();
}
if (throwableHandler != null) {
try {
throwableHandler.handle(response.getException());
} catch (Exception e) {
LOG
.warning("ThrowableHandler threw exception! What a pity! "
+ e);
}
}
if (response.getException() instanceof RuntimeException) {
throw (RuntimeException) response.getException();
}
throw new RemoteCallException(response.getException());
}
return response.getResult();
}
}
}