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