View Javadoc

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