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