View Javadoc

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