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()){LOG.debug("Cannot configure webapp "+context+" after it is started");}
129 return;
130 }
131
132 Resource web_inf = context.getWebInf();
133
134
135 if (web_inf != null && web_inf.isDirectory() && context.getClassLoader() instanceof WebAppClassLoader)
136 {
137
138 Resource classes= web_inf.addPath("classes/");
139 if (classes.exists())
140 ((WebAppClassLoader)context.getClassLoader()).addClassPath(classes);
141
142
143 Resource lib= web_inf.addPath("lib/");
144 if (lib.exists() || lib.isDirectory())
145 ((WebAppClassLoader)context.getClassLoader()).addJars(lib);
146 }
147
148
149 @SuppressWarnings("unchecked")
150 List<Resource> resources = (List<Resource>)context.getAttribute(RESOURCE_URLS);
151 if (resources!=null)
152 {
153 Resource[] collection=new Resource[resources.size()+1];
154 int i=0;
155 collection[i++]=context.getBaseResource();
156 for (Resource resource : resources)
157 collection[i++]=resource;
158 context.setBaseResource(new ResourceCollection(collection));
159 }
160 }
161
162 @Override
163 public void deconfigure(WebAppContext context) throws Exception
164 {
165
166 Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
167
168 if (context.getTempDirectory()!=null && (tmpdirConfigured == null || !tmpdirConfigured.booleanValue()) && !isTempWorkDirectory(context.getTempDirectory()))
169 {
170 IO.delete(context.getTempDirectory());
171 context.setTempDirectory(null);
172
173
174
175 context.setAttribute(TEMPDIR_CONFIGURED, null);
176 context.setAttribute(WebAppContext.TEMPDIR, null);
177 }
178
179
180
181 context.setBaseResource(_preUnpackBaseResource);
182 }
183
184
185
186
187
188 @Override
189 public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
190 {
191 File tmpDir=File.createTempFile(WebInfConfiguration.getCanonicalNameForWebAppTmpDir(context),"",template.getTempDirectory().getParentFile());
192 if (tmpDir.exists())
193 {
194 IO.delete(tmpDir);
195 }
196 tmpDir.mkdir();
197 tmpDir.deleteOnExit();
198 context.setTempDirectory(tmpDir);
199 }
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 public void resolveTempDirectory (WebAppContext context)
239 {
240
241 File tmpDir = context.getTempDirectory();
242 if (tmpDir != null && tmpDir.isDirectory() && tmpDir.canWrite())
243 {
244 context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE);
245 return;
246 }
247
248
249
250
251 File servletTmpDir = asFile(context.getAttribute(WebAppContext.TEMPDIR));
252 if (servletTmpDir != null && servletTmpDir.isDirectory() && servletTmpDir.canWrite())
253 {
254
255 tmpDir = servletTmpDir;
256
257 context.setAttribute(WebAppContext.TEMPDIR,tmpDir);
258
259 context.setTempDirectory(tmpDir);
260 return;
261 }
262
263 try
264 {
265
266 File work = new File(System.getProperty("jetty.home"),"work");
267 if (work.exists() && work.canWrite() && work.isDirectory())
268 {
269 makeTempDirectory(work, context, false);
270 }
271 else
272 {
273 File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
274 if (baseTemp != null && baseTemp.isDirectory() && baseTemp.canWrite())
275 {
276
277 makeTempDirectory(baseTemp,context,false);
278 }
279 else
280 {
281 makeTempDirectory(new File(System.getProperty("java.io.tmpdir")),context,true);
282 }
283 }
284 }
285 catch(Exception e)
286 {
287 tmpDir=null;
288 LOG.ignore(e);
289 }
290
291
292
293 if (context.getTempDirectory() == null)
294 {
295 try
296 {
297
298 tmpDir=File.createTempFile("JettyContext","");
299 if (tmpDir.exists())
300 IO.delete(tmpDir);
301 tmpDir.mkdir();
302 tmpDir.deleteOnExit();
303 context.setTempDirectory(tmpDir);
304 }
305 catch(IOException e)
306 {
307 LOG.warn("tmpdir",e); System.exit(1);
308 }
309 }
310 }
311
312
313
314
315
316
317
318 private File asFile(Object fileattr)
319 {
320 if (fileattr == null)
321 {
322 return null;
323 }
324 if (fileattr instanceof File)
325 {
326 return (File)fileattr;
327 }
328 if (fileattr instanceof String)
329 {
330 return new File((String)fileattr);
331 }
332 return null;
333 }
334
335
336
337 public void makeTempDirectory (File parent, WebAppContext context, boolean deleteExisting)
338 throws IOException
339 {
340 if (parent != null && parent.exists() && parent.canWrite() && parent.isDirectory())
341 {
342 String temp = getCanonicalNameForWebAppTmpDir(context);
343 File tmpDir = new File(parent,temp);
344
345 if (deleteExisting && tmpDir.exists())
346 {
347 if (!IO.delete(tmpDir))
348 {
349 if(LOG.isDebugEnabled())LOG.debug("Failed to delete temp dir "+tmpDir);
350 }
351
352
353 if (tmpDir.exists())
354 {
355 String old=tmpDir.toString();
356 tmpDir=File.createTempFile(temp+"_","");
357 if (tmpDir.exists())
358 IO.delete(tmpDir);
359 LOG.warn("Can't reuse "+old+", using "+tmpDir);
360 }
361 }
362
363 if (!tmpDir.exists())
364 tmpDir.mkdir();
365
366
367 if (!isTempWorkDirectory(tmpDir))
368 {
369 tmpDir.deleteOnExit();
370
371 File sentinel = new File(tmpDir, ".active");
372 if(!sentinel.exists())
373 sentinel.mkdir();
374 }
375
376 if(LOG.isDebugEnabled())
377 LOG.debug("Set temp dir "+tmpDir);
378 context.setTempDirectory(tmpDir);
379 }
380 }
381
382
383 public void unpack (WebAppContext context) throws IOException
384 {
385 Resource web_app = context.getBaseResource();
386 _preUnpackBaseResource = context.getBaseResource();
387
388 if (web_app == null)
389 {
390 String war = context.getWar();
391 if (war!=null && war.length()>0)
392 web_app = context.newResource(war);
393 else
394 web_app=context.getBaseResource();
395
396
397 if (web_app.getAlias() != null)
398 {
399 LOG.debug(web_app + " anti-aliased to " + web_app.getAlias());
400 web_app = context.newResource(web_app.getAlias());
401 }
402
403 if (LOG.isDebugEnabled())
404 LOG.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory());
405
406
407 if (web_app.exists() && !web_app.isDirectory() && !web_app.toString().startsWith("jar:"))
408 {
409
410 Resource jarWebApp = JarResource.newJarResource(web_app);
411 if (jarWebApp.exists() && jarWebApp.isDirectory())
412 web_app= jarWebApp;
413 }
414
415
416 if (web_app.exists() && (
417 (context.isCopyWebDir() && web_app.getFile() != null && web_app.getFile().isDirectory()) ||
418 (context.isExtractWAR() && web_app.getFile() != null && !web_app.getFile().isDirectory()) ||
419 (context.isExtractWAR() && web_app.getFile() == null) ||
420 !web_app.isDirectory())
421 )
422 {
423
424 File extractedWebAppDir = null;
425
426 if (war!=null)
427 {
428
429 File warfile=Resource.newResource(war).getFile();
430 if (warfile!=null && warfile.getName().toLowerCase().endsWith(".war"))
431 {
432 File sibling = new File(warfile.getParent(),warfile.getName().substring(0,warfile.getName().length()-4));
433 if (sibling.exists() && sibling.isDirectory() && sibling.canWrite())
434 extractedWebAppDir=sibling;
435 }
436 }
437
438 if (extractedWebAppDir==null)
439
440 extractedWebAppDir= new File(context.getTempDirectory(), "webapp");
441
442 if (web_app.getFile()!=null && web_app.getFile().isDirectory())
443 {
444
445 LOG.info("Copy " + web_app + " to " + extractedWebAppDir);
446 web_app.copyTo(extractedWebAppDir);
447 }
448 else
449 {
450 if (!extractedWebAppDir.exists())
451 {
452
453 extractedWebAppDir.mkdir();
454 LOG.info("Extract " + web_app + " to " + extractedWebAppDir);
455 Resource jar_web_app = JarResource.newJarResource(web_app);
456 jar_web_app.copyTo(extractedWebAppDir);
457 }
458 else
459 {
460
461 if (web_app.lastModified() > extractedWebAppDir.lastModified())
462 {
463 IO.delete(extractedWebAppDir);
464 extractedWebAppDir.mkdir();
465 LOG.info("Extract " + web_app + " to " + extractedWebAppDir);
466 Resource jar_web_app = JarResource.newJarResource(web_app);
467 jar_web_app.copyTo(extractedWebAppDir);
468 }
469 }
470 }
471 web_app = Resource.newResource(extractedWebAppDir.getCanonicalPath());
472 }
473
474
475 if (!web_app.exists() || !web_app.isDirectory())
476 {
477 LOG.warn("Web application not found " + war);
478 throw new java.io.FileNotFoundException(war);
479 }
480
481 context.setBaseResource(web_app);
482
483 if (LOG.isDebugEnabled())
484 LOG.debug("webapp=" + web_app);
485 }
486
487
488
489
490 if (context.isCopyWebInf())
491 {
492 Resource web_inf= web_app.addPath("WEB-INF/");
493
494 if (web_inf instanceof ResourceCollection ||
495 web_inf.exists() &&
496 web_inf.isDirectory() &&
497 (web_inf.getFile()==null || !web_inf.getFile().isDirectory()))
498 {
499 File extractedWebInfDir= new File(context.getTempDirectory(), "webinf");
500 if (extractedWebInfDir.exists())
501 IO.delete(extractedWebInfDir);
502 extractedWebInfDir.mkdir();
503 Resource web_inf_lib = web_inf.addPath("lib/");
504 File webInfDir=new File(extractedWebInfDir,"WEB-INF");
505 webInfDir.mkdir();
506
507 if (web_inf_lib.exists())
508 {
509 File webInfLibDir = new File(webInfDir, "lib");
510 if (webInfLibDir.exists())
511 IO.delete(webInfLibDir);
512 webInfLibDir.mkdir();
513
514 LOG.info("Copying WEB-INF/lib " + web_inf_lib + " to " + webInfLibDir);
515 web_inf_lib.copyTo(webInfLibDir);
516 }
517
518 Resource web_inf_classes = web_inf.addPath("classes/");
519 if (web_inf_classes.exists())
520 {
521 File webInfClassesDir = new File(webInfDir, "classes");
522 if (webInfClassesDir.exists())
523 IO.delete(webInfClassesDir);
524 webInfClassesDir.mkdir();
525 LOG.info("Copying WEB-INF/classes from "+web_inf_classes+" to "+webInfClassesDir.getAbsolutePath());
526 web_inf_classes.copyTo(webInfClassesDir);
527 }
528
529 web_inf=Resource.newResource(extractedWebInfDir.getCanonicalPath());
530
531 ResourceCollection rc = new ResourceCollection(web_inf,web_app);
532
533 if (LOG.isDebugEnabled())
534 LOG.debug("context.resourcebase = "+rc);
535
536 context.setBaseResource(rc);
537 }
538 }
539 }
540
541
542 public File findWorkDirectory (WebAppContext context) throws IOException
543 {
544 if (context.getBaseResource() != null)
545 {
546 Resource web_inf = context.getWebInf();
547 if (web_inf !=null && web_inf.exists())
548 {
549 return new File(web_inf.getFile(),"work");
550 }
551 }
552 return null;
553 }
554
555
556
557
558
559
560
561 public boolean isTempWorkDirectory (File tmpDir)
562 {
563 if (tmpDir == null)
564 return false;
565 if (tmpDir.getName().equalsIgnoreCase("work"))
566 return true;
567 File t = tmpDir.getParentFile();
568 if (t == null)
569 return false;
570 return (t.getName().equalsIgnoreCase("work"));
571 }
572
573
574
575
576
577
578
579
580
581
582
583 public static String getCanonicalNameForWebAppTmpDir (WebAppContext context)
584 {
585 StringBuffer canonicalName = new StringBuffer();
586 canonicalName.append("jetty-");
587
588
589 Server server=context.getServer();
590 if (server!=null)
591 {
592 Connector[] connectors = context.getServer().getConnectors();
593
594 if (connectors.length>0)
595 {
596
597 String host = (connectors==null||connectors[0]==null?"":connectors[0].getHost());
598 if (host == null)
599 host = "0.0.0.0";
600 canonicalName.append(host);
601
602
603 canonicalName.append("-");
604
605 int port = (connectors==null||connectors[0]==null?0:connectors[0].getLocalPort());
606
607
608 if (port < 0)
609 port = connectors[0].getPort();
610 canonicalName.append(port);
611 canonicalName.append("-");
612 }
613 }
614
615
616
617 try
618 {
619 Resource resource = context.getBaseResource();
620 if (resource == null)
621 {
622 if (context.getWar()==null || context.getWar().length()==0)
623 resource=context.newResource(context.getResourceBase());
624
625
626 resource = context.newResource(context.getWar());
627 }
628
629 String tmp = URIUtil.decodePath(resource.getURL().getPath());
630 if (tmp.endsWith("/"))
631 tmp = tmp.substring(0, tmp.length()-1);
632 if (tmp.endsWith("!"))
633 tmp = tmp.substring(0, tmp.length() -1);
634
635 int i = tmp.lastIndexOf("/");
636 canonicalName.append(tmp.substring(i+1, tmp.length()));
637 canonicalName.append("-");
638 }
639 catch (Exception e)
640 {
641 LOG.warn("Can't generate resourceBase as part of webapp tmp dir name", e);
642 }
643
644
645 String contextPath = context.getContextPath();
646 contextPath=contextPath.replace('/','_');
647 contextPath=contextPath.replace('\\','_');
648 canonicalName.append(contextPath);
649
650
651 canonicalName.append("-");
652 String[] vhosts = context.getVirtualHosts();
653 if (vhosts == null || vhosts.length <= 0)
654 canonicalName.append("any");
655 else
656 canonicalName.append(vhosts[0]);
657
658
659 for (int i=0;i<canonicalName.length();i++)
660 {
661 char c=canonicalName.charAt(i);
662 if (!Character.isJavaIdentifierPart(c) && "-.".indexOf(c)<0)
663 canonicalName.setCharAt(i,'.');
664 }
665
666 canonicalName.append("-");
667 return canonicalName.toString();
668 }
669
670
671
672
673
674
675
676 protected List<Resource> findJars (WebAppContext context)
677 throws Exception
678 {
679 List<Resource> jarResources = new ArrayList<Resource>();
680
681 Resource web_inf = context.getWebInf();
682 if (web_inf==null || !web_inf.exists())
683 return null;
684
685 Resource web_inf_lib = web_inf.addPath("/lib");
686
687
688 if (web_inf_lib.exists() && web_inf_lib.isDirectory())
689 {
690 String[] files=web_inf_lib.list();
691 for (int f=0;files!=null && f<files.length;f++)
692 {
693 try
694 {
695 Resource file = web_inf_lib.addPath(files[f]);
696 String fnlc = file.getName().toLowerCase();
697 int dot = fnlc.lastIndexOf('.');
698 String extension = (dot < 0 ? null : fnlc.substring(dot));
699 if (extension != null && (extension.equals(".jar") || extension.equals(".zip")))
700 {
701 jarResources.add(file);
702 }
703 }
704 catch (Exception ex)
705 {
706 LOG.warn(Log.EXCEPTION,ex);
707 }
708 }
709 }
710 return jarResources;
711 }
712 }