001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.geronimo.tomcat; 018 019 import java.beans.PropertyChangeListener; 020 import java.io.IOException; 021 import java.util.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 025 import javax.security.auth.Subject; 026 import javax.security.jacc.PolicyContext; 027 import javax.servlet.Servlet; 028 import javax.servlet.ServletException; 029 030 import org.apache.catalina.Container; 031 import org.apache.catalina.LifecycleException; 032 import org.apache.catalina.LifecycleListener; 033 import org.apache.catalina.Loader; 034 import org.apache.catalina.Manager; 035 import org.apache.catalina.Valve; 036 import org.apache.catalina.Wrapper; 037 import org.apache.catalina.connector.Request; 038 import org.apache.catalina.connector.Response; 039 import org.apache.catalina.core.StandardContext; 040 import org.apache.catalina.ha.CatalinaCluster; 041 import org.apache.catalina.valves.ValveBase; 042 import org.apache.geronimo.common.DeploymentException; 043 import org.apache.geronimo.common.GeronimoSecurityException; 044 import org.apache.geronimo.security.ContextManager; 045 import org.apache.geronimo.security.jacc.RunAsSource; 046 import org.apache.geronimo.tomcat.interceptor.BeforeAfter; 047 import org.apache.geronimo.tomcat.interceptor.ComponentContextBeforeAfter; 048 import org.apache.geronimo.tomcat.interceptor.InstanceContextBeforeAfter; 049 import org.apache.geronimo.tomcat.interceptor.PolicyContextBeforeAfter; 050 import org.apache.geronimo.tomcat.interceptor.UserTransactionBeforeAfter; 051 import org.apache.geronimo.tomcat.listener.RunAsInstanceListener; 052 import org.apache.geronimo.tomcat.util.SecurityHolder; 053 import org.apache.geronimo.tomcat.valve.DefaultSubjectValve; 054 import org.apache.geronimo.tomcat.valve.GeronimoBeforeAfterValve; 055 import org.apache.geronimo.webservices.POJOWebServiceServlet; 056 import org.apache.geronimo.webservices.WebServiceContainer; 057 import org.apache.geronimo.webservices.WebServiceContainerInvoker; 058 059 060 /** 061 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 062 */ 063 public class GeronimoStandardContext extends StandardContext { 064 065 private static final long serialVersionUID = 3834587716552831032L; 066 067 private Subject defaultSubject = null; 068 private RunAsSource runAsSource = RunAsSource.NULL; 069 070 private Map webServiceMap = null; 071 072 private boolean pipelineInitialized; 073 074 private BeforeAfter beforeAfter = null; 075 private int contextCount = 0; 076 077 private static final boolean allowLinking; 078 079 static { 080 allowLinking = new Boolean(System.getProperty("org.apache.geronimo.tomcat.GeronimoStandardContext.allowLinking", "false")); 081 } 082 083 public void setContextProperties(TomcatContext ctx) throws DeploymentException { 084 085 // Create ReadOnlyContext 086 javax.naming.Context enc = ctx.getJndiContext(); 087 setInstanceManager(ctx.getInstanceManager()); 088 089 //try to make sure this mbean properties match those of the TomcatWebAppContext 090 if (ctx instanceof TomcatWebAppContext) { 091 TomcatWebAppContext tctx = (TomcatWebAppContext) ctx; 092 setJavaVMs(tctx.getJavaVMs()); 093 setServer(tctx.getServer()); 094 setJ2EEApplication(tctx.getJ2EEApplication()); 095 setJ2EEServer(tctx.getJ2EEServer()); 096 //install jasper injection support if required 097 if (tctx.getRuntimeCustomizer() != null) { 098 Map<String, Object> servletContext = new HashMap<String, Object>(); 099 Map<Class, Object> customizerContext = new HashMap<Class, Object>(); 100 customizerContext.put(Map.class, servletContext); 101 customizerContext.put(javax.naming.Context.class, enc); 102 tctx.getRuntimeCustomizer().customize(customizerContext); 103 for (Map.Entry<String, Object> entry: servletContext.entrySet()) { 104 getServletContext().setAttribute(entry.getKey(), entry.getValue()); 105 } 106 } 107 } 108 109 int index = 0; 110 BeforeAfter interceptor = new InstanceContextBeforeAfter(null, 111 index++, 112 index++, ctx.getUnshareableResources(), 113 ctx.getApplicationManagedSecurityResources(), 114 ctx.getTrackedConnectionAssociator()); 115 116 // Set ComponentContext BeforeAfter 117 if (enc != null) { 118 interceptor = new ComponentContextBeforeAfter(interceptor, index++, enc); 119 } 120 121 //Set a PolicyContext BeforeAfter 122 SecurityHolder securityHolder = ctx.getSecurityHolder(); 123 if (securityHolder != null) { 124 125 // save the role designates for mapping servlets to their run-as roles 126 runAsSource = securityHolder.getRunAsSource(); 127 128 if (securityHolder.getPolicyContextID() != null) { 129 130 PolicyContext.setContextID(securityHolder.getPolicyContextID()); 131 132 /** 133 * Register our default subject with the ContextManager 134 */ 135 defaultSubject = securityHolder.getDefaultSubject(); 136 137 if (defaultSubject == null) { 138 defaultSubject = ContextManager.EMPTY; 139 } 140 141 interceptor = new PolicyContextBeforeAfter(interceptor, index++, index++, index++, securityHolder.getPolicyContextID(), defaultSubject); 142 143 } 144 } 145 146 //Set a UserTransactionBeforeAfter 147 interceptor = new UserTransactionBeforeAfter(interceptor, index++, ctx.getUserTransaction()); 148 149 Valve clusteredValve = ctx.getClusteredValve(); 150 if (null != clusteredValve) { 151 addValve(clusteredValve); 152 } 153 154 //Set the BeforeAfters as a valve 155 GeronimoBeforeAfterValve geronimoBAValve = new GeronimoBeforeAfterValve(interceptor, index); 156 addValve(geronimoBAValve); 157 beforeAfter = interceptor; 158 contextCount = index; 159 160 //Not clear if user defined valves should be involved in init processing. Probably not since 161 //request and response are null. 162 163 addValve(new SystemMethodValve()); 164 165 // Add User Defined Valves 166 List valveChain = ctx.getValveChain(); 167 if (valveChain != null) { 168 for (Object valve : valveChain) { 169 addValve((Valve)valve); 170 } 171 } 172 173 // Add User Defined Listeners 174 List listenerChain = ctx.getLifecycleListenerChain(); 175 if (listenerChain != null) { 176 for (Object listener : listenerChain) { 177 addLifecycleListener((LifecycleListener)listener); 178 } 179 } 180 181 CatalinaCluster cluster = ctx.getCluster(); 182 if (cluster != null) 183 this.setCluster(cluster); 184 185 Manager manager = ctx.getManager(); 186 if (manager != null) 187 this.setManager(manager); 188 189 pipelineInitialized = true; 190 this.webServiceMap = ctx.getWebServices(); 191 192 this.setCrossContext(ctx.isCrossContext()); 193 194 this.setWorkDir(ctx.getWorkDir()); 195 196 super.setAllowLinking(allowLinking); 197 198 this.setCookies(!ctx.isDisableCookies()); 199 200 //Set the Dispatch listener 201 this.addInstanceListener("org.apache.geronimo.tomcat.listener.DispatchListener"); 202 203 //Set the run-as listener. listeners must be added before start() is called 204 if (runAsSource != null) { 205 this.addInstanceListener(RunAsInstanceListener.class.getName()); 206 } 207 } 208 209 /* This method is called by a background thread to destroy sessions (among other things) 210 * so we need to apply appropriate context to the thread to expose JNDI, etc. 211 */ 212 public void backgroundProcess() { 213 Object context[] = null; 214 215 if (beforeAfter != null){ 216 context = new Object[contextCount]; 217 beforeAfter.before(context, null, null, BeforeAfter.EDGE_SERVLET); 218 } 219 220 try { 221 super.backgroundProcess(); 222 } finally { 223 if (beforeAfter != null){ 224 beforeAfter.after(context, null, null, 0); 225 } 226 } 227 } 228 229 public void kill() throws Exception { 230 Object context[] = null; 231 232 if (beforeAfter != null){ 233 context = new Object[contextCount]; 234 beforeAfter.before(context, null, null, BeforeAfter.EDGE_SERVLET); 235 } 236 237 try { 238 stop(); 239 destroy(); 240 } finally { 241 if (beforeAfter != null){ 242 beforeAfter.after(context, null, null, 0); 243 } 244 } 245 } 246 247 public synchronized void start() throws LifecycleException { 248 if (pipelineInitialized) { 249 try { 250 Valve valve = getFirst(); 251 valve.invoke(null, null); 252 //Install the DefaultSubjectValve after the authentication valve so the default subject is supplied 253 //only if no real subject is authenticated. 254 255 Valve defaultSubjectValve = new DefaultSubjectValve(defaultSubject); 256 addValve(defaultSubjectValve); 257 258 // if a servlet uses run-as then make sure role desgnates have been provided 259 if (hasRunAsServlet()) { 260 if (runAsSource == null) { 261 throw new GeronimoSecurityException("web.xml or annotation specifies a run-as role but no subject configuration supplied for run-as roles"); 262 } 263 } else { 264 // optimization 265 this.removeInstanceListener(RunAsInstanceListener.class.getName()); 266 } 267 268 } catch (IOException e) { 269 if (e.getCause() instanceof LifecycleException) { 270 throw (LifecycleException) e.getCause(); 271 } 272 throw new LifecycleException(e); 273 } catch (ServletException e) { 274 throw new LifecycleException(e); 275 } 276 } else 277 super.start(); 278 } 279 280 public void addChild(Container child) { 281 Wrapper wrapper = (Wrapper) child; 282 283 String servletClassName = wrapper.getServletClass(); 284 if (servletClassName == null) { 285 super.addChild(child); 286 return; 287 } 288 289 ClassLoader cl = this.getParentClassLoader(); 290 291 Class baseServletClass; 292 Class servletClass; 293 try { 294 baseServletClass = cl.loadClass(Servlet.class.getName()); 295 servletClass = cl.loadClass(servletClassName); 296 //Check if the servlet is of type Servlet class 297 if (!baseServletClass.isAssignableFrom(servletClass)) { 298 //Nope - its probably a webservice, so lets see... 299 if (webServiceMap != null) { 300 WebServiceContainer webServiceContainer = (WebServiceContainer) webServiceMap.get(wrapper.getName()); 301 302 if (webServiceContainer != null) { 303 //Yep its a web service 304 //So swap it out with a POJOWebServiceServlet 305 wrapper.setServletClass("org.apache.geronimo.webservices.POJOWebServiceServlet"); 306 307 //Set the WebServiceContainer stuff 308 String webServicecontainerID = wrapper.getName() + WebServiceContainerInvoker.WEBSERVICE_CONTAINER + webServiceContainer.hashCode(); 309 getServletContext().setAttribute(webServicecontainerID, webServiceContainer); 310 wrapper.addInitParameter(WebServiceContainerInvoker.WEBSERVICE_CONTAINER, webServicecontainerID); 311 312 //Set the SEI Class in the attribute 313 String pojoClassID = wrapper.getName() + POJOWebServiceServlet.POJO_CLASS + servletClass.hashCode(); 314 getServletContext().setAttribute(pojoClassID, servletClass); 315 wrapper.addInitParameter(POJOWebServiceServlet.POJO_CLASS, pojoClassID); 316 } 317 } 318 } 319 } catch (ClassNotFoundException e) { 320 throw new RuntimeException(e.getMessage(), e); 321 } 322 323 super.addChild(child); 324 } 325 326 public synchronized void setLoader(final Loader delegate) { 327 Loader loader = new Loader() { 328 329 public void backgroundProcess() { 330 delegate.backgroundProcess(); 331 } 332 333 public ClassLoader getClassLoader() { 334 // Implementation Note: the actual CL to be used by this 335 // context is the Geronimo one and not the Tomcat one. 336 return parentClassLoader; 337 } 338 339 public Container getContainer() { 340 return delegate.getContainer(); 341 } 342 343 public void setContainer(Container container) { 344 delegate.setContainer(container); 345 } 346 347 public boolean getDelegate() { 348 return delegate.getDelegate(); 349 } 350 351 public void setDelegate(boolean delegateBoolean) { 352 delegate.setDelegate(delegateBoolean); 353 } 354 355 public String getInfo() { 356 return delegate.getInfo(); 357 } 358 359 public boolean getReloadable() { 360 return false; 361 } 362 363 public void setReloadable(boolean reloadable) { 364 if (reloadable) { 365 throw new UnsupportedOperationException("Reloadable context is not supported."); 366 } 367 } 368 369 public void addPropertyChangeListener(PropertyChangeListener listener) { 370 delegate.addPropertyChangeListener(listener); 371 } 372 373 public void addRepository(String repository) { 374 delegate.addRepository(repository); 375 } 376 377 public String[] findRepositories() { 378 return delegate.findRepositories(); 379 } 380 381 public boolean modified() { 382 return delegate.modified(); 383 } 384 385 public void removePropertyChangeListener(PropertyChangeListener listener) { 386 delegate.removePropertyChangeListener(listener); 387 } 388 }; 389 390 super.setLoader(loader); 391 } 392 393 private class SystemMethodValve extends ValveBase { 394 395 public void invoke(Request request, Response response) throws IOException, ServletException { 396 if (request == null && response == null) { 397 try { 398 GeronimoStandardContext.super.start(); 399 } catch (LifecycleException e) { 400 throw (IOException) new IOException("wrapping lifecycle exception").initCause(e); 401 } 402 if (GeronimoStandardContext.this.getState() != 1 || !GeronimoStandardContext.this.getAvailable()){ 403 throw new IOException("Context did not start for an unknown reason"); 404 } 405 } else { 406 getNext().invoke(request, response); 407 } 408 409 } 410 } 411 412 413 public BeforeAfter getBeforeAfter() { 414 return beforeAfter; 415 } 416 417 public int getContextCount() { 418 return contextCount; 419 } 420 421 /** 422 * Determine if the context has at least one servlet that specifies a run-as role 423 * @return true if at least one servlet specifies a run-as role, false otherwise 424 */ 425 protected boolean hasRunAsServlet() { 426 for (Container servlet : findChildren()) { 427 if (servlet instanceof Wrapper) { 428 if (((Wrapper)servlet).getRunAs() != null) { 429 return true; 430 } 431 } 432 } 433 return false; 434 } 435 436 /** 437 * Get the Subject for the servlet's run-as role 438 * @param runAsRole Name of run as role to get Subject for 439 * @return Subject for the servlet's run-as role, if specified. otherwise null. 440 */ 441 public Subject getSubjectForRole(String runAsRole) { 442 return runAsSource.getSubjectForRole(runAsRole); 443 } 444 }