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