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 }