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 }