Skip to content
Snippets Groups Projects
MethodInvocation.java 4.97 KiB
Newer Older
/*
 * 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);
            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();
    public String toString()
    {
        return "MethodCall " + this.methodName + "(" + Arrays.asList(this.arguments) + ")";
        return this.methodName;
        return this.arguments;
    }
}