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