View Javadoc

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