001: /*
002: * Copyright 2004-2007 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: * http://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: package org.springframework.binding.method;
017:
018: import java.lang.reflect.InvocationTargetException;
019: import java.lang.reflect.Method;
020:
021: import org.apache.commons.logging.Log;
022: import org.apache.commons.logging.LogFactory;
023: import org.springframework.binding.convert.ConversionService;
024: import org.springframework.binding.convert.support.DefaultConversionService;
025: import org.springframework.core.style.StylerUtils;
026: import org.springframework.util.CachingMapDecorator;
027:
028: /**
029: * A helper for invoking typed methods on abritrary objects, with support for
030: * argument value type conversion from values retrieved from a argument
031: * attribute source.
032: *
033: * @author Keith Donald
034: */
035: public class MethodInvoker {
036:
037: private static final Log logger = LogFactory
038: .getLog(MethodInvoker.class);
039:
040: /**
041: * Conversion service for converting arguments to the neccessary type if
042: * required.
043: */
044: private ConversionService conversionService = new DefaultConversionService();
045:
046: /**
047: * A cache of invoked bean methods, keyed weakly.
048: */
049: private CachingMapDecorator methodCache = new CachingMapDecorator(
050: true) {
051: public Object create(Object key) {
052: return ((MethodKey) key).getMethod();
053: }
054: };
055:
056: /**
057: * Sets the conversion service to convert argument values as needed.
058: */
059: public void setConversionService(ConversionService conversionService) {
060: this .conversionService = conversionService;
061: }
062:
063: /**
064: * Invoke the method on the bean provided. Argument values are pulled from
065: * the provided argument source.
066: * @param signature the definition of the method to invoke, including the
067: * method name and the method argument types
068: * @param bean the bean to invoke
069: * @param argumentSource the source for method arguments
070: * @return the invoked method's return value
071: * @throws MethodInvocationException the method could not be invoked
072: */
073: public Object invoke(MethodSignature signature, Object bean,
074: Object argumentSource) throws MethodInvocationException {
075: Parameters parameters = signature.getParameters();
076: Object[] arguments = new Object[parameters.size()];
077: for (int i = 0; i < parameters.size(); i++) {
078: Parameter parameter = parameters.getParameter(i);
079: Object argument = parameter.evaluateArgument(
080: argumentSource, null);
081: arguments[i] = applyTypeConversion(argument, parameter
082: .getType());
083: }
084: Class[] parameterTypes = parameters.getTypesArray();
085: for (int i = 0; i < parameterTypes.length; i++) {
086: if (parameterTypes[i] == null) {
087: Object argument = arguments[i];
088: if (argument != null) {
089: parameterTypes[i] = argument.getClass();
090: }
091: }
092: }
093: MethodKey key = new MethodKey(bean.getClass(), signature
094: .getMethodName(), parameterTypes);
095: try {
096: Method method = (Method) methodCache.get(key);
097: if (logger.isDebugEnabled()) {
098: logger.debug("Invoking method with signature [" + key
099: + "] with arguments "
100: + StylerUtils.style(arguments) + " on bean ["
101: + bean + "]");
102:
103: }
104: Object returnValue = method.invoke(bean, arguments);
105: if (logger.isDebugEnabled()) {
106: logger.debug("Invoked method with signature [" + key
107: + "] returned value [" + returnValue + "]");
108: }
109: return returnValue;
110: } catch (InvocationTargetException e) {
111: throw new MethodInvocationException(signature, arguments, e
112: .getTargetException());
113: } catch (Exception e) {
114: throw new MethodInvocationException(signature, arguments, e);
115: }
116: }
117:
118: /**
119: * Apply type conversion on the event parameter if neccessary
120: *
121: * @param parameterValue the raw argument value
122: * @param targetType the target type for the matching method argument
123: * @return the converted method argument
124: */
125: protected Object applyTypeConversion(Object parameterValue,
126: Class targetType) {
127: if (parameterValue == null || targetType == null) {
128: return parameterValue;
129: }
130: return conversionService.getConversionExecutor(
131: parameterValue.getClass(), targetType).execute(
132: parameterValue);
133: }
134: }
|