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 package org.apache.geronimo.tomcat;
019
020 import java.io.File;
021 import java.util.HashMap;
022 import java.util.Map;
023 import java.net.URLStreamHandlerFactory;
024 import java.net.URL;
025
026 import javax.management.ObjectName;
027
028 import org.apache.catalina.Container;
029 import org.apache.catalina.Context;
030 import org.apache.catalina.Engine;
031 import org.apache.catalina.Realm;
032 import org.apache.catalina.connector.Connector;
033 import org.apache.catalina.realm.JAASRealm;
034 import org.apache.commons.logging.Log;
035 import org.apache.commons.logging.LogFactory;
036 import org.apache.geronimo.gbean.GBeanInfo;
037 import org.apache.geronimo.gbean.GBeanInfoBuilder;
038 import org.apache.geronimo.gbean.GBeanLifecycle;
039 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
040 import org.apache.geronimo.kernel.ObjectNameUtil;
041 import org.apache.geronimo.management.geronimo.NetworkConnector;
042 import org.apache.geronimo.management.geronimo.WebManager;
043 import org.apache.geronimo.system.serverinfo.ServerInfo;
044 import org.apache.geronimo.tomcat.realm.TomcatGeronimoRealm;
045 import org.apache.geronimo.tomcat.realm.TomcatJAASRealm;
046 import org.apache.geronimo.tomcat.util.SecurityHolder;
047 import org.apache.geronimo.webservices.SoapHandler;
048 import org.apache.geronimo.webservices.WebServiceContainer;
049 import org.apache.naming.resources.DirContextURLStreamHandlerFactory;
050
051
052 /**
053 * Apache Tomcat GBean
054 * http://wiki.apache.org/geronimo/Tomcat
055 * http://nagoya.apache.org/jira/browse/GERONIMO-215
056 *
057 * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
058 */
059 public class TomcatContainer implements SoapHandler, GBeanLifecycle, TomcatWebContainer {
060
061 private static final Log log = LogFactory.getLog(TomcatContainer.class);
062
063 /**
064 * The default value of CATALINA_HOME variable
065 */
066 private static final String DEFAULT_CATALINA_HOME = "var/catalina";
067
068 /**
069 * Reference to the org.apache.catalina.Embedded embedded.
070 */
071 private TomcatGeronimoEmbedded embedded;
072
073 /**
074 * Tomcat Engine that will contain the host
075 */
076 private Engine engine;
077
078 /**
079 * Geronimo class loader
080 */
081 private ClassLoader classLoader;
082
083 private final Map webServices = new HashMap();
084 private final String objectName;
085 private final WebManager manager;
086 private static boolean first = true;
087
088 // Required as it's referenced by deployed webapps
089 public TomcatContainer() {
090 this.objectName = null; // is this OK??
091 setCatalinaHome(DEFAULT_CATALINA_HOME);
092 manager = null;
093 }
094
095 /**
096 * GBean constructor (invoked dynamically when the gbean is declared in a plan)
097 */
098 public TomcatContainer(ClassLoader classLoader, String catalinaHome, ObjectRetriever engineGBean, ServerInfo serverInfo, String objectName, WebManager manager) {
099 // 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 }