1 // ========================================================================
2 // Copyright (c) 1999-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.jndi;
15
16
17 import java.util.Hashtable;
18 import java.util.WeakHashMap;
19
20 import javax.naming.Context;
21 import javax.naming.Name;
22 import javax.naming.NameParser;
23 import javax.naming.Reference;
24 import javax.naming.StringRefAddr;
25 import javax.naming.spi.ObjectFactory;
26
27 import org.eclipse.jetty.server.handler.ContextHandler;
28 import org.eclipse.jetty.util.log.Log;
29
30
31
32 /**
33 * ContextFactory.java
34 *
35 * This is an object factory that produces a jndi naming
36 * context based on a classloader.
37 *
38 * It is used for the java:comp context.
39 *
40 * This object factory is bound at java:comp. When a
41 * lookup arrives for java:comp, this object factory
42 * is invoked and will return a context specific to
43 * the caller's environment (so producing the java:comp/env
44 * specific to a webapp).
45 *
46 * The context selected is based on classloaders. First
47 * we try looking in at the classloader that is associated
48 * with the current webapp context (if there is one). If
49 * not, we use the thread context classloader.
50 *
51 * Created: Fri Jun 27 09:26:40 2003
52 *
53 *
54 *
55 */
56 public class ContextFactory implements ObjectFactory
57 {
58 /**
59 * Map of classloaders to contexts.
60 */
61 private static WeakHashMap _contextMap;
62
63 /**
64 * Threadlocal for injecting a context to use
65 * instead of looking up the map.
66 */
67 private static ThreadLocal _threadContext;
68
69 static
70 {
71 _contextMap = new WeakHashMap();
72 _threadContext = new ThreadLocal();
73 }
74
75
76
77 /**
78 * Find or create a context which pertains to a classloader.
79 *
80 * We use either the classloader for the current ContextHandler if
81 * we are handling a request, OR we use the thread context classloader
82 * if we are not processing a request.
83 * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
84 */
85 public Object getObjectInstance (Object obj,
86 Name name,
87 Context nameCtx,
88 Hashtable env)
89 throws Exception
90 {
91 //First, see if we have had a context injected into us to use.
92 Context ctx = (Context)_threadContext.get();
93 if (ctx != null)
94 {
95 if(Log.isDebugEnabled()) Log.debug("Using the Context that is bound on the thread");
96 return ctx;
97 }
98
99 // Next, see if we are in a webapp context, if we are, use
100 // the classloader of the webapp to find the right jndi comp context
101 ClassLoader loader = null;
102 if (ContextHandler.getCurrentContext() != null)
103 {
104 loader = ContextHandler.getCurrentContext().getContextHandler().getClassLoader();
105 }
106
107
108 if (loader != null)
109 {
110 if (Log.isDebugEnabled()) Log.debug("Using classloader of current org.eclipse.jetty.server.handler.ContextHandler");
111 }
112 else
113 {
114 //Not already in a webapp context, in that case, we try the
115 //curren't thread's classloader instead
116 loader = Thread.currentThread().getContextClassLoader();
117 if (Log.isDebugEnabled()) Log.debug("Using thread context classloader");
118 }
119
120 //Get the context matching the classloader
121 ctx = (Context)_contextMap.get(loader);
122
123 //The map does not contain an entry for this classloader
124 if (ctx == null)
125 {
126 //Check if a parent classloader has created the context
127 ctx = getParentClassLoaderContext(loader);
128
129 //Didn't find a context to match any of the ancestors
130 //of the classloader, so make a context
131 if (ctx == null)
132 {
133 Reference ref = (Reference)obj;
134 StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
135 String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
136 NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
137
138 ctx = new NamingContext (env,
139 name.get(0),
140 nameCtx,
141 parser);
142 if(Log.isDebugEnabled())Log.debug("No entry for classloader: "+loader);
143 _contextMap.put (loader, ctx);
144 }
145 }
146
147 return ctx;
148 }
149
150 /**
151 * Keep trying ancestors of the given classloader to find one to which
152 * the context is bound.
153 * @param loader
154 * @return
155 */
156 public Context getParentClassLoaderContext (ClassLoader loader)
157 {
158 Context ctx = null;
159 ClassLoader cl = loader;
160 for (cl = cl.getParent(); (cl != null) && (ctx == null); cl = cl.getParent())
161 {
162 ctx = (Context)_contextMap.get(cl);
163 }
164
165 return ctx;
166 }
167
168
169 /**
170 * Associate the given Context with the current thread.
171 * resetComponentContext method should be called to reset the context.
172 * @param ctx the context to associate to the current thread.
173 * @return the previous context associated on the thread (can be null)
174 */
175 public static Context setComponentContext(final Context ctx)
176 {
177 Context previous = (Context)_threadContext.get();
178 _threadContext.set(ctx);
179 return previous;
180 }
181
182 /**
183 * Set back the context with the given value.
184 * Don't return the previous context, use setComponentContext() method for this.
185 * @param ctx the context to associate to the current thread.
186 */
187 public static void resetComponentContext(final Context ctx)
188 {
189 _threadContext.set(ctx);
190 }
191
192 }