1 //
2 // ========================================================================
3 // Copyright (c) 1995-2014 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
36 import org.eclipse.jetty.util.log.Logger;
37
38
39
40 /**
41 * ContextFactory.java
42 *
43 * This is an object factory that produces a jndi naming
44 * context based on a classloader.
45 *
46 * It is used for the java:comp context.
47 *
48 * This object factory is bound at java:comp. When a
49 * lookup arrives for java:comp, this object factory
50 * is invoked and will return a context specific to
51 * the caller's environment (so producing the java:comp/env
52 * specific to a webapp).
53 *
54 * The context selected is based on classloaders. First
55 * we try looking at the thread context classloader if it is set, and walk its
56 * hierarchy, creating a context if none is found. If the thread context classloader
57 * is not set, then we use the classloader associated with the current Context.
58 *
59 * If there is no current context, or no classloader, we return null.
60 *
61 * Created: Fri Jun 27 09:26:40 2003
62 *
63 *
64 *
65 */
66 public class ContextFactory implements ObjectFactory
67 {
68 private static Logger __log = NamingUtil.__log;
69
70 /**
71 * Map of classloaders to contexts.
72 */
73 private static final WeakHashMap __contextMap = new WeakHashMap();
74
75 /**
76 * Threadlocal for injecting a context to use
77 * instead of looking up the map.
78 */
79 private static final ThreadLocal __threadContext = new ThreadLocal();
80
81
82 /**
83 * Find or create a context which pertains to a classloader.
84 *
85 * If the thread context classloader is set, we try to find an already-created naming context
86 * for it. If one does not exist, we walk its classloader hierarchy until one is found, or we
87 * run out of parent classloaders. In the latter case, we will create a new naming context associated
88 * with the original thread context classloader.
89 *
90 * If the thread context classloader is not set, we obtain the classloader from the current
91 * jetty Context, and look for an already-created naming context.
92 *
93 * If there is no current jetty Context, or it has no associated classloader, we
94 * return null.
95 * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
96 */
97 public Object getObjectInstance (Object obj,
98 Name name,
99 Context nameCtx,
100 Hashtable env)
101 throws Exception
102 {
103 //First, see if we have had a context injected into us to use.
104 Context ctx = (Context)__threadContext.get();
105 if (ctx != null)
106 {
107 if(__log.isDebugEnabled()) __log.debug("Using the Context that is bound on the thread");
108 return ctx;
109 }
110
111
112 ClassLoader tccl = Thread.currentThread().getContextClassLoader();
113 ClassLoader loader = tccl;
114 //If the thread context classloader is set, then try its hierarchy to find a matching context
115 if (loader != null)
116 {
117 if (__log.isDebugEnabled() && loader != null) __log.debug("Trying thread context classloader");
118 while (ctx == null && loader != null)
119 {
120 ctx = getContextForClassLoader(loader);
121 if (ctx == null && loader != null)
122 loader = loader.getParent();
123 }
124
125 if (ctx == null)
126 {
127 ctx = newNamingContext(obj, tccl, env, name, nameCtx);
128 __contextMap.put (tccl, ctx);
129 if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+tccl);
130 }
131 return ctx;
132 }
133
134
135 //If trying thread context classloader hierarchy failed, try the
136 //classloader associated with the current context
137 if (ContextHandler.getCurrentContext() != null)
138 {
139
140 if (__log.isDebugEnabled() && loader != null) __log.debug("Trying classloader of current org.eclipse.jetty.server.handler.ContextHandler");
141 loader = ContextHandler.getCurrentContext().getContextHandler().getClassLoader();
142 ctx = (Context)__contextMap.get(loader);
143
144 if (ctx == null && loader != null)
145 {
146 ctx = newNamingContext(obj, loader, env, name, nameCtx);
147 __contextMap.put (loader, ctx);
148 if(__log.isDebugEnabled())__log.debug("Made context "+name.get(0)+" for classloader: "+loader);
149 }
150
151 return ctx;
152 }
153 return null;
154 }
155
156
157 /**
158 * Create a new NamingContext.
159 * @param obj
160 * @param loader
161 * @param env
162 * @param name
163 * @param parentCtx
164 * @return
165 * @throws Exception
166 */
167 public NamingContext newNamingContext(Object obj, ClassLoader loader, Hashtable env, Name name, Context parentCtx)
168 throws Exception
169 {
170 Reference ref = (Reference)obj;
171 StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
172 String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
173 NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
174
175 return new NamingContext (env,
176 name.get(0),
177 (NamingContext)parentCtx,
178 parser);
179 }
180
181
182 /**
183 * Find the naming Context for the given classloader
184 * @param loader
185 * @return
186 */
187 public Context getContextForClassLoader(ClassLoader loader)
188 {
189 if (loader == null)
190 return null;
191
192 return (Context)__contextMap.get(loader);
193 }
194
195
196 /**
197 * Associate the given Context with the current thread.
198 * resetComponentContext method should be called to reset the context.
199 * @param ctx the context to associate to the current thread.
200 * @return the previous context associated on the thread (can be null)
201 */
202 public static Context setComponentContext(final Context ctx)
203 {
204 Context previous = (Context)__threadContext.get();
205 __threadContext.set(ctx);
206 return previous;
207 }
208
209 /**
210 * Set back the context with the given value.
211 * Don't return the previous context, use setComponentContext() method for this.
212 * @param ctx the context to associate to the current thread.
213 */
214 public static void resetComponentContext(final Context ctx)
215 {
216 __threadContext.set(ctx);
217 }
218
219 public static void dump(Appendable out, String indent) throws IOException
220 {
221 out.append("o.e.j.jndi.ContextFactory@").append(Long.toHexString(__contextMap.hashCode())).append("\n");
222 int size=__contextMap.size();
223 int i=0;
224 for (Map.Entry<ClassLoader,NamingContext> entry : ((Map<ClassLoader,NamingContext>)__contextMap).entrySet())
225 {
226 boolean last=++i==size;
227 ClassLoader loader=entry.getKey();
228 out.append(indent).append(" +- ").append(loader.getClass().getSimpleName()).append("@").append(Long.toHexString(loader.hashCode())).append(": ");
229
230 NamingContext context = entry.getValue();
231 context.dump(out,indent+(last?" ":" | "));
232 }
233 }
234
235 }