1// 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} 733