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      * @throws Exception
186      */
187     public NamingContext newNamingContext(Object obj, ClassLoader loader, Hashtable env, Name name, Context parentCtx)
188     throws Exception
189     {
190         Reference ref = (Reference)obj;
191         StringRefAddr parserAddr = (StringRefAddr)ref.get("parser");
192         String parserClassName = (parserAddr==null?null:(String)parserAddr.getContent());
193         NameParser parser = (NameParser)(parserClassName==null?null:loader.loadClass(parserClassName).newInstance());
194 
195         return new NamingContext (env,
196                                   name.get(0),
197                                   (NamingContext)parentCtx,
198                                   parser);
199     }
200 
201   
202     /**
203      * Find the naming Context for the given classloader
204      * @param loader
205      */
206     public Context getContextForClassLoader(ClassLoader loader)
207     {
208         if (loader == null)
209             return null;
210         
211         return (Context)__contextMap.get(loader);
212     }
213 
214 
215     /**
216      * Associate the given Context with the current thread.
217      * disassociate method should be called to reset the context.
218      * @param ctx the context to associate to the current thread.
219      * @return the previous context associated on the thread (can be null)
220      */
221     public static Context associateContext(final Context ctx)
222     {
223         Context previous = (Context)__threadContext.get();
224         __threadContext.set(ctx);
225         return previous;
226     }
227 
228     public static void disassociateContext(final Context ctx)
229     {
230         __threadContext.remove();
231     }
232     
233     
234     public static ClassLoader associateClassLoader(final ClassLoader loader)
235     {
236         ClassLoader prev = (ClassLoader)__threadClassLoader.get();
237         __threadClassLoader.set(loader);
238         return prev;
239     }
240     
241     
242     public static void disassociateClassLoader ()
243     {
244         __threadClassLoader.remove();
245     }
246 
247     public static void dump(Appendable out, String indent) throws IOException
248     {
249         out.append("o.e.j.jndi.ContextFactory@").append(Long.toHexString(__contextMap.hashCode())).append("\n");
250         int size=__contextMap.size();
251         int i=0;
252         for (Map.Entry<ClassLoader,NamingContext> entry : ((Map<ClassLoader,NamingContext>)__contextMap).entrySet())
253         {
254             boolean last=++i==size;
255             ClassLoader loader=entry.getKey();
256             out.append(indent).append(" +- ").append(loader.getClass().getSimpleName()).append("@").append(Long.toHexString(loader.hashCode())).append(": ");
257 
258             NamingContext context = entry.getValue();
259             context.dump(out,indent+(last?"    ":" |  "));
260         }
261     }
262 
263 }