/*
* Copyright 2008 the original author or authors.
*
* Licensed 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.rioproject.proxy.bean;
import org.rioproject.servicecore.Service;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.Set;
/**
* An {@link java.lang.reflect.InvocationHandler} that delegates method
* calls between the {@code BeanAdapter} and the bean
*
* @author Dennis Reedy
*/
public final class BeanDelegator implements InvocationHandler, Serializable {
private static final long serialVersionUID = 1L;
private final Set<PackagedMethod> methodSet = new HashSet<PackagedMethod>();
private final Object service;
private final Object bean;
/*
* Private constructor, instantiable only from the static factory
*/
private BeanDelegator(final Object service, final Object bean, final ClassLoader loader) throws ClassNotFoundException {
this.service = service;
this.bean = bean;
String interfaceName = Service.class.getName();
Class interfaceClass = Class.forName(interfaceName, false, loader);
Method[] methods = interfaceClass.getMethods();
for(Method method : methods) {
methodSet.add(new PackagedMethod(method));
}
}
/**
* Get an instance of the BeanDelegator using the ClassLoader of the
* <tt>bean</tt> parameter to define the proxy class
*
* @param service The service, must not be {@code null}
* @param bean The bean, must not be {@code null}
* @param interfaces The interfaces to expose, must not be {@code null}
*
* @return An object suitable for use as a proxy. A new
* proxy will be created each time
*
* @throws IllegalArgumentException if any of the parameters is {@code null}
* zero length
* @throws ClassNotFoundException If the class cannot be loaded
*/
public static Object getInstance(final Object service, final Object bean, final Class[] interfaces) throws ClassNotFoundException {
return getInstance(service, bean, interfaces, bean.getClass().getClassLoader());
}
/**
* Get an instance of the BeanDelegator
*
* @param service The service, must not be {@code null}
* @param bean The bean, must not be {@code null}
* @param interfaces The interfaces to expose, must not be {@code null}
* @param loader The class loader to define the proxy class
*
* @return An object suitable for use as a proxy. A new proxy will be created each time
*
* @throws IllegalArgumentException if any of the parameters is {@code null}
* length
* @throws ClassNotFoundException If the class cannot be loaded
*/
public static Object getInstance(final Object service, final Object bean, final Class[] interfaces, final ClassLoader loader)
throws ClassNotFoundException {
if(service == null)
throw new IllegalArgumentException("service is null");
if(bean == null)
throw new IllegalArgumentException("bean is null");
if(interfaces == null)
throw new IllegalArgumentException("interfaces is null");
if(interfaces.length == 0)
throw new IllegalArgumentException("interfaces must contain values");
return (Proxy.newProxyInstance(loader, interfaces, new BeanDelegator(service, bean, loader)));
}
/**
* Perform the invocation, directing the method request to the appropriate
* implementation
*
* @see InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
*/
@SuppressWarnings("unchecked")
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
PackagedMethod template = null;
try {
template = new PackagedMethod(method);
if(methodSet.contains(template)) {
return method.invoke(service, args);
} else {
Class beanClass = bean.getClass();
Method beanMethod = beanClass.getMethod(method.getName(), method.getParameterTypes());
return(beanMethod.invoke(bean, args));
}
} catch (InvocationTargetException e) {
throw e.getCause()==null? e.getTargetException(): e.getCause();
} finally {
if(template!=null)
template.clear();
}
}
}