1 //
2 // ========================================================================
3 // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.jndi;
20
21
22 import java.io.IOException;
23 import java.util.Hashtable;
24 import java.util.Map;
25 import java.util.WeakHashMap;
26
27 import javax.naming.Context;
28 import javax.naming.Name;
29 import javax.naming.NameParser;
30 import javax.naming.Reference;
31 import javax.naming.StringRefAddr;
32 import javax.naming.spi.ObjectFactory;
33
34 import org.eclipse.jetty.server.handler.ContextHandler;
35 import org.eclipse.jetty.util.log.Logger;
36
37
38
39 /**
40 * ContextFactory
41 * <p>
42 * This is an object factory that produces a jndi naming
43 * context based on a classloader.
44 * <p>
45 * It is used for the <code>java:comp</code> context.
46 * <p>
47 * This object factory is bound at <code>java:comp</code>. When a
48 * lookup arrives for java:comp, this object factory
49 * is invoked and will return a context specific to
50 * the caller's environment (so producing the <code>java:comp/env</code>
51 * specific to a webapp).
52 * <p>
53 * The context selected is based on classloaders. First
54 * we try looking at the thread context classloader if it is set, and walk its
55 * hierarchy, creating a context if none is found. If the thread context classloader
56 * is not set, then we use the classloader associated with the current Context.
57 * <p>
58 * If there is no current context, or no classloader, we return null.
59 */
60 public class ContextFactory implements ObjectFactory
61 {
62 private static Logger __log = NamingUtil.__log;
63
64 /**
65 * Map of classloaders to contexts.
66 */
67 private static final WeakHashMap __contextMap = new WeakHashMap();
68
69 /**
70 * Threadlocal for injecting a context to use
71 * instead of looking up the map.
72 */
73 private static final ThreadLocal<Context> __threadContext = new ThreadLocal<Context>();
74
75 /**
76 * Threadlocal for setting a classloader which must be used
77 * when finding the comp context.
78 */
79 private static final ThreadLocal<ClassLoader> __threadClassLoader = new ThreadLocal<ClassLoader>();
80
81
82
83 /**
84 * Find or create a context which pertains to a classloader.
85 * <p>
86 * If the thread context classloader is set, we try to find an already-created naming context
87 * for it. If one does not exist, we walk its classloader hierarchy until one is found, or we
88 * run out of parent classloaders. In the latter case, we will create a new naming context associated
89 * with the original thread context classloader.
90 * <p>
91 * If the thread context classloader is not set, we obtain the classloader from the current
92 * jetty Context, and look for an already-created naming context.
93 * <p>
94 * If there is no current jetty Context, or it has no associated classloader, we
95 * return null.
96 * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
97 */
98 public Object getObjectInstance (Object obj,
99 Name name,
100 Context nameCtx,
101 Hashtable env)
102 throws Exception
103 {
104 //First, see if we have had a context injected into us to use.
105 Context ctx = (Context)__threadContext.get();
106 if (ctx != null)
107 {
108 if(__log.isDebugEnabled()) __log.debug("Using the Context that is bound on the thread");
109 return ctx;
110 }
111
112 //See if there is a classloader to use for finding the comp context
113 //Don't use its parent hierarchy if set.
114 ClassLoader loader = (ClassLoader)__threadClassLoader.get();
115 if (loader != null)
116 {
117 if (__log.isDebugEnabled() && loader != null) __log.debug("Using threadlocal classloader");
118 ctx = getContextForClassLoader(loader);
119 if (ctx == null)
120 {
121 ctx = newNamingContext(obj, loader, env, name, nameCtx);
122 __contextMap.put (loader, ctx);
123 if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+loader);
124 }
125 return ctx;
126 }
127
128 //If the thread context classloader is set, then try its hierarchy to find a matching context
129 ClassLoader tccl = Thread.currentThread().getContextClassLoader();
130 loader = tccl;
131 if (loader != null)
132 {
133 if (__log.isDebugEnabled() && loader != null) __log.debug("Trying thread context classloader");
134 while (ctx == null && loader != null)
135 {
136 ctx = getContextForClassLoader(loader);
137 if (ctx == null && loader != null)
138 loader = loader.getParent();
139 }
140
141 if (ctx == null)
142 {
143 ctx = newNamingContext(obj, tccl, env, name, nameCtx);
144 __contextMap.put (tccl, ctx);
145 if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+tccl);
146 }
147 return ctx;
148 }
149
150
151 //If trying thread context classloader hierarchy failed, try the
152 //classloader associated with the current context
153 if (ContextHandler.getCurrentContext() != null)
154 {
155
156 if (__log.isDebugEnabled() && loader != null) __log.debug("Trying classloader of current org.eclipse.jetty.server.handler.ContextHandler");
157 loader = ContextHandler.getCurrentContext().getContextHandler().getClassLoader();
158 ctx = (Context)__contextMap.get(loader);
159
160 if (ctx == null && loader != null)
161 {
162 ctx = newNamingContext(obj, loader, env, name, nameCtx);
163 __contextMap.put (loader, ctx);
164 if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+loader);
165 }
166
167 return ctx;
168 }
169 return null;
170 }
171
172
173 /**
174 * Create a new NamingContext.
175 * @param obj the object to create
176 * @param loader the classloader for the naming context
177 * @param env the jndi env for the entry
178 * @param name the name of the entry
179 * @param parentCtx the parent context of the entry
180 * @return the newly created naming context
181 * @throws Exception if unable to create a new naming context
182 */
183 public NamingContext newNamingContext(Object obj, ClassLoader loader, Hashtable env, Name name, Context parentCtx)
184 throws Exception
185 {
186 Reference ref = (Reference)obj;
187 StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
188 String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
189 NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
190
191 return new NamingContext (env,
192 name.get(0),
193 (NamingContext)parentCtx,
194 parser);
195 }
196
197
198 /**
199 * Find the naming Context for the given classloader
200 * @param loader the classloader for the context
201 * @return the context for the classloader
202 */
203 public Context getContextForClassLoader(ClassLoader loader)
204 {
205 if (loader == null)
206 return null;
207
208 return (Context)__contextMap.get(loader);
209 }
210
211 /**
212 * Associate the given Context with the current thread.
213 * disassociate method should be called to reset the context.
214 * @param ctx the context to associate to the current thread.
215 * @return the previous context associated on the thread (can be null)
216 */
217 public static Context associateContext(final Context ctx)
218 {
219 Context previous = (Context)__threadContext.get();
220 __threadContext.set(ctx);
221 return previous;
222 }
223
224 public static void disassociateContext(final Context ctx)
225 {
226 __threadContext.set(null);
227 }
228
229 public static ClassLoader associateClassLoader(final ClassLoader loader)
230 {
231 ClassLoader prev = (ClassLoader)__threadClassLoader.get();
232 __threadClassLoader.set(loader);
233 return prev;
234 }
235
236 public static void disassociateClassLoader ()
237 {
238 __threadClassLoader.set(null);
239 }
240
241 public static void dump(Appendable out, String indent) throws IOException
242 {
243 out.append("o.e.j.jndi.ContextFactory@").append(Long.toHexString(__contextMap.hashCode())).append("\n");
244 int size=__contextMap.size();
245 int i=0;
246 for (Map.Entry<ClassLoader,NamingContext> entry : ((Map<ClassLoader,NamingContext>)__contextMap).entrySet())
247 {
248 boolean last=++i==size;
249 ClassLoader loader=entry.getKey();
250 out.append(indent).append(" +- ").append(loader.getClass().getSimpleName()).append("@").append(Long.toHexString(loader.hashCode())).append(": ");
251
252 NamingContext context = entry.getValue();
253 context.dump(out,indent+(last?" ":" | "));
254 }
255 }
256 }