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