blob: 3bb85604aaab6a48d1733a078b53daa75bf7c7d8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2015 Oracle.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* Bob Nettleton (Oracle) - Initial Reference Implementation
******************************************************************************/
package org.eclipse.gemini.naming;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.security.PrivilegedExceptionAction;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
/**
* Utility class for reflection calls made by the Gemini Naming implementation
*
*
* @version $Revision$
*/
class ReflectionUtils {
private static Logger logger = Logger.getLogger(ReflectionUtils.class.getName());
/**
* This method uses reflection to invoke the given Method
* on the passed in Context instance. This method also
* catches the InvocationTargeException, in order to always
* re-throw the original exception from the Method invocation.
*
* @param method the Method to invoke
* @param contextToInvokeOn the Context to invoke the Method on
* @param args the arguments to the Method
* @return an Object representing the result of the Method call
* @throws Throwable
*/
static Object invokeMethodOnContext(Method method, Context contextToInvokeOn, Object[] args) throws Throwable {
return invokeMethodOnObject(method, contextToInvokeOn, args);
}
/**
* This method uses reflection to invoke the given Method
* on the passed in Object instance. This method also
* catches the InvocationTargeException, in order to always
* re-throw the original exception from the Method invocation.
*
* @param method the Method to invoke
* @param objectToInvokeOn the Object to invoke the Method on
* @param args the arguments to the Method
* @return an Object representing the result of the Method call
* @throws Throwable
*/
static Object invokeMethodOnObject(Method method, Object objectToInvokeOn, Object[] args) throws IllegalAccessException, Throwable {
try {
return method.invoke(objectToInvokeOn, args);
} catch (InvocationTargetException invocationException) {
throw invocationException.getTargetException();
}
}
/**
* Creates a dynamic proxy for the given service. This method
* installs an InvocationHandler that will manage the dynamics of the
* underlying OSGi service (un-bind and re-bind).
*
* @param bundleContext the BundleContext used to obtain this service
* @param urlParser the OSGiURLParser used for this service
* @param serviceReference the ServiceReference for the service to proxy
* @return a ServiceProxyInfo instance, which includes the proxy or underlying service.
*/
static ServiceProxyInfo getProxyForSingleService(BundleContext bundleContext, OSGiURLParser urlParser, ServiceReference serviceReference) {
return getProxyForSingleService(bundleContext,
urlParser,
serviceReference,
new RetryInvocationHandlerFactory());
}
/**
* Creates a dynamic proxy for the given service. This method
* calls on an InvocationHandlerFactory to create the handler associated
* with this proxy. This method allows callers to customize the type of
* InvocationHandler desired to be used with the proxy.
*
* @param bundleContext the BundleContext used to obtain this service
* @param urlParser the OSGiURLParser used for this service
* @param serviceReference the ServiceReference for the service to proxy
* @param handlerFactory a factory method for creating the InvocationHandler to be
* associated with this service proxy
* @return a ServiceProxyInfo instance, which includes the proxy or underlying service.
*/
static ServiceProxyInfo getProxyForSingleService(BundleContext bundleContext, OSGiURLParser urlParser, ServiceReference serviceReference, InvocationHandlerFactory handlerFactory) {
final Object requestedService =
bundleContext.getService(serviceReference);
ClassLoader tempLoader = null;
try {
tempLoader = (ClassLoader)SecurityUtils.invokePrivilegedAction(new PrivilegedExceptionAction() {
@Override
public Object run() throws Exception {
return requestedService.getClass().getClassLoader();
}
});
} catch (Exception e) {
logger.log(Level.FINE,
"Exception occurred while trying to obtain OSGi service's ClassLoader",
e);
}
try {
Class clazz = Class.forName(urlParser.getServiceInterface(), true, tempLoader);
if (clazz.isInterface()) {
InvocationHandler handler =
handlerFactory.create(bundleContext, serviceReference, urlParser, requestedService);
final Object serviceProxy = Proxy.newProxyInstance(tempLoader, new Class[] {clazz}, handler);
return new ServiceProxyInfo(serviceProxy, handler, true);
}
else {
logger.log(Level.WARNING,
"The service type " + clazz.getName() +
" is not an interface. The Gemini Naming implementation cannot generate a proxy for this service.");
return new ServiceProxyInfo(requestedService, null, false);
}
}
catch (ClassNotFoundException classNotFoundException) {
tempLoader = requestedService.getClass().getClassLoader();
final Class[] interfaces = getInterfaces(serviceReference, bundleContext, tempLoader);
if (interfaces.length > 0) {
InvocationHandler handler =
handlerFactory.create(bundleContext, serviceReference,
urlParser, requestedService);
final Object serviceProxy = Proxy.newProxyInstance(tempLoader, interfaces, handler);
return new ServiceProxyInfo(serviceProxy, handler, true);
}
else {
logger.log(Level.WARNING,
"No compatible interfaces could be found for this OSGi service, type = " +
requestedService.getClass().getName() + ". The Gemini Naming implementation cannot generate a proxy for this service.");
throw new IllegalArgumentException("No compatible interfaces could be found for this OSGi service, type = " +
urlParser.getServiceInterface() + " (probably a JNDI Service Name)" + ". The Gemini Naming implementation cannot generate a proxy for this service.");
}
}
}
private static boolean isAssignable(ServiceReference serviceReference, BundleContext bundleContext, Class clazz) {
return serviceReference.isAssignableTo(bundleContext.getBundle(), clazz.getName());
}
private static boolean isInterfacePublic(Class clazz) {
return Modifier.isPublic(clazz.getModifiers());
}
private static Class[] getInterfaces(ServiceReference serviceReference, BundleContext bundleContext, ClassLoader classLoader) {
String[] objectClassValues = (String [])serviceReference.getProperty(Constants.OBJECTCLASS);
List listOfClasses = new LinkedList();
for(int i = 0; i < objectClassValues.length; i++) {
try {
Class clazz =
Class.forName(objectClassValues[i], true, classLoader);
if(clazz.isInterface()) {
if (isInterfacePublic(clazz)) {
if (isAssignable(serviceReference, bundleContext, clazz)) {
listOfClasses.add(clazz);
}
} else {
logger.warning("Unable to generate proxy for non-public interface: " +
clazz.getName() + ". This interface will not be available to clients");
}
}
}
catch (ClassNotFoundException e) {
// just continue
}
}
if(listOfClasses.isEmpty()) {
return new Class[0];
} else {
Class[] interfacesToReturn = new Class[listOfClasses.size()];
for(int i = 0; i < listOfClasses.size(); i++) {
interfacesToReturn[i] = (Class)listOfClasses.get(i);
}
return interfacesToReturn;
}
}
private static class RetryInvocationHandlerFactory implements InvocationHandlerFactory {
@Override
public InvocationHandler create(BundleContext bundleContext, ServiceReference serviceReference, OSGiURLParser urlParser, Object osgiService) {
return new ServiceInvocationHandler(bundleContext,
serviceReference,
urlParser,
osgiService);
}
}
}