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