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