View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2012 Sabre Holdings.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  
20  package org.eclipse.jetty.ant;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.net.URL;
26  import java.net.URLClassLoader;
27  import java.security.CodeSource;
28  import java.security.PermissionCollection;
29  import java.util.ArrayList;
30  import java.util.Enumeration;
31  import java.util.EventListener;
32  import java.util.HashSet;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Set;
36  import java.util.jar.Manifest;
37  
38  import javax.servlet.Servlet;
39  
40  import org.apache.tools.ant.AntClassLoader;
41  import org.apache.tools.ant.BuildException;
42  import org.apache.tools.ant.Project;
43  import org.apache.tools.ant.types.FileSet;
44  import org.eclipse.jetty.annotations.AnnotationConfiguration;
45  import org.eclipse.jetty.ant.types.Attribute;
46  import org.eclipse.jetty.ant.types.Attributes;
47  import org.eclipse.jetty.ant.types.FileMatchingConfiguration;
48  import org.eclipse.jetty.ant.utils.TaskLog;
49  import org.eclipse.jetty.plus.webapp.EnvConfiguration;
50  import org.eclipse.jetty.plus.webapp.PlusConfiguration;
51  import org.eclipse.jetty.servlet.FilterHolder;
52  import org.eclipse.jetty.servlet.FilterMapping;
53  import org.eclipse.jetty.servlet.Holder;
54  import org.eclipse.jetty.servlet.ServletHandler;
55  import org.eclipse.jetty.servlet.ServletHolder;
56  import org.eclipse.jetty.servlet.ServletMapping;
57  import org.eclipse.jetty.util.log.Log;
58  import org.eclipse.jetty.util.log.Logger;
59  import org.eclipse.jetty.util.resource.Resource;
60  import org.eclipse.jetty.webapp.Configuration;
61  import org.eclipse.jetty.webapp.FragmentConfiguration;
62  import org.eclipse.jetty.webapp.JettyWebXmlConfiguration;
63  import org.eclipse.jetty.webapp.MetaInfConfiguration;
64  import org.eclipse.jetty.webapp.WebAppClassLoader;
65  import org.eclipse.jetty.webapp.WebAppContext;
66  import org.eclipse.jetty.webapp.WebInfConfiguration;
67  import org.eclipse.jetty.webapp.WebXmlConfiguration;
68  import org.eclipse.jetty.xml.XmlConfiguration;
69  
70  
71  
72  /**
73   * AntWebAppContext
74   * 
75   * Extension of WebAppContext to allow configuration via Ant environment.
76   *
77   */
78  public class AntWebAppContext extends WebAppContext
79  {
80      private static final Logger LOG = Log.getLogger(WebAppContext.class);
81      
82      public final AntWebInfConfiguration antWebInfConfiguration = new AntWebInfConfiguration();
83      public final WebXmlConfiguration webXmlConfiguration = new WebXmlConfiguration();
84      public final MetaInfConfiguration metaInfConfiguration = new MetaInfConfiguration();
85      public final FragmentConfiguration fragmentConfiguration = new FragmentConfiguration();
86      public final EnvConfiguration envConfiguration = new EnvConfiguration();
87      public final PlusConfiguration plusConfiguration = new PlusConfiguration();
88      public final AnnotationConfiguration annotationConfiguration = new AnnotationConfiguration();
89      public final JettyWebXmlConfiguration jettyWebXmlConfiguration = new JettyWebXmlConfiguration();
90  
91  
92      public final Configuration[] DEFAULT_CONFIGURATIONS = 
93          { 
94           antWebInfConfiguration,
95           webXmlConfiguration,
96           metaInfConfiguration,
97           fragmentConfiguration,
98           envConfiguration,
99           plusConfiguration,
100          annotationConfiguration,
101          jettyWebXmlConfiguration
102         };
103     
104 
105     public final static String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN =
106     ".*/.*jsp-api-[^/]*\\.jar$|.*/.*jsp-[^/]*\\.jar$|.*/.*taglibs[^/]*\\.jar$|.*/.*jstl[^/]*\\.jar$|.*/.*jsf-impl-[^/]*\\.jar$|.*/.*javax.faces-[^/]*\\.jar$|.*/.*myfaces-impl-[^/]*\\.jar$";
107 
108 
109     /** Location of jetty-env.xml file. */
110     private File jettyEnvXml;
111     
112     /** List of web application libraries. */
113     private List libraries = new ArrayList();
114 
115     /** List of web application class directories. */
116     private List classes = new ArrayList();
117     
118     /** context xml file to apply to the webapp */
119     private File contextXml;
120     
121     /** List of extra scan targets for this web application. */
122     private FileSet scanTargets;
123     
124     /** context attributes to set **/
125     private Attributes attributes;
126     
127     private Project project;
128     
129     private List<File> scanFiles;
130     
131 
132 
133     /** Extra scan targets. */
134     private FileMatchingConfiguration extraScanTargetsConfiguration;
135 
136 
137     private FileMatchingConfiguration librariesConfiguration;
138     
139 
140     public static void dump(ClassLoader loader)
141     {
142         while (loader != null)
143         {
144             System.err.println(loader);
145             if (loader instanceof URLClassLoader)
146             {
147                 URL[] urls = ((URLClassLoader)loader).getURLs();
148                 if (urls != null)
149                 {
150                     for (URL u:urls)
151                         System.err.println("\t"+u+"\n");
152                 }
153             }
154             loader = loader.getParent();
155         }
156     }
157 
158     
159     /**
160      * AntURLClassLoader
161      *
162      * Adapt the AntClassLoader which is not a URLClassLoader - this is needed for
163      * jsp to be able to search the classpath.
164      */
165     public static class AntURLClassLoader extends URLClassLoader
166     {
167         private AntClassLoader antLoader;
168         
169         public AntURLClassLoader(AntClassLoader antLoader)
170         {
171             super(new URL[] {}, antLoader);
172             this.antLoader = antLoader;      
173         }
174 
175         @Override
176         public InputStream getResourceAsStream(String name)
177         {
178             return super.getResourceAsStream(name);
179         }
180 
181         @Override
182         public void close() throws IOException
183         {
184             super.close();
185         }
186 
187         @Override
188         protected void addURL(URL url)
189         {
190             super.addURL(url);
191         }
192 
193         @Override
194         public URL[] getURLs()
195         {
196             Set<URL> urls = new HashSet<URL>();
197             
198             //convert urls from antLoader
199             String[] paths = antLoader.getClasspath().split(new String(new char[]{File.pathSeparatorChar}));
200             if (paths != null)
201             {
202                 for (String p:paths)
203                 {
204                     File f = new File(p);
205                     try
206                     {
207                         urls.add(f.toURI().toURL());   
208                     }
209                     catch (Exception e)
210                     {
211                         LOG.ignore(e);
212                     }
213                 }
214             }
215             
216             //add in any that may have been added to us as a URL directly
217             URL[] ourURLS = super.getURLs();
218             if (ourURLS != null)
219             {
220                 for (URL u:ourURLS)
221                     urls.add(u);
222             }
223             
224             return urls.toArray(new URL[urls.size()]);
225         }
226 
227         @Override
228         protected Class<?> findClass(String name) throws ClassNotFoundException
229         {
230             return super.findClass(name);
231         }
232 
233         @Override
234         protected Package definePackage(String name, Manifest man, URL url) throws IllegalArgumentException
235         {
236             return super.definePackage(name, man, url);
237         }
238 
239         @Override
240         public URL findResource(String name)
241         {
242             return super.findResource(name);
243         }
244 
245         @Override
246         public Enumeration<URL> findResources(String name) throws IOException
247         {
248             return super.findResources(name);
249         }
250 
251         @Override
252         protected PermissionCollection getPermissions(CodeSource codesource)
253         {
254             return super.getPermissions(codesource);
255         }
256 
257         @Override
258         public Class<?> loadClass(String name) throws ClassNotFoundException
259         {
260             return super.loadClass(name);
261         }
262 
263         @Override
264         protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
265         {
266             return super.loadClass(name, resolve);
267         }
268 
269         @Override
270         protected Object getClassLoadingLock(String className)
271         {
272             return super.getClassLoadingLock(className);
273         }
274 
275         @Override
276         public URL getResource(String name)
277         {
278             return super.getResource(name);
279         }
280 
281         @Override
282         public Enumeration<URL> getResources(String name) throws IOException
283         {
284             return super.getResources(name);
285         }
286 
287         @Override
288         protected Package definePackage(String name, String specTitle, String specVersion, String specVendor, String implTitle, String implVersion,
289                                         String implVendor, URL sealBase) throws IllegalArgumentException
290         {
291             return super.definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
292         }
293 
294         @Override
295         protected Package getPackage(String name)
296         {
297             return super.getPackage(name);
298         }
299 
300         @Override
301         protected Package[] getPackages()
302         {
303             return super.getPackages();
304         }
305 
306         @Override
307         protected String findLibrary(String libname)
308         {
309             return super.findLibrary(libname);
310         }
311 
312         @Override
313         public void setDefaultAssertionStatus(boolean enabled)
314         {
315             super.setDefaultAssertionStatus(enabled);
316         }
317 
318         @Override
319         public void setPackageAssertionStatus(String packageName, boolean enabled)
320         {
321             super.setPackageAssertionStatus(packageName, enabled);
322         }
323 
324         @Override
325         public void setClassAssertionStatus(String className, boolean enabled)
326         {
327             super.setClassAssertionStatus(className, enabled);
328         }
329 
330         @Override
331         public void clearAssertionStatus()
332         {
333             super.clearAssertionStatus();
334         }
335     }
336     
337     
338     /**
339      * AntServletHolder
340      *
341      *
342      */
343     public static class AntServletHolder extends ServletHolder
344     {
345 
346         public AntServletHolder()
347         {
348             super();
349         }
350 
351 
352         public AntServletHolder(Class<? extends Servlet> servlet)
353         {
354             super(servlet);
355         }
356 
357 
358         public AntServletHolder(Servlet servlet)
359         {
360             super(servlet);
361         }
362 
363 
364         public AntServletHolder(String name, Class<? extends Servlet> servlet)
365         {
366             super(name, servlet);
367         }
368 
369 
370         public AntServletHolder(String name, Servlet servlet)
371         {
372             super(name, servlet);
373         }
374 
375         protected String getSystemClassPath (ClassLoader loader) throws Exception
376         {
377             StringBuilder classpath=new StringBuilder();
378             while (loader != null)
379             {
380                 if (loader instanceof URLClassLoader)
381                 {
382                     URL[] urls = ((URLClassLoader)loader).getURLs();
383                     if (urls != null)
384                     {
385                         for (int i=0;i<urls.length;i++)
386                         {
387                             Resource resource = Resource.newResource(urls[i]);
388                             File file=resource.getFile();
389                             if (file!=null && file.exists())
390                             {
391                                 if (classpath.length()>0)
392                                     classpath.append(File.pathSeparatorChar);
393                                 classpath.append(file.getAbsolutePath());
394                             }
395                         }
396                     }
397                 }
398                 else if (loader instanceof AntClassLoader)
399                 {
400                     classpath.append(((AntClassLoader)loader).getClasspath());
401                 }
402 
403                 loader = loader.getParent();
404             }
405 
406             return classpath.toString();
407         }
408 
409     }
410 
411     
412     
413     /**
414      * AntServletHandler
415      *
416      *
417      */
418     public static class AntServletHandler extends ServletHandler
419     {
420 
421         @Override
422         public ServletHolder newServletHolder(Holder.Source source)
423         {
424             return new AntServletHolder();
425         }
426 
427     }
428 
429 
430 
431     /**
432      * Default constructor. Takes project as an argument
433      *
434      * @param project the project.
435      */
436     public AntWebAppContext(Project project) throws Exception
437     {
438         super();
439         this.project = project;
440         setConfigurations(DEFAULT_CONFIGURATIONS);
441         setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN);
442         setParentLoaderPriority(true);
443     }
444     
445 
446     /**
447      * Adds a new Ant's attributes tag object if it have not been created yet.
448      */
449     public void addAttributes(Attributes atts)
450     {
451         if (this.attributes != null)
452         {
453             throw new BuildException("Only one <attributes> tag is allowed!");
454         }
455 
456         this.attributes = atts;
457     }
458 
459     
460     public void addLib(FileSet lib)
461     {
462         libraries.add(lib);
463     }
464 
465 
466     public void addClasses(FileSet classes)
467     {
468         this.classes.add(classes);
469     }
470     
471     
472 
473     @Override
474     protected ServletHandler newServletHandler()
475     {
476         return new AntServletHandler();
477     }
478 
479 
480     public void setJettyEnvXml(File jettyEnvXml)
481     {
482         this.jettyEnvXml = jettyEnvXml;
483         TaskLog.log("jetty-env.xml file: = " + (jettyEnvXml == null ? null : jettyEnvXml.getAbsolutePath()));
484     }
485 
486     public File getJettyEnvXml ()
487     {
488         return this.jettyEnvXml;
489     }
490 
491     
492 
493 
494     public List getLibraries()
495     {
496         return librariesConfiguration.getBaseDirectories();
497     }
498 
499  
500     public void addScanTargets(FileSet scanTargets)
501     {
502         if (this.scanTargets != null)
503         {
504             throw new BuildException("Only one <scanTargets> tag is allowed!");
505         }
506 
507         this.scanTargets = scanTargets;
508     }
509     
510     public List getScanTargetFiles () 
511     {
512         if (this.scanTargets == null)
513             return null;
514         
515       
516         FileMatchingConfiguration configuration = new FileMatchingConfiguration();
517         configuration.addDirectoryScanner(scanTargets.getDirectoryScanner(project));
518         return configuration.getBaseDirectories();
519     }
520     
521     public List<File> getScanFiles()
522     {
523         if (scanFiles == null)
524             scanFiles = initScanFiles();
525         return scanFiles;
526     }
527     
528     
529     public boolean isScanned (File file)
530     {
531        List<File> files = getScanFiles();
532        if (files == null || files.isEmpty())
533            return false;
534        return files.contains(file);
535     }
536     
537     
538     public List<File> initScanFiles ()
539     {
540         List<File> scanList = new ArrayList<File>();
541         
542         if (getDescriptor() != null)
543         {
544             try (Resource r = Resource.newResource(getDescriptor());)
545             {
546                 scanList.add(r.getFile());
547             }
548             catch (IOException e)
549             {
550                 throw new BuildException(e);
551             }
552         }
553 
554         if (getJettyEnvXml() != null)
555         {
556             try (Resource r = Resource.newResource(getJettyEnvXml());)
557             {
558                 scanList.add(r.getFile());
559             }
560             catch (IOException e)
561             {
562                 throw new BuildException("Problem configuring scanner for jetty-env.xml", e);
563             }
564         }
565 
566         if (getDefaultsDescriptor() != null)
567         {
568             try (Resource r = Resource.newResource(getDefaultsDescriptor());)
569             {
570                 if (!WebAppContext.WEB_DEFAULTS_XML.equals(getDefaultsDescriptor()))
571                 {   
572                     scanList.add(r.getFile());
573                 }
574             }
575             catch (IOException e)
576             {
577                 throw new BuildException("Problem configuring scanner for webdefaults.xml", e);
578             }
579         }
580 
581         if (getOverrideDescriptor() != null)
582         {
583             try
584             {
585                 Resource r = Resource.newResource(getOverrideDescriptor());
586                 scanList.add(r.getFile());
587             }
588             catch (IOException e)
589             {
590                 throw new BuildException("Problem configuring scanner for webdefaults.xml", e);
591             }
592         }
593 
594         //add any extra classpath and libs 
595         List<File> cpFiles = getClassPathFiles();
596         if (cpFiles != null)
597             scanList.addAll(cpFiles);
598         
599         //any extra scan targets
600         @SuppressWarnings("unchecked")
601         List<File> scanFiles = (List<File>)getScanTargetFiles();
602         if (scanFiles != null)
603             scanList.addAll(scanFiles);
604         
605         return scanList;
606     }
607     
608     
609     
610     @Override
611     public void setWar(String path)
612     {
613         super.setWar(path);
614 
615         try
616         {
617             Resource war = Resource.newResource(path);
618             if (war.exists() && war.isDirectory() && getDescriptor() == null)
619             {
620                 Resource webXml = war.addPath("WEB-INF/web.xml");
621                 setDescriptor(webXml.toString());
622             }
623         }
624         catch (IOException e)
625         {
626             throw new BuildException(e);
627         }
628     }
629 
630 
631     /**
632      * 
633      */
634     public void doStart()
635     {
636         try
637         {
638             TaskLog.logWithTimestamp("Starting web application "+this.getDescriptor());
639             if (jettyEnvXml != null && jettyEnvXml.exists())
640                 envConfiguration.setJettyEnvXml(Resource.toURL(jettyEnvXml));
641             
642             ClassLoader parentLoader = this.getClass().getClassLoader();
643             if (parentLoader instanceof AntClassLoader)
644                 parentLoader = new AntURLClassLoader((AntClassLoader)parentLoader);
645 
646             setClassLoader(new WebAppClassLoader(parentLoader, this));
647             if (attributes != null && attributes.getAttributes() != null)
648             {
649                 for (Attribute a:attributes.getAttributes())
650                     setAttribute(a.getName(), a.getValue());
651             }
652             
653             //apply a context xml file if one was supplied
654             if (contextXml != null)
655             {
656                 XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(contextXml));
657                 TaskLog.log("Applying context xml file "+contextXml);
658                 xmlConfiguration.configure(this);   
659             }
660             
661             super.doStart();
662         }
663         catch (Exception e)
664         {
665             TaskLog.log(e.toString());
666         }
667     }
668 
669     public void doStop()
670     {
671         try
672         {
673             scanFiles = null;
674             TaskLog.logWithTimestamp("Stopping web application "+this);
675             Thread.currentThread().sleep(500L);
676             super.doStop();
677             //remove all filters, servlets and listeners. They will be recreated
678             //either via application of a context xml file or web.xml or annotation or servlet api
679             setEventListeners(new EventListener[0]);
680             getServletHandler().setFilters(new FilterHolder[0]);
681             getServletHandler().setFilterMappings(new FilterMapping[0]);
682             getServletHandler().setServlets(new ServletHolder[0]);
683             getServletHandler().setServletMappings(new ServletMapping[0]);
684         }
685         catch (InterruptedException e)
686         {
687             TaskLog.log(e.toString());
688         }
689         catch (Exception e)
690         {
691             TaskLog.log(e.toString());
692         }
693     }
694 
695 
696     
697     /**
698      * @return a list of classpath files (libraries and class directories).
699      */
700     public List<File> getClassPathFiles()
701     {
702         List<File> classPathFiles = new ArrayList<File>();
703         Iterator classesIterator = classes.iterator();
704         while (classesIterator.hasNext())
705         {
706             FileSet clazz = (FileSet) classesIterator.next();
707             classPathFiles.add(clazz.getDirectoryScanner(project).getBasedir());
708         }
709 
710         Iterator iterator = libraries.iterator();
711         while (iterator.hasNext())
712         {
713             FileSet library = (FileSet) iterator.next();
714             String[] includedFiles = library.getDirectoryScanner(project).getIncludedFiles();
715             File baseDir = library.getDirectoryScanner(project).getBasedir();
716 
717             for (int i = 0; i < includedFiles.length; i++)
718             {
719                 classPathFiles.add(new File(baseDir, includedFiles[i]));
720             }
721         }
722 
723 
724         return classPathFiles;
725     }
726 
727     
728     /**
729      * @return a <code>FileMatchingConfiguration</code> object describing the
730      *         configuration of all libraries added to this particular web app
731      *         (both classes and libraries).
732      */
733     public FileMatchingConfiguration getLibrariesConfiguration()
734     {
735         FileMatchingConfiguration config = new FileMatchingConfiguration();
736 
737         Iterator classesIterator = classes.iterator();
738         while (classesIterator.hasNext())
739         {
740             FileSet clazz = (FileSet) classesIterator.next();
741             config.addDirectoryScanner(clazz.getDirectoryScanner(project));
742         }
743 
744         Iterator librariesIterator = libraries.iterator();
745         while (librariesIterator.hasNext())
746         {
747             FileSet library = (FileSet) librariesIterator.next();
748             config.addDirectoryScanner(library.getDirectoryScanner(project));
749         }
750 
751         return config;
752     }
753 
754 
755     public File getContextXml()
756     {
757         return contextXml;
758     }
759 
760 
761     public void setContextXml(File contextXml)
762     {
763         this.contextXml = contextXml;
764     }
765     
766 }