001 /** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package org.apache.geronimo.tomcat; 020 021 import java.net.MalformedURLException; 022 import java.net.URI; 023 import java.net.URL; 024 import java.util.ArrayList; 025 import java.util.HashMap; 026 import java.util.Hashtable; 027 import java.util.Iterator; 028 import java.util.List; 029 import java.util.Map; 030 import java.util.Set; 031 032 import javax.management.MalformedObjectNameException; 033 import javax.management.ObjectName; 034 import javax.naming.directory.DirContext; 035 import javax.transaction.TransactionManager; 036 import javax.transaction.UserTransaction; 037 038 import org.apache.catalina.Context; 039 import org.apache.catalina.Manager; 040 import org.apache.catalina.Realm; 041 import org.apache.catalina.Valve; 042 import org.apache.catalina.cluster.CatalinaCluster; 043 import org.apache.catalina.core.StandardContext; 044 import org.apache.commons.logging.Log; 045 import org.apache.commons.logging.LogFactory; 046 import org.apache.geronimo.connector.outbound.connectiontracking.TrackedConnectionAssociator; 047 import org.apache.geronimo.gbean.AbstractName; 048 import org.apache.geronimo.gbean.GBeanInfo; 049 import org.apache.geronimo.gbean.GBeanInfoBuilder; 050 import org.apache.geronimo.gbean.GBeanLifecycle; 051 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory; 052 import org.apache.geronimo.j2ee.management.impl.InvalidObjectNameException; 053 import org.apache.geronimo.kernel.Kernel; 054 import org.apache.geronimo.kernel.ObjectNameUtil; 055 import org.apache.geronimo.management.J2EEApplication; 056 import org.apache.geronimo.management.J2EEServer; 057 import org.apache.geronimo.management.geronimo.WebConnector; 058 import org.apache.geronimo.management.geronimo.WebContainer; 059 import org.apache.geronimo.management.geronimo.WebModule; 060 import org.apache.geronimo.tomcat.cluster.CatalinaClusterGBean; 061 import org.apache.geronimo.tomcat.util.SecurityHolder; 062 import org.apache.geronimo.transaction.GeronimoUserTransaction; 063 import org.apache.geronimo.webservices.WebServiceContainer; 064 import org.apache.geronimo.webservices.WebServiceContainerFactory; 065 import org.apache.naming.resources.DirContextURLStreamHandler; 066 067 /** 068 * Wrapper for a WebApplicationContext that sets up its J2EE environment. 069 * 070 * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $ 071 */ 072 public class TomcatWebAppContext implements GBeanLifecycle, TomcatContext, WebModule { 073 074 private static Log log = LogFactory.getLog(TomcatWebAppContext.class); 075 076 protected final TomcatContainer container; 077 078 private final ClassLoader classLoader; 079 080 protected Context context = null; 081 082 private String path = null; 083 084 private String docBase = null; 085 086 private String virtualServer = null; 087 088 private final Realm realm; 089 090 private final List valveChain; 091 092 private final CatalinaCluster catalinaCluster; 093 094 private final Manager manager; 095 096 private final boolean crossContext; 097 098 private final boolean disableCookies; 099 100 private final UserTransaction userTransaction; 101 102 private final Map componentContext; 103 104 private final Kernel kernel; 105 106 private final Set unshareableResources; 107 108 private final Set applicationManagedSecurityResources; 109 110 private final TrackedConnectionAssociator trackedConnectionAssociator; 111 112 private final SecurityHolder securityHolder; 113 114 private final J2EEServer server; 115 116 private final Map webServices; 117 118 private final String objectName; 119 120 private final String originalSpecDD; 121 122 private final URL configurationBaseURL; 123 124 // JSR 77 125 126 private final String j2EEServer; 127 128 private final String j2EEApplication; 129 130 public TomcatWebAppContext( 131 ClassLoader classLoader, 132 String objectName, 133 String originalSpecDD, 134 URL configurationBaseUrl, 135 SecurityHolder securityHolder, 136 String virtualServer, 137 Map componentContext, 138 Set unshareableResources, 139 Set applicationManagedSecurityResources, 140 TransactionManager transactionManager, 141 TrackedConnectionAssociator trackedConnectionAssociator, 142 TomcatContainer container, 143 ObjectRetriever tomcatRealm, 144 ValveGBean tomcatValveChain, 145 CatalinaClusterGBean cluster, 146 ManagerGBean manager, 147 boolean crossContext, 148 boolean disableCookies, 149 Map webServices, 150 J2EEServer server, 151 J2EEApplication application, 152 Kernel kernel) 153 throws Exception { 154 155 assert classLoader != null; 156 assert configurationBaseUrl != null; 157 assert transactionManager != null; 158 assert trackedConnectionAssociator != null; 159 assert componentContext != null; 160 assert container != null; 161 162 163 this.objectName = objectName; 164 URI root; 165 // TODO is there a simpler way to do this? 166 if (configurationBaseUrl.getProtocol().equalsIgnoreCase("file")) { 167 root = new URI("file", configurationBaseUrl.getPath(), null); 168 } else { 169 root = URI.create(configurationBaseUrl.toString()); 170 } 171 this.setDocBase(root.getPath()); 172 this.container = container; 173 this.originalSpecDD = originalSpecDD; 174 175 this.virtualServer = virtualServer; 176 this.securityHolder = securityHolder; 177 178 this.userTransaction = new GeronimoUserTransaction(transactionManager); 179 this.componentContext = componentContext; 180 this.unshareableResources = unshareableResources; 181 this.applicationManagedSecurityResources = applicationManagedSecurityResources; 182 this.trackedConnectionAssociator = trackedConnectionAssociator; 183 184 this.server = server; 185 186 this.configurationBaseURL = configurationBaseUrl; 187 188 if (tomcatRealm != null){ 189 realm = (Realm)tomcatRealm.getInternalObject(); 190 if (realm == null){ 191 throw new IllegalArgumentException("tomcatRealm must be an instance of org.apache.catalina.Realm."); 192 } 193 } else{ 194 realm = null; 195 } 196 197 //Add the valve list 198 if (tomcatValveChain != null){ 199 ArrayList chain = new ArrayList(); 200 ValveGBean valveGBean = tomcatValveChain; 201 while(valveGBean != null){ 202 chain.add((Valve)valveGBean.getInternalObject()); 203 valveGBean = valveGBean.getNextValve(); 204 } 205 valveChain = chain; 206 } else { 207 valveChain = null; 208 } 209 210 //Add the cluster 211 if (cluster != null) { 212 catalinaCluster = (CatalinaCluster) cluster.getInternalObject(); 213 } else { 214 catalinaCluster = null; 215 } 216 217 //Add the manager 218 if (manager != null) { 219 this.manager = (Manager) manager.getInternalObject(); 220 } else { 221 this.manager = null; 222 } 223 224 this.crossContext = crossContext; 225 226 this.disableCookies = disableCookies; 227 228 this.webServices = createWebServices(webServices, kernel); 229 230 this.classLoader = classLoader; 231 232 this.kernel = kernel; 233 234 if (objectName != null) { 235 ObjectName myObjectName = ObjectNameUtil.getObjectName(objectName); 236 verifyObjectName(myObjectName); 237 j2EEServer = myObjectName.getKeyProperty(NameFactory.J2EE_SERVER); 238 j2EEApplication = myObjectName.getKeyProperty(NameFactory.J2EE_APPLICATION); 239 } else { 240 // StandardContext uses default value of these as "none" 241 j2EEServer = null; 242 j2EEApplication = null; 243 } 244 } 245 246 private Map createWebServices(Map webServiceFactoryMap, Kernel kernel) throws Exception { 247 Map webServices = new HashMap(); 248 if (webServiceFactoryMap != null) { 249 for (Iterator iterator = webServiceFactoryMap.entrySet().iterator(); iterator.hasNext();) { 250 Map.Entry entry = (Map.Entry) iterator.next(); 251 String servletName = (String) entry.getKey(); 252 AbstractName factoryName = (AbstractName) entry.getValue(); 253 WebServiceContainerFactory webServiceContainerFactory = (WebServiceContainerFactory) kernel.getGBean(factoryName); 254 WebServiceContainer webServiceContainer = webServiceContainerFactory.getWebServiceContainer(); 255 webServices.put(servletName, webServiceContainer); 256 } 257 } 258 return webServices; 259 } 260 261 public String getObjectName() { 262 return objectName; 263 } 264 265 public String getJ2EEApplication() { 266 return j2EEApplication; 267 } 268 269 public String getJ2EEServer() { 270 return j2EEServer; 271 } 272 273 public boolean isStateManageable() { 274 return true; 275 } 276 277 public boolean isStatisticsProvider() { 278 return false; 279 } 280 281 public boolean isEventProvider() { 282 return true; 283 } 284 285 public URL getWARDirectory() { 286 return configurationBaseURL; 287 } 288 289 public String getWARName() { 290 //todo: make this return something more consistent 291 try { 292 return ObjectName.getInstance(objectName).getKeyProperty(NameFactory.J2EE_NAME); 293 } catch (MalformedObjectNameException e) { 294 return null; 295 } 296 } 297 298 public WebContainer getContainer() { 299 return container; 300 } 301 302 public String getServer() { 303 return server == null? null: server.getObjectName(); 304 } 305 306 public String getDocBase() { 307 return docBase; 308 } 309 310 public void setDocBase(String docBase) { 311 this.docBase = docBase; 312 } 313 314 public UserTransaction getUserTransaction() { 315 return userTransaction; 316 } 317 318 public Map getComponentContext() { 319 return componentContext; 320 } 321 322 public String getVirtualServer() { 323 return virtualServer; 324 } 325 326 public ClassLoader getClassLoader() { 327 return classLoader; 328 } 329 330 public Kernel getKernel() { 331 return kernel; 332 } 333 334 public boolean isDisableCookies() { 335 return disableCookies; 336 } 337 338 public Context getContext() { 339 return context; 340 } 341 342 public void setContext(Context context) { 343 this.context = context; 344 } 345 346 public URL getURLFor() { 347 WebConnector[] connectors = (WebConnector[]) container.getConnectors(); 348 Map map = new HashMap(); 349 for (int i = 0; i < connectors.length; i++) { 350 WebConnector connector = connectors[i]; 351 map.put(connector.getProtocol(), connector.getConnectUrl()); 352 } 353 String urlPrefix; 354 if((urlPrefix = (String) map.get("HTTP")) == null) { 355 if((urlPrefix = (String) map.get("HTTPS")) == null) { 356 urlPrefix = (String) map.get("AJP"); 357 } 358 } 359 if(urlPrefix == null) { 360 return null; 361 } 362 try { 363 return new URL(urlPrefix + getContextPath()); 364 } catch (MalformedURLException e) { 365 log.error("Bad URL to connect to web app", e); 366 return null; 367 } 368 } 369 370 public String getContextPath() { 371 return path; 372 } 373 374 public void setContextPath(String path) { 375 this.path = path.startsWith("/") ? path : "/" + path; 376 } 377 378 public SecurityHolder getSecurityHolder() { 379 return securityHolder; 380 } 381 382 383 public Set getApplicationManagedSecurityResources() { 384 return applicationManagedSecurityResources; 385 } 386 387 public TrackedConnectionAssociator getTrackedConnectionAssociator() { 388 return trackedConnectionAssociator; 389 } 390 391 public Set getUnshareableResources() { 392 return unshareableResources; 393 } 394 395 public Realm getRealm() { 396 return realm; 397 } 398 399 public List getValveChain() { 400 return valveChain; 401 } 402 403 public CatalinaCluster getCluster() { 404 return catalinaCluster; 405 } 406 407 public Manager getManager() { 408 return manager; 409 } 410 411 public boolean isCrossContext() { 412 return crossContext; 413 } 414 415 public Map getWebServices(){ 416 return webServices; 417 } 418 419 public String[] getServlets(){ 420 String[] result = null; 421 if ((context != null) && (context instanceof StandardContext)) { 422 result = ((StandardContext) context).getServlets(); 423 } 424 425 return result; 426 } 427 428 /** 429 * ObjectName must match this pattern: <p/> 430 * domain:j2eeType=WebModule,name=MyName,J2EEServer=MyServer,J2EEApplication=MyApplication 431 */ 432 private void verifyObjectName(ObjectName objectName) { 433 if (objectName.isPattern()) { 434 throw new InvalidObjectNameException( 435 "ObjectName can not be a pattern", objectName); 436 } 437 Hashtable keyPropertyList = objectName.getKeyPropertyList(); 438 if (!NameFactory.WEB_MODULE.equals(keyPropertyList.get("j2eeType"))) { 439 throw new InvalidObjectNameException( 440 "WebModule object name j2eeType property must be 'WebModule'", 441 objectName); 442 } 443 if (!keyPropertyList.containsKey(NameFactory.J2EE_NAME)) { 444 throw new InvalidObjectNameException( 445 "WebModule object must contain a name property", objectName); 446 } 447 if (!keyPropertyList.containsKey(NameFactory.J2EE_SERVER)) { 448 throw new InvalidObjectNameException( 449 "WebModule object name must contain a J2EEServer property", 450 objectName); 451 } 452 if (!keyPropertyList.containsKey(NameFactory.J2EE_APPLICATION)) { 453 throw new InvalidObjectNameException( 454 "WebModule object name must contain a J2EEApplication property", 455 objectName); 456 } 457 if (keyPropertyList.size() != 4) { 458 throw new InvalidObjectNameException( 459 "WebModule object name can only have j2eeType, name, J2EEApplication, and J2EEServer properties", 460 objectName); 461 } 462 } 463 464 public String[] getJavaVMs() { 465 return server == null? new String[0]: server.getJavaVMs(); 466 } 467 468 public String getDeploymentDescriptor() { 469 return originalSpecDD; 470 } 471 472 public void doStart() throws Exception { 473 474 // See the note of TomcatContainer::addContext 475 container.addContext(this); 476 // Is it necessary - doesn't Tomcat Embedded take care of it? 477 // super.start(); 478 //register the classloader <> dir context association so that tomcat's jndi based getResources works. 479 DirContext resources = context.getResources(); 480 DirContextURLStreamHandler.bind(classLoader, resources); 481 482 log.debug("TomcatWebAppContext started for " + path); 483 } 484 485 public void doStop() throws Exception { 486 container.removeContext(this); 487 DirContextURLStreamHandler.unbind(classLoader); 488 489 // No more logging will occur for this ClassLoader. Inform the LogFactory to avoid a memory leak. 490 // LogFactory.release(classLoader); 491 492 log.debug("TomcatWebAppContext stopped"); 493 } 494 495 public void doFail() { 496 container.removeContext(this); 497 498 // No more logging will occur for this ClassLoader. Inform the LogFactory to avoid a memory leak. 499 // LogFactory.release(classLoader); 500 501 log.warn("TomcatWebAppContext failed"); 502 } 503 504 public static final GBeanInfo GBEAN_INFO; 505 506 static { 507 GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic("Tomcat WebApplication Context", TomcatWebAppContext.class, NameFactory.WEB_MODULE); 508 509 infoBuilder.addAttribute("classLoader", ClassLoader.class, false); 510 infoBuilder.addAttribute("objectName", String.class, false); 511 infoBuilder.addAttribute("deploymentDescriptor", String.class, true); 512 infoBuilder.addAttribute("configurationBaseUrl", URL.class, true); 513 514 infoBuilder.addAttribute("contextPath", String.class, true); 515 516 infoBuilder.addAttribute("securityHolder", SecurityHolder.class, true); 517 infoBuilder.addAttribute("virtualServer", String.class, true); 518 infoBuilder.addAttribute("componentContext", Map.class, true); 519 infoBuilder.addAttribute("unshareableResources", Set.class, true); 520 infoBuilder.addAttribute("applicationManagedSecurityResources", Set.class, true); 521 infoBuilder.addReference("TransactionManager", TransactionManager.class, NameFactory.TRANSACTION_MANAGER); 522 infoBuilder.addReference("TrackedConnectionAssociator", TrackedConnectionAssociator.class, NameFactory.JCA_CONNECTION_TRACKER); 523 524 infoBuilder.addReference("Container", TomcatContainer.class, NameFactory.GERONIMO_SERVICE); 525 infoBuilder.addReference("TomcatRealm", ObjectRetriever.class); 526 infoBuilder.addReference("TomcatValveChain", ValveGBean.class); 527 infoBuilder.addReference("Cluster", CatalinaClusterGBean.class, CatalinaClusterGBean.J2EE_TYPE); 528 infoBuilder.addReference("Manager", ManagerGBean.class); 529 infoBuilder.addAttribute("crossContext", boolean.class, true); 530 infoBuilder.addAttribute("disableCookies", boolean.class, true); 531 infoBuilder.addAttribute("webServices", Map.class, true); 532 infoBuilder.addReference("J2EEServer", J2EEServer.class); 533 infoBuilder.addReference("J2EEApplication", J2EEApplication.class); 534 infoBuilder.addAttribute("kernel", Kernel.class, false); 535 536 infoBuilder.addInterface(WebModule.class); 537 538 infoBuilder.setConstructor(new String[] { 539 "classLoader", 540 "objectName", 541 "deploymentDescriptor", 542 "configurationBaseUrl", 543 "securityHolder", 544 "virtualServer", 545 "componentContext", 546 "unshareableResources", 547 "applicationManagedSecurityResources", 548 "TransactionManager", 549 "TrackedConnectionAssociator", 550 "Container", 551 "TomcatRealm", 552 "TomcatValveChain", 553 "Cluster", 554 "Manager", 555 "crossContext", 556 "disableCookies", 557 "webServices", 558 "J2EEServer", 559 "J2EEApplication", 560 "kernel" 561 } 562 ); 563 564 GBEAN_INFO = infoBuilder.getBeanInfo(); 565 } 566 567 public static GBeanInfo getGBeanInfo() { 568 return GBEAN_INFO; 569 } 570 }