blob: 069559120c4abc831f27380660a285a791d981d6 [file] [log] [blame]
Jake Slack03928ae2014-05-13 18:41:56 -07001//
2// ========================================================================
3// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4// ------------------------------------------------------------------------
5// All rights reserved. This program and the accompanying materials
6// are made available under the terms of the Eclipse Public License v1.0
7// and Apache License v2.0 which accompanies this distribution.
8//
9// The Eclipse Public License is available at
10// http://www.eclipse.org/legal/epl-v10.html
11//
12// The Apache License v2.0 is available at
13// http://www.opensource.org/licenses/apache2.0.php
14//
15// You may elect to redistribute this code under either of these licenses.
16// ========================================================================
17//
18
19package org.eclipse.jetty.webapp;
20
21import java.io.File;
22import java.io.IOException;
23import java.net.URI;
24import java.net.URISyntaxException;
25import java.net.URL;
26import java.net.URLClassLoader;
27import java.util.ArrayList;
28import java.util.List;
29import java.util.Locale;
30import java.util.regex.Pattern;
31
32import org.eclipse.jetty.server.Connector;
33import org.eclipse.jetty.server.Server;
34import org.eclipse.jetty.util.IO;
35import org.eclipse.jetty.util.PatternMatcher;
36import org.eclipse.jetty.util.URIUtil;
37import org.eclipse.jetty.util.log.Log;
38import org.eclipse.jetty.util.log.Logger;
39import org.eclipse.jetty.util.resource.JarResource;
40import org.eclipse.jetty.util.resource.Resource;
41import org.eclipse.jetty.util.resource.ResourceCollection;
42
43public class WebInfConfiguration extends AbstractConfiguration
44{
45 private static final Logger LOG = Log.getLogger(WebInfConfiguration.class);
46
47 public static final String TEMPDIR_CONFIGURED = "org.eclipse.jetty.tmpdirConfigured";
48 public static final String CONTAINER_JAR_PATTERN = "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern";
49 public static final String WEBINF_JAR_PATTERN = "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern";
50
51 /**
52 * If set, to a list of URLs, these resources are added to the context
53 * resource base as a resource collection.
54 */
55 public static final String RESOURCE_URLS = "org.eclipse.jetty.resources";
56
57 protected Resource _preUnpackBaseResource;
58
59 @Override
60 public void preConfigure(final WebAppContext context) throws Exception
61 {
62 // Look for a work directory
63 File work = findWorkDirectory(context);
64 if (work != null)
65 makeTempDirectory(work, context, false);
66
67 //Make a temp directory for the webapp if one is not already set
68 resolveTempDirectory(context);
69
70 //Extract webapp if necessary
71 unpack (context);
72
73
74 //Apply an initial ordering to the jars which governs which will be scanned for META-INF
75 //info and annotations. The ordering is based on inclusion patterns.
76 String tmp = (String)context.getAttribute(WEBINF_JAR_PATTERN);
77 Pattern webInfPattern = (tmp==null?null:Pattern.compile(tmp));
78 tmp = (String)context.getAttribute(CONTAINER_JAR_PATTERN);
79 Pattern containerPattern = (tmp==null?null:Pattern.compile(tmp));
80
81 //Apply ordering to container jars - if no pattern is specified, we won't
82 //match any of the container jars
83 PatternMatcher containerJarNameMatcher = new PatternMatcher ()
84 {
85 public void matched(URI uri) throws Exception
86 {
87 context.getMetaData().addContainerJar(Resource.newResource(uri));
88 }
89 };
90 ClassLoader loader = null;
91 if (context.getClassLoader() != null)
92 loader = context.getClassLoader().getParent();
93
94 while (loader != null && (loader instanceof URLClassLoader))
95 {
96 URL[] urls = ((URLClassLoader)loader).getURLs();
97 if (urls != null)
98 {
99 URI[] containerUris = new URI[urls.length];
100 int i=0;
101 for (URL u : urls)
102 {
103 try
104 {
105 containerUris[i] = u.toURI();
106 }
107 catch (URISyntaxException e)
108 {
109 containerUris[i] = new URI(u.toString().replaceAll(" ", "%20"));
110 }
111 i++;
112 }
113 containerJarNameMatcher.match(containerPattern, containerUris, false);
114 }
115 loader = loader.getParent();
116 }
117
118 //Apply ordering to WEB-INF/lib jars
119 PatternMatcher webInfJarNameMatcher = new PatternMatcher ()
120 {
121 @Override
122 public void matched(URI uri) throws Exception
123 {
124 context.getMetaData().addWebInfJar(Resource.newResource(uri));
125 }
126 };
127 List<Resource> jars = findJars(context);
128
129 //Convert to uris for matching
130 URI[] uris = null;
131 if (jars != null)
132 {
133 uris = new URI[jars.size()];
134 int i=0;
135 for (Resource r: jars)
136 {
137 uris[i++] = r.getURI();
138 }
139 }
140 webInfJarNameMatcher.match(webInfPattern, uris, true); //null is inclusive, no pattern == all jars match
141 }
142
143
144 @Override
145 public void configure(WebAppContext context) throws Exception
146 {
147 //cannot configure if the context is already started
148 if (context.isStarted())
149 {
150 if (LOG.isDebugEnabled())
151 LOG.debug("Cannot configure webapp "+context+" after it is started");
152 return;
153 }
154
155 Resource web_inf = context.getWebInf();
156
157 // Add WEB-INF classes and lib classpaths
158 if (web_inf != null && web_inf.isDirectory() && context.getClassLoader() instanceof WebAppClassLoader)
159 {
160 // Look for classes directory
161 Resource classes= web_inf.addPath("classes/");
162 if (classes.exists())
163 ((WebAppClassLoader)context.getClassLoader()).addClassPath(classes);
164
165 // Look for jars
166 Resource lib= web_inf.addPath("lib/");
167 if (lib.exists() || lib.isDirectory())
168 ((WebAppClassLoader)context.getClassLoader()).addJars(lib);
169 }
170
171 // Look for extra resource
172 @SuppressWarnings("unchecked")
173 List<Resource> resources = (List<Resource>)context.getAttribute(RESOURCE_URLS);
174 if (resources!=null)
175 {
176 Resource[] collection=new Resource[resources.size()+1];
177 int i=0;
178 collection[i++]=context.getBaseResource();
179 for (Resource resource : resources)
180 collection[i++]=resource;
181 context.setBaseResource(new ResourceCollection(collection));
182 }
183 }
184
185 @Override
186 public void deconfigure(WebAppContext context) throws Exception
187 {
188 // delete temp directory if we had to create it or if it isn't called work
189 Boolean tmpdirConfigured = (Boolean)context.getAttribute(TEMPDIR_CONFIGURED);
190
191 if (context.getTempDirectory()!=null && (tmpdirConfigured == null || !tmpdirConfigured.booleanValue()) && !isTempWorkDirectory(context.getTempDirectory()))
192 {
193 IO.delete(context.getTempDirectory());
194 context.setTempDirectory(null);
195
196 //clear out the context attributes for the tmp dir only if we had to
197 //create the tmp dir
198 context.setAttribute(TEMPDIR_CONFIGURED, null);
199 context.setAttribute(WebAppContext.TEMPDIR, null);
200 }
201
202
203 //reset the base resource back to what it was before we did any unpacking of resources
204 context.setBaseResource(_preUnpackBaseResource);
205 }
206
207 /* ------------------------------------------------------------ */
208 /**
209 * @see org.eclipse.jetty.webapp.AbstractConfiguration#cloneConfigure(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.WebAppContext)
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 * Get a temporary directory in which to unpack the war etc etc.
228 * The algorithm for determining this is to check these alternatives
229 * in the order shown:
230 *
231 * <p>A. Try to use an explicit directory specifically for this webapp:</p>
232 * <ol>
233 * <li>
234 * Iff an explicit directory is set for this webapp, use it. Do NOT set
235 * delete on exit.
236 * </li>
237 * <li>
238 * Iff javax.servlet.context.tempdir context attribute is set for
239 * this webapp && exists && writeable, then use it. Do NOT set delete on exit.
240 * </li>
241 * </ol>
242 *
243 * <p>B. Create a directory based on global settings. The new directory
244 * will be called "Jetty_"+host+"_"+port+"__"+context+"_"+virtualhost
245 * Work out where to create this directory:
246 * <ol>
247 * <li>
248 * Iff $(jetty.home)/work exists create the directory there. Do NOT
249 * set delete on exit. Do NOT delete contents if dir already exists.
250 * </li>
251 * <li>
252 * Iff WEB-INF/work exists create the directory there. Do NOT set
253 * delete on exit. Do NOT delete contents if dir already exists.
254 * </li>
255 * <li>
256 * Else create dir in $(java.io.tmpdir). Set delete on exit. Delete
257 * contents if dir already exists.
258 * </li>
259 * </ol>
260 */
261 public void resolveTempDirectory (WebAppContext context)
262 {
263 //If a tmp directory is already set, we're done
264 File tmpDir = context.getTempDirectory();
265 if (tmpDir != null && tmpDir.isDirectory() && tmpDir.canWrite())
266 {
267 context.setAttribute(TEMPDIR_CONFIGURED, Boolean.TRUE);
268 return; // Already have a suitable tmp dir configured
269 }
270
271
272 // No temp directory configured, try to establish one.
273 // First we check the context specific, javax.servlet specified, temp directory attribute
274 File servletTmpDir = asFile(context.getAttribute(WebAppContext.TEMPDIR));
275 if (servletTmpDir != null && servletTmpDir.isDirectory() && servletTmpDir.canWrite())
276 {
277 // Use as tmpDir
278 tmpDir = servletTmpDir;
279 // Ensure Attribute has File object
280 context.setAttribute(WebAppContext.TEMPDIR,tmpDir);
281 // Set as TempDir in context.
282 context.setTempDirectory(tmpDir);
283 return;
284 }
285
286 try
287 {
288 // Put the tmp dir in the work directory if we had one
289 File work = new File(System.getProperty("jetty.home"),"work");
290 if (work.exists() && work.canWrite() && work.isDirectory())
291 {
292 makeTempDirectory(work, context, false); //make a tmp dir inside work, don't delete if it exists
293 }
294 else
295 {
296 File baseTemp = asFile(context.getAttribute(WebAppContext.BASETEMPDIR));
297 if (baseTemp != null && baseTemp.isDirectory() && baseTemp.canWrite())
298 {
299 // Use baseTemp directory (allow the funky Jetty_0_0_0_0.. subdirectory logic to kick in
300 makeTempDirectory(baseTemp,context,false);
301 }
302 else
303 {
304 makeTempDirectory(new File(System.getProperty("java.io.tmpdir")),context,true); //make a tmpdir, delete if it already exists
305 }
306 }
307 }
308 catch(Exception e)
309 {
310 tmpDir=null;
311 LOG.ignore(e);
312 }
313
314 //Third ... Something went wrong trying to make the tmp directory, just make
315 //a jvm managed tmp directory
316 if (context.getTempDirectory() == null)
317 {
318 try
319 {
320 // Last resort
321 tmpDir=File.createTempFile("JettyContext","");
322 if (tmpDir.exists())
323 IO.delete(tmpDir);
324 tmpDir.mkdir();
325 tmpDir.deleteOnExit();
326 context.setTempDirectory(tmpDir);
327 }
328 catch(IOException e)
329 {
330 tmpDir = null;
331 throw new IllegalStateException("Cannot create tmp dir in "+System.getProperty("java.io.tmpdir")+ " for context "+context,e);
332 }
333 }
334 }
335
336 /**
337 * Given an Object, return File reference for object.
338 * Typically used to convert anonymous Object from getAttribute() calls to a File object.
339 * @param fileattr the file attribute to analyze and return from (supports type File and type String, all others return null)
340 * @return the File object, null if null, or null if not a File or String
341 */
342 private File asFile(Object fileattr)
343 {
344 if (fileattr == null)
345 {
346 return null;
347 }
348 if (fileattr instanceof File)
349 {
350 return (File)fileattr;
351 }
352 if (fileattr instanceof String)
353 {
354 return new File((String)fileattr);
355 }
356 return null;
357 }
358
359
360
361 public void makeTempDirectory (File parent, WebAppContext context, boolean deleteExisting)
362 throws IOException
363 {
364 if (parent != null && parent.exists() && parent.canWrite() && parent.isDirectory())
365 {
366 String temp = getCanonicalNameForWebAppTmpDir(context);
367 File tmpDir = new File(parent,temp);
368
369 if (deleteExisting && tmpDir.exists())
370 {
371 if (!IO.delete(tmpDir))
372 {
373 if(LOG.isDebugEnabled())LOG.debug("Failed to delete temp dir "+tmpDir);
374 }
375
376 //If we can't delete the existing tmp dir, create a new one
377 if (tmpDir.exists())
378 {
379 String old=tmpDir.toString();
380 tmpDir=File.createTempFile(temp+"_","");
381 if (tmpDir.exists())
382 IO.delete(tmpDir);
383 LOG.warn("Can't reuse "+old+", using "+tmpDir);
384 }
385 }
386
387 if (!tmpDir.exists())
388 tmpDir.mkdir();
389
390 //If the parent is not a work directory
391 if (!isTempWorkDirectory(tmpDir))
392 {
393 tmpDir.deleteOnExit();
394 }
395
396 if(LOG.isDebugEnabled())
397 LOG.debug("Set temp dir "+tmpDir);
398 context.setTempDirectory(tmpDir);
399 }
400 }
401
402
403 public void unpack (WebAppContext context) throws IOException
404 {
405 Resource web_app = context.getBaseResource();
406 _preUnpackBaseResource = context.getBaseResource();
407
408 if (web_app == null)
409 {
410 String war = context.getWar();
411 if (war!=null && war.length()>0)
412 web_app = context.newResource(war);
413 else
414 web_app=context.getBaseResource();
415
416 // Accept aliases for WAR files
417 if (web_app.getAlias() != null)
418 {
419 LOG.debug(web_app + " anti-aliased to " + web_app.getAlias());
420 web_app = context.newResource(web_app.getAlias());
421 }
422
423 if (LOG.isDebugEnabled())
424 LOG.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory()+" file="+(web_app.getFile()));
425 // Is the WAR usable directly?
426 if (web_app.exists() && !web_app.isDirectory() && !web_app.toString().startsWith("jar:"))
427 {
428 // No - then lets see if it can be turned into a jar URL.
429 Resource jarWebApp = JarResource.newJarResource(web_app);
430 if (jarWebApp.exists() && jarWebApp.isDirectory())
431 web_app= jarWebApp;
432 }
433
434 // If we should extract or the URL is still not usable
435 if (web_app.exists() && (
436 (context.isCopyWebDir() && web_app.getFile() != null && web_app.getFile().isDirectory()) ||
437 (context.isExtractWAR() && web_app.getFile() != null && !web_app.getFile().isDirectory()) ||
438 (context.isExtractWAR() && web_app.getFile() == null) ||
439 !web_app.isDirectory())
440 )
441 {
442 // Look for sibling directory.
443 File extractedWebAppDir = null;
444
445 if (war!=null)
446 {
447 // look for a sibling like "foo/" to a "foo.war"
448 File warfile=Resource.newResource(war).getFile();
449 if (warfile!=null && warfile.getName().toLowerCase(Locale.ENGLISH).endsWith(".war"))
450 {
451 File sibling = new File(warfile.getParent(),warfile.getName().substring(0,warfile.getName().length()-4));
452 if (sibling.exists() && sibling.isDirectory() && sibling.canWrite())
453 extractedWebAppDir=sibling;
454 }
455 }
456
457 if (extractedWebAppDir==null)
458 // Then extract it if necessary to the temporary location
459 extractedWebAppDir= new File(context.getTempDirectory(), "webapp");
460
461 if (web_app.getFile()!=null && web_app.getFile().isDirectory())
462 {
463 // Copy directory
464 LOG.info("Copy " + web_app + " to " + extractedWebAppDir);
465 web_app.copyTo(extractedWebAppDir);
466 }
467 else
468 {
469 //Use a sentinel file that will exist only whilst the extraction is taking place.
470 //This will help us detect interrupted extractions.
471 File extractionLock = new File (context.getTempDirectory(), ".extract_lock");
472
473 if (!extractedWebAppDir.exists())
474 {
475 //it hasn't been extracted before so extract it
476 extractionLock.createNewFile();
477 extractedWebAppDir.mkdir();
478 LOG.info("Extract " + web_app + " to " + extractedWebAppDir);
479 Resource jar_web_app = JarResource.newJarResource(web_app);
480 jar_web_app.copyTo(extractedWebAppDir);
481 extractionLock.delete();
482 }
483 else
484 {
485 //only extract if the war file is newer, or a .extract_lock file is left behind meaning a possible partial extraction
486 if (web_app.lastModified() > extractedWebAppDir.lastModified() || extractionLock.exists())
487 {
488 extractionLock.createNewFile();
489 IO.delete(extractedWebAppDir);
490 extractedWebAppDir.mkdir();
491 LOG.info("Extract " + web_app + " to " + extractedWebAppDir);
492 Resource jar_web_app = JarResource.newJarResource(web_app);
493 jar_web_app.copyTo(extractedWebAppDir);
494 extractionLock.delete();
495 }
496 }
497 }
498 web_app = Resource.newResource(extractedWebAppDir.getCanonicalPath());
499 }
500
501 // Now do we have something usable?
502 if (!web_app.exists() || !web_app.isDirectory())
503 {
504 LOG.warn("Web application not found " + war);
505 throw new java.io.FileNotFoundException(war);
506 }
507
508 context.setBaseResource(web_app);
509
510 if (LOG.isDebugEnabled())
511 LOG.debug("webapp=" + web_app);
512 }
513
514
515 // Do we need to extract WEB-INF/lib?
516 if (context.isCopyWebInf() && !context.isCopyWebDir())
517 {
518 Resource web_inf= web_app.addPath("WEB-INF/");
519
520 File extractedWebInfDir= new File(context.getTempDirectory(), "webinf");
521 if (extractedWebInfDir.exists())
522 IO.delete(extractedWebInfDir);
523 extractedWebInfDir.mkdir();
524 Resource web_inf_lib = web_inf.addPath("lib/");
525 File webInfDir=new File(extractedWebInfDir,"WEB-INF");
526 webInfDir.mkdir();
527
528 if (web_inf_lib.exists())
529 {
530 File webInfLibDir = new File(webInfDir, "lib");
531 if (webInfLibDir.exists())
532 IO.delete(webInfLibDir);
533 webInfLibDir.mkdir();
534
535 LOG.info("Copying WEB-INF/lib " + web_inf_lib + " to " + webInfLibDir);
536 web_inf_lib.copyTo(webInfLibDir);
537 }
538
539 Resource web_inf_classes = web_inf.addPath("classes/");
540 if (web_inf_classes.exists())
541 {
542 File webInfClassesDir = new File(webInfDir, "classes");
543 if (webInfClassesDir.exists())
544 IO.delete(webInfClassesDir);
545 webInfClassesDir.mkdir();
546 LOG.info("Copying WEB-INF/classes from "+web_inf_classes+" to "+webInfClassesDir.getAbsolutePath());
547 web_inf_classes.copyTo(webInfClassesDir);
548 }
549
550 web_inf=Resource.newResource(extractedWebInfDir.getCanonicalPath());
551
552 ResourceCollection rc = new ResourceCollection(web_inf,web_app);
553
554 if (LOG.isDebugEnabled())
555 LOG.debug("context.resourcebase = "+rc);
556
557 context.setBaseResource(rc);
558 }
559 }
560
561
562 public File findWorkDirectory (WebAppContext context) throws IOException
563 {
564 if (context.getBaseResource() != null)
565 {
566 Resource web_inf = context.getWebInf();
567 if (web_inf !=null && web_inf.exists())
568 {
569 return new File(web_inf.getFile(),"work");
570 }
571 }
572 return null;
573 }
574
575
576 /**
577 * Check if the tmpDir itself is called "work", or if the tmpDir
578 * is in a directory called "work".
579 * @return true if File is a temporary or work directory
580 */
581 public boolean isTempWorkDirectory (File tmpDir)
582 {
583 if (tmpDir == null)
584 return false;
585 if (tmpDir.getName().equalsIgnoreCase("work"))
586 return true;
587 File t = tmpDir.getParentFile();
588 if (t == null)
589 return false;
590 return (t.getName().equalsIgnoreCase("work"));
591 }
592
593
594 /**
595 * Create a canonical name for a webapp temp directory.
596 * The form of the name is:
597 * <code>"Jetty_"+host+"_"+port+"__"+resourceBase+"_"+context+"_"+virtualhost+base36_hashcode_of_whole_string</code>
598 *
599 * host and port uniquely identify the server
600 * context and virtual host uniquely identify the webapp
601 * @return the canonical name for the webapp temp directory
602 */
603 public static String getCanonicalNameForWebAppTmpDir (WebAppContext context)
604 {
605 StringBuffer canonicalName = new StringBuffer();
606 canonicalName.append("jetty-");
607
608 //get the host and the port from the first connector
609 Server server=context.getServer();
610 if (server!=null)
611 {
612 Connector[] connectors = context.getServer().getConnectors();
613
614 if (connectors.length>0)
615 {
616 //Get the host
617 String host = (connectors==null||connectors[0]==null?"":connectors[0].getHost());
618 if (host == null)
619 host = "0.0.0.0";
620 canonicalName.append(host);
621
622 //Get the port
623 canonicalName.append("-");
624 //try getting the real port being listened on
625 int port = (connectors==null||connectors[0]==null?0:connectors[0].getLocalPort());
626 //if not available (eg no connectors or connector not started),
627 //try getting one that was configured.
628 if (port < 0)
629 port = connectors[0].getPort();
630 canonicalName.append(port);
631 canonicalName.append("-");
632 }
633 }
634
635
636 //Resource base
637 try
638 {
639 Resource resource = context.getBaseResource();
640 if (resource == null)
641 {
642 if (context.getWar()==null || context.getWar().length()==0)
643 resource=context.newResource(context.getResourceBase());
644
645 // Set dir or WAR
646 resource = context.newResource(context.getWar());
647 }
648
649 String tmp = URIUtil.decodePath(resource.getURL().getPath());
650 if (tmp.endsWith("/"))
651 tmp = tmp.substring(0, tmp.length()-1);
652 if (tmp.endsWith("!"))
653 tmp = tmp.substring(0, tmp.length() -1);
654 //get just the last part which is the filename
655 int i = tmp.lastIndexOf("/");
656 canonicalName.append(tmp.substring(i+1, tmp.length()));
657 canonicalName.append("-");
658 }
659 catch (Exception e)
660 {
661 LOG.warn("Can't generate resourceBase as part of webapp tmp dir name", e);
662 }
663
664 //Context name
665 String contextPath = context.getContextPath();
666 contextPath=contextPath.replace('/','_');
667 contextPath=contextPath.replace('\\','_');
668 canonicalName.append(contextPath);
669
670 //Virtual host (if there is one)
671 canonicalName.append("-");
672 String[] vhosts = context.getVirtualHosts();
673 if (vhosts == null || vhosts.length <= 0)
674 canonicalName.append("any");
675 else
676 canonicalName.append(vhosts[0]);
677
678 // sanitize
679 for (int i=0;i<canonicalName.length();i++)
680 {
681 char c=canonicalName.charAt(i);
682 if (!Character.isJavaIdentifierPart(c) && "-.".indexOf(c)<0)
683 canonicalName.setCharAt(i,'.');
684 }
685
686 canonicalName.append("-");
687 return canonicalName.toString();
688 }
689
690 /**
691 * Look for jars in WEB-INF/lib
692 * @param context
693 * @return the list of jar resources found within context
694 * @throws Exception
695 */
696 protected List<Resource> findJars (WebAppContext context)
697 throws Exception
698 {
699 List<Resource> jarResources = new ArrayList<Resource>();
700
701 Resource web_inf = context.getWebInf();
702 if (web_inf==null || !web_inf.exists())
703 return null;
704
705 Resource web_inf_lib = web_inf.addPath("/lib");
706
707
708 if (web_inf_lib.exists() && web_inf_lib.isDirectory())
709 {
710 String[] files=web_inf_lib.list();
711 for (int f=0;files!=null && f<files.length;f++)
712 {
713 try
714 {
715 Resource file = web_inf_lib.addPath(files[f]);
716 String fnlc = file.getName().toLowerCase(Locale.ENGLISH);
717 int dot = fnlc.lastIndexOf('.');
718 String extension = (dot < 0 ? null : fnlc.substring(dot));
719 if (extension != null && (extension.equals(".jar") || extension.equals(".zip")))
720 {
721 jarResources.add(file);
722 }
723 }
724 catch (Exception ex)
725 {
726 LOG.warn(Log.EXCEPTION,ex);
727 }
728 }
729 }
730 return jarResources;
731 }
732}