001: // ========================================================================
002: // $Id: ContextFactory.java 1327 2006-11-27 18:40:14Z janb $
003: // Copyright 1999-2006 Mort Bay Consulting Pty. Ltd.
004: // ------------------------------------------------------------------------
005: // Licensed under the Apache License, Version 2.0 (the "License");
006: // you may not use this file except in compliance with the License.
007: // You may obtain a copy of the License at
008: // http://www.apache.org/licenses/LICENSE-2.0
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014: // ========================================================================
015:
016: package org.mortbay.naming;
017:
018: import java.util.Hashtable;
019: import java.util.WeakHashMap;
020:
021: import javax.naming.Context;
022: import javax.naming.Name;
023: import javax.naming.NameParser;
024: import javax.naming.Reference;
025: import javax.naming.StringRefAddr;
026: import javax.naming.spi.ObjectFactory;
027:
028: import org.mortbay.jetty.handler.ContextHandler;
029: import org.mortbay.log.Log;
030:
031: /**
032: * ContextFactory.java
033: *
034: * This is an object factory that produces a jndi naming
035: * context based on a classloader.
036: *
037: * It is used for the java:comp context.
038: *
039: * This object factory is bound at java:comp. When a
040: * lookup arrives for java:comp, this object factory
041: * is invoked and will return a context specific to
042: * the caller's environment (so producing the java:comp/env
043: * specific to a webapp).
044: *
045: * The context selected is based on classloaders. First
046: * we try looking in at the classloader that is associated
047: * with the current webapp context (if there is one). If
048: * not, we use the thread context classloader.
049: *
050: * Created: Fri Jun 27 09:26:40 2003
051: *
052: * @author <a href="mailto:janb@mortbay.com">Jan Bartel</a>
053: *
054: */
055: public class ContextFactory implements ObjectFactory {
056: /**
057: * Map of classloaders to contexts.
058: */
059: private static WeakHashMap _contextMap;
060:
061: /**
062: * Threadlocal for injecting a context to use
063: * instead of looking up the map.
064: */
065: private static ThreadLocal _threadContext;
066:
067: static {
068: _contextMap = new WeakHashMap();
069: _threadContext = new ThreadLocal();
070: }
071:
072: /**
073: * Find or create a context which pertains to a classloader.
074: *
075: * We use either the classloader for the current ContextHandler if
076: * we are handling a request, OR we use the thread context classloader
077: * if we are not processing a request.
078: * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
079: */
080: public Object getObjectInstance(Object obj, Name name,
081: Context nameCtx, Hashtable env) throws Exception {
082: //First, see if we have had a context injected into us to use.
083: Context ctx = (Context) _threadContext.get();
084: if (ctx != null) {
085: if (Log.isDebugEnabled())
086: Log
087: .debug("Using the Context that is bound on the thread");
088: return ctx;
089: }
090:
091: // Next, see if we are in a webapp context, if we are, use
092: // the classloader of the webapp to find the right jndi comp context
093: ClassLoader loader = null;
094: if (ContextHandler.getCurrentContext() != null) {
095: loader = ContextHandler.getCurrentContext()
096: .getContextHandler().getClassLoader();
097: }
098:
099: if (loader != null) {
100: if (Log.isDebugEnabled())
101: Log
102: .debug("Using classloader of current org.mortbay.jetty.handler.ContextHandler");
103: } else {
104: //Not already in a webapp context, in that case, we try the
105: //curren't thread's classloader instead
106: loader = Thread.currentThread().getContextClassLoader();
107: if (Log.isDebugEnabled())
108: Log.debug("Using thread context classloader");
109: }
110:
111: //Get the context matching the classloader
112: ctx = (Context) _contextMap.get(loader);
113:
114: //The map does not contain an entry for this classloader
115: if (ctx == null) {
116: //Check if a parent classloader has created the context
117: ctx = getParentClassLoaderContext(loader);
118:
119: //Didn't find a context to match any of the ancestors
120: //of the classloader, so make a context
121: if (ctx == null) {
122: Reference ref = (Reference) obj;
123: StringRefAddr parserAddr = (StringRefAddr) ref
124: .get("parser");
125: String parserClassName = (parserAddr == null ? null
126: : (String) parserAddr.getContent());
127: NameParser parser = (NameParser) (parserClassName == null ? null
128: : loader.loadClass(parserClassName)
129: .newInstance());
130:
131: ctx = new NamingContext(env, name.get(0), nameCtx,
132: parser);
133: if (Log.isDebugEnabled())
134: Log.debug("No entry for classloader: " + loader);
135: _contextMap.put(loader, ctx);
136: }
137: }
138:
139: return ctx;
140: }
141:
142: /**
143: * Keep trying ancestors of the given classloader to find one to which
144: * the context is bound.
145: * @param loader
146: * @return
147: */
148: public Context getParentClassLoaderContext(ClassLoader loader) {
149: Context ctx = null;
150: ClassLoader cl = loader;
151: for (cl = cl.getParent(); (cl != null) && (ctx == null); cl = cl
152: .getParent()) {
153: ctx = (Context) _contextMap.get(cl);
154: }
155:
156: return ctx;
157: }
158:
159: /**
160: * Associate the given Context with the current thread.
161: * resetComponentContext method should be called to reset the context.
162: * @param ctx the context to associate to the current thread.
163: * @return the previous context associated on the thread (can be null)
164: */
165: public static Context setComponentContext(final Context ctx) {
166: Context previous = (Context) _threadContext.get();
167: _threadContext.set(ctx);
168: return previous;
169: }
170:
171: /**
172: * Set back the context with the given value.
173: * Don't return the previous context, use setComponentContext() method for this.
174: * @param ctx the context to associate to the current thread.
175: */
176: public static void resetComponentContext(final Context ctx) {
177: _threadContext.set(ctx);
178: }
179:
180: }
|