001: /**
002: * EasyBeans
003: * Copyright (C) 2007 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: JOnASDeployer.java 1335 2007-04-24 12:47:31Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.deployer;
025:
026: import java.lang.reflect.Method;
027: import java.net.URL;
028: import java.util.List;
029:
030: import javax.servlet.ServletContextEvent;
031:
032: import org.ow2.util.ee.deploy.api.archive.ArchiveException;
033: import org.ow2.util.ee.deploy.api.deployable.EARDeployable;
034: import org.ow2.util.ee.deploy.api.deployable.EJBDeployable;
035: import org.ow2.util.ee.deploy.api.deployable.IDeployable;
036: import org.ow2.util.ee.deploy.api.deployable.WARDeployable;
037: import org.ow2.util.ee.deploy.api.deployer.DeployerException;
038: import org.ow2.util.ee.deploy.api.deployer.IDeployer;
039: import org.ow2.util.log.Log;
040: import org.ow2.util.log.LogFactory;
041:
042: /**
043: * Implementation of the Deployer for EasyBeans in Jetty. <br />
044: * It will deploy EJB3 and EAR. EJB3 will be deployed in EasyBeans while WAR
045: * file will go in Jetty.
046: * @author Florent Benoit
047: */
048: public class JettyDeployer extends AbsWebContainerDeployer implements
049: IDeployer {
050:
051: /**
052: * Logger.
053: */
054: private static Log logger = LogFactory.getLog(JettyDeployer.class);
055:
056: /**
057: * Link to the Jetty Server used to deploy the web applications.
058: * Static as shared by all jetty deployers.
059: */
060: private static Object jettyServer = null;
061:
062: /**
063: * Name of the WebAppContext class used to deploy a war file.
064: */
065: private static final String WEB_APP_CONTEXT_CLASS_NAME = "org.mortbay.jetty.webapp.WebAppContext";
066:
067: /**
068: * WebAppContext class used to deploy a war file.
069: */
070: private Class webAppContextClass = null;
071:
072: /**
073: * Name of the method used to get the child handler on the server object.
074: */
075: private static final String GET_CHILD_HANDLER_METHOD_NAME = "getChildHandlerByClass";
076:
077: /**
078: * Name of the class that represent an handler collection.
079: */
080: private static final String HANDLER_COLLECTION_CLASS_NAME = "org.mortbay.jetty.handler.HandlerCollection";
081:
082: /**
083: * List of the contexts deployed on the current jetty server.
084: */
085: private Object contexts = null;
086:
087: /**
088: * Name of the method used to set the URL of the war file on the web app context.
089: */
090: private static final String SET_WAR_METHOD_NAME = "setWar";
091:
092: /**
093: * Method object used to set the URL of the war file on the web app context.
094: */
095: private Method setWarMethod = null;
096:
097: /**
098: * Name of the method used to set the name of the context on the web app context.
099: */
100: private static final String SET_CONTEXT_METHOD_NAME = "setContextPath";
101:
102: /**
103: * Method object used to set the name of the context on the web app context.
104: */
105: private Method setContextPathMethod = null;
106:
107: /**
108: * Name of the method used to get the name of the context on the web app context.
109: */
110: private static final String GET_CONTEXT_METHOD_NAME = "getContextPath";
111:
112: /**
113: * Method object used to get the name of the context on the web app context.
114: */
115: private Method getContextPathMethod = null;
116:
117: /**
118: * Name of the method used to start the web app context.
119: */
120: private static final String START_CONTEXT_METHOD_NAME = "start";
121:
122: /**
123: * Method object used to start the web app context.
124: */
125: private Method startContextMethod = null;
126:
127: /**
128: * Name of the method used to stop the web app context.
129: */
130: private static final String STOP_CONTEXT_METHOD_NAME = "stop";
131:
132: /**
133: * Method object used to stop the web app context.
134: */
135: private Method stopContextMethod = null;
136:
137: /**
138: * Name of the class that is an handler.
139: */
140: private static final String HANDLER_CLASS_NAME = "org.mortbay.jetty.Handler";
141:
142: /**
143: * Name of the method used to add an handler.
144: */
145: private static final String ADD_HANDLER_METHOD_NAME = "addHandler";
146:
147: /**
148: * Method object used to add an handler.
149: */
150: private Method addHandlerMethod = null;
151:
152: /**
153: * Name of the method used to remove an handler.
154: */
155: private static final String REMOVE_HANDLER_METHOD_NAME = "removeHandler";
156:
157: /**
158: * Method object used to remove an handler.
159: */
160: private Method removeHandlerMethod = null;
161:
162: /**
163: * Name of the method used to get all handlers.
164: */
165: private static final String GET_ALL_HANDLER_METHOD_NAME = "getHandlers";
166:
167: /**
168: * Method object used to get all handlers.
169: */
170: private Method getHandlersMethod = null;
171:
172: /**
173: * Name of the method for changing the Java Delegation model.
174: */
175: private static final String SET_JAVA_DELEGATION_MODEL_METHOD_NAME = "setParentLoaderPriority";
176:
177: /**
178: * Method object used for changing the Java Delegation model.
179: */
180: private Method javaDelegationModelMethod = null;
181:
182: /**
183: * Build a new instance of this deployer.
184: * @throws DeployerException if the instance is not built.
185: */
186: public JettyDeployer() throws DeployerException {
187: super ();
188:
189: // Check if this deployer has been initialized correctly
190: if (jettyServer == null) {
191: throw new DeployerException(
192: "This deployer has not be initialized correctly as the Jetty server is null");
193: }
194:
195: // Load the class
196: webAppContextClass = loadClass(WEB_APP_CONTEXT_CLASS_NAME,
197: getClassLoader());
198:
199: // get Handler collection from jetty
200: Method getChildHandlerByClassMethod = getMethod(jettyServer
201: .getClass(), GET_CHILD_HANDLER_METHOD_NAME, Class.class);
202: Class handlerCollectionClass = loadClass(
203: HANDLER_COLLECTION_CLASS_NAME, getClassLoader());
204: Object handlerCollection = invoke(getChildHandlerByClassMethod,
205: jettyServer, handlerCollectionClass);
206: if (handlerCollection == null) {
207: throw new DeployerException(
208: "No handler collection has been defined on the jetty server object '"
209: + jettyServer + "'.");
210: }
211: contexts = invoke(getChildHandlerByClassMethod,
212: handlerCollection, handlerCollectionClass);
213:
214: // add/remove/get Handler method
215: Class handlerClazz = loadClass(HANDLER_CLASS_NAME,
216: getClassLoader());
217: addHandlerMethod = getMethod(contexts.getClass(),
218: ADD_HANDLER_METHOD_NAME, handlerClazz);
219: removeHandlerMethod = getMethod(contexts.getClass(),
220: REMOVE_HANDLER_METHOD_NAME, handlerClazz);
221: getHandlersMethod = getMethod(contexts.getClass(),
222: GET_ALL_HANDLER_METHOD_NAME);
223:
224: // get the setters methods
225: setWarMethod = getMethod(webAppContextClass,
226: SET_WAR_METHOD_NAME, String.class);
227: setContextPathMethod = getMethod(webAppContextClass,
228: SET_CONTEXT_METHOD_NAME, String.class);
229: javaDelegationModelMethod = getMethod(webAppContextClass,
230: SET_JAVA_DELEGATION_MODEL_METHOD_NAME, boolean.class);
231:
232: // getters
233: getContextPathMethod = getMethod(webAppContextClass,
234: GET_CONTEXT_METHOD_NAME);
235:
236: // and the start method
237: startContextMethod = getMethod(webAppContextClass,
238: START_CONTEXT_METHOD_NAME);
239: stopContextMethod = getMethod(webAppContextClass,
240: STOP_CONTEXT_METHOD_NAME);
241:
242: }
243:
244: /**
245: * Deploy a deployable. It can be an EJB jar, EAR, WAR, etc.
246: * @param deployable a given deployable
247: * @throws DeployerException if the deployment is not done.
248: */
249: public void deploy(final IDeployable deployable)
250: throws DeployerException {
251: checkSupportedDeployable(deployable);
252: if (deployable instanceof EJBDeployable) {
253: deployEJB((EJBDeployable) deployable);
254: } else if (deployable instanceof EARDeployable) {
255: // needs to unpack it before deploying it
256: EARDeployable earDeployable = unpackEARDeployable((EARDeployable) deployable);
257: deployEAR(earDeployable);
258: }
259: }
260:
261: /**
262: * Deploy the WAR files present in the given EAR.
263: * @param earDeployable the EAR containing the WARs
264: * @param earURL the EAR URL
265: * @param earClassLoader the EAR classloader
266: * @param parentClassLoader the parent classloader (EJB) to use
267: * @throws DeployerException if the wars are not deployed.
268: */
269: @Override
270: protected void deployWARs(final EARDeployable earDeployable,
271: final URL earURL, final ClassLoader earClassLoader,
272: final ClassLoader parentClassLoader)
273: throws DeployerException {
274: // First, try to see if there are .war in this EAR
275: List<WARDeployable> wars = earDeployable.getWARDeployables();
276:
277: for (WARDeployable war : wars) {
278:
279: // Build a new instance of the webapp context
280: Object webAppContext = newInstance(webAppContextClass);
281:
282: // invoke setters
283: // for the URL of the war file
284: try {
285: invoke(setWarMethod, webAppContext, war.getArchive()
286: .getURL().getPath());
287: } catch (ArchiveException e) {
288: throw new DeployerException(
289: "Cannot get URL from the archive '"
290: + war.getArchive() + "'.", e);
291: }
292: // for defining the name of the context
293: invoke(setContextPathMethod, webAppContext, "/"
294: + war.getContextRoot());
295:
296: // Java delegation model = true
297: invoke(javaDelegationModelMethod, webAppContext,
298: Boolean.TRUE);
299:
300: // add the built context on the existing list
301: invoke(addHandlerMethod, contexts, webAppContext);
302:
303: // set the thread context classloader to the parent classloader
304: ClassLoader oldCl = Thread.currentThread()
305: .getContextClassLoader();
306: Thread.currentThread().setContextClassLoader(
307: parentClassLoader);
308:
309: // start context
310: try {
311: invoke(startContextMethod, webAppContext);
312: } finally {
313: // reset classloader
314: Thread.currentThread().setContextClassLoader(oldCl);
315: }
316:
317: // War has been deployed
318: logger
319: .info(
320: "The war ''{0}'' has been deployed on the ''{1}'' context.",
321: war, war.getContextRoot());
322: }
323: }
324:
325: /**
326: * @return the classloader for loading the class.
327: */
328: private static ClassLoader getClassLoader() {
329: return jettyServer.getClass().getClassLoader();
330: }
331:
332: /**
333: * Check that the given deployable is supported by this deployer. If it is
334: * not supported, throw an error.
335: * @param deployable the deployable that needs to be deployed
336: * @throws DeployerException if this deployable is not supported.
337: */
338: private void checkSupportedDeployable(final IDeployable deployable)
339: throws DeployerException {
340: if (!(deployable instanceof EARDeployable || deployable instanceof EJBDeployable)) {
341: throw new DeployerException("The deployable '" + deployable
342: + "' is not supported by this deployer");
343: }
344: }
345:
346: /**
347: * Undeploy an given WAR (called by the undeploy method).
348: * @param warDeployable a given WAR deployable
349: * @throws DeployerException if the undeployment is not done.
350: */
351: @Override
352: protected void undeployWAR(final WARDeployable warDeployable)
353: throws DeployerException {
354: // get the root context of this deployable
355: String contextRoot = warDeployable.getContextRoot();
356:
357: // Get all handlers
358: Object[] handlers = (Object[]) invoke(getHandlersMethod,
359: contexts);
360:
361: // For each handler, check the name of the context root
362: Object context = null;
363: for (Object handler : handlers) {
364: // Handler is a webapp context ?
365: if (handler.getClass().equals(webAppContextClass)) {
366: // get context
367: String foundCtx = (String) invoke(getContextPathMethod,
368: handler);
369: if (contextRoot.equals(foundCtx)
370: || ("/" + contextRoot).equals(foundCtx)) {
371: // found !
372: context = handler;
373: break;
374: }
375: }
376: }
377:
378: // Context not found
379: if (context == null) {
380: throw new DeployerException(
381: "Unable to find a context with the name '"
382: + contextRoot
383: + "' for the War deployable '"
384: + warDeployable + "'.");
385: }
386:
387: // Stop the context
388: invoke(stopContextMethod, context);
389:
390: // Remove the context
391: invoke(removeHandlerMethod, contexts, context);
392:
393: logger
394: .info(
395: "The context ''{0}'' of the War ''{1}'' has been undeployed",
396: contextRoot, warDeployable);
397:
398: }
399:
400: /**
401: * Set the given context event object.
402: * @param contextEvent the given object.
403: * @throws DeployerException if the jetty server object is not found from the given context event.
404: */
405: public static void setContextEvent(
406: final ServletContextEvent contextEvent)
407: throws DeployerException {
408:
409: // First, try to get the source on the given context event
410: Object source = contextEvent.getSource();
411: if (source == null) {
412: throw new DeployerException(
413: "No source object on the given contextEvent '"
414: + contextEvent + "'.");
415: }
416:
417: // try to get getContextHandler method on this source object
418: Method getContextHandlerMethod = getMethod(source.getClass(),
419: "getContextHandler");
420: if (getContextHandlerMethod == null) {
421: throw new DeployerException(
422: "No getContextHandler method was found on the '"
423: + source + "' object");
424: }
425:
426: // get Handler
427: Object contextHandler = invoke(getContextHandlerMethod, source);
428: if (contextHandler == null) {
429: throw new DeployerException(
430: "No context handler object was returned from the '"
431: + source + "' object");
432: }
433:
434: // get the getServer method
435: Method getServerMethod = getMethod(contextHandler.getClass(),
436: "getServer");
437: if (getContextHandlerMethod == null) {
438: throw new DeployerException(
439: "No getServer method was found on the '"
440: + contextHandler + "' object");
441: }
442:
443: // Set the jetty server
444: jettyServer = invoke(getServerMethod, contextHandler);
445:
446: // No server ?
447: if (jettyServer == null) {
448: throw new DeployerException(
449: "No Jetty server found on the servlet context event '"
450: + contextEvent + "'.");
451: }
452:
453: }
454:
455: }
|