001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.statemachine.transition;
021
022import java.lang.reflect.InvocationTargetException;
023import java.lang.reflect.Method;
024import java.util.Arrays;
025
026import org.apache.mina.statemachine.State;
027import org.apache.mina.statemachine.StateMachine;
028import org.apache.mina.statemachine.context.StateContext;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032/**
033 * {@link SelfTransition} which invokes a {@link Method}. The {@link Method} can
034 * have zero or any number of StateContext and State regarding order
035 * <p>
036 * Normally you wouldn't create instances of this class directly but rather use
037 * the {@link SelfTransition} annotation to define the methods which should be
038 * used as transitions in your state machine and then let
039 * {@link org.apache.mina.statemachine.StateMachineFactory} create a
040 * {@link StateMachine} for you.
041 * </p>
042 * 
043 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
044 */
045public class MethodSelfTransition extends AbstractSelfTransition {
046    private static final Logger LOGGER = LoggerFactory.getLogger(MethodTransition.class);
047
048    private Method method;
049
050    private final Object target;
051
052    private static final Object[] EMPTY_ARGUMENTS = new Object[0];
053
054    public MethodSelfTransition(Method method, Object target) {
055        super();
056        this.method = method;
057        this.target = target;
058    }
059
060    /**
061     * Creates a new instance
062     * 
063     * @param methodName
064     *            the target method.
065     * @param target
066     *            the target object.
067     */
068    public MethodSelfTransition(String methodName, Object target) {
069
070        this.target = target;
071
072        Method[] candidates = target.getClass().getMethods();
073        Method result = null;
074
075        for (int i = 0; i < candidates.length; i++) {
076            if (candidates[i].getName().equals(methodName)) {
077                if (result != null) {
078                    throw new AmbiguousMethodException(methodName);
079                }
080                result = candidates[i];
081            }
082        }
083
084        if (result == null) {
085            throw new NoSuchMethodException(methodName);
086        }
087
088        this.method = result;
089
090    }
091
092    /**
093     * @return the target {@link Method}.
094     */
095    public Method getMethod() {
096        return method;
097    }
098
099    public boolean doExecute(StateContext stateContext, State state) {
100        Class<?>[] types = method.getParameterTypes();
101
102        if (types.length == 0) {
103            invokeMethod(EMPTY_ARGUMENTS);
104            return true;
105        }
106
107        if (types.length > 2) {
108            return false;
109        }
110
111        Object[] args = new Object[types.length];
112
113        int i = 0;
114
115        if (types[i].isAssignableFrom(StateContext.class)) {
116            args[i++] = stateContext;
117        }
118        if ((i < types.length) && types[i].isAssignableFrom(State.class)) {
119            args[i++] = state;
120        }
121
122        invokeMethod(args);
123
124        return true;
125    }
126
127    private void invokeMethod(Object[] arguments) {
128        try {
129            if (LOGGER.isDebugEnabled()) {
130                LOGGER.debug("Executing method " + method + " with arguments " + Arrays.asList(arguments));
131            }
132            method.invoke(target, arguments);
133        } catch (InvocationTargetException ite) {
134            if (ite.getCause() instanceof RuntimeException) {
135                throw (RuntimeException) ite.getCause();
136            }
137            throw new MethodInvocationException(method, ite);
138        } catch (IllegalAccessException iae) {
139            throw new MethodInvocationException(method, iae);
140        }
141    }
142
143}