1 //
2 // ========================================================================
3 // Copyright (c) 1995-2012 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 in at the classloader that is associated
56 * with the current webapp context (if there is one). If
57 * not, we use the thread context classloader.
58 *
59 * Created: Fri Jun 27 09:26:40 2003
60 *
61 *
62 *
63 */
64 public class ContextFactory implements ObjectFactory
65 {
66 private static Logger __log = NamingUtil.__log;
67
68 /**
69 * Map of classloaders to contexts.
70 */
71 private static final WeakHashMap __contextMap = new WeakHashMap();
72
73 /**
74 * Threadlocal for injecting a context to use
75 * instead of looking up the map.
76 */
77 private static final ThreadLocal __threadContext = new ThreadLocal();
78
79
80 /**
81 * Find or create a context which pertains to a classloader.
82 *
83 * We use either the classloader for the current ContextHandler if
84 * we are handling a request, OR we use the thread context classloader
85 * if we are not processing a request.
86 * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
87 */
88 public Object getObjectInstance (Object obj,
89 Name name,
90 Context nameCtx,
91 Hashtable env)
92 throws Exception
93 {
94 //First, see if we have had a context injected into us to use.
95 Context ctx = (Context)__threadContext.get();
96 if (ctx != null)
97 {
98 if(__log.isDebugEnabled()) __log.debug("Using the Context that is bound on the thread");
99 return ctx;
100 }
101
102 // Next, see if we are in a webapp context, if we are, use
103 // the classloader of the webapp to find the right jndi comp context
104 ClassLoader loader = null;
105 if (ContextHandler.getCurrentContext() != null)
106 {
107 loader = ContextHandler.getCurrentContext().getContextHandler().getClassLoader();
108 }
109
110
111 if (loader != null)
112 {
113 if (__log.isDebugEnabled()) __log.debug("Using classloader of current org.eclipse.jetty.server.handler.ContextHandler");
114 }
115 else
116 {
117 //Not already in a webapp context, in that case, we try the
118 //curren't thread's classloader instead
119 loader = Thread.currentThread().getContextClassLoader();
120 if (__log.isDebugEnabled()) __log.debug("Using thread context classloader");
121 }
122
123 //Get the context matching the classloader
124 ctx = (Context)__contextMap.get(loader);
125
126 //The map does not contain an entry for this classloader
127 if (ctx == null)
128 {
129 //Check if a parent classloader has created the context
130 ctx = getParentClassLoaderContext(loader);
131
132 //Didn't find a context to match any of the ancestors
133 //of the classloader, so make a context
134 if (ctx == null)
135 {
136 Reference ref = (Reference)obj;
137 StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
138 String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
139 NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
140
141 ctx = new NamingContext (env,
142 name.get(0),
143 (NamingContext)nameCtx,
144 parser);
145 if(__log.isDebugEnabled())__log.debug("No entry for classloader: "+loader);
146 __contextMap.put (loader, ctx);
147 }
148 }
149
150 return ctx;
151 }
152
153 /**
154 * Keep trying ancestors of the given classloader to find one to which
155 * the context is bound.
156 * @param loader
157 * @return the context from the parent class loader
158 */
159 public Context getParentClassLoaderContext (ClassLoader loader)
160 {
161 Context ctx = null;
162 ClassLoader cl = loader;
163 for (cl = cl.getParent(); (cl != null) && (ctx == null); cl = cl.getParent())
164 {
165 ctx = (Context)__contextMap.get(cl);
166 }
167
168 return ctx;
169 }
170
171
172 /**
173 * Associate the given Context with the current thread.
174 * resetComponentContext method should be called to reset the context.
175 * @param ctx the context to associate to the current thread.
176 * @return the previous context associated on the thread (can be null)
177 */
178 public static Context setComponentContext(final Context ctx)
179 {
180 Context previous = (Context)__threadContext.get();
181 __threadContext.set(ctx);
182 return previous;
183 }
184
185 /**
186 * Set back the context with the given value.
187 * Don't return the previous context, use setComponentContext() method for this.
188 * @param ctx the context to associate to the current thread.
189 */
190 public static void resetComponentContext(final Context ctx)
191 {
192 __threadContext.set(ctx);
193 }
194
195 public static void dump(Appendable out, String indent) throws IOException
196 {
197 out.append("o.e.j.jndi.ContextFactory@").append(Long.toHexString(__contextMap.hashCode())).append("\n");
198 int size=__contextMap.size();
199 int i=0;
200 for (Map.Entry<ClassLoader,NamingContext> entry : ((Map<ClassLoader,NamingContext>)__contextMap).entrySet())
201 {
202 boolean last=++i==size;
203 ClassLoader loader=entry.getKey();
204 out.append(indent).append(" +- ").append(loader.getClass().getSimpleName()).append("@").append(Long.toHexString(loader.hashCode())).append(": ");
205
206 NamingContext context = entry.getValue();
207 context.dump(out,indent+(last?" ":" | "));
208 }
209 }
210
211 }