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