001/*
002 * Copyright 2002-2008 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017/*
018 * Copyright 2002-2008 the original author or authors.
019 *
020 * Licensed under the Apache License, Version 2.0 (the "License");
021 * you may not use this file except in compliance with the License.
022 * You may obtain a copy of the License at
023 *
024 *      https://www.apache.org/licenses/LICENSE-2.0
025 *
026 * Unless required by applicable law or agreed to in writing, software
027 * distributed under the License is distributed on an "AS IS" BASIS,
028 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
029 * See the License for the specific language governing permissions and
030 * limitations under the License.
031 */
032package org.springframework.batch.support;
033
034import java.lang.reflect.Method;
035import java.util.Arrays;
036
037import org.springframework.aop.framework.Advised;
038import org.springframework.util.Assert;
039import org.springframework.util.ClassUtils;
040
041/**
042 * Simple implementation of the {@link MethodInvoker} interface that invokes a
043 * method on an object. If the method has no arguments, but arguments are
044 * provided, they are ignored and the method is invoked anyway. If there are
045 * more arguments than there are provided, then an exception is thrown.
046 * 
047 * @author Lucas Ward
048 * @since 2.0
049 */
050public class SimpleMethodInvoker implements MethodInvoker {
051
052        private final Object object;
053
054        private Method method;
055
056        public SimpleMethodInvoker(Object object, Method method) {
057                Assert.notNull(object, "Object to invoke must not be null");
058                Assert.notNull(method, "Method to invoke must not be null");
059                this.method = method;
060                this.object = object;
061        }
062
063        public SimpleMethodInvoker(Object object, String methodName, Class<?>... paramTypes) {
064                Assert.notNull(object, "Object to invoke must not be null");
065                this.method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, paramTypes);
066                if (this.method == null) {
067                        // try with no params
068                        this.method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName);
069                }
070                if (this.method == null) {
071                        throw new IllegalArgumentException("No methods found for name: [" + methodName + "] in class: ["
072                                        + object.getClass() + "] with arguments of type: [" + Arrays.toString(paramTypes) + "]");
073                }
074                this.object = object;
075        }
076
077        /*
078         * (non-Javadoc)
079         * 
080         * @see
081         * org.springframework.batch.core.configuration.util.MethodInvoker#invokeMethod
082         * (java.lang.Object[])
083         */
084    @Override
085        public Object invokeMethod(Object... args) {
086
087                Class<?>[] parameterTypes = method.getParameterTypes();
088                Object[] invokeArgs;
089                if (parameterTypes.length == 0) {
090                        invokeArgs = new Object[] {};
091                }
092                else if (parameterTypes.length != args.length) {
093                        throw new IllegalArgumentException("Wrong number of arguments, expected no more than: ["
094                                        + parameterTypes.length + "]");
095                }
096                else {
097                        invokeArgs = args;
098                }
099
100                method.setAccessible(true);
101
102                try {
103                        // Extract the target from an Advised as late as possible
104                        // in case it contains a lazy initialization
105                        Object target = extractTarget(object, method);
106                        return method.invoke(target, invokeArgs);
107                }
108                catch (Exception e) {
109                        throw new IllegalArgumentException("Unable to invoke method: [" + method + "] on object: [" + object
110                                        + "] with arguments: [" + Arrays.toString(args) + "]", e);
111                }
112        }
113
114        private Object extractTarget(Object target, Method method) {
115                if (target instanceof Advised) {
116                        Object source;
117                        try {
118                                source = ((Advised) target).getTargetSource().getTarget();
119                        }
120                        catch (Exception e) {
121                                throw new IllegalStateException("Could not extract target from proxy", e);
122                        }
123                        if (source instanceof Advised) {
124                                source = extractTarget(source, method);
125                        }
126                        if (method.getDeclaringClass().isAssignableFrom(source.getClass())) {
127                                target = source;
128                        }
129                }
130                return target;
131        }
132
133        @Override
134        public boolean equals(Object obj) {
135                if (!(obj instanceof SimpleMethodInvoker)) {
136                        return false;
137                }
138
139                if (obj == this) {
140                        return true;
141                }
142                SimpleMethodInvoker rhs = (SimpleMethodInvoker) obj;
143                return (rhs.method.equals(this.method)) && (rhs.object.equals(this.object));
144        }
145
146        @Override
147        public int hashCode() {
148                int result = 25;
149                result = 31 * result + object.hashCode();
150                result = 31 * result + method.hashCode();
151                return result;
152        }
153}