View Javadoc

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