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