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