/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.service.invoker;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.cxf.common.i18n.Message;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.continuations.SuspendedInvocationException;
import org.apache.cxf.frontend.MethodDispatcher;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.FaultMode;
import org.apache.cxf.message.MessageContentsList;
import org.apache.cxf.service.Service;
import org.apache.cxf.service.model.BindingOperationInfo;
/**
* Abstract implementation of Invoker.
* <p>
*/
public abstract class AbstractInvoker implements Invoker {
private static final Logger LOG = LogUtils.getL7dLogger(AbstractInvoker.class);
public Object invoke(Exchange exchange, Object o) {
final Object serviceObject = getServiceObject(exchange);
try {
BindingOperationInfo bop = exchange.get(BindingOperationInfo.class);
MethodDispatcher md = (MethodDispatcher)
exchange.get(Service.class).get(MethodDispatcher.class.getName());
Method m = bop == null ? null : md.getMethod(bop);
if (m == null && bop == null) {
LOG.severe(new Message("MISSING_BINDING_OPERATION", LOG).toString());
throw new Fault(new Message("EXCEPTION_INVOKING_OBJECT", LOG,
"No binding operation info", "unknown method", "unknown"));
}
//Method m = (Method)bop.getOperationInfo().getProperty(Method.class.getName());
m = matchMethod(m, serviceObject);
List<Object> params = null;
if (o instanceof List) {
params = CastUtils.cast((List<?>)o);
} else if (o != null) {
params = new MessageContentsList(o);
}
return invoke(exchange, serviceObject, m, params);
} finally {
releaseServiceObject(exchange, serviceObject);
}
}
protected Object invoke(Exchange exchange, final Object serviceObject, Method m, List<Object> params) {
Object res;
try {
Object[] paramArray = new Object[]{};
if (params != null) {
paramArray = params.toArray();
}
res = performInvocation(exchange, serviceObject, m, paramArray);
if (exchange.isOneWay()) {
return null;
}
return new MessageContentsList(res);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t == null) {
t = e;
}
checkSuspendedInvocation(exchange, serviceObject, m, params, t);
exchange.getInMessage().put(FaultMode.class, FaultMode.UNCHECKED_APPLICATION_FAULT);
for (Class<?> cl : m.getExceptionTypes()) {
if (cl.isInstance(t)) {
exchange.getInMessage().put(FaultMode.class,
FaultMode.CHECKED_APPLICATION_FAULT);
}
}
if (t instanceof Fault) {
exchange.getInMessage().put(FaultMode.class,
FaultMode.CHECKED_APPLICATION_FAULT);
throw (Fault)t;
}
throw createFault(t, m, params, true);
} catch (SuspendedInvocationException suspendedEx) {
// to avoid duplicating the same log statement
checkSuspendedInvocation(exchange, serviceObject, m, params, suspendedEx);
// unreachable
throw suspendedEx;
} catch (Fault f) {
exchange.getInMessage().put(FaultMode.class, FaultMode.UNCHECKED_APPLICATION_FAULT);
throw f;
} catch (Exception e) {
checkSuspendedInvocation(exchange, serviceObject, m, params, e);
exchange.getInMessage().put(FaultMode.class, FaultMode.UNCHECKED_APPLICATION_FAULT);
throw createFault(e, m, params, false);
}
}
protected void checkSuspendedInvocation(Exchange exchange,
Object serviceObject,
Method m,
List<Object> params,
Throwable t) {
if (t instanceof SuspendedInvocationException) {
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "SUSPENDED_INVOCATION_EXCEPTION",
new Object[]{serviceObject, m.toString(), params});
}
throw (SuspendedInvocationException)t;
}
}
protected Fault createFault(Throwable ex, Method m, List<Object> params, boolean checked) {
if (checked) {
return new Fault(ex);
} else {
String message = (ex == null) ? "" : ex.getMessage();
String method = (m == null) ? "<null>" : m.toString();
return new Fault(new Message("EXCEPTION_INVOKING_OBJECT", LOG,
message, method, params),
ex);
}
}
protected Object performInvocation(Exchange exchange, final Object serviceObject, Method m,
Object[] paramArray) throws Exception {
paramArray = insertExchange(m, paramArray, exchange);
if (LOG.isLoggable(Level.FINER)) {
LOG.log(Level.FINER, "INVOKING_METHOD", new Object[] {serviceObject,
m,
Arrays.asList(paramArray)});
}
return m.invoke(serviceObject, paramArray);
}
public Object[] insertExchange(Method method, Object[] params, Exchange context) {
Object[] newParams = params;
for (int i = 0; i < method.getParameterTypes().length; i++) {
if (method.getParameterTypes()[i].equals(Exchange.class)) {
newParams = new Object[params.length + 1];
for (int j = 0; j < newParams.length; j++) {
if (j == i) {
newParams[j] = context;
} else if (j > i) {
newParams[j] = params[j - 1];
} else {
newParams[j] = params[j];
}
}
}
}
return newParams;
}
/**
* Creates and returns a service object depending on the scope.
*/
public abstract Object getServiceObject(final Exchange context);
/**
* Called when the invoker is done with the object. Default implementation
* does nothing.
* @param context
* @param obj
*/
public void releaseServiceObject(final Exchange context, Object obj) {
}
/**
* Returns a Method that has the same declaring class as the class of
* targetObject to avoid the IllegalArgumentException when invoking the
* method on the target object. The methodToMatch will be returned if the
* targetObject doesn't have a similar method.
*
* @param methodToMatch The method to be used when finding a matching method
* in targetObject
* @param targetObject The object to search in for the method.
* @return The methodToMatch if no such method exist in the class of
* targetObject; otherwise, a method from the class of targetObject
* matching the matchToMethod method.
*/
private static Method matchMethod(Method methodToMatch, Object targetObject) {
if (isJdkDynamicProxy(targetObject)) {
Class[] interfaces = targetObject.getClass().getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Method m = getMostSpecificMethod(methodToMatch, interfaces[i]);
if (!methodToMatch.equals(m)) {
return m;
}
}
}
return methodToMatch;
}
/**
* Return whether the given object is a J2SE dynamic proxy.
*
* @param object the object to check
* @see java.lang.reflect.Proxy#isProxyClass
*/
public static boolean isJdkDynamicProxy(Object object) {
return object != null && Proxy.isProxyClass(object.getClass());
}
/**
* Given a method, which may come from an interface, and a targetClass used
* in the current AOP invocation, find the most specific method if there is
* one. E.g. the method may be IFoo.bar() and the target class may be
* DefaultFoo. In this case, the method may be DefaultFoo.bar(). This
* enables attributes on that method to be found.
*
* @param method method to be invoked, which may come from an interface
* @param targetClass target class for the curren invocation. May be
* <code>null</code> or may not even implement the method.
* @return the more specific method, or the original method if the
* targetClass doesn't specialize it or implement it or is null
*/
public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
if (method != null && targetClass != null) {
try {
method = targetClass.getMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException ex) {
// Perhaps the target class doesn't implement this method:
// that's fine, just use the original method
}
}
return method;
}
}