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