1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 package org.apache.cxf.service.invoker;
21
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Proxy;
25 import java.util.Arrays;
26 import java.util.List;
27 import java.util.logging.Level;
28 import java.util.logging.Logger;
29
30 import org.apache.cxf.common.i18n.Message;
31 import org.apache.cxf.common.logging.LogUtils;
32 import org.apache.cxf.frontend.MethodDispatcher;
33 import org.apache.cxf.helpers.CastUtils;
34 import org.apache.cxf.interceptor.Fault;
35 import org.apache.cxf.message.Exchange;
36 import org.apache.cxf.message.FaultMode;
37 import org.apache.cxf.message.MessageContentsList;
38 import org.apache.cxf.service.Service;
39 import org.apache.cxf.service.model.BindingOperationInfo;
40
41 /**
42 * Abstract implementation of Invoker.
43 * <p>
44 */
45 public abstract class AbstractInvoker implements Invoker {
46 private static final Logger LOG = LogUtils.getL7dLogger(AbstractInvoker.class);
47
48
49 public Object invoke(Exchange exchange, Object o) {
50
51 final Object serviceObject = getServiceObject(exchange);
52 try {
53
54 BindingOperationInfo bop = exchange.get(BindingOperationInfo.class);
55 MethodDispatcher md = (MethodDispatcher)
56 exchange.get(Service.class).get(MethodDispatcher.class.getName());
57 Method m = md.getMethod(bop);
58 //Method m = (Method)bop.getOperationInfo().getProperty(Method.class.getName());
59 m = matchMethod(m, serviceObject);
60
61 List<Object> params = null;
62 if (o instanceof List) {
63 params = CastUtils.cast((List<?>)o);
64 } else if (o != null) {
65 params = new MessageContentsList(o);
66 }
67
68 return invoke(exchange, serviceObject, m, params);
69 } finally {
70 releaseServiceObject(exchange, serviceObject);
71 }
72 }
73
74 protected Object invoke(Exchange exchange, final Object serviceObject, Method m, List<Object> params) {
75 Object res;
76 try {
77 Object[] paramArray = new Object[]{};
78 if (params != null) {
79 paramArray = params.toArray();
80 }
81
82 res = performInvocation(exchange, serviceObject, m, paramArray);
83
84 if (exchange.isOneWay()) {
85 return null;
86 }
87
88 return new MessageContentsList(res);
89 } catch (InvocationTargetException e) {
90 Throwable t = e.getCause();
91 if (t == null) {
92 t = e;
93 }
94 exchange.getInMessage().put(FaultMode.class, FaultMode.UNCHECKED_APPLICATION_FAULT);
95 for (Class<?> cl : m.getExceptionTypes()) {
96 if (cl.isInstance(t)) {
97 exchange.getInMessage().put(FaultMode.class,
98 FaultMode.CHECKED_APPLICATION_FAULT);
99 }
100 }
101
102 if (t instanceof Fault) {
103 exchange.getInMessage().put(FaultMode.class,
104 FaultMode.CHECKED_APPLICATION_FAULT);
105 throw (Fault)t;
106 }
107 throw createFault(t, m, params, true);
108 } catch (Fault f) {
109 exchange.getInMessage().put(FaultMode.class, FaultMode.UNCHECKED_APPLICATION_FAULT);
110 throw f;
111 } catch (Exception e) {
112 exchange.getInMessage().put(FaultMode.class, FaultMode.UNCHECKED_APPLICATION_FAULT);
113 throw createFault(e, m, params, false);
114 }
115 }
116
117 protected Fault createFault(Throwable ex, Method m, List<Object> params, boolean checked) {
118 if (checked) {
119 return new Fault(ex);
120 } else {
121 return new Fault(new Message("EXCEPTION_INVOKING_OBJECT",
122 LOG,
123 ex.getMessage(), m.toString(), params),
124 ex);
125 }
126 }
127
128 protected Object performInvocation(Exchange exchange, final Object serviceObject, Method m,
129 Object[] paramArray) throws Exception {
130 paramArray = insertExchange(m, paramArray, exchange);
131 if (LOG.isLoggable(Level.FINER)) {
132 LOG.log(Level.FINER, "INVOKING_METHOD", new Object[] {serviceObject,
133 m,
134 Arrays.asList(paramArray)});
135 }
136 return m.invoke(serviceObject, paramArray);
137 }
138
139 public Object[] insertExchange(Method method, Object[] params, Exchange context) {
140 Object[] newParams = params;
141 for (int i = 0; i < method.getParameterTypes().length; i++) {
142 if (method.getParameterTypes()[i].equals(Exchange.class)) {
143 newParams = new Object[params.length + 1];
144
145 for (int j = 0; j < newParams.length; j++) {
146 if (j == i) {
147 newParams[j] = context;
148 } else if (j > i) {
149 newParams[j] = params[j - 1];
150 } else {
151 newParams[j] = params[j];
152 }
153 }
154 }
155 }
156 return newParams;
157 }
158
159 /**
160 * Creates and returns a service object depending on the scope.
161 */
162 public abstract Object getServiceObject(final Exchange context);
163
164 /**
165 * Called when the invoker is done with the object. Default implementation
166 * does nothing.
167 * @param context
168 * @param obj
169 */
170 public void releaseServiceObject(final Exchange context, Object obj) {
171 }
172
173 /**
174 * Returns a Method that has the same declaring class as the class of
175 * targetObject to avoid the IllegalArgumentException when invoking the
176 * method on the target object. The methodToMatch will be returned if the
177 * targetObject doesn't have a similar method.
178 *
179 * @param methodToMatch The method to be used when finding a matching method
180 * in targetObject
181 * @param targetObject The object to search in for the method.
182 * @return The methodToMatch if no such method exist in the class of
183 * targetObject; otherwise, a method from the class of targetObject
184 * matching the matchToMethod method.
185 */
186 private static Method matchMethod(Method methodToMatch, Object targetObject) {
187 if (isJdkDynamicProxy(targetObject)) {
188 Class[] interfaces = targetObject.getClass().getInterfaces();
189 for (int i = 0; i < interfaces.length; i++) {
190 Method m = getMostSpecificMethod(methodToMatch, interfaces[i]);
191 if (!methodToMatch.equals(m)) {
192 return m;
193 }
194 }
195 }
196 return methodToMatch;
197 }
198
199 /**
200 * Return whether the given object is a J2SE dynamic proxy.
201 *
202 * @param object the object to check
203 * @see java.lang.reflect.Proxy#isProxyClass
204 */
205 public static boolean isJdkDynamicProxy(Object object) {
206 return object != null && Proxy.isProxyClass(object.getClass());
207 }
208
209 /**
210 * Given a method, which may come from an interface, and a targetClass used
211 * in the current AOP invocation, find the most specific method if there is
212 * one. E.g. the method may be IFoo.bar() and the target class may be
213 * DefaultFoo. In this case, the method may be DefaultFoo.bar(). This
214 * enables attributes on that method to be found.
215 *
216 * @param method method to be invoked, which may come from an interface
217 * @param targetClass target class for the curren invocation. May be
218 * <code>null</code> or may not even implement the method.
219 * @return the more specific method, or the original method if the
220 * targetClass doesn't specialize it or implement it or is null
221 */
222 public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
223 if (method != null && targetClass != null) {
224 try {
225 method = targetClass.getMethod(method.getName(), method.getParameterTypes());
226 } catch (NoSuchMethodException ex) {
227 // Perhaps the target class doesn't implement this method:
228 // that's fine, just use the original method
229 }
230 }
231 return method;
232 }
233 }