KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > web > deployers > AbstractWarDeployer


1 /*
2  * JBoss, Home of Professional Open Source
3  * Copyright 2006, Red Hat Middleware LLC, and individual contributors
4  * by the @authors tag. See the copyright.txt in the distribution for a
5  * full listing of individual contributors.
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this software; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21  */

22 package org.jboss.web.deployers;
23
24 import java.io.File JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.lang.reflect.Method JavaDoc;
27 import java.net.URL JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Collection JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33
34 import javax.management.MBeanServer JavaDoc;
35 import javax.management.ObjectName JavaDoc;
36
37 import org.jboss.deployers.plugins.deployers.helpers.AbstractSimpleRealDeployer;
38 import org.jboss.deployers.plugins.deployers.helpers.AttachmentLocator;
39 import org.jboss.deployers.spi.DeploymentException;
40 import org.jboss.deployers.spi.deployer.DeploymentUnit;
41 import org.jboss.deployers.spi.structure.DeploymentContext;
42 import org.jboss.deployment.J2eeApplicationMetaData;
43 import org.jboss.deployment.J2eeModuleMetaData;
44 import org.jboss.metadata.WebMetaData;
45 import org.jboss.mx.util.MBeanServerLocator;
46 import org.jboss.system.metadata.ServiceConstructorMetaData;
47 import org.jboss.system.metadata.ServiceDependencyMetaData;
48 import org.jboss.system.metadata.ServiceMetaData;
49 import org.jboss.system.server.ServerConfig;
50 import org.jboss.system.server.ServerConfigLocator;
51 import org.jboss.util.file.Files;
52 import org.jboss.util.file.JarUtils;
53 import org.jboss.virtual.VirtualFile;
54 import org.jboss.web.WebApplication;
55
56 /** A template pattern class for web container integration into JBoss. This class
57  should be subclassed by web container providers wishing to integrate their
58  container into a JBoss server. The sole method to implement is:
59  {@link #getDeployment(DeploymentUnit, WebMetaData)}. This is called from
60  within {@linkplain #deploy(DeploymentUnit, WebMetaData)} to translate the
61  WebMetaData into a AbstractWarDeployment bean that will be passed to the
62  {@link org.jboss.system.deployers.ServiceDeployer} by creating ServiceMetaData
63  for the AbstractWarDeployment in
64  {@link #deployWebModule(DeploymentUnit, WebMetaData, AbstractWarDeployment)}
65  
66  The output of this deployer is a ServiceMetaData attachment. When this is
67  translated into a service instance by the ServiceDeployer, the
68  AbstractWarDeployment start/stop trigger the actual deployment/undeployment of
69  the web application.
70
71  @see org.jboss.web.deployers.AbstractWarDeployment
72
73  @author Scott.Stark@jboss.org
74  @author Christoph.Jung@infor.de
75  @author Thomas.Diesler@arcor.de
76  @version $Revision: 55985 $
77  */

78 public abstract class AbstractWarDeployer extends AbstractSimpleRealDeployer<WebMetaData>
79 {
80    public static final String JavaDoc DEPLOYER = "org.jboss.web.AbstractWebContainer.deployer";
81    public static final String JavaDoc WEB_APP = "org.jboss.web.AbstractWebContainer.webApp";
82    public static final String JavaDoc WEB_MODULE = "org.jboss.web.AbstractWebContainer.webModule";
83    public static final String JavaDoc ERROR = "org.jboss.web.AbstractWebContainer.error";
84
85    /** A mapping of deployed warUrl strings to the WebApplication object */
86    protected HashMap JavaDoc deploymentMap = new HashMap JavaDoc();
87    /** The parent class loader first model flag */
88    protected boolean java2ClassLoadingCompliance = false;
89    /** A flag indicating if war archives should be unpacked */
90    protected boolean unpackWars = true;
91    /** A flag indicating if local dirs with WEB-INF/web.xml should be treated as wars
92     */

93    protected boolean acceptNonWarDirs = false;
94
95    /** If true, ejb-links that don't resolve don't cause an error (fallback to jndi-name) */
96    protected boolean lenientEjbLink = false;
97
98    /** The default security-domain name to use */
99    protected String JavaDoc defaultSecurityDomain = "java:/jaas/other";
100    /** The request attribute name under which the JAAS Subject is store */
101    private String JavaDoc subjectAttributeName = null;
102    /** Legacy support for MBeanServer */
103    private MBeanServer JavaDoc server;
104
105    /**
106     * Set the relative order to COMPONENT_DEPLOYER+1 by default
107     */

108    public AbstractWarDeployer()
109    {
110       super(WebMetaData.class);
111       setRelativeOrder(COMPONENT_DEPLOYER+1);
112    }
113
114    /** Get the flag indicating if the normal Java2 parent first class loading
115     * model should be used over the servlet 2.3 web container first model.
116     * @return true for parent first, false for the servlet 2.3 model
117     * @jmx:managed-attribute
118     */

119    public boolean getJava2ClassLoadingCompliance()
120    {
121       return java2ClassLoadingCompliance;
122    }
123
124    /** Set the flag indicating if the normal Java2 parent first class loading
125     * model should be used over the servlet 2.3 web container first model.
126     * @param flag true for parent first, false for the servlet 2.3 model
127     * @jmx:managed-attribute
128     */

129    public void setJava2ClassLoadingCompliance(boolean flag)
130    {
131       java2ClassLoadingCompliance = flag;
132    }
133
134    /** Set the flag indicating if war archives should be unpacked. This may
135     * need to be set to false as long extraction paths under deploy can
136     * show up as deployment failures on some platforms.
137     *
138     * @jmx:managed-attribute
139     * @return true is war archives should be unpacked
140     */

141    public boolean getUnpackWars()
142    {
143       return unpackWars;
144    }
145
146    /** Get the flag indicating if war archives should be unpacked. This may
147     * need to be set to false as long extraction paths under deploy can
148     * show up as deployment failures on some platforms.
149     *
150     * @jmx:managed-attribute
151     * @param flag , true is war archives should be unpacked
152     */

153    public void setUnpackWars(boolean flag)
154    {
155       this.unpackWars = flag;
156    }
157
158    /**
159     * Get the flag indicating if local dirs with WEB-INF/web.xml should be
160     * treated as wars
161     * @return true if local dirs with WEB-INF/web.xml should be treated as wars
162     * @jmx.managed-attribute
163     */

164    public boolean getAcceptNonWarDirs()
165    {
166       return acceptNonWarDirs;
167    }
168    /**
169     * Set the flag indicating if local dirs with WEB-INF/web.xml should be
170     * treated as wars
171     * @param flag - true if local dirs with WEB-INF/web.xml should be treated as wars
172     * @jmx.managed-attribute
173     */

174    public void setAcceptNonWarDirs(boolean flag)
175    {
176       this.acceptNonWarDirs = flag;
177    }
178
179    /**
180     * Get the flag indicating if ejb-link errors should be ignored
181     * in favour of trying the jndi-name in jboss-web.xml
182     * @return the LenientEjbLink flag
183     *
184     * @jmx:managed-attribute
185     */

186    public boolean getLenientEjbLink()
187    {
188       return lenientEjbLink;
189    }
190
191    /**
192     * Set the flag indicating if ejb-link errors should be ignored
193     * in favour of trying the jndi-name in jboss-web.xml
194     *
195     * @jmx:managed-attribute
196     */

197    public void setLenientEjbLink(boolean flag)
198    {
199       lenientEjbLink = flag;
200    }
201
202    /** Get the default security domain implementation to use if a war
203     * does not declare a security-domain.
204     *
205     * @return jndi name of the security domain binding to use.
206     * @jmx:managed-attribute
207     */

208    public String JavaDoc getDefaultSecurityDomain()
209    {
210       return defaultSecurityDomain;
211    }
212    /** Set the default security domain implementation to use if a war
213     * does not declare a security-domain.
214     *
215     * @param defaultSecurityDomain - jndi name of the security domain binding
216     * to use.
217     * @jmx:managed-attribute
218     */

219    public void setDefaultSecurityDomain(String JavaDoc defaultSecurityDomain)
220    {
221       this.defaultSecurityDomain = defaultSecurityDomain;
222    }
223
224    /** Get the session attribute number under which the caller Subject is stored
225     * @jmx:managed-attribute
226     */

227    public String JavaDoc getSubjectAttributeName()
228    {
229       return subjectAttributeName;
230    }
231    /** Set the session attribute number under which the caller Subject is stored
232     * @jmx:managed-attribute
233     */

234    public void setSubjectAttributeName(String JavaDoc subjectAttributeName)
235    {
236       this.subjectAttributeName = subjectAttributeName;
237    }
238
239    public void start() throws Exception JavaDoc
240    {
241       // TODO: remove dependency on jmx
242
this.server = MBeanServerLocator.locateJBoss();
243    }
244    public void stop() throws Exception JavaDoc
245    {
246       
247    }
248
249    /**
250     * Get the AbstractWarDeployment bean for the deployment metadata. Subclasses
251     * override this method to provide a AbstractWarDeployment bean whose
252     * start/stop will control the deployment/undeployment of the web
253     * application.
254     *
255     * @param unit - the deployment unit
256     * @param metaData - the input web application metadata
257     * @return the AbstractWarDeployment for the input WebMetaData
258     * @throws Exception - thrown on any failure
259     */

260    public abstract AbstractWarDeployment getDeployment(DeploymentUnit unit, WebMetaData metaData)
261       throws Exception JavaDoc;
262
263    /**
264     * Deploy a web app based on the WebMetaData. This calls
265     * {@link #getDeployment(DeploymentUnit, WebMetaData)} to obtain an
266     * AbstractWarDeployment bean that is wrapped in a ServiceMetaData by
267     * deployWebModule.
268     *
269     * This will set the WebMetaData.contextRoot if it has not been set based
270     * on the war deployment name.
271     *
272     * @see #deployWebModule(DeploymentUnit, WebMetaData, AbstractWarDeployment)
273     * @see #buildWebContext(DeploymentUnit, String, String, WebMetaData)
274     *
275     * @param unit - the war for the deployment
276     * @param metaData - the metadata for the deployment
277     */

278    @Override JavaDoc
279    public void deploy(DeploymentUnit unit, WebMetaData metaData) throws DeploymentException
280    {
281       log.debug("Begin deploy, " + metaData);
282
283       try
284       {
285          /* TODO: This needs to be moved to a webservice deployer which updates the WebMetaData
286           // We need to unpack the WAR if it has webservices.xml, because we need
287           // to manipulate th web.xml before deploying to the web container
288           boolean unpackWebservice = di.localCl.findResource("WEB-INF/webservices.xml") != null;
289           // With JSR-181 annotated JSE endpoints we need to do it as well even if there is no webservices.xml
290           // unpackWebservice |= server.isRegistered(ObjectNameFactory.create("jboss.ws:service=ServiceEndpointManager"));
291           */

292          /* Unpack wars to the tmp directory for now until tomcat can use the vfs directly. Since
293           * the vfs deals with the distinction between a true directory, the only way we can tell from
294           * this level of the api is to look for a url that ends in '/'. Here we assume that the name is
295           * the root url.
296           */

297          String JavaDoc warName = unit.getName();
298          URL JavaDoc expWarUrl = unit.getDeploymentContext().getRoot().toURL();
299          if (warName.endsWith("/") == false
300          // Hack for jar urls being exposed
301
|| warName.endsWith("!/") == true)
302          {
303             if (warName.startsWith("jar:"))
304             {
305                if (warName.endsWith("!/"))
306                   warName = warName.substring(4, warName.length() - 2);
307                else
308                   warName = warName.substring(4, warName.length());
309             }
310             URL JavaDoc warURL = new URL JavaDoc(warName);
311             ServerConfig config = ServerConfigLocator.locate();
312             String JavaDoc prefix = warURL.getFile();
313             int lastSlash = prefix.lastIndexOf('/');
314             if (lastSlash > 0)
315                prefix = prefix.substring(lastSlash + 1);
316             int dotWar = prefix.lastIndexOf(".war");
317             if (dotWar > 0)
318                prefix = prefix.substring(0, dotWar);
319             
320             File JavaDoc expWarFile = File.createTempFile(prefix, "-exp.war", config.getServerTempDeployDir());
321             expWarFile.delete();
322             if (expWarFile.mkdir() == false)
323                throw new DeploymentException("Was unable to mkdir: " + expWarFile);
324             log.debug("Unpacking war to: " + expWarFile);
325             VirtualFile root = unit.getDeploymentContext().getRoot();
326             InputStream JavaDoc is = root.openStream();
327             JarUtils.unjar(is, expWarFile);
328             is.close();
329             expWarUrl = expWarFile.toURL();
330
331             // Map
332
DeploymentContext ctx = unit.getDeploymentContext();
333             VirtualFile warVF = ctx.getRoot();
334             String JavaDoc warPathName = warVF.getPathName();
335             if( warPathName.endsWith("/") == false )
336                warPathName += "/";
337             List JavaDoc<VirtualFile> classpathVFs = ctx.getClassPath();
338             if( classpathVFs != null )
339             {
340                ArrayList JavaDoc<URL JavaDoc> classpath = new ArrayList JavaDoc<URL JavaDoc>();
341                for(VirtualFile vf : classpathVFs)
342                {
343                   try
344                   {
345                      String JavaDoc path = vf.getPathName();
346                      path = path.substring(warPathName.length());
347                      URL JavaDoc pathURL = new URL JavaDoc(expWarUrl, path);
348                      classpath.add(pathURL);
349                   }
350                   catch(Exception JavaDoc e)
351                   {
352                      log.debug("Ignoring path element: "+vf, e);
353                   }
354                }
355                unit.addAttachment("org.jboss.web.expandedWarClasspath", classpath);
356             }
357
358             // Indicate that an expanded URL exists
359
unit.addAttachment("org.jboss.web.expandedWarURL", expWarUrl, URL JavaDoc.class);
360          }
361
362          // Resolve any ear relative alt-dd path to an expWarUrl/WEB-INF/alt-dd.xml file
363
String JavaDoc altDDPath = metaData.getAltDDPath();
364          if( altDDPath != null )
365          {
366             // First see if this is already a war local dd
367
VirtualFile altDD = unit.getMetaDataFile(altDDPath);
368             if( altDD == null )
369             {
370                // Pass absolute paths through
371
File JavaDoc file = new File JavaDoc(altDDPath);
372                if (!file.exists() || !file.isAbsolute())
373                {
374                   // Should be an relative to the top deployment
375
DeploymentUnit topUnit = unit.getDeploymentContext().getTopLevel().getDeploymentUnit();
376                   if( topUnit == unit )
377                      throw new DeploymentException("Unable to resolve "+altDDPath+" as WEB-INF path");
378                   altDD = topUnit.getFile(altDDPath);
379                   if( altDD == null )
380                      throw new DeploymentException("Unable to resolve "+altDDPath+" as a deployment path");
381                   File JavaDoc webInf = new File JavaDoc(expWarUrl.toURI());
382                   File JavaDoc altDDFile = new File JavaDoc(webInf, "WEB-INF/"+altDD.getName());
383                   log.debug("Copying the altDD to: "+altDDFile);
384                   Files.copy(altDD.toURL(), altDDFile);
385                   metaData.setAltDDPath(altDDFile.getAbsolutePath());
386                }
387             }
388          }
389
390          //
391
metaData.setJava2ClassLoadingCompliance(this.java2ClassLoadingCompliance);
392
393          // Build the context root if its not been set or is specified at the ear
394
String JavaDoc webContext = metaData.getContextRoot();
395          webContext = buildWebContext(webContext, warName, metaData, unit);
396          metaData.setContextRoot(webContext);
397
398          AbstractWarDeployment deployment = getDeployment(unit, metaData);
399          deployWebModule(unit, metaData, deployment);
400       }
401       catch (Exception JavaDoc e)
402       {
403          throw new DeploymentException("Failed to create web module", e);
404       }
405    }
406
407    /**
408     * Cleanup war deployer specifics.
409     */

410    @Override JavaDoc
411    public void undeploy(DeploymentUnit unit, WebMetaData metaData)
412    {
413       try
414       {
415          // Delete any expanded war
416
URL JavaDoc warURL = unit.getAttachment("org.jboss.web.expandedWarURL", URL JavaDoc.class);
417          if( warURL != null )
418          {
419             File JavaDoc war = new File JavaDoc(warURL.toURI());
420             Files.delete(war);
421          }
422       }
423       catch(Exception JavaDoc e)
424       {
425          log.debug("Failed to remove expanded war", e);
426       }
427    }
428
429    public void addDeployedApp(String JavaDoc warURL, WebApplication webApp)
430    {
431       deploymentMap.put(warURL, webApp);
432    }
433    /** Get the WebApplication object for a deployed war.
434     @param warUrl the war url string as originally passed to deploy().
435     @return The WebApplication created during the deploy step if the
436     warUrl is valid, null if no such deployment exists.
437     */

438    public WebApplication getDeployedApp(String JavaDoc warUrl)
439    {
440       WebApplication appInfo = (WebApplication) deploymentMap.get(warUrl);
441       return appInfo;
442    }
443    public WebApplication removeDeployedApp(String JavaDoc warURL)
444    {
445       WebApplication appInfo = (WebApplication) deploymentMap.remove(warURL);
446       return appInfo;
447    }
448
449    /** Returns the applications deployed by the web container subclasses.
450     @jmx:managed-attribute
451     @return An Iterator of WebApplication objects for the deployed wars.
452     */

453    public Iterator JavaDoc getDeployedApplications()
454    {
455       return deploymentMap.values().iterator();
456    }
457
458    /** A utility method that uses reflection to access a URL[] getURLs method
459     * so that non-URLClassLoader class loaders that support this method can
460     * provide info.
461     */

462    public static URL JavaDoc[] getClassLoaderURLs(ClassLoader JavaDoc cl)
463    {
464       URL JavaDoc[] urls = {};
465       try
466       {
467          Class JavaDoc returnType = urls.getClass();
468          Class JavaDoc[] parameterTypes = {};
469          Method JavaDoc getURLs = cl.getClass().getMethod("getURLs", parameterTypes);
470          if( returnType.isAssignableFrom(getURLs.getReturnType()) )
471          {
472             Object JavaDoc[] args = {};
473             urls = (URL JavaDoc[]) getURLs.invoke(cl, args);
474          }
475          if( urls == null || urls.length == 0 )
476          {
477             getURLs = cl.getClass().getMethod("getAllURLs", parameterTypes);
478             if( returnType.isAssignableFrom(getURLs.getReturnType()) )
479             {
480                Object JavaDoc[] args = {};
481                urls = (URL JavaDoc[]) getURLs.invoke(cl, args);
482             }
483          }
484       }
485       catch(Exception JavaDoc ignore)
486       {
487       }
488       return urls;
489    }
490    
491    /** This method creates a context-root string from either the
492    WEB-INF/jboss-web.xml context-root element is one exists, or the
493    filename portion of the warURL. It is called if the deployment
494    webContext value is null which indicates a standalone war deployment.
495    A war name of ROOT.war is handled as a special case of a war that
496    should be installed as the default web context.
497    @param ctxPath - war level context-root
498    @param warName -
499    */

500    protected String JavaDoc buildWebContext(String JavaDoc ctxPath, String JavaDoc warName,
501          WebMetaData metaData, DeploymentUnit unit)
502    {
503       // Build a war root context from the war name if one was not specified
504
String JavaDoc webContext = ctxPath;
505
506       // Build the context from the deployment name
507
if( webContext == null )
508       {
509          // Build the context from the war name, strip the .war suffix
510
webContext = warName;
511          webContext = webContext.replace('\\', '/');
512          if( webContext.endsWith("/") )
513             webContext = webContext.substring(0, webContext.length()-1);
514          int prefix = webContext.lastIndexOf('/');
515          if( prefix > 0 )
516             webContext = webContext.substring(prefix+1);
517          int suffix = webContext.lastIndexOf(".war");
518          if( suffix > 0 )
519             webContext = webContext.substring(0, suffix);
520           // Strip any '<int-value>.' prefix
521
int index = 0;
522           for(; index < webContext.length(); index ++)
523           {
524              char c = webContext.charAt(index);
525              if( Character.isDigit(c) == false && c != '.' )
526                 break;
527           }
528           webContext = webContext.substring(index);
529       }
530
531       // Servlet containers are anal about the web context starting with '/'
532
if( webContext.length() > 0 && webContext.charAt(0) != '/' )
533          webContext = "/" + webContext;
534       // And also the default root context must be an empty string, not '/'
535
else if( webContext.equals("/") )
536          webContext = "";
537       return webContext;
538    }
539
540    /**
541     * TODO: The use of an MBeanServer needs to be removed
542     * @return
543     */

544    @Deprecated JavaDoc
545    protected MBeanServer JavaDoc getServer()
546    {
547       return server;
548    }
549
550    /**
551     * Get the object name of the ServiceMetaData instance associated with
552     * the WebMetaData. This uses the pattern:
553     * "jboss.web.deployment:war="+metaData.getContextRoot()
554     *
555     * @param metaData - the web app metaData
556     * @return "jboss.web.deployment:war="+metaData.getContextRoot();
557     */

558    protected String JavaDoc getObjectName(WebMetaData metaData)
559    {
560       String JavaDoc ctxPath = metaData.getContextRoot();
561       String JavaDoc objectName = "jboss.web.deployment:war="+ctxPath;
562       return objectName;
563    }
564
565    /**
566     * Called by deploy to create a ServiceMetaData instance that wraps the
567     * AbstractWarDeployment bean.
568     *
569     * @param unit - the deployment unit
570     * @param metaData - the web app metadata passed to deploy
571     * @param deployment - the web app deployment bean created by getDeployment
572     * @throws Exception
573     */

574    protected void deployWebModule(DeploymentUnit unit, WebMetaData metaData,
575          AbstractWarDeployment deployment)
576       throws Exception JavaDoc
577    {
578       log.debug("deployWebModule");
579       try
580       {
581          ServiceMetaData webModule = new ServiceMetaData();
582          String JavaDoc name = getObjectName(metaData);
583          ObjectName JavaDoc objectName = new ObjectName JavaDoc(name);
584          webModule.setObjectName(objectName);
585          webModule.setCode(WebModule.class.getName());
586          // WebModule(DeploymentUnit, AbstractWarDeployer, AbstractWarDeployment)
587
ServiceConstructorMetaData constructor = new ServiceConstructorMetaData();
588          constructor.setSignature(new String JavaDoc[] { DeploymentUnit.class.getName(),
589                AbstractWarDeployer.class.getName(), AbstractWarDeployment.class.getName()});
590          constructor.setParameters(new Object JavaDoc[] {unit, this, deployment});
591          webModule.setConstructor(constructor);
592          
593          // Dependencies...Still have old jmx names here
594
Collection JavaDoc<String JavaDoc> depends = metaData.getDependencies();
595          List JavaDoc<ServiceDependencyMetaData> dependencies = new ArrayList JavaDoc<ServiceDependencyMetaData>();
596          for(String JavaDoc iDependOn : depends)
597          {
598             ServiceDependencyMetaData sdmd = new ServiceDependencyMetaData();
599             sdmd.setIDependOn(iDependOn);
600          }
601          webModule.setDependencies(dependencies);
602
603          // TODO could create multiple components for the deployment
604
unit.addAttachment(ServiceMetaData.class, webModule);
605       }
606       catch (Exception JavaDoc e)
607       {
608          throw DeploymentException.rethrowAsDeploymentException("Error creating rar deployment " + unit.getName(), e);
609       }
610
611    }
612 }
613
Popular Tags