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