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 018 package org.apache.geronimo.tomcat; 019 020 import java.net.URI; 021 import java.net.URL; 022 import java.util.ArrayList; 023 import java.util.HashMap; 024 import java.util.Hashtable; 025 import java.util.Iterator; 026 import java.util.List; 027 import java.util.Map; 028 import java.util.Set; 029 030 import javax.management.MalformedObjectNameException; 031 import javax.management.ObjectName; 032 import javax.management.j2ee.statistics.Stats; 033 import javax.naming.directory.DirContext; 034 import javax.transaction.TransactionManager; 035 import javax.transaction.UserTransaction; 036 037 import org.apache.InstanceManager; 038 import org.apache.catalina.Context; 039 import org.apache.catalina.LifecycleListener; 040 import org.apache.catalina.Manager; 041 import org.apache.catalina.Realm; 042 import org.apache.catalina.Valve; 043 import org.apache.catalina.core.StandardContext; 044 import org.apache.catalina.ha.CatalinaCluster; 045 import org.apache.commons.logging.Log; 046 import org.apache.commons.logging.LogFactory; 047 import org.apache.geronimo.connector.outbound.connectiontracking.TrackedConnectionAssociator; 048 import org.apache.geronimo.gbean.AbstractName; 049 import org.apache.geronimo.gbean.GBeanInfo; 050 import org.apache.geronimo.gbean.GBeanInfoBuilder; 051 import org.apache.geronimo.gbean.GBeanLifecycle; 052 import org.apache.geronimo.j2ee.RuntimeCustomizer; 053 import org.apache.geronimo.j2ee.annotation.Holder; 054 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory; 055 import org.apache.geronimo.j2ee.management.impl.InvalidObjectNameException; 056 import org.apache.geronimo.kernel.Kernel; 057 import org.apache.geronimo.kernel.ObjectNameUtil; 058 import org.apache.geronimo.management.J2EEApplication; 059 import org.apache.geronimo.management.J2EEServer; 060 import org.apache.geronimo.management.StatisticsProvider; 061 import org.apache.geronimo.management.geronimo.WebContainer; 062 import org.apache.geronimo.management.geronimo.WebModule; 063 import org.apache.geronimo.naming.enc.EnterpriseNamingContext; 064 import org.apache.geronimo.security.jacc.RunAsSource; 065 import org.apache.geronimo.tomcat.cluster.CatalinaClusterGBean; 066 import org.apache.geronimo.tomcat.stats.ModuleStats; 067 import org.apache.geronimo.tomcat.util.SecurityHolder; 068 import org.apache.geronimo.transaction.GeronimoUserTransaction; 069 import org.apache.geronimo.webservices.WebServiceContainer; 070 import org.apache.geronimo.webservices.WebServiceContainerFactory; 071 import org.apache.naming.resources.DirContextURLStreamHandler; 072 073 /** 074 * Wrapper for a WebApplicationContext that sets up its J2EE environment. 075 * 076 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 077 */ 078 public class TomcatWebAppContext implements GBeanLifecycle, TomcatContext, WebModule, StatisticsProvider { 079 080 private static Log log = LogFactory.getLog(TomcatWebAppContext.class); 081 082 protected final TomcatContainer container; 083 private final ClassLoader classLoader; 084 protected Context context = null; 085 private String path = null; 086 private String docBase = null; 087 private String virtualServer = null; 088 private final Realm realm; 089 private final List valveChain; 090 private final List listenerChain; 091 private final CatalinaCluster catalinaCluster; 092 private final Manager manager; 093 private final boolean crossContext; 094 private final String workDir; 095 private final boolean disableCookies; 096 private final UserTransaction userTransaction; 097 private final javax.naming.Context componentContext; 098 private final Kernel kernel; 099 private final Set unshareableResources; 100 private final Set applicationManagedSecurityResources; 101 private final TrackedConnectionAssociator trackedConnectionAssociator; 102 private final SecurityHolder securityHolder; 103 private final RunAsSource runAsSource; 104 private final J2EEServer server; 105 private final Map webServices; 106 private final String objectName; 107 private final String originalSpecDD; 108 private final URL configurationBaseURL; 109 private final Holder holder; 110 private final RuntimeCustomizer contextCustomizer; 111 112 // JSR 77 113 private final String j2EEServer; 114 private final String j2EEApplication; 115 116 // statistics 117 private ModuleStats statsProvider; 118 private boolean reset = true; 119 120 private final Valve clusteredValve; 121 122 public TomcatWebAppContext( 123 ClassLoader classLoader, 124 String objectName, 125 String originalSpecDD, 126 URL configurationBaseUrl, 127 SecurityHolder securityHolder, 128 String virtualServer, 129 Map componentContext, 130 Set unshareableResources, 131 Set applicationManagedSecurityResources, 132 TransactionManager transactionManager, 133 TrackedConnectionAssociator trackedConnectionAssociator, 134 TomcatContainer container, 135 RunAsSource runAsSource, 136 ObjectRetriever tomcatRealm, 137 ObjectRetriever clusteredValveRetriever, 138 ValveGBean tomcatValveChain, 139 LifecycleListenerGBean lifecycleListenerChain, 140 CatalinaClusterGBean cluster, 141 ObjectRetriever managerRetriever, 142 boolean crossContext, 143 String workDir, 144 boolean disableCookies, 145 Map webServices, 146 Holder holder, 147 RuntimeCustomizer contextCustomizer, 148 J2EEServer server, 149 J2EEApplication application, 150 Kernel kernel) 151 throws Exception { 152 assert classLoader != null; 153 assert configurationBaseUrl != null; 154 assert transactionManager != null; 155 assert trackedConnectionAssociator != null; 156 assert componentContext != null; 157 assert container != null; 158 159 if (null != clusteredValveRetriever) { 160 clusteredValve = (Valve) clusteredValveRetriever.getInternalObject(); 161 } else { 162 clusteredValve = null; 163 } 164 165 this.objectName = objectName; 166 URI root; 167 // TODO is there a simpler way to do this? 168 if (configurationBaseUrl.getProtocol().equalsIgnoreCase("file")) { 169 root = new URI("file", configurationBaseUrl.getPath(), null); 170 } else { 171 root = URI.create(configurationBaseUrl.toString()); 172 } 173 this.setDocBase(root.getPath()); 174 this.container = container; 175 this.originalSpecDD = originalSpecDD; 176 177 this.virtualServer = virtualServer; 178 this.securityHolder = securityHolder; 179 180 userTransaction = new GeronimoUserTransaction(transactionManager); 181 this.componentContext = EnterpriseNamingContext.createEnterpriseNamingContext(componentContext, userTransaction, kernel, classLoader); 182 183 this.unshareableResources = unshareableResources; 184 this.applicationManagedSecurityResources = applicationManagedSecurityResources; 185 this.trackedConnectionAssociator = trackedConnectionAssociator; 186 187 this.server = server; 188 this.runAsSource = runAsSource == null? RunAsSource.NULL: runAsSource; 189 if (securityHolder != null) { 190 securityHolder.setDefaultSubject(this.runAsSource.getDefaultSubject()); 191 securityHolder.setRunAsSource(this.runAsSource); 192 } 193 194 195 this.configurationBaseURL = configurationBaseUrl; 196 197 this.holder = holder == null? new Holder(): holder; 198 this.contextCustomizer = contextCustomizer; 199 200 if (tomcatRealm != null){ 201 realm = (Realm)tomcatRealm.getInternalObject(); 202 if (realm == null){ 203 throw new IllegalArgumentException("tomcatRealm must be an instance of org.apache.catalina.Realm."); 204 } 205 } else{ 206 realm = null; 207 } 208 209 //Add the valve list 210 if (tomcatValveChain != null){ 211 ArrayList<Valve> chain = new ArrayList<Valve>(); 212 ValveGBean valveGBean = tomcatValveChain; 213 while(valveGBean != null){ 214 chain.add((Valve)valveGBean.getInternalObject()); 215 valveGBean = valveGBean.getNextValve(); 216 } 217 valveChain = chain; 218 } else { 219 valveChain = null; 220 } 221 222 //Add the Lifecycle Listener list 223 if (lifecycleListenerChain != null){ 224 ArrayList<LifecycleListener> chain = new ArrayList<LifecycleListener>(); 225 LifecycleListenerGBean listenerGBean = lifecycleListenerChain; 226 while(listenerGBean != null){ 227 chain.add((LifecycleListener)listenerGBean.getInternalObject()); 228 listenerGBean = listenerGBean.getNextListener(); 229 } 230 listenerChain = chain; 231 } else { 232 listenerChain = null; 233 } 234 235 //Add the cluster 236 if (cluster != null) { 237 catalinaCluster = (CatalinaCluster) cluster.getInternalObject(); 238 } else { 239 catalinaCluster = null; 240 } 241 242 //Add the manager 243 if (managerRetriever != null) { 244 this.manager = (Manager) managerRetriever.getInternalObject(); 245 } else { 246 this.manager = null; 247 } 248 249 this.crossContext = crossContext; 250 251 this.workDir = workDir; 252 253 this.disableCookies = disableCookies; 254 255 this.webServices = createWebServices(webServices, kernel); 256 257 this.classLoader = classLoader; 258 259 this.kernel = kernel; 260 261 if (objectName != null) { 262 ObjectName myObjectName = ObjectNameUtil.getObjectName(objectName); 263 verifyObjectName(myObjectName); 264 j2EEServer = myObjectName.getKeyProperty(NameFactory.J2EE_SERVER); 265 j2EEApplication = myObjectName.getKeyProperty(NameFactory.J2EE_APPLICATION); 266 } else { 267 // StandardContext uses default value of these as "none" 268 j2EEServer = null; 269 j2EEApplication = null; 270 } 271 } 272 273 private Map createWebServices(Map webServiceFactoryMap, Kernel kernel) throws Exception { 274 Map webServices = new HashMap(); 275 if (webServiceFactoryMap != null) { 276 for (Iterator iterator = webServiceFactoryMap.entrySet().iterator(); iterator.hasNext();) { 277 Map.Entry entry = (Map.Entry) iterator.next(); 278 String servletName = (String) entry.getKey(); 279 AbstractName factoryName = (AbstractName) entry.getValue(); 280 WebServiceContainerFactory webServiceContainerFactory = (WebServiceContainerFactory) kernel.getGBean(factoryName); 281 WebServiceContainer webServiceContainer = webServiceContainerFactory.getWebServiceContainer(); 282 webServices.put(servletName, webServiceContainer); 283 } 284 } 285 return webServices; 286 } 287 288 public String getObjectName() { 289 return objectName; 290 } 291 292 public String getJ2EEApplication() { 293 return j2EEApplication; 294 } 295 296 public String getJ2EEServer() { 297 return j2EEServer; 298 } 299 300 public boolean isStateManageable() { 301 return true; 302 } 303 304 public boolean isStatisticsProvider() { 305 return true; 306 } 307 308 public boolean isEventProvider() { 309 return true; 310 } 311 312 public URL getWARDirectory() { 313 return configurationBaseURL; 314 } 315 316 public String getWARName() { 317 //todo: make this return something more consistent 318 try { 319 return ObjectName.getInstance(objectName).getKeyProperty(NameFactory.J2EE_NAME); 320 } catch (MalformedObjectNameException e) { 321 return null; 322 } 323 } 324 325 public WebContainer getContainer() { 326 return container; 327 } 328 329 public String getServer() { 330 return server == null? null: server.getObjectName(); 331 } 332 333 public String getDocBase() { 334 return docBase; 335 } 336 337 public void setDocBase(String docBase) { 338 this.docBase = docBase; 339 } 340 341 public UserTransaction getUserTransaction() { 342 return userTransaction; 343 } 344 345 public javax.naming.Context getJndiContext() { 346 return componentContext; 347 } 348 349 public String getVirtualServer() { 350 return virtualServer; 351 } 352 353 public ClassLoader getClassLoader() { 354 return classLoader; 355 } 356 357 public Kernel getKernel() { 358 return kernel; 359 } 360 361 public boolean isDisableCookies() { 362 return disableCookies; 363 } 364 365 public Context getContext() { 366 return context; 367 } 368 369 public void setContext(Context context) { 370 this.context = context; 371 } 372 373 public String getContextPath() { 374 return path; 375 } 376 377 public void setContextPath(String path) { 378 this.path = path.startsWith("/") ? path : "/" + path; 379 } 380 381 public SecurityHolder getSecurityHolder() { 382 return securityHolder; 383 } 384 385 386 public Set getApplicationManagedSecurityResources() { 387 return applicationManagedSecurityResources; 388 } 389 390 public TrackedConnectionAssociator getTrackedConnectionAssociator() { 391 return trackedConnectionAssociator; 392 } 393 394 public Set getUnshareableResources() { 395 return unshareableResources; 396 } 397 398 public Realm getRealm() { 399 return realm; 400 } 401 402 public Valve getClusteredValve() { 403 return clusteredValve; 404 } 405 406 public List getValveChain() { 407 return valveChain; 408 } 409 410 public List getLifecycleListenerChain() { 411 return listenerChain; 412 } 413 414 public CatalinaCluster getCluster() { 415 return catalinaCluster; 416 } 417 418 public Manager getManager() { 419 return manager; 420 } 421 422 public boolean isCrossContext() { 423 return crossContext; 424 } 425 426 public String getWorkDir() { 427 return workDir; 428 } 429 430 public Map getWebServices(){ 431 return webServices; 432 } 433 434 public InstanceManager getInstanceManager() { 435 return new TomcatInstanceManager(holder, classLoader, componentContext); 436 } 437 438 public RuntimeCustomizer getRuntimeCustomizer() { 439 return contextCustomizer; 440 } 441 442 public String[] getServlets(){ 443 String[] result = null; 444 if ((context != null) && (context instanceof StandardContext)) { 445 result = ((StandardContext) context).getServlets(); 446 } 447 448 return result; 449 } 450 451 /** 452 * ObjectName must match this pattern: <p/> 453 * domain:j2eeType=WebModule,name=MyName,J2EEServer=MyServer,J2EEApplication=MyApplication 454 */ 455 private void verifyObjectName(ObjectName objectName) { 456 if (objectName.isPattern()) { 457 throw new InvalidObjectNameException( 458 "ObjectName can not be a pattern", objectName); 459 } 460 Hashtable keyPropertyList = objectName.getKeyPropertyList(); 461 if (!NameFactory.WEB_MODULE.equals(keyPropertyList.get("j2eeType"))) { 462 throw new InvalidObjectNameException( 463 "WebModule object name j2eeType property must be 'WebModule'", 464 objectName); 465 } 466 if (!keyPropertyList.containsKey(NameFactory.J2EE_NAME)) { 467 throw new InvalidObjectNameException( 468 "WebModule object must contain a name property", objectName); 469 } 470 if (!keyPropertyList.containsKey(NameFactory.J2EE_SERVER)) { 471 throw new InvalidObjectNameException( 472 "WebModule object name must contain a J2EEServer property", 473 objectName); 474 } 475 if (!keyPropertyList.containsKey(NameFactory.J2EE_APPLICATION)) { 476 throw new InvalidObjectNameException( 477 "WebModule object name must contain a J2EEApplication property", 478 objectName); 479 } 480 if (keyPropertyList.size() != 4) { 481 throw new InvalidObjectNameException( 482 "WebModule object name can only have j2eeType, name, J2EEApplication, and J2EEServer properties", 483 objectName); 484 } 485 } 486 487 public String[] getJavaVMs() { 488 return server == null? new String[0]: server.getJavaVMs(); 489 } 490 491 public String getDeploymentDescriptor() { 492 return originalSpecDD; 493 } 494 495 // JSR 77 statistics - The static values are initialized at the time of 496 // creration, getStats return fresh value everytime 497 public Stats getStats() { 498 if (reset) { 499 reset = false; 500 return statsProvider.getStats(); 501 } 502 else return statsProvider.updateStats(); 503 } 504 505 public void resetStats() { 506 reset = true; 507 } 508 509 public void doStart() throws Exception { 510 511 // See the note of TomcatContainer::addContext 512 container.addContext(this); 513 // Is it necessary - doesn't Tomcat Embedded take care of it? 514 // super.start(); 515 //register the classloader <> dir context association so that tomcat's jndi based getResources works. 516 DirContext resources = context.getResources(); 517 if (resources == null) { 518 throw new IllegalStateException("JNDI environment was not set up correctly due to previous error"); 519 } 520 DirContextURLStreamHandler.bind(classLoader, resources); 521 if (context instanceof StandardContext) 522 statsProvider = new ModuleStats((StandardContext)context); 523 524 log.debug("TomcatWebAppContext started for " + path); 525 } 526 527 public void doStop() throws Exception { 528 statsProvider = null; 529 container.removeContext(this); 530 DirContextURLStreamHandler.unbind(classLoader); 531 532 // No more logging will occur for this ClassLoader. Inform the LogFactory to avoid a memory leak. 533 // LogFactory.release(classLoader); 534 535 log.debug("TomcatWebAppContext stopped"); 536 } 537 538 public void doFail() { 539 statsProvider = null; 540 container.removeContext(this); 541 542 // No more logging will occur for this ClassLoader. Inform the LogFactory to avoid a memory leak. 543 // LogFactory.release(classLoader); 544 545 log.warn("TomcatWebAppContext failed"); 546 } 547 548 public static final GBeanInfo GBEAN_INFO; 549 public static final String GBEAN_REF_CLUSTERED_VALVE_RETRIEVER = "ClusteredValveRetriever"; 550 public static final String GBEAN_REF_MANAGER_RETRIEVER = "ManagerRetriever"; 551 552 static { 553 GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic("Tomcat WebApplication Context", TomcatWebAppContext.class, NameFactory.WEB_MODULE); 554 555 infoBuilder.addAttribute("classLoader", ClassLoader.class, false); 556 infoBuilder.addAttribute("objectName", String.class, false); 557 infoBuilder.addAttribute("deploymentDescriptor", String.class, true); 558 infoBuilder.addAttribute("configurationBaseUrl", URL.class, true); 559 560 infoBuilder.addAttribute("contextPath", String.class, true); 561 562 infoBuilder.addAttribute("securityHolder", SecurityHolder.class, true); 563 infoBuilder.addAttribute("virtualServer", String.class, true); 564 infoBuilder.addAttribute("componentContext", Map.class, true); 565 infoBuilder.addAttribute("unshareableResources", Set.class, true); 566 infoBuilder.addAttribute("applicationManagedSecurityResources", Set.class, true); 567 infoBuilder.addReference("TransactionManager", TransactionManager.class, NameFactory.JTA_RESOURCE); 568 infoBuilder.addReference("TrackedConnectionAssociator", TrackedConnectionAssociator.class, NameFactory.JCA_CONNECTION_TRACKER); 569 570 infoBuilder.addReference("Container", TomcatContainer.class, NameFactory.GERONIMO_SERVICE); 571 infoBuilder.addReference("RunAsSource", RunAsSource.class, NameFactory.JACC_MANAGER); 572 infoBuilder.addReference("TomcatRealm", ObjectRetriever.class); 573 infoBuilder.addReference(GBEAN_REF_CLUSTERED_VALVE_RETRIEVER, ObjectRetriever.class); 574 infoBuilder.addReference("TomcatValveChain", ValveGBean.class); 575 infoBuilder.addReference("LifecycleListenerChain", LifecycleListenerGBean.class, LifecycleListenerGBean.J2EE_TYPE); 576 infoBuilder.addReference("Cluster", CatalinaClusterGBean.class, CatalinaClusterGBean.J2EE_TYPE); 577 infoBuilder.addReference(GBEAN_REF_MANAGER_RETRIEVER, ObjectRetriever.class); 578 infoBuilder.addAttribute("crossContext", boolean.class, true); 579 infoBuilder.addAttribute("workDir", String.class, true); 580 infoBuilder.addAttribute("disableCookies", boolean.class, true); 581 infoBuilder.addAttribute("webServices", Map.class, true); 582 infoBuilder.addAttribute("holder", Holder.class, true); 583 infoBuilder.addReference("ContextCustomizer", RuntimeCustomizer.class, NameFactory.GERONIMO_SERVICE); 584 infoBuilder.addReference("J2EEServer", J2EEServer.class); 585 infoBuilder.addReference("J2EEApplication", J2EEApplication.class); 586 infoBuilder.addAttribute("kernel", Kernel.class, false); 587 588 infoBuilder.addInterface(WebModule.class); 589 590 infoBuilder.setConstructor(new String[] { 591 "classLoader", 592 "objectName", 593 "deploymentDescriptor", 594 "configurationBaseUrl", 595 "securityHolder", 596 "virtualServer", 597 "componentContext", 598 "unshareableResources", 599 "applicationManagedSecurityResources", 600 "TransactionManager", 601 "TrackedConnectionAssociator", 602 "Container", 603 "RunAsSource", 604 "TomcatRealm", 605 GBEAN_REF_CLUSTERED_VALVE_RETRIEVER, 606 "TomcatValveChain", 607 "LifecycleListenerChain", 608 "Cluster", 609 GBEAN_REF_MANAGER_RETRIEVER, 610 "crossContext", 611 "workDir", 612 "disableCookies", 613 "webServices", 614 "holder", 615 "ContextCustomizer", 616 "J2EEServer", 617 "J2EEApplication", 618 "kernel" 619 } 620 ); 621 622 GBEAN_INFO = infoBuilder.getBeanInfo(); 623 } 624 625 public static GBeanInfo getGBeanInfo() { 626 return GBEAN_INFO; 627 } 628 }