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