View Javadoc

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