1 package org.eclipse.jetty.webapp;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.net.URI;
6 import java.net.URISyntaxException;
7 import java.net.URL;
8 import java.net.URLClassLoader;
9 import java.util.ArrayList;
10 import java.util.List;
11 import java.util.regex.Pattern;
12
13 import org.eclipse.jetty.server.Connector;
14 import org.eclipse.jetty.server.Server;
15 import org.eclipse.jetty.util.IO;
16 import org.eclipse.jetty.util.PatternMatcher;
17 import org.eclipse.jetty.util.URIUtil;
18 import org.eclipse.jetty.util.log.Log;
19 import org.eclipse.jetty.util.log.Logger;
20 import org.eclipse.jetty.util.resource.JarResource;
21 import org.eclipse.jetty.util.resource.Resource;
22 import org.eclipse.jetty.util.resource.ResourceCollection;
23
24 public class WebInfConfiguration extends AbstractConfiguration
25 {
26 private static final Logger LOG = Log.getLogger(WebInfConfiguration.class);
27
28 public static final String TEMPDIR_CONFIGURED = "org.eclipse.jetty.tmpdirConfigured";
29 public static final String CONTAINER_JAR_PATTERN = "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern";
30 public static final String WEBINF_JAR_PATTERN = "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern";
31
32
33
34
35
36 public static final String RESOURCE_URLS = "org.eclipse.jetty.resources";
37
38 protected Resource _preUnpackBaseResource;
39
40 @Override
41 public void preConfigure(final WebAppContext context) throws Exception
42 {
43
44 File work = findWorkDirectory(context);
45 if (work != null)
46 makeTempDirectory(work, context, false);
47
48
49 resolveTempDirectory(context);
50
51
52 unpack (context);
53
54
55
56
57 String tmp = (String)context.getAttribute(WEBINF_JAR_PATTERN);
58 Pattern webInfPattern = (tmp==null?null:Pattern.compile(tmp));
59 tmp = (String)context.getAttribute(CONTAINER_JAR_PATTERN);
60 Pattern containerPattern = (tmp==null?null:Pattern.compile(tmp));
61
62
63
64 PatternMatcher containerJarNameMatcher = new PatternMatcher ()
65 {
66 public void matched(URI uri) throws Exception
67 {
68 context.getMetaData().addContainerJar(Resource.newResource(uri));
69 }
70 };
71 ClassLoader loader = context.getClassLoader();
72 while (loader != null && (loader instanceof URLClassLoader))
73 {
74 URL[] urls = ((URLClassLoader)loader).getURLs();
75 if (urls != null)
76 {
77 URI[] containerUris = new URI[urls.length];
78 int i=0;
79 for (URL u : urls)
80 {
81 try
82 {
83 containerUris[i] = u.toURI();
84 }
85 catch (URISyntaxException e)
86 {
87 containerUris[i] = new URI(u.toString().replaceAll(" ", "%20"));
88 }
89 i++;
90 }
91 containerJarNameMatcher.match(containerPattern, containerUris, false);
92 }
93 loader = loader.getParent();
94 }
95
96
97 PatternMatcher webInfJarNameMatcher = new PatternMatcher ()
98 {
99 @Override
100 public void matched(URI uri) throws Exception
101 {
102 context.getMetaData().addWebInfJar(Resource.newResource(uri));
103 }
104 };
105 List<Resource> jars = findJars(context);
106
107
108 URI[] uris = null;
109 if (jars != null)
110 {
111 uris = new URI[jars.size()];
112 int i=0;
113 for (Resource r: jars)
114 {
115 uris[i++] = r.getURI();
116 }
117 }
118 webInfJarNameMatcher.match(webInfPattern, uris, true);
119 }
120
121
122 @Override
123 public void configure(WebAppContext context) throws Exception
124 {
125
126 if (context.isStarted())
127 {
128 if (LOG.isDebugEnabled())
129 LOG.debug("Cannot configure webapp "+context+" after it is started");
130 return;
131 }
132
133 Resource web_inf = context.getWebInf();
134
135
136 if (web_inf != null && web_inf.isDirectory() && context.getClassLoader() instanceof WebAppClassLoader)
137 {
138
139 Resource classes= web_inf.addPath("classes/");
140 if (classes.exists())
141 ((WebAppClassLoader)context.getClassLoader()).addClassPath(classes);
142
143
144 Resource lib= web_inf.addPath("lib/");
145 if (lib.exists() || lib.isDirectory())
146 ((WebAppClassLoader)context.getClassLoader()).addJars(lib);
147 }
148
149
150 @SuppressWarnings("unchecked")
151 List<Resource> resources = (List<Resource>)context.getAttribute(RESOURCE_URLS);
152 if (resources!=null)
153 {
154 Resource[] collection=new Resource[resources.size()+1];
155 int i=0;
156 collection[i++]=context.getBaseResource();
157 for (Resource resource : resources)
158 collection[i++]=resource;
159 context.setBaseResource(new ResourceCollection(collection));
160 }
161 }
162
163 @Override
164 public void deconfigure(WebAppContext context) throws Exception
165 {
166
167 Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
168
169 if (context.getTempDirectory()!=null && (tmpdirConfigured == null || !tmpdirConfigured.booleanValue()) && !isTempWorkDirectory(context.getTempDirectory()))
170 {
171 IO.delete(context.getTempDirectory());
172 context.setTempDirectory(null);
173
174
175
176 context.setAttribute(TEMPDIR_CONFIGURED, null);
177 context.setAttribute(WebAppContext.TEMPDIR, null);
178 }
179
180
181
182 context.setBaseResource(_preUnpackBaseResource);
183 }
184
185
186
187
188
189 @Override
190 public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
191 {
192 File tmpDir=File.createTempFile(WebInfConfiguration.getCanonicalNameForWebAppTmpDir(context),"",template.getTempDirectory().getParentFile());
193 if (tmpDir.exists())
194 {
195 IO.delete(tmpDir);
196 }
197 tmpDir.mkdir();
198 tmpDir.deleteOnExit();
199 context.setTempDirectory(tmpDir);
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239 public void resolveTempDirectory (WebAppContext context)
240 {
241
242 File tmpDir = context.getTempDirectory();
243 if (tmpDir != null && tmpDir.isDirectory() && tmpDir.canWrite())
244 {
245 context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE);
246 return;
247 }
248
249
250
251
252 File servletTmpDir = asFile(context.getAttribute(WebAppContext.TEMPDIR));
253 if (servletTmpDir != null && servletTmpDir.isDirectory() && servletTmpDir.canWrite())
254 {
255
256 tmpDir = servletTmpDir;
257
258 context.setAttribute(WebAppContext.TEMPDIR,tmpDir);
259
260 context.setTempDirectory(tmpDir);
261 return;
262 }
263
264 try
265 {
266
267 File work = new File(System.getProperty("jetty.home"),"work");
268 if (work.exists() && work.canWrite() && work.isDirectory())
269 {
270 makeTempDirectory(work, context, false);
271 }
272 else
273 {
274 File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
275 if (baseTemp != null && baseTemp.isDirectory() && baseTemp.canWrite())
276 {
277
278 makeTempDirectory(baseTemp,context,false);
279 }
280 else
281 {
282 makeTempDirectory(new File(System.getProperty("java.io.tmpdir")),context,true);
283 }
284 }
285 }
286 catch(Exception e)
287 {
288 tmpDir=null;
289 LOG.ignore(e);
290 }
291
292
293
294 if (context.getTempDirectory() == null)
295 {
296 try
297 {
298
299 tmpDir=File.createTempFile("JettyContext","");
300 if (tmpDir.exists())
301 IO.delete(tmpDir);
302 tmpDir.mkdir();
303 tmpDir.deleteOnExit();
304 context.setTempDirectory(tmpDir);
305 }
306 catch(IOException e)
307 {
308 LOG.warn("tmpdir",e); System.exit(1);
309 }
310 }
311 }
312
313
314
315
316
317
318
319 private File asFile(Object fileattr)
320 {
321 if (fileattr == null)
322 {
323 return null;
324 }
325 if (fileattr instanceof File)
326 {
327 return (File)fileattr;
328 }
329 if (fileattr instanceof String)
330 {
331 return new File((String)fileattr);
332 }
333 return null;
334 }
335
336
337
338 public void makeTempDirectory (File parent, WebAppContext context, boolean deleteExisting)
339 throws IOException
340 {
341 if (parent != null && parent.exists() && parent.canWrite() && parent.isDirectory())
342 {
343 String temp = getCanonicalNameForWebAppTmpDir(context);
344 File tmpDir = new File(parent,temp);
345
346 if (deleteExisting && tmpDir.exists())
347 {
348 if (!IO.delete(tmpDir))
349 {
350 if(LOG.isDebugEnabled())LOG.debug("Failed to delete temp dir "+tmpDir);
351 }
352
353
354 if (tmpDir.exists())
355 {
356 String old=tmpDir.toString();
357 tmpDir=File.createTempFile(temp+"_","");
358 if (tmpDir.exists())
359 IO.delete(tmpDir);
360 LOG.warn("Can't reuse "+old+", using "+tmpDir);
361 }
362 }
363
364 if (!tmpDir.exists())
365 tmpDir.mkdir();
366
367
368 if (!isTempWorkDirectory(tmpDir))
369 {
370 tmpDir.deleteOnExit();
371
372 File sentinel = new File(tmpDir, ".active");
373 if(!sentinel.exists())
374 sentinel.mkdir();
375 }
376
377 if(LOG.isDebugEnabled())
378 LOG.debug("Set temp dir "+tmpDir);
379 context.setTempDirectory(tmpDir);
380 }
381 }
382
383
384 public void unpack (WebAppContext context) throws IOException
385 {
386 Resource web_app = context.getBaseResource();
387 _preUnpackBaseResource = context.getBaseResource();
388
389 if (web_app == null)
390 {
391 String war = context.getWar();
392 if (war!=null && war.length()>0)
393 web_app = context.newResource(war);
394 else
395 web_app=context.getBaseResource();
396
397
398 if (web_app.getAlias() != null)
399 {
400 LOG.debug(web_app + " anti-aliased to " + web_app.getAlias());
401 web_app = context.newResource(web_app.getAlias());
402 }
403
404 if (LOG.isDebugEnabled())
405 LOG.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory());
406
407
408 if (web_app.exists() && !web_app.isDirectory() && !web_app.toString().startsWith("jar:"))
409 {
410
411 Resource jarWebApp = JarResource.newJarResource(web_app);
412 if (jarWebApp.exists() && jarWebApp.isDirectory())
413 web_app= jarWebApp;
414 }
415
416
417 if (web_app.exists() && (
418 (context.isCopyWebDir() && web_app.getFile() != null && web_app.getFile().isDirectory()) ||
419 (context.isExtractWAR() && web_app.getFile() != null && !web_app.getFile().isDirectory()) ||
420 (context.isExtractWAR() && web_app.getFile() == null) ||
421 !web_app.isDirectory())
422 )
423 {
424
425 File extractedWebAppDir = null;
426
427 if (war!=null)
428 {
429
430 File warfile=Resource.newResource(war).getFile();
431 if (warfile!=null && warfile.getName().toLowerCase().endsWith(".war"))
432 {
433 File sibling = new File(warfile.getParent(),warfile.getName().substring(0,warfile.getName().length()-4));
434 if (sibling.exists() && sibling.isDirectory() && sibling.canWrite())
435 extractedWebAppDir=sibling;
436 }
437 }
438
439 if (extractedWebAppDir==null)
440
441 extractedWebAppDir= new File(context.getTempDirectory(), "webapp");
442
443 if (web_app.getFile()!=null && web_app.getFile().isDirectory())
444 {
445
446 LOG.info("Copy " + web_app + " to " + extractedWebAppDir);
447 web_app.copyTo(extractedWebAppDir);
448 }
449 else
450 {
451 if (!extractedWebAppDir.exists())
452 {
453
454 extractedWebAppDir.mkdir();
455 LOG.info("Extract " + web_app + " to " + extractedWebAppDir);
456 Resource jar_web_app = JarResource.newJarResource(web_app);
457 jar_web_app.copyTo(extractedWebAppDir);
458 }
459 else
460 {
461
462 if (web_app.lastModified() > extractedWebAppDir.lastModified())
463 {
464 IO.delete(extractedWebAppDir);
465 extractedWebAppDir.mkdir();
466 LOG.info("Extract " + web_app + " to " + extractedWebAppDir);
467 Resource jar_web_app = JarResource.newJarResource(web_app);
468 jar_web_app.copyTo(extractedWebAppDir);
469 }
470 }
471 }
472 web_app = Resource.newResource(extractedWebAppDir.getCanonicalPath());
473 }
474
475
476 if (!web_app.exists() || !web_app.isDirectory())
477 {
478 LOG.warn("Web application not found " + war);
479 throw new java.io.FileNotFoundException(war);
480 }
481
482 context.setBaseResource(web_app);
483
484 if (LOG.isDebugEnabled())
485 LOG.debug("webapp=" + web_app);
486 }
487
488
489
490
491 if (context.isCopyWebInf())
492 {
493 Resource web_inf= web_app.addPath("WEB-INF/");
494
495 if (web_inf instanceof ResourceCollection ||
496 web_inf.exists() &&
497 web_inf.isDirectory() &&
498 (web_inf.getFile()==null || !web_inf.getFile().isDirectory()))
499 {
500 File extractedWebInfDir= new File(context.getTempDirectory(), "webinf");
501 if (extractedWebInfDir.exists())
502 IO.delete(extractedWebInfDir);
503 extractedWebInfDir.mkdir();
504 Resource web_inf_lib = web_inf.addPath("lib/");
505 File webInfDir=new File(extractedWebInfDir,"WEB-INF");
506 webInfDir.mkdir();
507
508 if (web_inf_lib.exists())
509 {
510 File webInfLibDir = new File(webInfDir, "lib");
511 if (webInfLibDir.exists())
512 IO.delete(webInfLibDir);
513 webInfLibDir.mkdir();
514
515 LOG.info("Copying WEB-INF/lib " + web_inf_lib + " to " + webInfLibDir);
516 web_inf_lib.copyTo(webInfLibDir);
517 }
518
519 Resource web_inf_classes = web_inf.addPath("classes/");
520 if (web_inf_classes.exists())
521 {
522 File webInfClassesDir = new File(webInfDir, "classes");
523 if (webInfClassesDir.exists())
524 IO.delete(webInfClassesDir);
525 webInfClassesDir.mkdir();
526 LOG.info("Copying WEB-INF/classes from "+web_inf_classes+" to "+webInfClassesDir.getAbsolutePath());
527 web_inf_classes.copyTo(webInfClassesDir);
528 }
529
530 web_inf=Resource.newResource(extractedWebInfDir.getCanonicalPath());
531
532 ResourceCollection rc = new ResourceCollection(web_inf,web_app);
533
534 if (LOG.isDebugEnabled())
535 LOG.debug("context.resourcebase = "+rc);
536
537 context.setBaseResource(rc);
538 }
539 }
540 }
541
542
543 public File findWorkDirectory (WebAppContext context) throws IOException
544 {
545 if (context.getBaseResource() != null)
546 {
547 Resource web_inf = context.getWebInf();
548 if (web_inf !=null && web_inf.exists())
549 {
550 return new File(web_inf.getFile(),"work");
551 }
552 }
553 return null;
554 }
555
556
557
558
559
560
561
562 public boolean isTempWorkDirectory (File tmpDir)
563 {
564 if (tmpDir == null)
565 return false;
566 if (tmpDir.getName().equalsIgnoreCase("work"))
567 return true;
568 File t = tmpDir.getParentFile();
569 if (t == null)
570 return false;
571 return (t.getName().equalsIgnoreCase("work"));
572 }
573
574
575
576
577
578
579
580
581
582
583
584 public static String getCanonicalNameForWebAppTmpDir (WebAppContext context)
585 {
586 StringBuffer canonicalName = new StringBuffer();
587 canonicalName.append("jetty-");
588
589
590 Server server=context.getServer();
591 if (server!=null)
592 {
593 Connector[] connectors = context.getServer().getConnectors();
594
595 if (connectors.length>0)
596 {
597
598 String host = (connectors==null||connectors[0]==null?"":connectors[0].getHost());
599 if (host == null)
600 host = "0.0.0.0";
601 canonicalName.append(host);
602
603
604 canonicalName.append("-");
605
606 int port = (connectors==null||connectors[0]==null?0:connectors[0].getLocalPort());
607
608
609 if (port < 0)
610 port = connectors[0].getPort();
611 canonicalName.append(port);
612 canonicalName.append("-");
613 }
614 }
615
616
617
618 try
619 {
620 Resource resource = context.getBaseResource();
621 if (resource == null)
622 {
623 if (context.getWar()==null || context.getWar().length()==0)
624 resource=context.newResource(context.getResourceBase());
625
626
627 resource = context.newResource(context.getWar());
628 }
629
630 String tmp = URIUtil.decodePath(resource.getURL().getPath());
631 if (tmp.endsWith("/"))
632 tmp = tmp.substring(0, tmp.length()-1);
633 if (tmp.endsWith("!"))
634 tmp = tmp.substring(0, tmp.length() -1);
635
636 int i = tmp.lastIndexOf("/");
637 canonicalName.append(tmp.substring(i+1, tmp.length()));
638 canonicalName.append("-");
639 }
640 catch (Exception e)
641 {
642 LOG.warn("Can't generate resourceBase as part of webapp tmp dir name", e);
643 }
644
645
646 String contextPath = context.getContextPath();
647 contextPath=contextPath.replace('/','_');
648 contextPath=contextPath.replace('\\','_');
649 canonicalName.append(contextPath);
650
651
652 canonicalName.append("-");
653 String[] vhosts = context.getVirtualHosts();
654 if (vhosts == null || vhosts.length <= 0)
655 canonicalName.append("any");
656 else
657 canonicalName.append(vhosts[0]);
658
659
660 for (int i=0;i<canonicalName.length();i++)
661 {
662 char c=canonicalName.charAt(i);
663 if (!Character.isJavaIdentifierPart(c) && "-.".indexOf(c)<0)
664 canonicalName.setCharAt(i,'.');
665 }
666
667 canonicalName.append("-");
668 return canonicalName.toString();
669 }
670
671
672
673
674
675
676
677 protected List<Resource> findJars (WebAppContext context)
678 throws Exception
679 {
680 List<Resource> jarResources = new ArrayList<Resource>();
681
682 Resource web_inf = context.getWebInf();
683 if (web_inf==null || !web_inf.exists())
684 return null;
685
686 Resource web_inf_lib = web_inf.addPath("/lib");
687
688
689 if (web_inf_lib.exists() && web_inf_lib.isDirectory())
690 {
691 String[] files=web_inf_lib.list();
692 for (int f=0;files!=null && f<files.length;f++)
693 {
694 try
695 {
696 Resource file = web_inf_lib.addPath(files[f]);
697 String fnlc = file.getName().toLowerCase();
698 int dot = fnlc.lastIndexOf('.');
699 String extension = (dot < 0 ? null : fnlc.substring(dot));
700 if (extension != null && (extension.equals(".jar") || extension.equals(".zip")))
701 {
702 jarResources.add(file);
703 }
704 }
705 catch (Exception ex)
706 {
707 LOG.warn(Log.EXCEPTION,ex);
708 }
709 }
710 }
711 return jarResources;
712 }
713 }