View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
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.maven.plugin;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.net.MalformedURLException;
24  import java.util.ArrayList;
25  import java.util.EventListener;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.TreeSet;
31  
32  import org.eclipse.jetty.annotations.AnnotationConfiguration;
33  import org.eclipse.jetty.plus.webapp.EnvConfiguration;
34  import org.eclipse.jetty.plus.webapp.PlusConfiguration;
35  import org.eclipse.jetty.servlet.FilterHolder;
36  import org.eclipse.jetty.servlet.FilterMapping;
37  import org.eclipse.jetty.servlet.ServletHolder;
38  import org.eclipse.jetty.servlet.ServletMapping;
39  import org.eclipse.jetty.util.URIUtil;
40  import org.eclipse.jetty.util.log.Log;
41  import org.eclipse.jetty.util.log.Logger;
42  import org.eclipse.jetty.util.resource.Resource;
43  import org.eclipse.jetty.util.resource.ResourceCollection;
44  import org.eclipse.jetty.webapp.Configuration;
45  import org.eclipse.jetty.webapp.FragmentConfiguration;
46  import org.eclipse.jetty.webapp.JettyWebXmlConfiguration;
47  import org.eclipse.jetty.webapp.MetaInfConfiguration;
48  import org.eclipse.jetty.webapp.WebAppContext;
49  import org.eclipse.jetty.webapp.WebInfConfiguration;
50  import org.eclipse.jetty.webapp.WebXmlConfiguration;
51  
52  /**
53   * JettyWebAppContext
54   * 
55   * Extends the WebAppContext to specialize for the maven environment.
56   * We pass in the list of files that should form the classpath for
57   * the webapp when executing in the plugin, and any jetty-env.xml file
58   * that may have been configured.
59   *
60   */
61  public class JettyWebAppContext extends WebAppContext
62  {
63      private static final Logger LOG = Log.getLogger(JettyWebAppContext.class);
64  
65      private static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$";
66      private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes";
67      private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib";
68  
69      private File _classes = null;
70      private File _testClasses = null;
71      private final List<File> _webInfClasses = new ArrayList<File>();
72      private final List<File> _webInfJars = new ArrayList<File>();
73      private final Map<String, File> _webInfJarMap = new HashMap<String, File>();
74      private final EnvConfiguration _envConfig;
75      private List<File> _classpathFiles;  //webInfClasses+testClasses+webInfJars
76      private String _jettyEnvXml;
77      private List<Overlay> _overlays;
78      
79   
80      
81      /**
82       * Set the "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern" with a pattern for matching jars on
83       * container classpath to scan. This is analogous to the WebAppContext.setAttribute() call.
84       */
85      private String _containerIncludeJarPattern = null;
86      
87      /**
88       * Set the "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern" with a pattern for matching jars on
89       * webapp's classpath to scan. This is analogous to the WebAppContext.setAttribute() call.
90       */
91      private String _webInfIncludeJarPattern = null;
92    
93  
94      
95      /**
96       * If there is no maven-war-plugin config for ordering of the current project in the
97       * sequence of overlays, use this to control whether the current project is added 
98       * first or last in list of overlaid resources
99       */
100     private boolean _baseAppFirst = true;
101 
102   
103 
104     public JettyWebAppContext ()
105     throws Exception
106     {
107         super();   
108         setConfigurations(new Configuration[]{
109                 new MavenWebInfConfiguration(),
110                 new WebXmlConfiguration(),
111                 new MetaInfConfiguration(),
112                 new FragmentConfiguration(),
113                 _envConfig = new EnvConfiguration(),
114                 new PlusConfiguration(),
115                 new AnnotationConfiguration(),
116                 new JettyWebXmlConfiguration()
117         });
118         // Turn off copyWebInf option as it is not applicable for plugin.
119         super.setCopyWebInf(false);
120     }
121     public void setContainerIncludeJarPattern(String pattern)
122     {
123     	_containerIncludeJarPattern = pattern;
124     }
125     
126     public String getContainerIncludeJarPattern()
127     {
128         return _containerIncludeJarPattern;
129     }
130     
131     
132     public String getWebInfIncludeJarPattern()
133     {
134     	return _webInfIncludeJarPattern;
135     }
136     public void setWebInfIncludeJarPattern(String pattern)
137     {
138         _webInfIncludeJarPattern = pattern;
139     }
140    
141    
142     public List<File> getClassPathFiles()
143     {
144         return this._classpathFiles;
145     }
146     
147     
148     public void setJettyEnvXml (String jettyEnvXml)
149     {
150         this._jettyEnvXml = jettyEnvXml;
151     }
152     
153     public String getJettyEnvXml()
154     {
155         return this._jettyEnvXml;
156     }
157 
158    
159     public void setClasses(File dir)
160     {
161         _classes = dir;
162     }
163     
164     public File getClasses()
165     {
166         return _classes;
167     }
168     
169     public void setWebInfLib (List<File> jars)
170     {
171         _webInfJars.addAll(jars);
172     }
173     
174     
175     public void setTestClasses (File dir)
176     {
177         _testClasses = dir;
178     }
179     
180     
181     public File getTestClasses ()
182     {
183         return _testClasses;
184     }
185     
186     /**
187      * Ordered list of wars to overlay on top of the current project. The list
188      * may contain an overlay that represents the current project.
189      * @param overlays
190      */
191     public void setOverlays (List<Overlay> overlays)
192     {
193         _overlays = overlays;
194     }
195     
196     public List<Overlay> getOverlays()
197     {
198         return _overlays;
199     }
200 
201     /* ------------------------------------------------------------ */
202     public void setBaseAppFirst(boolean value)
203     {
204         _baseAppFirst = value;
205     }
206 
207     /* ------------------------------------------------------------ */
208     public boolean getBaseAppFirst()
209     {
210         return _baseAppFirst;
211     }
212 
213     /* ------------------------------------------------------------ */
214     /**
215      * This method is provided as a convenience for jetty maven plugin configuration 
216      * @param resourceBases Array of resources strings to set as a {@link ResourceCollection}. Each resource string may be a comma separated list of resources
217      * @see Resource
218      */
219     public void setResourceBases(String[] resourceBases)
220     {
221         List<String> resources = new ArrayList<String>();
222         for (String rl:resourceBases)
223         {
224             String[] rs = rl.split(" *, *");
225             for (String r:rs)
226                 resources.add(r);
227         }
228         
229         setBaseResource(new ResourceCollection(resources.toArray(new String[resources.size()])));
230     }
231 
232     public List<File> getWebInfLib()
233     {
234         return _webInfJars;
235     }
236 
237     public void doStart () throws Exception
238     {
239         //Set up the pattern that tells us where the jars are that need scanning for
240         //stuff like taglibs so we can tell jasper about it (see TagLibConfiguration)
241 
242         //Allow user to set up pattern for names of jars from the container classpath
243         //that will be scanned - note that by default NO jars are scanned
244         String tmp = _containerIncludeJarPattern;
245         if (tmp==null || "".equals(tmp))
246             tmp = (String)getAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN);           
247         
248         tmp = addPattern(tmp, DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN);
249         setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, tmp);
250         
251         //Allow user to set up pattern of jar names from WEB-INF that will be scanned.
252         //Note that by default ALL jars considered to be in WEB-INF will be scanned - setting
253         //a pattern restricts scanning
254         if (_webInfIncludeJarPattern != null)
255             setAttribute(WebInfConfiguration.WEBINF_JAR_PATTERN, _webInfIncludeJarPattern);
256    
257         //Set up the classes dirs that comprises the equivalent of WEB-INF/classes
258         if (_testClasses != null)
259             _webInfClasses.add(_testClasses);
260         if (_classes != null)
261             _webInfClasses.add(_classes);
262         
263         // Set up the classpath
264         _classpathFiles = new ArrayList<File>();
265         _classpathFiles.addAll(_webInfClasses);
266         _classpathFiles.addAll(_webInfJars);
267 
268         // Initialize map containing all jars in /WEB-INF/lib
269         _webInfJarMap.clear();
270         for (File file : _webInfJars)
271         {
272             // Return all jar files from class path
273             String fileName = file.getName();
274             if (fileName.endsWith(".jar"))
275                 _webInfJarMap.put(fileName, file);
276         }
277 
278         if (this._jettyEnvXml != null)
279             _envConfig.setJettyEnvXml(Resource.toURL(new File(this._jettyEnvXml)));
280         
281         // CHECK setShutdown(false);
282         super.doStart();
283     }
284      
285     public void doStop () throws Exception
286     { 
287         if (_classpathFiles != null)
288             _classpathFiles.clear();
289         _classpathFiles = null;
290         
291         _classes = null;
292         _testClasses = null;
293         
294         if (_webInfJarMap != null)
295             _webInfJarMap.clear();
296        
297         _webInfClasses.clear();
298         _webInfJars.clear();
299         
300         
301         
302         // CHECK setShutdown(true);
303         //just wait a little while to ensure no requests are still being processed
304         Thread.currentThread().sleep(500L);
305         super.doStop();
306         
307         //remove all listeners, servlets and filters. This is because we will re-apply
308         //any context xml file, which means they would potentially be added multiple times.
309         setEventListeners(new EventListener[0]);
310         getServletHandler().setFilters(new FilterHolder[0]);
311         getServletHandler().setFilterMappings(new FilterMapping[0]);
312         getServletHandler().setServlets(new ServletHolder[0]);
313         getServletHandler().setServletMappings(new ServletMapping[0]);
314     }
315 
316     @Override
317     public Resource getResource(String uriInContext) throws MalformedURLException
318     {
319         Resource resource = null;
320         // Try to get regular resource
321         resource = super.getResource(uriInContext);
322 
323         // If no regular resource exists check for access to /WEB-INF/lib or /WEB-INF/classes
324         if ((resource == null || !resource.exists()) && uriInContext != null && _classes != null)
325         {
326             String uri = URIUtil.canonicalPath(uriInContext);
327             if (uri == null)
328                 return null;
329 
330             try
331             {
332                 // Replace /WEB-INF/classes with candidates for the classpath
333                 if (uri.startsWith(WEB_INF_CLASSES_PREFIX))
334                 {
335                     if (uri.equalsIgnoreCase(WEB_INF_CLASSES_PREFIX) || uri.equalsIgnoreCase(WEB_INF_CLASSES_PREFIX+"/"))
336                     {
337                         //exact match for a WEB-INF/classes, so preferentially return the resource matching the web-inf classes
338                         //rather than the test classes
339                         if (_classes != null)
340                             return Resource.newResource(_classes);
341                         else if (_testClasses != null)
342                             return Resource.newResource(_testClasses);
343                     }
344                     else
345                     {
346                         //try matching                       
347                         Resource res = null;
348                         int i=0;
349                         while (res == null && (i < _webInfClasses.size()))
350                         {
351                             String newPath = uri.replace(WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath());
352                             res = Resource.newResource(newPath);
353                             if (!res.exists())
354                             {
355                                 res = null; 
356                                 i++;
357                             }
358                         }
359                         return res;
360                     }
361                 }       
362                 else if (uri.startsWith(WEB_INF_LIB_PREFIX))
363                 {
364                     // Return the real jar file for all accesses to
365                     // /WEB-INF/lib/*.jar
366                     String jarName = uri.replace(WEB_INF_LIB_PREFIX, "");
367                     if (jarName.startsWith("/") || jarName.startsWith("\\")) 
368                         jarName = jarName.substring(1);
369                     if (jarName.length()==0) 
370                         return null;
371                     File jarFile = _webInfJarMap.get(jarName);
372                     if (jarFile != null)
373                         return Resource.newResource(jarFile.getPath());
374 
375                     return null;
376                 }
377             }
378             catch (MalformedURLException e)
379             {
380                 throw e;
381             }
382             catch (IOException e)
383             {
384                 LOG.ignore(e);
385             }
386         }
387         return resource;
388     }
389 
390     @Override
391     public Set<String> getResourcePaths(String path)
392     {
393         // Try to get regular resource paths - this will get appropriate paths from any overlaid wars etc
394         Set<String> paths = super.getResourcePaths(path);
395         
396         if (path != null)
397         {
398             TreeSet<String> allPaths = new TreeSet<String>();
399             allPaths.addAll(paths);
400             
401             //add in the dependency jars as a virtual WEB-INF/lib entry
402             if (path.startsWith(WEB_INF_LIB_PREFIX))
403             {
404                 for (String fileName : _webInfJarMap.keySet())
405                 {
406                     // Return all jar files from class path
407                     allPaths.add(WEB_INF_LIB_PREFIX + "/" + fileName);
408                 }
409             }
410             else if (path.startsWith(WEB_INF_CLASSES_PREFIX))
411             {
412                 int i=0;
413                
414                 while (i < _webInfClasses.size())
415                 {
416                     String newPath = path.replace(WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath());
417                     allPaths.addAll(super.getResourcePaths(newPath));
418                     i++;
419                 }
420             }
421             return allPaths;
422         }
423         return paths;
424     }
425     
426     
427     public String addPattern (String s, String pattern)
428     {
429         if (s == null)
430             s = "";
431         else
432             s = s.trim();
433         
434         if (!s.contains(pattern))
435         {
436             if (s.length() != 0)
437                 s = s + "|";
438             s = s + pattern;
439         }
440         
441         return s;
442     }
443 }