View Javadoc

1   /**
2    *
3    *  Licensed to the Apache Software Foundation (ASF) under one or more
4    *  contributor license agreements.  See the NOTICE file distributed with
5    *  this work for additional information regarding copyright ownership.
6    *  The ASF licenses this file to You under the Apache License, Version 2.0
7    *  (the "License"); you may not use this file except in compliance with
8    *  the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software
13   *  distributed under the License is distributed on an "AS IS" BASIS,
14   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *  See the License for the specific language governing permissions and
16   *  limitations under the License.
17   */
18  package org.apache.geronimo.tomcat;
19  
20  import java.io.File;
21  import java.util.HashMap;
22  import java.util.Map;
23  import java.net.URLStreamHandlerFactory;
24  import java.net.URL;
25  
26  import javax.management.ObjectName;
27  
28  import org.apache.catalina.Container;
29  import org.apache.catalina.Context;
30  import org.apache.catalina.Engine;
31  import org.apache.catalina.Realm;
32  import org.apache.catalina.connector.Connector;
33  import org.apache.catalina.realm.JAASRealm;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.geronimo.gbean.GBeanInfo;
37  import org.apache.geronimo.gbean.GBeanInfoBuilder;
38  import org.apache.geronimo.gbean.GBeanLifecycle;
39  import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
40  import org.apache.geronimo.kernel.ObjectNameUtil;
41  import org.apache.geronimo.management.geronimo.NetworkConnector;
42  import org.apache.geronimo.management.geronimo.WebManager;
43  import org.apache.geronimo.system.serverinfo.ServerInfo;
44  import org.apache.geronimo.tomcat.realm.TomcatGeronimoRealm;
45  import org.apache.geronimo.tomcat.realm.TomcatJAASRealm;
46  import org.apache.geronimo.tomcat.util.SecurityHolder;
47  import org.apache.geronimo.webservices.SoapHandler;
48  import org.apache.geronimo.webservices.WebServiceContainer;
49  import org.apache.naming.resources.DirContextURLStreamHandlerFactory;
50  
51  
52  /**
53   * Apache Tomcat GBean
54   * http://wiki.apache.org/geronimo/Tomcat
55   * http://nagoya.apache.org/jira/browse/GERONIMO-215
56   *
57   * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
58   */
59  public class TomcatContainer implements SoapHandler, GBeanLifecycle, TomcatWebContainer {
60  
61      private static final Log log = LogFactory.getLog(TomcatContainer.class);
62  
63      /**
64       * The default value of CATALINA_HOME variable
65       */
66      private static final String DEFAULT_CATALINA_HOME = "var/catalina";
67  
68      /**
69       * Reference to the org.apache.catalina.Embedded embedded.
70       */
71      private TomcatGeronimoEmbedded embedded;
72  
73      /**
74       * Tomcat Engine that will contain the host
75       */
76      private Engine engine;
77  
78      /**
79       * Geronimo class loader
80       */
81      private ClassLoader classLoader;
82  
83      private final Map webServices = new HashMap();
84      private final String objectName;
85      private final WebManager manager;
86      private static boolean first = true;
87  
88      // Required as it's referenced by deployed webapps
89      public TomcatContainer() {
90          this.objectName = null; // is this OK??
91          setCatalinaHome(DEFAULT_CATALINA_HOME);
92          manager = null;
93      }
94  
95      /**
96       * GBean constructor (invoked dynamically when the gbean is declared in a plan)
97       */
98      public TomcatContainer(ClassLoader classLoader, String catalinaHome, ObjectRetriever engineGBean, ServerInfo serverInfo, String objectName, WebManager manager) {
99          // Register a stream handler factory for the JNDI protocol
100         URLStreamHandlerFactory streamHandlerFactory =
101             new DirContextURLStreamHandlerFactory();
102         if (first) {
103             first = false;
104             try {
105                 URL.setURLStreamHandlerFactory(streamHandlerFactory);
106             } catch (Exception e) {
107                 // Log and continue anyway, this is not critical
108                 log.error("Error registering jndi stream handler", e);
109             } catch (Throwable t) {
110                 // This is likely a dual registration
111                 log.info("Dual registration of jndi stream handler: "
112                          + t.getMessage());
113             }
114         }
115 
116 
117         if (catalinaHome == null)
118             catalinaHome = DEFAULT_CATALINA_HOME;
119 
120         setCatalinaHome(serverInfo.resolveServerPath(catalinaHome));
121 
122         if (classLoader == null) {
123             throw new IllegalArgumentException("classLoader cannot be null.");
124         }
125 
126         if (engineGBean == null) {
127             throw new IllegalArgumentException("engineGBean cannot be null.");
128         }
129 
130         this.classLoader = classLoader;
131 
132         this.engine = (Engine) engineGBean.getInternalObject();
133 
134         this.objectName = objectName;
135         this.manager = manager;
136     }
137 
138     public String getObjectName() {
139         return objectName;
140     }
141 
142     public boolean isStateManageable() {
143         return true;
144     }
145 
146     public boolean isStatisticsProvider() {
147         return false; // todo: return true once stats are integrated
148     }
149 
150     public boolean isEventProvider() {
151         return true;
152     }
153 
154     public NetworkConnector[] getConnectors() {
155         return manager.getConnectorsForContainer(this);
156     }
157 
158     public NetworkConnector[] getConnectors(String protocol) {
159         return manager.getConnectorsForContainer(this, protocol);
160     }
161 
162     public void doFail() {
163         try {
164             doStop();
165         } catch (Exception ignored) {
166         }
167     }
168 
169     /**
170      * Instantiate and start up Tomcat's Embedded class
171      * <p/>
172      * See org.apache.catalina.startup.Embedded for details (TODO: provide the link to the javadoc)
173      */
174     public void doStart() throws Exception {
175         log.debug("doStart()");
176 
177         log.debug("Endorsed Dirs set to:" + System.getProperty("java.endorsed.dirs"));
178 
179         // The comments are from the javadoc of the Embedded class
180 
181         // 1. Instantiate a new instance of this class.
182         if (embedded == null) {
183             embedded = new TomcatGeronimoEmbedded();
184         }
185 
186         // Assemble FileLogger as a gbean
187         /*
188          * FileLogger fileLog = new FileLogger(); fileLog.setDirectory("."); fileLog.setPrefix("vsjMbedTC5");
189          * fileLog.setSuffix(".log"); fileLog.setTimestamp(true);
190          */
191 
192         // 2. Set the relevant properties of this object itself. In particular,
193         // you will want to establish the default Logger to be used, as well as
194         // the default Realm if you are using container-managed security.
195         embedded.setUseNaming(false);
196 
197         //Add default contexts
198         File rootContext = new File(System.getProperty("catalina.home") + "/ROOT");
199 
200         String docBase = "";
201         if (rootContext.exists()) {
202             docBase = "ROOT";
203         }
204 
205         Container[] hosts = engine.findChildren();
206         Context defaultContext;
207         for (int i = 0; i < hosts.length; i++) {
208             defaultContext = embedded.createContext("", docBase, classLoader);
209             if (defaultContext instanceof GeronimoStandardContext) {
210                 GeronimoStandardContext ctx = (GeronimoStandardContext) defaultContext;
211                 // Without this the Tomcat FallBack Application is left behind,
212                 // MBean - ...J2EEApplication=none,J2EEServer=none,..........
213                 ctx.setJ2EEApplication(null);
214                 // TODO if objectName != null extract J2EEServer from objectName/host
215                 ctx.setJ2EEServer("geronimo");
216             }
217             hosts[i].addChild(defaultContext);
218         }
219 
220         // 6. Call addEngine() to attach this Engine to the set of defined
221         // Engines for this object.
222         embedded.addEngine(engine);
223 
224         // 9. Call start() to initiate normal operations of all the attached
225         // components.
226         embedded.start();
227     }
228 
229     public void doStop() throws Exception {
230         if (embedded != null) {
231             embedded.stop();
232             embedded = null;
233         }
234 
235     }
236 
237     /**
238      * Creates and adds the context to the running host
239      * <p/>
240      * It simply delegates the call to Tomcat's Embedded and Host classes
241      *
242      * @param ctx the context to be added
243      * @see org.apache.catalina.startup.Embedded
244      * @see org.apache.catalina.Host
245      */
246     public void addContext(TomcatContext ctx) throws Exception {
247         Context anotherCtxObj = embedded.createContext(ctx.getContextPath(), ctx.getDocBase(), ctx.getClassLoader());
248 
249         // Set the context for the Tomcat implementation
250         ctx.setContext(anotherCtxObj);
251 
252         // Have the context to set its properties if its a GeronimoStandardContext
253         if (anotherCtxObj instanceof GeronimoStandardContext) {
254             ((GeronimoStandardContext) anotherCtxObj).setContextProperties(ctx);
255         }
256         //Was a virtual server defined?
257         String virtualServer = ctx.getVirtualServer();
258         if (virtualServer == null) {
259             virtualServer = engine.getDefaultHost();
260         }
261         Container host = engine.findChild(virtualServer);
262         if (host == null) {
263             throw new IllegalArgumentException("Invalid virtual host '" + virtualServer + "'.  Do you have a matching Host entry in the plan?");
264         }
265 
266         //Get the security-realm-name if there is one
267         String securityRealmName = null;
268         SecurityHolder secHolder = ctx.getSecurityHolder();
269         if (secHolder != null)
270             securityRealmName = secHolder.getSecurityRealm();
271 
272         //Did we declare a GBean at the context level?
273         if (ctx.getRealm() != null) {
274             Realm realm = ctx.getRealm();
275 
276             //Allow for the <security-realm-name> override from the
277             //geronimo-web.xml file to be used if our Realm is a JAAS type
278             if (securityRealmName != null) {
279                 if (realm instanceof JAASRealm) {
280                     ((JAASRealm) realm).setAppName(securityRealmName);
281                 }
282             }
283             anotherCtxObj.setRealm(realm);
284         } else {
285             Realm realm = host.getRealm();
286             //Check and see if we have a declared realm name and no match to a parent name
287             if (securityRealmName != null) {
288                 String parentRealmName = null;
289                 if (realm instanceof JAASRealm) {
290                     parentRealmName = ((JAASRealm) realm).getAppName();
291                 }
292 
293                 //Do we have a match to a parent?
294                 if (!securityRealmName.equals(parentRealmName)) {
295                     //No...we need to create a default adapter
296 
297                     //Is the context requiring JACC?
298                     if (secHolder.isSecurity()) {
299                         //JACC
300                         realm = new TomcatGeronimoRealm();
301                     } else {
302                         //JAAS
303                         realm = new TomcatJAASRealm();
304                     }
305 
306                     log.debug("The security-realm-name '" + securityRealmName +
307                             "' was specified and a parent (Engine/Host) is not named the same or no RealmGBean was configured for this context. " +
308                             "Creating a default " + realm.getClass().getName() +
309                             " adapter for this context.");
310 
311                     ((JAASRealm) realm).setUserClassNames("org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal");
312                     ((JAASRealm) realm).setRoleClassNames("org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal");
313                     ((JAASRealm) realm).setAppName(securityRealmName);
314 
315                     anotherCtxObj.setRealm(realm);
316                 } else {
317                     //Use the parent since a name matches
318                     anotherCtxObj.setRealm(realm);
319                 }
320             } else {
321                 anotherCtxObj.setRealm(realm);
322             }
323         }
324 
325         host.addChild(anotherCtxObj);
326     }
327 
328     public void removeContext(TomcatContext ctx) {
329         Context context = ctx.getContext();
330 
331         if (context != null) {
332             if (context instanceof GeronimoStandardContext) {
333                 GeronimoStandardContext stdctx = (GeronimoStandardContext) context;
334 
335                 try {
336                     stdctx.stop();
337                     stdctx.destroy();
338                 } catch (Exception e) {
339                     throw new RuntimeException(e);
340                 }
341 
342             }
343             context.getParent().removeChild(context);
344         }
345 
346     }
347 
348     public void setCatalinaHome(String catalinaHome) {
349         System.setProperty("catalina.home", catalinaHome);
350     }
351 
352     public void addConnector(Connector connector) {
353         embedded.addConnector(connector);
354     }
355 
356     public void removeConnector(Connector connector) {
357         embedded.removeConnector(connector);
358     }
359 
360     public void addWebService(String contextPath, String[] virtualHosts, WebServiceContainer webServiceContainer, String securityRealmName, String realmName, String transportGuarantee, String authMethod, ClassLoader classLoader) throws Exception {
361         Context webServiceContext = embedded.createEJBWebServiceContext(contextPath, webServiceContainer, securityRealmName, realmName, transportGuarantee, authMethod, classLoader);
362 
363         String virtualServer;
364         if (virtualHosts != null && virtualHosts.length > 0) {
365             virtualServer = virtualHosts[0];
366         } else {
367             virtualServer = engine.getDefaultHost();
368         }
369 
370         Container host = engine.findChild(virtualServer);
371         if (host == null) {
372             throw new IllegalArgumentException("Invalid virtual host '" + virtualServer + "'.  Do you have a matchiing Host entry in the plan?");
373         }
374 
375         host.addChild(webServiceContext);
376         webServices.put(contextPath, webServiceContext);
377     }
378 
379     public void removeWebService(String contextPath) {
380         TomcatEJBWebServiceContext context = (TomcatEJBWebServiceContext) webServices.get(contextPath);
381         try {
382             context.stop();
383             context.destroy();
384         } catch (Exception e) {
385             throw new RuntimeException(e);
386         }
387         context.getParent().removeChild(context);
388         webServices.remove(contextPath);
389     }
390 
391     public static final GBeanInfo GBEAN_INFO;
392 
393     static {
394         GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic("Tomcat Web Container", TomcatContainer.class);
395 
396         infoFactory.setConstructor(new String[]{"classLoader", "catalinaHome", "EngineGBean", "ServerInfo", "objectName", "WebManager"});
397 
398         infoFactory.addAttribute("classLoader", ClassLoader.class, false);
399 
400         infoFactory.addAttribute("catalinaHome", String.class, true);
401 
402         infoFactory.addAttribute("objectName", String.class, false);
403 
404         infoFactory.addReference("EngineGBean", ObjectRetriever.class, NameFactory.GERONIMO_SERVICE);
405 
406         infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
407         infoFactory.addReference("WebManager", WebManager.class);
408 
409         infoFactory.addOperation("addContext", new Class[]{TomcatContext.class});
410         infoFactory.addOperation("removeContext", new Class[]{TomcatContext.class});
411 
412         infoFactory.addOperation("addConnector", new Class[]{Connector.class});
413         infoFactory.addOperation("removeConnector", new Class[]{Connector.class});
414 
415         infoFactory.addInterface(SoapHandler.class);
416         infoFactory.addInterface(TomcatWebContainer.class);
417 
418         GBEAN_INFO = infoFactory.getBeanInfo();
419     }
420 
421     public static GBeanInfo getGBeanInfo() {
422         return GBEAN_INFO;
423     }
424 
425 }