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 tmpDir = null;
309 throw new IllegalStateException("Cannot create tmp dir in "+System.getProperty("java.io.tmpdir")+ " for context "+context,e);
310 }
311 }
312 }
313
314
315
316
317
318
319
320 private File asFile(Object fileattr)
321 {
322 if (fileattr == null)
323 {
324 return null;
325 }
326 if (fileattr instanceof File)
327 {
328 return (File)fileattr;
329 }
330 if (fileattr instanceof String)
331 {
332 return new File((String)fileattr);
333 }
334 return null;
335 }
336
337
338
339 public void makeTempDirectory (File parent, WebAppContext context, boolean deleteExisting)
340 throws IOException
341 {
342 if (parent != null && parent.exists() && parent.canWrite() && parent.isDirectory())
343 {
344 String temp = getCanonicalNameForWebAppTmpDir(context);
345 File tmpDir = new File(parent,temp);
346
347 if (deleteExisting && tmpDir.exists())
348 {
349 if (!IO.delete(tmpDir))
350 {
351 if(LOG.isDebugEnabled())LOG.debug("Failed to delete temp dir "+tmpDir);
352 }
353
354
355 if (tmpDir.exists())
356 {
357 String old=tmpDir.toString();
358 tmpDir=File.createTempFile(temp+"_","");
359 if (tmpDir.exists())
360 IO.delete(tmpDir);
361 LOG.warn("Can't reuse "+old+", using "+tmpDir);
362 }
363 }
364
365 if (!tmpDir.exists())
366 tmpDir.mkdir();
367
368
369 if (!isTempWorkDirectory(tmpDir))
370 {
371 tmpDir.deleteOnExit();
372 }
373
374 if(LOG.isDebugEnabled())
375 LOG.debug("Set temp dir "+tmpDir);
376 context.setTempDirectory(tmpDir);
377 }
378 }
379
380
381 public void unpack (WebAppContext context) throws IOException
382 {
383 Resource web_app = context.getBaseResource();
384 _preUnpackBaseResource = context.getBaseResource();
385
386 if (web_app == null)
387 {
388 String war = context.getWar();
389 if (war!=null && war.length()>0)
390 web_app = context.newResource(war);
391 else
392 web_app=context.getBaseResource();
393
394
395 if (web_app.getAlias() != null)
396 {
397 LOG.debug(web_app + " anti-aliased to " + web_app.getAlias());
398 web_app = context.newResource(web_app.getAlias());
399 }
400
401 if (LOG.isDebugEnabled())
402 LOG.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory()+" file="+(web_app.getFile()));
403
404 if (web_app.exists() && !web_app.isDirectory() && !web_app.toString().startsWith("jar:"))
405 {
406
407 Resource jarWebApp = JarResource.newJarResource(web_app);
408 if (jarWebApp.exists() && jarWebApp.isDirectory())
409 web_app= jarWebApp;
410 }
411
412
413 if (web_app.exists() && (
414 (context.isCopyWebDir() && web_app.getFile() != null && web_app.getFile().isDirectory()) ||
415 (context.isExtractWAR() && web_app.getFile() != null && !web_app.getFile().isDirectory()) ||
416 (context.isExtractWAR() && web_app.getFile() == null) ||
417 !web_app.isDirectory())
418 )
419 {
420
421 File extractedWebAppDir = null;
422
423 if (war!=null)
424 {
425
426 File warfile=Resource.newResource(war).getFile();
427 if (warfile!=null && warfile.getName().toLowerCase().endsWith(".war"))
428 {
429 File sibling = new File(warfile.getParent(),warfile.getName().substring(0,warfile.getName().length()-4));
430 if (sibling.exists() && sibling.isDirectory() && sibling.canWrite())
431 extractedWebAppDir=sibling;
432 }
433 }
434
435 if (extractedWebAppDir==null)
436
437 extractedWebAppDir= new File(context.getTempDirectory(), "webapp");
438
439 if (web_app.getFile()!=null && web_app.getFile().isDirectory())
440 {
441
442 LOG.info("Copy " + web_app + " to " + extractedWebAppDir);
443 web_app.copyTo(extractedWebAppDir);
444 }
445 else
446 {
447
448
449 File extractionLock = new File (context.getTempDirectory(), ".extract_lock");
450
451 if (!extractedWebAppDir.exists())
452 {
453
454 extractionLock.createNewFile();
455 extractedWebAppDir.mkdir();
456 LOG.info("Extract " + web_app + " to " + extractedWebAppDir);
457 Resource jar_web_app = JarResource.newJarResource(web_app);
458 jar_web_app.copyTo(extractedWebAppDir);
459 extractionLock.delete();
460 }
461 else
462 {
463
464 if (web_app.lastModified() > extractedWebAppDir.lastModified() || extractionLock.exists())
465 {
466 extractionLock.createNewFile();
467 IO.delete(extractedWebAppDir);
468 extractedWebAppDir.mkdir();
469 LOG.info("Extract " + web_app + " to " + extractedWebAppDir);
470 Resource jar_web_app = JarResource.newJarResource(web_app);
471 jar_web_app.copyTo(extractedWebAppDir);
472 extractionLock.delete();
473 }
474 }
475 }
476 web_app = Resource.newResource(extractedWebAppDir.getCanonicalPath());
477 }
478
479
480 if (!web_app.exists() || !web_app.isDirectory())
481 {
482 LOG.warn("Web application not found " + war);
483 throw new java.io.FileNotFoundException(war);
484 }
485
486 context.setBaseResource(web_app);
487
488 if (LOG.isDebugEnabled())
489 LOG.debug("webapp=" + web_app);
490 }
491
492
493
494 if (context.isCopyWebInf() && !context.isCopyWebDir())
495 {
496 Resource web_inf= web_app.addPath("WEB-INF/");
497
498 File extractedWebInfDir= new File(context.getTempDirectory(), "webinf");
499 if (extractedWebInfDir.exists())
500 IO.delete(extractedWebInfDir);
501 extractedWebInfDir.mkdir();
502 Resource web_inf_lib = web_inf.addPath("lib/");
503 File webInfDir=new File(extractedWebInfDir,"WEB-INF");
504 webInfDir.mkdir();
505
506 if (web_inf_lib.exists())
507 {
508 File webInfLibDir = new File(webInfDir, "lib");
509 if (webInfLibDir.exists())
510 IO.delete(webInfLibDir);
511 webInfLibDir.mkdir();
512
513 LOG.info("Copying WEB-INF/lib " + web_inf_lib + " to " + webInfLibDir);
514 web_inf_lib.copyTo(webInfLibDir);
515 }
516
517 Resource web_inf_classes = web_inf.addPath("classes/");
518 if (web_inf_classes.exists())
519 {
520 File webInfClassesDir = new File(webInfDir, "classes");
521 if (webInfClassesDir.exists())
522 IO.delete(webInfClassesDir);
523 webInfClassesDir.mkdir();
524 LOG.info("Copying WEB-INF/classes from "+web_inf_classes+" to "+webInfClassesDir.getAbsolutePath());
525 web_inf_classes.copyTo(webInfClassesDir);
526 }
527
528 web_inf=Resource.newResource(extractedWebInfDir.getCanonicalPath());
529
530 ResourceCollection rc = new ResourceCollection(web_inf,web_app);
531
532 if (LOG.isDebugEnabled())
533 LOG.debug("context.resourcebase = "+rc);
534
535 context.setBaseResource(rc);
536 }
537 }
538
539
540 public File findWorkDirectory (WebAppContext context) throws IOException
541 {
542 if (context.getBaseResource() != null)
543 {
544 Resource web_inf = context.getWebInf();
545 if (web_inf !=null && web_inf.exists())
546 {
547 return new File(web_inf.getFile(),"work");
548 }
549 }
550 return null;
551 }
552
553
554
555
556
557
558
559 public boolean isTempWorkDirectory (File tmpDir)
560 {
561 if (tmpDir == null)
562 return false;
563 if (tmpDir.getName().equalsIgnoreCase("work"))
564 return true;
565 File t = tmpDir.getParentFile();
566 if (t == null)
567 return false;
568 return (t.getName().equalsIgnoreCase("work"));
569 }
570
571
572
573
574
575
576
577
578
579
580
581 public static String getCanonicalNameForWebAppTmpDir (WebAppContext context)
582 {
583 StringBuffer canonicalName = new StringBuffer();
584 canonicalName.append("jetty-");
585
586
587 Server server=context.getServer();
588 if (server!=null)
589 {
590 Connector[] connectors = context.getServer().getConnectors();
591
592 if (connectors.length>0)
593 {
594
595 String host = (connectors==null||connectors[0]==null?"":connectors[0].getHost());
596 if (host == null)
597 host = "0.0.0.0";
598 canonicalName.append(host);
599
600
601 canonicalName.append("-");
602
603 int port = (connectors==null||connectors[0]==null?0:connectors[0].getLocalPort());
604
605
606 if (port < 0)
607 port = connectors[0].getPort();
608 canonicalName.append(port);
609 canonicalName.append("-");
610 }
611 }
612
613
614
615 try
616 {
617 Resource resource = context.getBaseResource();
618 if (resource == null)
619 {
620 if (context.getWar()==null || context.getWar().length()==0)
621 resource=context.newResource(context.getResourceBase());
622
623
624 resource = context.newResource(context.getWar());
625 }
626
627 String tmp = URIUtil.decodePath(resource.getURL().getPath());
628 if (tmp.endsWith("/"))
629 tmp = tmp.substring(0, tmp.length()-1);
630 if (tmp.endsWith("!"))
631 tmp = tmp.substring(0, tmp.length() -1);
632
633 int i = tmp.lastIndexOf("/");
634 canonicalName.append(tmp.substring(i+1, tmp.length()));
635 canonicalName.append("-");
636 }
637 catch (Exception e)
638 {
639 LOG.warn("Can't generate resourceBase as part of webapp tmp dir name", e);
640 }
641
642
643 String contextPath = context.getContextPath();
644 contextPath=contextPath.replace('/','_');
645 contextPath=contextPath.replace('\\','_');
646 canonicalName.append(contextPath);
647
648
649 canonicalName.append("-");
650 String[] vhosts = context.getVirtualHosts();
651 if (vhosts == null || vhosts.length <= 0)
652 canonicalName.append("any");
653 else
654 canonicalName.append(vhosts[0]);
655
656
657 for (int i=0;i<canonicalName.length();i++)
658 {
659 char c=canonicalName.charAt(i);
660 if (!Character.isJavaIdentifierPart(c) && "-.".indexOf(c)<0)
661 canonicalName.setCharAt(i,'.');
662 }
663
664 canonicalName.append("-");
665 return canonicalName.toString();
666 }
667
668
669
670
671
672
673
674 protected List<Resource> findJars (WebAppContext context)
675 throws Exception
676 {
677 List<Resource> jarResources = new ArrayList<Resource>();
678
679 Resource web_inf = context.getWebInf();
680 if (web_inf==null || !web_inf.exists())
681 return null;
682
683 Resource web_inf_lib = web_inf.addPath("/lib");
684
685
686 if (web_inf_lib.exists() && web_inf_lib.isDirectory())
687 {
688 String[] files=web_inf_lib.list();
689 for (int f=0;files!=null && f<files.length;f++)
690 {
691 try
692 {
693 Resource file = web_inf_lib.addPath(files[f]);
694 String fnlc = file.getName().toLowerCase();
695 int dot = fnlc.lastIndexOf('.');
696 String extension = (dot < 0 ? null : fnlc.substring(dot));
697 if (extension != null && (extension.equals(".jar") || extension.equals(".zip")))
698 {
699 jarResources.add(file);
700 }
701 }
702 catch (Exception ex)
703 {
704 LOG.warn(Log.EXCEPTION,ex);
705 }
706 }
707 }
708 return jarResources;
709 }
710 }