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.webflow.util;
017:
018: import java.lang.reflect.InvocationTargetException;
019: import java.lang.reflect.Method;
020: import java.util.Map;
021:
022: import org.springframework.binding.method.InvalidMethodKeyException;
023: import org.springframework.binding.method.MethodKey;
024: import org.springframework.core.NestedRuntimeException;
025: import org.springframework.util.Assert;
026: import org.springframework.util.CachingMapDecorator;
027:
028: /**
029: * Invoker and cache for dispatch methods that all share the same target object.
030: * The dispatch methods typically share the same form, but multiple exist per
031: * target object, and they only differ in name.
032: *
033: * @author Keith Donald
034: * @author Ben Hale
035: */
036: public class DispatchMethodInvoker {
037:
038: /**
039: * The target object to dispatch to.
040: */
041: private Object target;
042:
043: /**
044: * The parameter types describing the dispatch method signature.
045: */
046: private Class[] parameterTypes;
047:
048: /**
049: * The resolved method cache.
050: */
051: private Map methodCache = new CachingMapDecorator() {
052: public Object create(Object key) {
053: String methodName = (String) key;
054: try {
055: return new MethodKey(target.getClass(), methodName,
056: parameterTypes).getMethod();
057: } catch (InvalidMethodKeyException e) {
058: throw new MethodLookupException(
059: "Unable to resolve dispatch method "
060: + e.getMethodKey()
061: + "'; make sure the method name is correct and such a method is defined on targetClass "
062: + target.getClass().getName(), e);
063: }
064: }
065: };
066:
067: /**
068: * Creates a dispatch method invoker.
069: * @param target the target to dispatch to
070: * @param parameterTypes the parameter types defining the argument signature
071: * of the dispatch methods
072: */
073: public DispatchMethodInvoker(Object target, Class[] parameterTypes) {
074: Assert
075: .notNull(target,
076: "The target of a dispatch method invocation is required");
077: this .target = target;
078: this .parameterTypes = parameterTypes;
079: }
080:
081: /**
082: * Returns the target object method calls are dispatched to.
083: */
084: public Object getTarget() {
085: return target;
086: }
087:
088: /**
089: * Returns the parameter types defining the argument signature of the
090: * dispatch methods.
091: */
092: public Class[] getParameterTypes() {
093: return parameterTypes;
094: }
095:
096: /**
097: * Dispatch a call with given arguments to named dispatcher method.
098: * @param methodName the name of the method to invoke
099: * @param arguments the arguments to pass to the method
100: * @return the result of the method invokation
101: * @throws MethodLookupException when the method cannot be resolved
102: * @throws Exception when the invoked method throws an exception
103: */
104: public Object invoke(String methodName, Object[] arguments)
105: throws MethodLookupException, Exception {
106: try {
107: Method dispatchMethod = getDispatchMethod(methodName);
108: return dispatchMethod.invoke(target, arguments);
109: } catch (InvocationTargetException e) {
110: // the invoked method threw an exception; have it propagate to the caller
111: Throwable t = e.getTargetException();
112: if (t instanceof Exception) {
113: throw (Exception) e.getTargetException();
114: } else {
115: throw (Error) e.getTargetException();
116: }
117: }
118: }
119:
120: /**
121: * Get a handle to the method of the specified name, with the signature
122: * defined by the configured parameter types and return type.
123: * @param methodName the method name
124: * @return the method
125: * @throws MethodLookupException when the method cannot be resolved
126: */
127: private Method getDispatchMethod(String methodName)
128: throws MethodLookupException {
129: return (Method) methodCache.get(methodName);
130: }
131:
132: /**
133: * Thrown when a dispatch method could not be resolved.
134: */
135: public static class MethodLookupException extends
136: NestedRuntimeException {
137:
138: /**
139: * Create a new method lookup exception.
140: * @param msg a descriptive message
141: * @param ex the underlying cause of this exception
142: */
143: public MethodLookupException(String msg, Throwable ex) {
144: super(msg, ex);
145: }
146: }
147: }
|