/* * Copyright 2012 ETH Zuerich, CISD * * 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 ch.systemsx.cisd.common.conversation; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import ch.systemsx.cisd.common.serviceconversation.server.ServiceConversationServer; /** * MethodInvocation represents a remote method invocation. It contains the name and the arguments of * a method to be executed on a remote server. It is Serializable to be transferable through the * service conversation framework. * * @author anttil */ public class MethodInvocation implements Serializable { private static final long serialVersionUID = 8679256131459236150L; private String methodName; private Object[] arguments; public MethodInvocation(String methodName, Object[] arguments) { this.methodName = methodName; this.arguments = arguments; } /** * Executes the method on given target object. Adds a ProgressListener instance as a last * argument of the call. * * @param target The target object on which the method call will be executed * @param server ServiceConversationServer that will receive the progress reports * @param conversationId Id of the conversation * @param clientTimeOut The remote client making this method call will abort if it has not * received any messages from the server within the timeout (represented in * milliseconds) * @returns The return value of the method call */ public Serializable executeOn(Object target, ServiceConversationServer server, String conversationId, int clientTimeOut) { RateLimitedProgressListener progressListener = new RateLimitedProgressListener(server, conversationId, clientTimeOut / 10); Object[] argumentsWithProgressListener = createArgumentsWithProgressListener(progressListener); try { Method m = findMethodOn(target, this.methodName, argumentsWithProgressListener); return (Serializable) m.invoke(target, argumentsWithProgressListener); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) e.getCause(); } else { throw new RuntimeException(cause); } } catch (Exception e) { throw new RuntimeException("Method call failed", e); } finally { progressListener.close(); } } private Object[] createArgumentsWithProgressListener(IProgressListener progressListener) { List<Object> argumentsWithProgressListener = new ArrayList<Object>(); argumentsWithProgressListener.addAll(Arrays.asList(this.arguments)); argumentsWithProgressListener.add(progressListener); return argumentsWithProgressListener.toArray(new Object[0]); } private Method findMethodOn(Object o, String name, Object[] args) throws SecurityException, NoSuchMethodException { for (Class<?> inter : o.getClass().getInterfaces()) { for (Method method : inter.getMethods()) { if (!method.getName().equals(name)) { continue; } Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length != args.length) { continue; } boolean matches = true; for (int i = 0; i < parameterTypes.length; i++) { if (!parameterTypes[i].isAssignableFrom(args[i].getClass())) { matches = false; break; } } if (matches) { return method; } } } throw new NoSuchMethodException(); } @Override public String toString() { return "MethodCall " + this.methodName + "(" + Arrays.asList(this.arguments) + ")"; } public String getMethodName() { return this.methodName; } public Object[] getArguments() { return this.arguments; } }