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