blob: b530d814ab0ed23398ffc545418bf082481bece5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 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.security.PrivilegedExceptionAction;
import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleReference;
import org.osgi.service.jndi.JNDIConstants;
/**
* Utility methods for the "traditional" JNDI builder implementations.
*
*
* @version $Revision$
*/
class BuilderUtils {
private static Logger logger = Logger.getLogger(BuilderUtils.class.getName());
/* private constructor, static utility class */
private BuilderUtils() {}
/**
* Array of strategies to use, in priority order, when
* attempting to find the caller's BundleContext.
*/
private static final GetBundleContextStrategy[] getBundleContextStrategies =
{ new EnvironmentPropertyStrategyImpl(),
new ThreadContextStrategyImpl(),
new CallStackStrategyImpl() };
/**
* This utility method implements the process for obtaining the
* JNDI client's BundleContext. This method should only be used within
* the "traditional" method of JNDI support.
*
* This method will try the following methods to obtain the BundleContext:
* 1. check for the "osgi.service.jndi.bundleContext" environment property
* 2. check the thread context ClassLoader to see if it supports BundleReference. If so, it uses
* this ClassLoader to obtain the caller's BundleContext.
* 3. checks the current call stack to find the code that is invoking the naming function. The BundleContext
* that is associated with this code is returned.
*
* @param environment the JNDI environment
* @param namingClassType the class name of the type to be used to walk the call stack. Typically,
* this value will be one of the following values:
* "javax.naming.InitialContext" - to detect Context creation
* "javax.naming.spi.NamingManager" - to detect Object resolution
* "javax.naming.spi.DirectoryManager" - to detect Object resolution for directories
*
* @return the BundleContext associated with the JNDI client, or
* null if no BundleContext could be found.
*/
static BundleContext getBundleContext(Hashtable environment, String namingClassType) {
// iterate over the existing strategies to attempt to find a BundleContext
// for the calling Bundle
for(int i = 0; i < getBundleContextStrategies.length; i++) {
BundleContext clientBundleContext =
getBundleContextStrategies[i].getBundleContext(environment, namingClassType);
if(clientBundleContext != null) {
return clientBundleContext;
}
}
// if a BundleContext isn't found at this point, return null
return null;
}
/**
* Internal interface used to abstract the process of obtaining
* the caller's BundleContext.
*
*/
private interface GetBundleContextStrategy {
/**
* Obtain the caller's BundleContext.
*
* @param environment the JNDI environment properties
* @param namingClassType the name of the javax.naming class to use when
* searching for the calling Bundle.
* @return the caller's BundleContext, or
* null if none found.
*/
public BundleContext getBundleContext(Hashtable environment, String namingClassType);
}
/**
* Strategy implementation that attempts to find the caller's
* BundleContext in the JNDI environment.
*
*/
private static class EnvironmentPropertyStrategyImpl implements GetBundleContextStrategy {
public BundleContext getBundleContext(Hashtable environment, String namingClassType) {
if((environment != null) && (environment.containsKey(JNDIConstants.BUNDLE_CONTEXT))) {
Object result =
environment.get(JNDIConstants.BUNDLE_CONTEXT);
if(result instanceof BundleContext) {
return (BundleContext)result;
}
}
return null;
}
}
/**
* Strategy implementation that attempts to find the
* caller's BundleContext on the ThreadContext ClassLoader.
*
*/
private static class ThreadContextStrategyImpl implements GetBundleContextStrategy {
public BundleContext getBundleContext(Hashtable environment, String namingClassType) {
ClassLoader threadContextClassloader = null;
try {
// this code must run in a doPrivileged() block
threadContextClassloader = (ClassLoader)SecurityUtils.invokePrivilegedAction(new PrivilegedExceptionAction() {
public Object run() throws Exception {
return Thread.currentThread().getContextClassLoader();
}
});
} catch (Exception e) {
logger.log(Level.FINE, "Exception occurred while trying to obtain the ThreadContextClassloader.", e);
}
if ((threadContextClassloader != null)
&& (threadContextClassloader instanceof BundleReference)) {
BundleContext result = getBundleContextFromClassLoader(threadContextClassloader);
if(result != null) {
return result;
}
}
return null;
}
}
/**
* Strategy implementation that attempts to find the caller's
* BundleContext on the call stack.
*
*/
private static class CallStackStrategyImpl implements GetBundleContextStrategy {
public BundleContext getBundleContext(Hashtable environment, String namingClassType) {
Class[] callStack = null;
try {
// creation of SecurityManager must take place in a doPrivileged() block,
// since JNDI clients should not have to include this permission in
// order to use JNDI services.
callStack = (Class[])SecurityUtils.invokePrivilegedAction(new PrivilegedExceptionAction() {
public Object run() throws Exception {
return new CallStackSecurityManager().getClientCallStack();
}
});
} catch (Exception e) {
logger.log(Level.FINE, "Exception occurred while attempting to traverse client call stack to find caller's BundleContext.",
e);
}
int indexOfConstructor = -1;
for(int i = 0; callStack != null && i < callStack.length; i++) {
if(callStack[i].getName().equals(namingClassType)) {
indexOfConstructor = i;
}
}
// the next stack frame should include the caller of the InitialContext constructor
if (indexOfConstructor >= 0) {
for (int i = indexOfConstructor + 1; i < callStack.length; i++) {
final Class clientClass = callStack[i];
ClassLoader clientClassLoader = null;
try {
clientClassLoader =
(ClassLoader)SecurityUtils.invokePrivilegedAction(new PrivilegedExceptionAction() {
public Object run() throws Exception {
return clientClass.getClassLoader();
}
});
} catch (Exception e) {
logger.log(Level.FINE, "Exception occurred while trying to obtain the client classloader.",
e);
}
if(clientClassLoader instanceof BundleReference) {
return getBundleContextFromClassLoader(clientClassLoader);
}
}
}
return null;
}
private static class CallStackSecurityManager extends SecurityManager {
public Class[] getClientCallStack() {
return getClassContext();
}
}
}
/**
* Attempts to obtain the client's BundleContext from the
* ClassLoader specified in the method call.
*
* If the ClassLoader supports BundleReference, this loader
* represents a client bundle making a request for an OSGi service.
*
* @param classLoader
* @return BundleContext associated with this ClassLoader, or
* null if no BundleContext was associated with this ClassLoaer
*/
private static BundleContext getBundleContextFromClassLoader(ClassLoader classLoader) {
BundleReference bundleRef = (BundleReference) classLoader;
if (bundleRef.getBundle() != null) {
return bundleRef.getBundle().getBundleContext();
}
return null;
}
}