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