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