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 context.setBaseResource(_preUnpackBaseResource);
205 }
206
207
208
209
210
211 @Override
212 public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
213 {
214 File tmpDir=File.createTempFile(WebInfConfiguration.getCanonicalNameForWebAppTmpDir(context),"",template.getTempDirectory().getParentFile());
215 if (tmpDir.exists())
216 {
217 IO.delete(tmpDir);
218 }
219 tmpDir.mkdir();
220 tmpDir.deleteOnExit();
221 context.setTempDirectory(tmpDir);
222 }
223
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 public void resolveTempDirectory (WebAppContext context)
256 throws Exception
257 {
258
259 File tmpDir = context.getTempDirectory();
260 if (tmpDir != null)
261 {
262 configureTempDirectory(tmpDir, context);
263 context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE);
264 return;
265 }
266
267
268 File servletTmpDir = asFile(context.getAttribute(WebAppContext.TEMPDIR));
269 if (servletTmpDir != null)
270 {
271
272 tmpDir = servletTmpDir;
273 configureTempDirectory(tmpDir, context);
274
275 context.setAttribute(WebAppContext.TEMPDIR,tmpDir);
276
277 context.setTempDirectory(tmpDir);
278 return;
279 }
280
281
282
283 File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
284 if (baseTemp != null && baseTemp.isDirectory() && baseTemp.canWrite())
285 {
286
287 makeTempDirectory(baseTemp,context);
288 return;
289 }
290
291
292
293 File jettyBase = asFile(System.getProperty("jetty.base"));
294 if (jettyBase != null)
295 {
296 File work = new File (jettyBase, "work");
297 if (work.exists() && work.isDirectory() && work.canWrite())
298 {
299 context.setPersistTempDirectory(true);
300 makeTempDirectory(work,context);
301 return;
302 }
303 }
304
305
306 makeTempDirectory(new File(System.getProperty("java.io.tmpdir")),context);
307 }
308
309
310
311
312
313
314
315 private File asFile(Object fileattr)
316 {
317 if (fileattr == null)
318 {
319 return null;
320 }
321 if (fileattr instanceof File)
322 {
323 return (File)fileattr;
324 }
325 if (fileattr instanceof String)
326 {
327 return new File((String)fileattr);
328 }
329 return null;
330 }
331
332
333
334 public void makeTempDirectory (File parent, WebAppContext context)
335 throws Exception
336 {
337 if (parent == null || !parent.exists() || !parent.canWrite() || !parent.isDirectory())
338 throw new IllegalStateException("Parent for temp dir not configured correctly: "+(parent==null?"null":"writeable="+parent.canWrite()));
339
340
341 String temp = getCanonicalNameForWebAppTmpDir(context);
342 File tmpDir = null;
343 if (context.isPersistTempDirectory())
344 {
345
346
347 tmpDir = new File (parent, temp);
348 }
349 else
350 {
351
352 tmpDir = File.createTempFile(temp, ".dir", parent);
353
354 tmpDir.delete();
355
356 tmpDir.mkdirs();
357 }
358 configureTempDirectory(tmpDir, context);
359
360 if(LOG.isDebugEnabled())
361 LOG.debug("Set temp dir "+tmpDir);
362 context.setTempDirectory(tmpDir);
363 }
364
365 public void configureTempDirectory (File dir, WebAppContext context)
366 {
367 if (dir == null)
368 throw new IllegalArgumentException("Null temp dir");
369
370
371 if (dir.exists() && !context.isPersistTempDirectory())
372 {
373 if (!IO.delete(dir))
374 throw new IllegalStateException("Failed to delete temp dir "+dir);
375 }
376
377
378 if (!dir.exists())
379 dir.mkdirs();
380
381 if (!context.isPersistTempDirectory())
382 dir.deleteOnExit();
383
384
385 if (!dir.canWrite() || !dir.isDirectory())
386 throw new IllegalStateException("Temp dir "+dir+" not useable: writeable="+dir.canWrite()+", dir="+dir.isDirectory());
387 }
388
389
390 public void unpack (WebAppContext context) throws IOException
391 {
392 Resource web_app = context.getBaseResource();
393 _preUnpackBaseResource = context.getBaseResource();
394
395 if (web_app == null)
396 {
397 String war = context.getWar();
398 if (war!=null && war.length()>0)
399 web_app = context.newResource(war);
400 else
401 web_app=context.getBaseResource();
402
403 if (web_app == null)
404 throw new IllegalStateException("No resourceBase or war set for context");
405
406
407 if (web_app.isAlias())
408 {
409 LOG.debug(web_app + " anti-aliased to " + web_app.getAlias());
410 web_app = context.newResource(web_app.getAlias());
411 }
412
413 if (LOG.isDebugEnabled())
414 LOG.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory()+" file="+(web_app.getFile()));
415
416 if (web_app.exists() && !web_app.isDirectory() && !web_app.toString().startsWith("jar:"))
417 {
418
419 Resource jarWebApp = JarResource.newJarResource(web_app);
420 if (jarWebApp.exists() && jarWebApp.isDirectory())
421 web_app= jarWebApp;
422 }
423
424
425 if (web_app.exists() && (
426 (context.isCopyWebDir() && web_app.getFile() != null && web_app.getFile().isDirectory()) ||
427 (context.isExtractWAR() && web_app.getFile() != null && !web_app.getFile().isDirectory()) ||
428 (context.isExtractWAR() && web_app.getFile() == null) ||
429 !web_app.isDirectory())
430 )
431 {
432
433 File extractedWebAppDir = null;
434
435 if (war!=null)
436 {
437
438 File warfile=Resource.newResource(war).getFile();
439 if (warfile!=null && warfile.getName().toLowerCase(Locale.ENGLISH).endsWith(".war"))
440 {
441 File sibling = new File(warfile.getParent(),warfile.getName().substring(0,warfile.getName().length()-4));
442 if (sibling.exists() && sibling.isDirectory() && sibling.canWrite())
443 extractedWebAppDir=sibling;
444 }
445 }
446
447 if (extractedWebAppDir==null)
448
449 extractedWebAppDir= new File(context.getTempDirectory(), "webapp");
450
451 if (web_app.getFile()!=null && web_app.getFile().isDirectory())
452 {
453
454 LOG.debug("Copy " + web_app + " to " + extractedWebAppDir);
455 web_app.copyTo(extractedWebAppDir);
456 }
457 else
458 {
459
460
461 File extractionLock = new File (context.getTempDirectory(), ".extract_lock");
462
463 if (!extractedWebAppDir.exists())
464 {
465
466 extractionLock.createNewFile();
467 extractedWebAppDir.mkdir();
468 LOG.debug("Extract " + web_app + " to " + extractedWebAppDir);
469 Resource jar_web_app = JarResource.newJarResource(web_app);
470 jar_web_app.copyTo(extractedWebAppDir);
471 extractionLock.delete();
472 }
473 else
474 {
475
476 if (web_app.lastModified() > extractedWebAppDir.lastModified() || extractionLock.exists())
477 {
478 extractionLock.createNewFile();
479 IO.delete(extractedWebAppDir);
480 extractedWebAppDir.mkdir();
481 LOG.debug("Extract " + web_app + " to " + extractedWebAppDir);
482 Resource jar_web_app = JarResource.newJarResource(web_app);
483 jar_web_app.copyTo(extractedWebAppDir);
484 extractionLock.delete();
485 }
486 }
487 }
488 web_app = Resource.newResource(extractedWebAppDir.getCanonicalPath());
489 }
490
491
492 if (!web_app.exists() || !web_app.isDirectory())
493 {
494 LOG.warn("Web application not found " + war);
495 throw new java.io.FileNotFoundException(war);
496 }
497
498 context.setBaseResource(web_app);
499
500 if (LOG.isDebugEnabled())
501 LOG.debug("webapp=" + web_app);
502 }
503
504
505
506 if (context.isCopyWebInf() && !context.isCopyWebDir())
507 {
508 Resource web_inf= web_app.addPath("WEB-INF/");
509
510 File extractedWebInfDir= new File(context.getTempDirectory(), "webinf");
511 if (extractedWebInfDir.exists())
512 IO.delete(extractedWebInfDir);
513 extractedWebInfDir.mkdir();
514 Resource web_inf_lib = web_inf.addPath("lib/");
515 File webInfDir=new File(extractedWebInfDir,"WEB-INF");
516 webInfDir.mkdir();
517
518 if (web_inf_lib.exists())
519 {
520 File webInfLibDir = new File(webInfDir, "lib");
521 if (webInfLibDir.exists())
522 IO.delete(webInfLibDir);
523 webInfLibDir.mkdir();
524
525 LOG.debug("Copying WEB-INF/lib " + web_inf_lib + " to " + webInfLibDir);
526 web_inf_lib.copyTo(webInfLibDir);
527 }
528
529 Resource web_inf_classes = web_inf.addPath("classes/");
530 if (web_inf_classes.exists())
531 {
532 File webInfClassesDir = new File(webInfDir, "classes");
533 if (webInfClassesDir.exists())
534 IO.delete(webInfClassesDir);
535 webInfClassesDir.mkdir();
536 LOG.debug("Copying WEB-INF/classes from "+web_inf_classes+" to "+webInfClassesDir.getAbsolutePath());
537 web_inf_classes.copyTo(webInfClassesDir);
538 }
539
540 web_inf=Resource.newResource(extractedWebInfDir.getCanonicalPath());
541
542 ResourceCollection rc = new ResourceCollection(web_inf,web_app);
543
544 if (LOG.isDebugEnabled())
545 LOG.debug("context.resourcebase = "+rc);
546
547 context.setBaseResource(rc);
548 }
549 }
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565 public static String getCanonicalNameForWebAppTmpDir (WebAppContext context)
566 {
567 StringBuffer canonicalName = new StringBuffer();
568 canonicalName.append("jetty-");
569
570
571 Server server=context.getServer();
572 if (server!=null)
573 {
574 Connector[] connectors = context.getServer().getConnectors();
575
576 if (connectors.length>0)
577 {
578
579 String host=null;
580 int port=0;
581 if (connectors!=null && (connectors[0] instanceof NetworkConnector))
582 {
583 NetworkConnector connector = (NetworkConnector)connectors[0];
584 host=connector.getHost();
585 port=connector.getLocalPort();
586 if (port < 0)
587 port = connector.getPort();
588 }
589 if (host == null)
590 host = "0.0.0.0";
591 canonicalName.append(host);
592
593
594 canonicalName.append("-");
595
596
597
598 canonicalName.append(port);
599 canonicalName.append("-");
600 }
601 }
602
603
604 try
605 {
606 Resource resource = context.getBaseResource();
607 if (resource == null)
608 {
609 if (context.getWar()==null || context.getWar().length()==0)
610 throw new IllegalStateException("No resourceBase or war set for context");
611
612
613 resource = context.newResource(context.getWar());
614 }
615
616 String tmp = URIUtil.decodePath(resource.getURL().getPath());
617 if (tmp.endsWith("/"))
618 tmp = tmp.substring(0, tmp.length()-1);
619 if (tmp.endsWith("!"))
620 tmp = tmp.substring(0, tmp.length() -1);
621
622 int i = tmp.lastIndexOf("/");
623 canonicalName.append(tmp.substring(i+1, tmp.length()));
624 canonicalName.append("-");
625 }
626 catch (Exception e)
627 {
628 LOG.warn("Can't generate resourceBase as part of webapp tmp dir name: " + e);
629 LOG.debug(e);
630 }
631
632
633 String contextPath = context.getContextPath();
634 contextPath=contextPath.replace('/','_');
635 contextPath=contextPath.replace('\\','_');
636 canonicalName.append(contextPath);
637
638
639 canonicalName.append("-");
640 String[] vhosts = context.getVirtualHosts();
641 if (vhosts == null || vhosts.length <= 0)
642 canonicalName.append("any");
643 else
644 canonicalName.append(vhosts[0]);
645
646
647 for (int i=0;i<canonicalName.length();i++)
648 {
649 char c=canonicalName.charAt(i);
650 if (!Character.isJavaIdentifierPart(c) && "-.".indexOf(c)<0)
651 canonicalName.setCharAt(i,'.');
652 }
653
654 canonicalName.append("-");
655
656 return canonicalName.toString();
657 }
658
659
660 protected List<Resource> findClassDirs (WebAppContext context)
661 throws Exception
662 {
663 if (context == null)
664 return null;
665
666 List<Resource> classDirs = new ArrayList<Resource>();
667
668 Resource webInfClasses = findWebInfClassesDir(context);
669 if (webInfClasses != null)
670 classDirs.add(webInfClasses);
671 List<Resource> extraClassDirs = findExtraClasspathDirs(context);
672 if (extraClassDirs != null)
673 classDirs.addAll(extraClassDirs);
674
675 return classDirs;
676 }
677
678
679
680
681
682
683
684
685
686 protected List<Resource> findJars (WebAppContext context)
687 throws Exception
688 {
689 List<Resource> jarResources = new ArrayList<Resource>();
690 List<Resource> webInfLibJars = findWebInfLibJars(context);
691 if (webInfLibJars != null)
692 jarResources.addAll(webInfLibJars);
693 List<Resource> extraClasspathJars = findExtraClasspathJars(context);
694 if (extraClasspathJars != null)
695 jarResources.addAll(extraClasspathJars);
696 return jarResources;
697 }
698
699
700
701
702
703
704
705
706 protected List<Resource> findWebInfLibJars(WebAppContext context)
707 throws Exception
708 {
709 Resource web_inf = context.getWebInf();
710 if (web_inf==null || !web_inf.exists())
711 return null;
712
713 List<Resource> jarResources = new ArrayList<Resource>();
714 Resource web_inf_lib = web_inf.addPath("/lib");
715 if (web_inf_lib.exists() && web_inf_lib.isDirectory())
716 {
717 String[] files=web_inf_lib.list();
718 for (int f=0;files!=null && f<files.length;f++)
719 {
720 try
721 {
722 Resource file = web_inf_lib.addPath(files[f]);
723 String fnlc = file.getName().toLowerCase(Locale.ENGLISH);
724 int dot = fnlc.lastIndexOf('.');
725 String extension = (dot < 0 ? null : fnlc.substring(dot));
726 if (extension != null && (extension.equals(".jar") || extension.equals(".zip")))
727 {
728 jarResources.add(file);
729 }
730 }
731 catch (Exception ex)
732 {
733 LOG.warn(Log.EXCEPTION,ex);
734 }
735 }
736 }
737 return jarResources;
738 }
739
740
741
742
743
744
745
746
747
748
749 protected List<Resource> findExtraClasspathJars(WebAppContext context)
750 throws Exception
751 {
752 if (context == null || context.getExtraClasspath() == null)
753 return null;
754
755 List<Resource> jarResources = new ArrayList<Resource>();
756 StringTokenizer tokenizer = new StringTokenizer(context.getExtraClasspath(), ",;");
757 while (tokenizer.hasMoreTokens())
758 {
759 Resource resource = context.newResource(tokenizer.nextToken().trim());
760 String fnlc = resource.getName().toLowerCase(Locale.ENGLISH);
761 int dot = fnlc.lastIndexOf('.');
762 String extension = (dot < 0 ? null : fnlc.substring(dot));
763 if (extension != null && (extension.equals(".jar") || extension.equals(".zip")))
764 {
765 jarResources.add(resource);
766 }
767 }
768
769 return jarResources;
770 }
771
772
773
774
775
776
777
778
779 protected Resource findWebInfClassesDir (WebAppContext context)
780 throws Exception
781 {
782 if (context == null)
783 return null;
784
785 Resource web_inf = context.getWebInf();
786
787
788 if (web_inf != null && web_inf.isDirectory())
789 {
790
791 Resource classes= web_inf.addPath("classes/");
792 if (classes.exists())
793 return classes;
794 }
795 return null;
796 }
797
798
799
800
801
802
803
804
805
806 protected List<Resource> findExtraClasspathDirs(WebAppContext context)
807 throws Exception
808 {
809 if (context == null || context.getExtraClasspath() == null)
810 return null;
811
812 List<Resource> dirResources = new ArrayList<Resource>();
813 StringTokenizer tokenizer = new StringTokenizer(context.getExtraClasspath(), ",;");
814 while (tokenizer.hasMoreTokens())
815 {
816 Resource resource = context.newResource(tokenizer.nextToken().trim());
817 if (resource.exists() && resource.isDirectory())
818 dirResources.add(resource);
819 }
820
821 return dirResources;
822 }
823
824
825 }