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