001: /**
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * 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, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package org.apache.openejb.core.interceptor;
018:
019: import org.apache.openejb.core.Operation;
020:
021: import javax.interceptor.InvocationContext;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.List;
025: import java.util.TreeMap;
026: import java.lang.reflect.Method;
027: import java.lang.reflect.InvocationTargetException;
028:
029: /**
030: * @version $Rev: 634462 $ $Date: 2008-03-06 15:45:47 -0800 $
031: */
032: public class ReflectionInvocationContext implements InvocationContext {
033: private final Iterator<Interceptor> interceptors;
034: private final Object target;
035: private final Method method;
036: private final Object[] parameters;
037: private final Map<String, Object> contextData = new TreeMap<String, Object>();
038: private final Class<?>[] parameterTypes;
039:
040: private final Operation operation;
041:
042: public ReflectionInvocationContext(Operation operation,
043: List<Interceptor> interceptors, Object target,
044: Method method, Object... parameters) {
045: if (operation == null)
046: throw new NullPointerException("operation is null");
047: if (interceptors == null)
048: throw new NullPointerException("interceptors is null");
049: if (target == null)
050: throw new NullPointerException("target is null");
051:
052: this .operation = operation;
053: this .interceptors = interceptors.iterator();
054: this .target = target;
055: this .method = method;
056: this .parameters = parameters;
057:
058: if (method == null) {
059: parameterTypes = new Class[0];
060: } else {
061: parameterTypes = method.getParameterTypes();
062: }
063: }
064:
065: public Object getTarget() {
066: return target;
067: }
068:
069: public Method getMethod() {
070: return method;
071: }
072:
073: public Object[] getParameters() {
074: if (operation.isCallback()) {
075: throw new IllegalStateException(
076: getIllegalParameterAccessMessage());
077: }
078: return parameters.clone();
079: }
080:
081: private String getIllegalParameterAccessMessage() {
082: String m = "Callback methods cannot access parameters.";
083: m += " Callback Type: " + operation;
084: if (method != null) {
085: m += ", Target Method: " + method.getName();
086: }
087: if (target != null) {
088: m += ", Target Bean: " + target.getClass().getName();
089: }
090: return m;
091: }
092:
093: public void setParameters(Object[] parameters) {
094: if (operation.isCallback()) {
095: throw new IllegalStateException(
096: getIllegalParameterAccessMessage());
097: }
098: if (parameters == null)
099: throw new NullPointerException("parameters is null");
100: if (parameters.length != this .parameters.length) {
101: throw new IllegalArgumentException("Expected "
102: + this .parameters.length
103: + " parameters, but only got " + parameters.length
104: + " parameters");
105: }
106: // for (int i = 0; i < parameters.length; i++) {
107: // Object parameter = parameters[i];
108: // Class<?> parameterType = parameterTypes[i];
109: //
110: // if (parameter == null) {
111: // if (parameterType.isPrimitive()) {
112: // throw new IllegalArgumentException("Expected parameter " + i + " to be primitive type " + parameterType.getName() +
113: // ", but got a parameter that is null");
114: // }
115: // } else if (!parameterType.isInstance(parameter)) {
116: // throw new IllegalArgumentException("Expected parameter " + i + " to be of type " + parameterType.getName() +
117: // ", but got a parameter of type " + parameter.getClass().getName());
118: // }
119: // }
120: System.arraycopy(parameters, 0, this .parameters, 0,
121: parameters.length);
122: }
123:
124: public Map<String, Object> getContextData() {
125: return contextData;
126: }
127:
128: private Invocation next() {
129: if (interceptors.hasNext()) {
130: Interceptor interceptor = interceptors.next();
131: Object nextInstance = interceptor.getInstance();
132: Method nextMethod = interceptor.getMethod();
133:
134: if (nextMethod.getParameterTypes().length > 0) {
135: return new InterceptorInvocation(nextInstance,
136: nextMethod, this );
137: } else {
138: return new LifecycleInvocation(nextInstance,
139: nextMethod, this );
140: }
141: } else if (method != null) {
142: return new BeanInvocation(target, method, parameters);
143: } else {
144: return new NoOpInvocation();
145: }
146: }
147:
148: public Object proceed() throws Exception {
149: // The bulk of the logic of this method has intentionally been moved
150: // out so stepping through a large stack in a debugger can be done quickly.
151: // Simply put one break point on 'next.invoke()' or one inside that method.
152: try {
153: Invocation next = next();
154: return next.invoke();
155: } catch (InvocationTargetException e) {
156: throw unwrapInvocationTargetException(e);
157: }
158: }
159:
160: private abstract static class Invocation {
161: private final Method method;
162: private final Object[] args;
163: private final Object target;
164:
165: public Invocation(Object target, Method method, Object[] args) {
166: this .target = target;
167: this .method = method;
168: this .args = args;
169: }
170:
171: public Object invoke() throws Exception {
172: Object value = method.invoke(target, args);
173: return value;
174: }
175:
176: public String toString() {
177: return method.getDeclaringClass().getName() + "."
178: + method.getName();
179: }
180: }
181:
182: private static class BeanInvocation extends Invocation {
183: public BeanInvocation(Object target, Method method,
184: Object[] args) {
185: super (target, method, args);
186: }
187: }
188:
189: private static class InterceptorInvocation extends Invocation {
190: public InterceptorInvocation(Object target, Method method,
191: InvocationContext invocationContext) {
192: super (target, method, new Object[] { invocationContext });
193: }
194: }
195:
196: private static class LifecycleInvocation extends Invocation {
197: private final InvocationContext invocationContext;
198:
199: public LifecycleInvocation(Object target, Method method,
200: InvocationContext invocationContext) {
201: super (target, method, new Object[] {});
202: this .invocationContext = invocationContext;
203: }
204:
205: public Object invoke() throws Exception {
206: // invoke the callback
207: super .invoke();
208:
209: // we need to call proceed so callbacks in subclasses get invoked
210: Object value = invocationContext.proceed();
211: return value;
212: }
213: }
214:
215: private static class NoOpInvocation extends Invocation {
216: public NoOpInvocation() {
217: super (null, null, null);
218: }
219:
220: public Object invoke() throws IllegalAccessException,
221: InvocationTargetException {
222: return null;
223: }
224: }
225:
226: // todo verify excpetion types
227:
228: /**
229: * Business method interceptors can only throw exception allowed by the target business method.
230: * Lifecycle interceptors can only throw RuntimeException.
231: * @param e the invocation target exception of a reflection method invoke
232: * @return the cause of the exception
233: * @throws AssertionError if the cause is not an Exception or Error.
234: */
235: private Exception unwrapInvocationTargetException(
236: InvocationTargetException e) {
237: Throwable cause = e.getCause();
238: if (cause == null) {
239: return e;
240: } else if (cause instanceof Exception) {
241: return (Exception) cause;
242: } else if (cause instanceof Error) {
243: throw (Error) cause;
244: } else {
245: throw new AssertionError(cause);
246: }
247: }
248:
249: public String toString() {
250: String methodName = (method != null) ? method.getName() : null;
251:
252: return "InvocationContext(operation=" + operation + ", target="
253: + target.getClass().getName() + ", method="
254: + methodName + ")";
255: }
256: }
|