Skip to content
Snippets Groups Projects
MethodInvocation.java 4.44 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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)
            {
                throw (RuntimeException)e.getCause();
            } 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 (Method method : o.getClass().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;
        }
    }