View Javadoc

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