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    }