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