View Javadoc

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