org.soybeanMilk.core.exe.Invoke.java Source code

Java tutorial

Introduction

Here is the source code for org.soybeanMilk.core.exe.Invoke.java

Source

/**
 * 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 org.soybeanMilk.core.exe;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Arrays;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.soybeanMilk.SbmUtils;
import org.soybeanMilk.core.ExecuteException;
import org.soybeanMilk.core.ObjectSource;
import org.soybeanMilk.core.ObjectSourceException;
import org.soybeanMilk.core.exe.AbstractExecutable;
import org.soybeanMilk.core.exe.ArgPrepareExecuteException;
import org.soybeanMilk.core.exe.InvocationExecuteException;

/**
 * 
 * @author earthangry@gmail.com
 * @date 2010-9-30
 */
public class Invoke extends AbstractExecutable {
    private static final long serialVersionUID = 1L;

    private static Log log = LogFactory.getLog(Invoke.class);

    /**??*/
    private String methodName;

    /**?*/
    private transient Arg[] args;

    /**?*/
    private Serializable resultKey;

    /***/
    private transient Resolver resolver;

    /**???*/
    private Serializable breaker;

    private transient volatile MethodInfo methodInfo;

    public Invoke() {
        super();
    }

    public Invoke(String name, Resolver resolver, String methodName, Arg[] args, Serializable resultKey) {
        super.setName(name);
        this.methodName = methodName;
        this.args = args;
        this.resolver = resolver;
        this.resultKey = resultKey;
    }

    /**
     * ???
     * @return
     * @date 2012-5-9
     */
    public String getMethodName() {
        return methodName;
    }

    /**
     * ??
     * @return
     * @date 2012-5-9
     */
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    /**
     * ??
     * @return
     * @date 2012-5-9
     */
    public Arg[] getArgs() {
        return args;
    }

    /**
     * ?
     * @return
     * @date 2012-5-9
     */
    public void setArgs(Arg[] args) {
        this.args = args;
    }

    /**
     * ?
     * @return
     * @date 2012-5-9
     */
    public Serializable getResultKey() {
        return resultKey;
    }

    /**
     * 
     * @return
     * @date 2012-5-9
     */
    public void setResultKey(Serializable resultKey) {
        this.resultKey = resultKey;
    }

    /**
     * ?
     * @return
     * @date 2012-5-9
     */
    public Resolver getResolver() {
        return resolver;
    }

    /**
     * 
     * @return
     * @date 2012-5-9
     */
    public void setResolver(Resolver resolver) {
        this.resolver = resolver;
    }

    /**
     * ?
     * @return
     * @date 2012-5-9
     */
    public Serializable getBreaker() {
        return breaker;
    }

    /**
     * 
     * @param breaker
     * @date 2012-5-9
     */
    public void setBreaker(Serializable breaker) {
        this.breaker = breaker;
    }

    //@Override
    public void execute(ObjectSource objectSource) throws ExecuteException {
        if (log.isDebugEnabled())
            log.debug("start  execute " + SbmUtils.toString(this));

        boolean breaked = isBreaked(objectSource);

        if (!breaked) {
            Object result = executeMethod(objectSource);
            saveMethodResult(result, objectSource);
        } else {
            if (log.isDebugEnabled())
                log.debug("Invoke method not executed, it is breaked in current object source");
        }

        if (log.isDebugEnabled())
            log.debug("finish execute " + SbmUtils.toString(this));
    }

    //@Override
    public String toString() {
        return getClass().getSimpleName() + " [name=" + getName() + ", resultKey=" + resultKey + ", resolver="
                + resolver + ", methodName=" + methodName + ", args=" + Arrays.toString(args) + ", breaker="
                + breaker + "]";
    }

    /**
     * 
     * @param objectSource
     * @throws ExecuteException
     * @date 2011-1-12
     */
    protected Object executeMethod(ObjectSource objectSource) throws ExecuteException {
        Resolver resolver = getResolver();
        MethodInfo methodInfo = getMethodInfo();
        Object resolverObject = null;

        try {
            resolverObject = resolver.getResolverObject(objectSource);
        } catch (Exception e) {
            throw new ResolverObjectPrepareExecuteException(this, e);
        }

        if (methodInfo == null) {
            Class<?> resolverClass = null;

            try {
                resolverClass = resolver.getResolverClass(objectSource);
            } catch (Exception e) {
                throw new ExecuteException(e);
            }

            if (resolverClass == null && resolverObject != null)
                resolverClass = resolverObject.getClass();

            if (resolverClass == null)
                throw new ExecuteException("got null resolver class from Resolver " + SbmUtils.toString(resolver));

            methodInfo = findMethodInfo(resolverClass, this.methodName, this.args, true);
            if (methodInfo == null)
                methodInfo = findMethodInfo(resolverClass, this.methodName, this.args, false);

            if (methodInfo == null)
                throw new ExecuteException("no method named " + SbmUtils.toString(this.methodName) + " with "
                        + SbmUtils.toString(this.args) + " arguments can be found in resolver class "
                        + SbmUtils.toString(resolverClass));

            setMethodInfo(methodInfo);
        }

        Object[] argValues = prepareMethodArgValues(methodInfo, objectSource);

        try {
            return methodInfo.getMethod().invoke(resolverObject, argValues);
        } catch (InvocationTargetException e) {
            throw new InvocationExecuteException(this, e.getCause());
        } catch (IllegalArgumentException e) {
            throw new ExecuteException(e);
        } catch (IllegalAccessException e) {
            throw new ExecuteException(e);
        }
    }

    /**
     * ??
     * @param methodResult
     * @param objectSource
     * @throws ExecuteException
     * @date 2012-5-7
     */
    protected void saveMethodResult(Object methodResult, ObjectSource objectSource) throws ExecuteException {
        Serializable resultKey = getResultKey();

        if (resultKey != null) {
            try {
                objectSource.set(resultKey, methodResult);
            } catch (ObjectSourceException e) {
                throw new ExecuteException(e);
            }
        }
    }

    /**
     * ??
     * @param methodInfo
     * @param objectSource
     * @return
     * @throws ExecuteException
     * @date 2012-5-6
     */
    protected Object[] prepareMethodArgValues(MethodInfo methodInfo, ObjectSource objectSource)
            throws ExecuteException {
        Object[] values = null;

        Arg[] args = this.args;

        if (args != null) {
            values = new Object[args.length];

            for (int i = 0; i < args.length; i++) {
                try {
                    values[i] = args[i].getValue(objectSource, methodInfo.getArgType(i), methodInfo.getMethod(),
                            methodInfo.getMethodClass());
                } catch (Exception e) {
                    throw new ArgPrepareExecuteException(this, i, e);
                }
            }
        }

        if (log.isDebugEnabled())
            log.debug("prepared method arguments: " + SbmUtils.toString(values));

        return values;
    }

    /**
     * ??
     * @param objectSource
     * @return
     * @date 2011-10-28
     */
    protected boolean isBreaked(ObjectSource objectSource) throws ExecuteException {
        if (this.breaker == null)
            return false;

        Boolean breaked = null;

        if (this.breaker instanceof Boolean)
            breaked = (Boolean) this.breaker;
        else {
            Object breakerObj = null;

            try {
                breakerObj = objectSource.get(this.breaker);
            } catch (Exception e) {
                throw new ExecuteException(e);
            }

            if (breakerObj instanceof Boolean)
                breaked = (Boolean) breakerObj;
            else
                breaked = (breakerObj != null);
        }

        return breaked == null ? false : breaked;
    }

    protected MethodInfo getMethodInfo() {
        return methodInfo;
    }

    protected void setMethodInfo(MethodInfo methodInfo) {
        this.methodInfo = methodInfo;
    }

    /**
     * ??
     * @return
     * @date 2012-5-7
     */
    protected int getArgNums() {
        return (this.args == null ? 0 : this.args.length);
    }

    /**
     * ????
     * @param clazz
     * @param methodName
     * @param args
     * @param exactMatch ??
     * @return
     * @throws ExecuteException
     * @date 2012-5-11
     */
    protected MethodInfo findMethodInfo(Class<?> clazz, String methodName, Arg[] args, boolean exactMatch) {
        MethodInfo result = null;

        //?????????
        if (isAncestorType(Proxy.class, clazz)) {
            Class<?>[] interfaces = clazz.getInterfaces();

            if (interfaces != null && interfaces.length > 0) {
                for (Class<?> si : interfaces) {
                    result = findMethodInfo(si, methodName, args, exactMatch);

                    if (result != null)
                        break;
                }
            }
        } else {
            Method method = null;
            Class<?> methodClass = clazz;

            int al = (args == null ? 0 : args.length);
            Type[] argTypes = new Type[al];
            for (int i = 0; i < al; i++)
                argTypes[i] = args[i].getType();

            Method[] ms = clazz.getMethods();

            for (Method m : ms) {
                //?
                if (m.isSynthetic())
                    continue;

                if (m.getName().equals(methodName) && Modifier.isPublic(m.getModifiers())) {
                    Class<?>[] types = m.getParameterTypes();
                    int mal = (types == null ? 0 : types.length);

                    if (mal == al) {
                        boolean match = true;

                        for (int i = 0; i < mal; i++) {
                            //null?
                            if (argTypes[i] == null)
                                continue;

                            //?
                            if (exactMatch) {
                                if (!argTypes[i].equals(types[i])) {
                                    match = false;
                                    break;
                                }
                            }
                            //?
                            else {
                                Class<?> methodType = SbmUtils.narrowToClass(wrapType(types[i]));
                                Type argType = wrapType(argTypes[i]);

                                if (!isAncestorType(methodType, argType)) {
                                    match = false;
                                    break;
                                }
                            }
                        }

                        if (match) {
                            method = m;
                            break;
                        }
                    }
                }
            }

            if (method != null)
                result = new MethodInfo(method, methodClass);
        }

        return result;
    }

    /**
     * ??
     * @param ancestor
     * @param descendant
     * @return
     * @date 2012-5-24
     */
    protected boolean isAncestorType(Type ancestor, Type descendant) {
        return SbmUtils.isAncestorType(ancestor, descendant);
    }

    /**
     * ?<code>type</code>?
     * @param type
     * @return
     * @date 2012-5-24
     */
    protected Type wrapType(Type type) {
        return SbmUtils.wrapType(type);
    }

    /**
     * ?
     * @author earthangry@gmail.com
     * @date 2012-5-6
     */
    protected static class MethodInfo {
        private Method method;

        private Type[] argTypes;

        private Class<?> methodClass;

        public MethodInfo(Method method, Class<?> methodClass) {
            this.method = method;
            this.methodClass = methodClass;
            this.argTypes = this.method.getGenericParameterTypes();
        }

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }

        public Type[] getArgTypes() {
            return argTypes;
        }

        public void setArgTypes(Type[] argTypes) {
            this.argTypes = argTypes;
        }

        public Class<?> getMethodClass() {
            return methodClass;
        }

        public void setMethodClass(Class<?> methodClass) {
            this.methodClass = methodClass;
        }

        public Type getArgType(int argIdx) {
            return this.argTypes[argIdx];
        }
    }

    /**
     * {@linkplain Invoke }???
     * @author earthangry@gmail.com
     * @date 2010-10-19
     */
    public static interface Resolver {
        /**
         * ?
         * @param objectSource
         * @return
         * @throws Exception
         * @date 2012-5-7
         */
        Object getResolverObject(ObjectSource objectSource) throws Exception;

        /**
         * ?{@linkplain Invoke }?
         * <code>null</code>{@linkplain #getResolverObject(ObjectSource)}
         * @param objectSource
         * @return
         * @throws Exception
         * @date 2012-5-18
         */
        Class<?> getResolverClass(ObjectSource objectSource) throws Exception;
    }

    /**
     * ?
     * @author earthangry@gmail.com
     * @date 2010-10-3
     */
    public static interface Arg {
        /**
         * ??
         * @param objectSource
         * @param argType
         * @param method
         * @param methodClass
         * @return
         * @throws Exception
         * @date 2012-5-7
         */
        Object getValue(ObjectSource objectSource, Type argType, Method method, Class<?> methodClass)
                throws Exception;

        /**
         * ???{@linkplain Invoke }?<code>null</code>{@linkplain Invoke }
         * @return
         * @date 2012-5-11
         */
        Type getType();
    }
}