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 package org.apache.geronimo.tomcat; 019 020 import java.io.File; 021 import java.util.HashMap; 022 import java.util.Map; 023 import java.net.URLStreamHandlerFactory; 024 import java.net.URL; 025 026 import javax.management.ObjectName; 027 028 import org.apache.catalina.Container; 029 import org.apache.catalina.Context; 030 import org.apache.catalina.Engine; 031 import org.apache.catalina.Realm; 032 import org.apache.catalina.connector.Connector; 033 import org.apache.catalina.realm.JAASRealm; 034 import org.apache.commons.logging.Log; 035 import org.apache.commons.logging.LogFactory; 036 import org.apache.geronimo.gbean.GBeanInfo; 037 import org.apache.geronimo.gbean.GBeanInfoBuilder; 038 import org.apache.geronimo.gbean.GBeanLifecycle; 039 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory; 040 import org.apache.geronimo.kernel.ObjectNameUtil; 041 import org.apache.geronimo.management.geronimo.NetworkConnector; 042 import org.apache.geronimo.management.geronimo.WebManager; 043 import org.apache.geronimo.system.serverinfo.ServerInfo; 044 import org.apache.geronimo.tomcat.realm.TomcatGeronimoRealm; 045 import org.apache.geronimo.tomcat.realm.TomcatJAASRealm; 046 import org.apache.geronimo.tomcat.util.SecurityHolder; 047 import org.apache.geronimo.webservices.SoapHandler; 048 import org.apache.geronimo.webservices.WebServiceContainer; 049 import org.apache.naming.resources.DirContextURLStreamHandlerFactory; 050 051 052 /** 053 * Apache Tomcat GBean 054 * http://wiki.apache.org/geronimo/Tomcat 055 * http://nagoya.apache.org/jira/browse/GERONIMO-215 056 * 057 * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $ 058 */ 059 public class TomcatContainer implements SoapHandler, GBeanLifecycle, TomcatWebContainer { 060 061 private static final Log log = LogFactory.getLog(TomcatContainer.class); 062 063 /** 064 * The default value of CATALINA_HOME variable 065 */ 066 private static final String DEFAULT_CATALINA_HOME = "var/catalina"; 067 068 /** 069 * Reference to the org.apache.catalina.Embedded embedded. 070 */ 071 private TomcatGeronimoEmbedded embedded; 072 073 /** 074 * Tomcat Engine that will contain the host 075 */ 076 private Engine engine; 077 078 /** 079 * Geronimo class loader 080 */ 081 private ClassLoader classLoader; 082 083 private final Map webServices = new HashMap(); 084 private final String objectName; 085 private final WebManager manager; 086 private static boolean first = true; 087 088 // Required as it's referenced by deployed webapps 089 public TomcatContainer() { 090 this.objectName = null; // is this OK?? 091 setCatalinaHome(DEFAULT_CATALINA_HOME); 092 manager = null; 093 } 094 095 /** 096 * GBean constructor (invoked dynamically when the gbean is declared in a plan) 097 */ 098 public TomcatContainer(ClassLoader classLoader, String catalinaHome, ObjectRetriever engineGBean, ServerInfo serverInfo, String objectName, WebManager manager) { 099 // Register a stream handler factory for the JNDI protocol 100 URLStreamHandlerFactory streamHandlerFactory = 101 new DirContextURLStreamHandlerFactory(); 102 if (first) { 103 first = false; 104 try { 105 URL.setURLStreamHandlerFactory(streamHandlerFactory); 106 } catch (Exception e) { 107 // Log and continue anyway, this is not critical 108 log.error("Error registering jndi stream handler", e); 109 } catch (Throwable t) { 110 // This is likely a dual registration 111 log.info("Dual registration of jndi stream handler: " 112 + t.getMessage()); 113 } 114 } 115 116 117 if (catalinaHome == null) 118 catalinaHome = DEFAULT_CATALINA_HOME; 119 120 setCatalinaHome(serverInfo.resolveServerPath(catalinaHome)); 121 122 if (classLoader == null) { 123 throw new IllegalArgumentException("classLoader cannot be null."); 124 } 125 126 if (engineGBean == null) { 127 throw new IllegalArgumentException("engineGBean cannot be null."); 128 } 129 130 this.classLoader = classLoader; 131 132 this.engine = (Engine) engineGBean.getInternalObject(); 133 134 this.objectName = objectName; 135 this.manager = manager; 136 } 137 138 public String getObjectName() { 139 return objectName; 140 } 141 142 public boolean isStateManageable() { 143 return true; 144 } 145 146 public boolean isStatisticsProvider() { 147 return false; // todo: return true once stats are integrated 148 } 149 150 public boolean isEventProvider() { 151 return true; 152 } 153 154 public NetworkConnector[] getConnectors() { 155 return manager.getConnectorsForContainer(this); 156 } 157 158 public NetworkConnector[] getConnectors(String protocol) { 159 return manager.getConnectorsForContainer(this, protocol); 160 } 161 162 public void doFail() { 163 try { 164 doStop(); 165 } catch (Exception ignored) { 166 } 167 } 168 169 /** 170 * Instantiate and start up Tomcat's Embedded class 171 * <p/> 172 * See org.apache.catalina.startup.Embedded for details (TODO: provide the link to the javadoc) 173 */ 174 public void doStart() throws Exception { 175 log.debug("doStart()"); 176 177 log.debug("Endorsed Dirs set to:" + System.getProperty("java.endorsed.dirs")); 178 179 // The comments are from the javadoc of the Embedded class 180 181 // 1. Instantiate a new instance of this class. 182 if (embedded == null) { 183 embedded = new TomcatGeronimoEmbedded(); 184 } 185 186 // Assemble FileLogger as a gbean 187 /* 188 * FileLogger fileLog = new FileLogger(); fileLog.setDirectory("."); fileLog.setPrefix("vsjMbedTC5"); 189 * fileLog.setSuffix(".log"); fileLog.setTimestamp(true); 190 */ 191 192 // 2. Set the relevant properties of this object itself. In particular, 193 // you will want to establish the default Logger to be used, as well as 194 // the default Realm if you are using container-managed security. 195 embedded.setUseNaming(false); 196 197 //Add default contexts 198 File rootContext = new File(System.getProperty("catalina.home") + "/ROOT"); 199 200 String docBase = ""; 201 if (rootContext.exists()) { 202 docBase = "ROOT"; 203 } 204 205 Container[] hosts = engine.findChildren(); 206 Context defaultContext; 207 for (int i = 0; i < hosts.length; i++) { 208 defaultContext = embedded.createContext("", docBase, classLoader); 209 if (defaultContext instanceof GeronimoStandardContext) { 210 GeronimoStandardContext ctx = (GeronimoStandardContext) defaultContext; 211 // Without this the Tomcat FallBack Application is left behind, 212 // MBean - ...J2EEApplication=none,J2EEServer=none,.......... 213 ctx.setJ2EEApplication(null); 214 // TODO if objectName != null extract J2EEServer from objectName/host 215 ctx.setJ2EEServer("geronimo"); 216 } 217 hosts[i].addChild(defaultContext); 218 } 219 220 // 6. Call addEngine() to attach this Engine to the set of defined 221 // Engines for this object. 222 embedded.addEngine(engine); 223 224 // 9. Call start() to initiate normal operations of all the attached 225 // components. 226 embedded.start(); 227 } 228 229 public void doStop() throws Exception { 230 if (embedded != null) { 231 embedded.stop(); 232 embedded = null; 233 } 234 235 } 236 237 /** 238 * Creates and adds the context to the running host 239 * <p/> 240 * It simply delegates the call to Tomcat's Embedded and Host classes 241 * 242 * @param ctx the context to be added 243 * @see org.apache.catalina.startup.Embedded 244 * @see org.apache.catalina.Host 245 */ 246 public void addContext(TomcatContext ctx) throws Exception { 247 Context anotherCtxObj = embedded.createContext(ctx.getContextPath(), ctx.getDocBase(), ctx.getClassLoader()); 248 249 // Set the context for the Tomcat implementation 250 ctx.setContext(anotherCtxObj); 251 252 // Have the context to set its properties if its a GeronimoStandardContext 253 if (anotherCtxObj instanceof GeronimoStandardContext) { 254 ((GeronimoStandardContext) anotherCtxObj).setContextProperties(ctx); 255 } 256 //Was a virtual server defined? 257 String virtualServer = ctx.getVirtualServer(); 258 if (virtualServer == null) { 259 virtualServer = engine.getDefaultHost(); 260 } 261 Container host = engine.findChild(virtualServer); 262 if (host == null) { 263 throw new IllegalArgumentException("Invalid virtual host '" + virtualServer + "'. Do you have a matching Host entry in the plan?"); 264 } 265 266 //Get the security-realm-name if there is one 267 String securityRealmName = null; 268 SecurityHolder secHolder = ctx.getSecurityHolder(); 269 if (secHolder != null) 270 securityRealmName = secHolder.getSecurityRealm(); 271 272 //Did we declare a GBean at the context level? 273 if (ctx.getRealm() != null) { 274 Realm realm = ctx.getRealm(); 275 276 //Allow for the <security-realm-name> override from the 277 //geronimo-web.xml file to be used if our Realm is a JAAS type 278 if (securityRealmName != null) { 279 if (realm instanceof JAASRealm) { 280 ((JAASRealm) realm).setAppName(securityRealmName); 281 } 282 } 283 anotherCtxObj.setRealm(realm); 284 } else { 285 Realm realm = host.getRealm(); 286 //Check and see if we have a declared realm name and no match to a parent name 287 if (securityRealmName != null) { 288 String parentRealmName = null; 289 if (realm instanceof JAASRealm) { 290 parentRealmName = ((JAASRealm) realm).getAppName(); 291 } 292 293 //Do we have a match to a parent? 294 if (!securityRealmName.equals(parentRealmName)) { 295 //No...we need to create a default adapter 296 297 //Is the context requiring JACC? 298 if (secHolder.isSecurity()) { 299 //JACC 300 realm = new TomcatGeronimoRealm(); 301 } else { 302 //JAAS 303 realm = new TomcatJAASRealm(); 304 } 305 306 log.debug("The security-realm-name '" + securityRealmName + 307 "' was specified and a parent (Engine/Host) is not named the same or no RealmGBean was configured for this context. " + 308 "Creating a default " + realm.getClass().getName() + 309 " adapter for this context."); 310 311 ((JAASRealm) realm).setUserClassNames("org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal"); 312 ((JAASRealm) realm).setRoleClassNames("org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal"); 313 ((JAASRealm) realm).setAppName(securityRealmName); 314 315 anotherCtxObj.setRealm(realm); 316 } else { 317 //Use the parent since a name matches 318 anotherCtxObj.setRealm(realm); 319 } 320 } else { 321 anotherCtxObj.setRealm(realm); 322 } 323 } 324 325 host.addChild(anotherCtxObj); 326 } 327 328 public void removeContext(TomcatContext ctx) { 329 Context context = ctx.getContext(); 330 331 if (context != null) { 332 if (context instanceof GeronimoStandardContext) { 333 GeronimoStandardContext stdctx = (GeronimoStandardContext) context; 334 335 try { 336 stdctx.stop(); 337 stdctx.destroy(); 338 } catch (Exception e) { 339 throw new RuntimeException(e); 340 } 341 342 } 343 context.getParent().removeChild(context); 344 } 345 346 } 347 348 public void setCatalinaHome(String catalinaHome) { 349 System.setProperty("catalina.home", catalinaHome); 350 } 351 352 public void addConnector(Connector connector) { 353 embedded.addConnector(connector); 354 } 355 356 public void removeConnector(Connector connector) { 357 embedded.removeConnector(connector); 358 } 359 360 public void addWebService(String contextPath, String[] virtualHosts, WebServiceContainer webServiceContainer, String securityRealmName, String realmName, String transportGuarantee, String authMethod, ClassLoader classLoader) throws Exception { 361 Context webServiceContext = embedded.createEJBWebServiceContext(contextPath, webServiceContainer, securityRealmName, realmName, transportGuarantee, authMethod, classLoader); 362 363 String virtualServer; 364 if (virtualHosts != null && virtualHosts.length > 0) { 365 virtualServer = virtualHosts[0]; 366 } else { 367 virtualServer = engine.getDefaultHost(); 368 } 369 370 Container host = engine.findChild(virtualServer); 371 if (host == null) { 372 throw new IllegalArgumentException("Invalid virtual host '" + virtualServer + "'. Do you have a matchiing Host entry in the plan?"); 373 } 374 375 host.addChild(webServiceContext); 376 webServices.put(contextPath, webServiceContext); 377 } 378 379 public void removeWebService(String contextPath) { 380 TomcatEJBWebServiceContext context = (TomcatEJBWebServiceContext) webServices.get(contextPath); 381 try { 382 context.stop(); 383 context.destroy(); 384 } catch (Exception e) { 385 throw new RuntimeException(e); 386 } 387 context.getParent().removeChild(context); 388 webServices.remove(contextPath); 389 } 390 391 public static final GBeanInfo GBEAN_INFO; 392 393 static { 394 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic("Tomcat Web Container", TomcatContainer.class); 395 396 infoFactory.setConstructor(new String[]{"classLoader", "catalinaHome", "EngineGBean", "ServerInfo", "objectName", "WebManager"}); 397 398 infoFactory.addAttribute("classLoader", ClassLoader.class, false); 399 400 infoFactory.addAttribute("catalinaHome", String.class, true); 401 402 infoFactory.addAttribute("objectName", String.class, false); 403 404 infoFactory.addReference("EngineGBean", ObjectRetriever.class, NameFactory.GERONIMO_SERVICE); 405 406 infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean"); 407 infoFactory.addReference("WebManager", WebManager.class); 408 409 infoFactory.addOperation("addContext", new Class[]{TomcatContext.class}); 410 infoFactory.addOperation("removeContext", new Class[]{TomcatContext.class}); 411 412 infoFactory.addOperation("addConnector", new Class[]{Connector.class}); 413 infoFactory.addOperation("removeConnector", new Class[]{Connector.class}); 414 415 infoFactory.addInterface(SoapHandler.class); 416 infoFactory.addInterface(TomcatWebContainer.class); 417 418 GBEAN_INFO = infoFactory.getBeanInfo(); 419 } 420 421 public static GBeanInfo getGBeanInfo() { 422 return GBEAN_INFO; 423 } 424 425 }