KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > loader > WebappClassLoader


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18
19 package org.apache.catalina.loader;
20
21 import java.io.ByteArrayInputStream JavaDoc;
22 import java.io.File JavaDoc;
23 import java.io.FileOutputStream JavaDoc;
24 import java.io.FilePermission JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.lang.reflect.Field JavaDoc;
28 import java.lang.reflect.Modifier JavaDoc;
29 import java.net.MalformedURLException JavaDoc;
30 import java.net.URL JavaDoc;
31 import java.net.URLClassLoader JavaDoc;
32 import java.security.AccessControlException JavaDoc;
33 import java.security.AccessController JavaDoc;
34 import java.security.CodeSource JavaDoc;
35 import java.security.Permission JavaDoc;
36 import java.security.PermissionCollection JavaDoc;
37 import java.security.Policy JavaDoc;
38 import java.security.PrivilegedAction JavaDoc;
39 import java.sql.Driver JavaDoc;
40 import java.sql.DriverManager JavaDoc;
41 import java.sql.SQLException JavaDoc;
42 import java.util.ArrayList JavaDoc;
43 import java.util.Enumeration JavaDoc;
44 import java.util.HashMap JavaDoc;
45 import java.util.Iterator JavaDoc;
46 import java.util.Vector JavaDoc;
47 import java.util.jar.Attributes JavaDoc;
48 import java.util.jar.JarEntry JavaDoc;
49 import java.util.jar.JarFile JavaDoc;
50 import java.util.jar.Manifest JavaDoc;
51 import java.util.jar.Attributes.Name;
52
53 import javax.naming.NameClassPair JavaDoc;
54 import javax.naming.NamingEnumeration JavaDoc;
55 import javax.naming.NamingException JavaDoc;
56 import javax.naming.directory.DirContext JavaDoc;
57
58 import org.apache.catalina.Lifecycle;
59 import org.apache.catalina.LifecycleException;
60 import org.apache.catalina.LifecycleListener;
61 import org.apache.catalina.util.StringManager;
62 import org.apache.naming.JndiPermission;
63 import org.apache.naming.resources.Resource;
64 import org.apache.naming.resources.ResourceAttributes;
65 import org.apache.tomcat.util.IntrospectionUtils;
66
67 /**
68  * Specialized web application class loader.
69  * <p>
70  * This class loader is a full reimplementation of the
71  * <code>URLClassLoader</code> from the JDK. It is desinged to be fully
72  * compatible with a normal <code>URLClassLoader</code>, although its internal
73  * behavior may be completely different.
74  * <p>
75  * <strong>IMPLEMENTATION NOTE</strong> - This class loader faithfully follows
76  * the delegation model recommended in the specification. The system class
77  * loader will be queried first, then the local repositories, and only then
78  * delegation to the parent class loader will occur. This allows the web
79  * application to override any shared class except the classes from J2SE.
80  * Special handling is provided from the JAXP XML parser interfaces, the JNDI
81  * interfaces, and the classes from the servlet API, which are never loaded
82  * from the webapp repository.
83  * <p>
84  * <strong>IMPLEMENTATION NOTE</strong> - Due to limitations in Jasper
85  * compilation technology, any repository which contains classes from
86  * the servlet API will be ignored by the class loader.
87  * <p>
88  * <strong>IMPLEMENTATION NOTE</strong> - The class loader generates source
89  * URLs which include the full JAR URL when a class is loaded from a JAR file,
90  * which allows setting security permission at the class level, even when a
91  * class is contained inside a JAR.
92  * <p>
93  * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in
94  * the order they are added via the initial constructor and/or any subsequent
95  * calls to <code>addRepository()</code> or <code>addJar()</code>.
96  * <p>
97  * <strong>IMPLEMENTATION NOTE</strong> - No check for sealing violations or
98  * security is made unless a security manager is present.
99  *
100  * @author Remy Maucherat
101  * @author Craig R. McClanahan
102  * @version $Revision: 471263 $ $Date: 2006-11-04 22:21:07 +0100 (sam., 04 nov. 2006) $
103  */

104 public class WebappClassLoader
105     extends URLClassLoader JavaDoc
106     implements Reloader, Lifecycle
107  {
108
109     protected static org.apache.commons.logging.Log log=
110         org.apache.commons.logging.LogFactory.getLog( WebappClassLoader.class );
111
112     protected class PrivilegedFindResource
113         implements PrivilegedAction JavaDoc {
114
115         protected File JavaDoc file;
116         protected String JavaDoc path;
117
118         PrivilegedFindResource(File JavaDoc file, String JavaDoc path) {
119             this.file = file;
120             this.path = path;
121         }
122
123         public Object JavaDoc run() {
124             return findResourceInternal(file, path);
125         }
126
127     }
128
129
130     // ------------------------------------------------------- Static Variables
131

132
133     /**
134      * The set of trigger classes that will cause a proposed repository not
135      * to be added if this class is visible to the class loader that loaded
136      * this factory class. Typically, trigger classes will be listed for
137      * components that have been integrated into the JDK for later versions,
138      * but where the corresponding JAR files are required to run on
139      * earlier versions.
140      */

141     protected static final String JavaDoc[] triggers = {
142         "javax.servlet.Servlet" // Servlet API
143
};
144
145
146     /**
147      * Set of package names which are not allowed to be loaded from a webapp
148      * class loader without delegating first.
149      */

150     protected static final String JavaDoc[] packageTriggers = {
151     };
152
153
154     /**
155      * The string manager for this package.
156      */

157     protected static final StringManager sm =
158         StringManager.getManager(Constants.Package);
159
160     
161     /**
162      * Use anti JAR locking code, which does URL rerouting when accessing
163      * resources.
164      */

165     boolean antiJARLocking = false;
166     
167
168     // ----------------------------------------------------------- Constructors
169

170
171     /**
172      * Construct a new ClassLoader with no defined repositories and no
173      * parent ClassLoader.
174      */

175     public WebappClassLoader() {
176
177         super(new URL JavaDoc[0]);
178         this.parent = getParent();
179         system = getSystemClassLoader();
180         securityManager = System.getSecurityManager();
181
182         if (securityManager != null) {
183             refreshPolicy();
184         }
185
186     }
187
188
189     /**
190      * Construct a new ClassLoader with no defined repositories and no
191      * parent ClassLoader.
192      */

193     public WebappClassLoader(ClassLoader JavaDoc parent) {
194
195         super(new URL JavaDoc[0], parent);
196                 
197         this.parent = getParent();
198         
199         system = getSystemClassLoader();
200         securityManager = System.getSecurityManager();
201
202         if (securityManager != null) {
203             refreshPolicy();
204         }
205     }
206
207
208     // ----------------------------------------------------- Instance Variables
209

210
211     /**
212      * Associated directory context giving access to the resources in this
213      * webapp.
214      */

215     protected DirContext JavaDoc resources = null;
216
217
218     /**
219      * The cache of ResourceEntry for classes and resources we have loaded,
220      * keyed by resource name.
221      */

222     protected HashMap JavaDoc resourceEntries = new HashMap JavaDoc();
223
224
225     /**
226      * The list of not found resources.
227      */

228     protected HashMap JavaDoc notFoundResources = new HashMap JavaDoc();
229
230
231     /**
232      * Should this class loader delegate to the parent class loader
233      * <strong>before</strong> searching its own repositories (i.e. the
234      * usual Java2 delegation model)? If set to <code>false</code>,
235      * this class loader will search its own repositories first, and
236      * delegate to the parent only if the class or resource is not
237      * found locally.
238      */

239     protected boolean delegate = false;
240
241
242     /**
243      * Last time a JAR was accessed.
244      */

245     protected long lastJarAccessed = 0L;
246
247
248     /**
249      * The list of local repositories, in the order they should be searched
250      * for locally loaded classes or resources.
251      */

252     protected String JavaDoc[] repositories = new String JavaDoc[0];
253
254
255      /**
256       * Repositories URLs, used to cache the result of getURLs.
257       */

258      protected URL JavaDoc[] repositoryURLs = null;
259
260
261     /**
262      * Repositories translated as path in the work directory (for Jasper
263      * originally), but which is used to generate fake URLs should getURLs be
264      * called.
265      */

266     protected File JavaDoc[] files = new File JavaDoc[0];
267
268
269     /**
270      * The list of JARs, in the order they should be searched
271      * for locally loaded classes or resources.
272      */

273     protected JarFile JavaDoc[] jarFiles = new JarFile JavaDoc[0];
274
275
276     /**
277      * The list of JARs, in the order they should be searched
278      * for locally loaded classes or resources.
279      */

280     protected File JavaDoc[] jarRealFiles = new File JavaDoc[0];
281
282
283     /**
284      * The path which will be monitored for added Jar files.
285      */

286     protected String JavaDoc jarPath = null;
287
288
289     /**
290      * The list of JARs, in the order they should be searched
291      * for locally loaded classes or resources.
292      */

293     protected String JavaDoc[] jarNames = new String JavaDoc[0];
294
295
296     /**
297      * The list of JARs last modified dates, in the order they should be
298      * searched for locally loaded classes or resources.
299      */

300     protected long[] lastModifiedDates = new long[0];
301
302
303     /**
304      * The list of resources which should be checked when checking for
305      * modifications.
306      */

307     protected String JavaDoc[] paths = new String JavaDoc[0];
308
309
310     /**
311      * A list of read File and Jndi Permission's required if this loader
312      * is for a web application context.
313      */

314     protected ArrayList JavaDoc permissionList = new ArrayList JavaDoc();
315
316
317     /**
318      * Path where resources loaded from JARs will be extracted.
319      */

320     protected File JavaDoc loaderDir = null;
321
322
323     /**
324      * The PermissionCollection for each CodeSource for a web
325      * application context.
326      */

327     protected HashMap JavaDoc loaderPC = new HashMap JavaDoc();
328
329
330     /**
331      * Instance of the SecurityManager installed.
332      */

333     protected SecurityManager JavaDoc securityManager = null;
334
335
336     /**
337      * The parent class loader.
338      */

339     protected ClassLoader JavaDoc parent = null;
340
341
342     /**
343      * The system class loader.
344      */

345     protected ClassLoader JavaDoc system = null;
346
347
348     /**
349      * Has this component been started?
350      */

351     protected boolean started = false;
352
353
354     /**
355      * Has external repositories.
356      */

357     protected boolean hasExternalRepositories = false;
358
359     /**
360      * need conversion for properties files
361      */

362     protected boolean needConvert = false;
363
364
365     /**
366      * All permission.
367      */

368     protected Permission JavaDoc allPermission = new java.security.AllPermission JavaDoc();
369
370
371     // ------------------------------------------------------------- Properties
372

373
374     /**
375      * Get associated resources.
376      */

377     public DirContext JavaDoc getResources() {
378
379         return this.resources;
380
381     }
382
383
384     /**
385      * Set associated resources.
386      */

387     public void setResources(DirContext JavaDoc resources) {
388
389         this.resources = resources;
390
391     }
392
393
394     /**
395      * Return the "delegate first" flag for this class loader.
396      */

397     public boolean getDelegate() {
398
399         return (this.delegate);
400
401     }
402
403
404     /**
405      * Set the "delegate first" flag for this class loader.
406      *
407      * @param delegate The new "delegate first" flag
408      */

409     public void setDelegate(boolean delegate) {
410
411         this.delegate = delegate;
412
413     }
414
415
416     /**
417      * @return Returns the antiJARLocking.
418      */

419     public boolean getAntiJARLocking() {
420         return antiJARLocking;
421     }
422     
423     
424     /**
425      * @param antiJARLocking The antiJARLocking to set.
426      */

427     public void setAntiJARLocking(boolean antiJARLocking) {
428         this.antiJARLocking = antiJARLocking;
429     }
430
431     
432     /**
433      * If there is a Java SecurityManager create a read FilePermission
434      * or JndiPermission for the file directory path.
435      *
436      * @param path file directory path
437      */

438     public void addPermission(String JavaDoc path) {
439         if (path == null) {
440             return;
441         }
442
443         if (securityManager != null) {
444             Permission JavaDoc permission = null;
445             if( path.startsWith("jndi:") || path.startsWith("jar:jndi:") ) {
446                 if (!path.endsWith("/")) {
447                     path = path + "/";
448                 }
449                 permission = new JndiPermission(path + "*");
450                 addPermission(permission);
451             } else {
452                 if (!path.endsWith(File.separator)) {
453                     permission = new FilePermission JavaDoc(path, "read");
454                     addPermission(permission);
455                     path = path + File.separator;
456                 }
457                 permission = new FilePermission JavaDoc(path + "-", "read");
458                 addPermission(permission);
459             }
460         }
461     }
462
463
464     /**
465      * If there is a Java SecurityManager create a read FilePermission
466      * or JndiPermission for URL.
467      *
468      * @param url URL for a file or directory on local system
469      */

470     public void addPermission(URL JavaDoc url) {
471         if (url != null) {
472             addPermission(url.toString());
473         }
474     }
475
476
477     /**
478      * If there is a Java SecurityManager create a Permission.
479      *
480      * @param permission The permission
481      */

482     public void addPermission(Permission JavaDoc permission) {
483         if ((securityManager != null) && (permission != null)) {
484             permissionList.add(permission);
485         }
486     }
487
488
489     /**
490      * Return the JAR path.
491      */

492     public String JavaDoc getJarPath() {
493
494         return this.jarPath;
495
496     }
497
498
499     /**
500      * Change the Jar path.
501      */

502     public void setJarPath(String JavaDoc jarPath) {
503
504         this.jarPath = jarPath;
505
506     }
507
508
509     /**
510      * Change the work directory.
511      */

512     public void setWorkDir(File JavaDoc workDir) {
513         this.loaderDir = new File JavaDoc(workDir, "loader");
514     }
515
516      /**
517       * Utility method for use in subclasses.
518       * Must be called before Lifecycle methods to have any effect.
519       */

520      protected void setParentClassLoader(ClassLoader JavaDoc pcl) {
521          parent = pcl;
522      }
523
524     // ------------------------------------------------------- Reloader Methods
525

526
527     /**
528      * Add a new repository to the set of places this ClassLoader can look for
529      * classes to be loaded.
530      *
531      * @param repository Name of a source of classes to be loaded, such as a
532      * directory pathname, a JAR file pathname, or a ZIP file pathname
533      *
534      * @exception IllegalArgumentException if the specified repository is
535      * invalid or does not exist
536      */

537     public void addRepository(String JavaDoc repository) {
538
539         // Ignore any of the standard repositories, as they are set up using
540
// either addJar or addRepository
541
if (repository.startsWith("/WEB-INF/lib")
542             || repository.startsWith("/WEB-INF/classes"))
543             return;
544
545         // Add this repository to our underlying class loader
546
try {
547             URL JavaDoc url = new URL JavaDoc(repository);
548             super.addURL(url);
549             hasExternalRepositories = true;
550             repositoryURLs = null;
551         } catch (MalformedURLException JavaDoc e) {
552             IllegalArgumentException JavaDoc iae = new IllegalArgumentException JavaDoc
553                 ("Invalid repository: " + repository);
554             iae.initCause(e);
555             throw iae;
556         }
557
558     }
559
560
561     /**
562      * Add a new repository to the set of places this ClassLoader can look for
563      * classes to be loaded.
564      *
565      * @param repository Name of a source of classes to be loaded, such as a
566      * directory pathname, a JAR file pathname, or a ZIP file pathname
567      *
568      * @exception IllegalArgumentException if the specified repository is
569      * invalid or does not exist
570      */

571     synchronized void addRepository(String JavaDoc repository, File JavaDoc file) {
572
573         // Note : There should be only one (of course), but I think we should
574
// keep this a bit generic
575

576         if (repository == null)
577             return;
578
579         if (log.isDebugEnabled())
580             log.debug("addRepository(" + repository + ")");
581
582         int i;
583
584         // Add this repository to our internal list
585
String JavaDoc[] result = new String JavaDoc[repositories.length + 1];
586         for (i = 0; i < repositories.length; i++) {
587             result[i] = repositories[i];
588         }
589         result[repositories.length] = repository;
590         repositories = result;
591
592         // Add the file to the list
593
File JavaDoc[] result2 = new File JavaDoc[files.length + 1];
594         for (i = 0; i < files.length; i++) {
595             result2[i] = files[i];
596         }
597         result2[files.length] = file;
598         files = result2;
599
600     }
601
602
603     synchronized void addJar(String JavaDoc jar, JarFile JavaDoc jarFile, File JavaDoc file)
604         throws IOException JavaDoc {
605
606         if (jar == null)
607             return;
608         if (jarFile == null)
609             return;
610         if (file == null)
611             return;
612
613         if (log.isDebugEnabled())
614             log.debug("addJar(" + jar + ")");
615
616         int i;
617
618         if ((jarPath != null) && (jar.startsWith(jarPath))) {
619
620             String JavaDoc jarName = jar.substring(jarPath.length());
621             while (jarName.startsWith("/"))
622                 jarName = jarName.substring(1);
623
624             String JavaDoc[] result = new String JavaDoc[jarNames.length + 1];
625             for (i = 0; i < jarNames.length; i++) {
626                 result[i] = jarNames[i];
627             }
628             result[jarNames.length] = jarName;
629             jarNames = result;
630
631         }
632
633         try {
634
635             // Register the JAR for tracking
636

637             long lastModified =
638                 ((ResourceAttributes) resources.getAttributes(jar))
639                 .getLastModified();
640
641             String JavaDoc[] result = new String JavaDoc[paths.length + 1];
642             for (i = 0; i < paths.length; i++) {
643                 result[i] = paths[i];
644             }
645             result[paths.length] = jar;
646             paths = result;
647
648             long[] result3 = new long[lastModifiedDates.length + 1];
649             for (i = 0; i < lastModifiedDates.length; i++) {
650                 result3[i] = lastModifiedDates[i];
651             }
652             result3[lastModifiedDates.length] = lastModified;
653             lastModifiedDates = result3;
654
655         } catch (NamingException JavaDoc e) {
656             // Ignore
657
}
658
659         // If the JAR currently contains invalid classes, don't actually use it
660
// for classloading
661
if (!validateJarFile(file))
662             return;
663
664         JarFile JavaDoc[] result2 = new JarFile JavaDoc[jarFiles.length + 1];
665         for (i = 0; i < jarFiles.length; i++) {
666             result2[i] = jarFiles[i];
667         }
668         result2[jarFiles.length] = jarFile;
669         jarFiles = result2;
670
671         // Add the file to the list
672
File JavaDoc[] result4 = new File JavaDoc[jarRealFiles.length + 1];
673         for (i = 0; i < jarRealFiles.length; i++) {
674             result4[i] = jarRealFiles[i];
675         }
676         result4[jarRealFiles.length] = file;
677         jarRealFiles = result4;
678     }
679
680
681     /**
682      * Return a String array of the current repositories for this class
683      * loader. If there are no repositories, a zero-length array is
684      * returned.For security reason, returns a clone of the Array (since
685      * String are immutable).
686      */

687     public String JavaDoc[] findRepositories() {
688
689         return ((String JavaDoc[])repositories.clone());
690
691     }
692
693
694     /**
695      * Have one or more classes or resources been modified so that a reload
696      * is appropriate?
697      */

698     public boolean modified() {
699
700         if (log.isDebugEnabled())
701             log.debug("modified()");
702
703         // Checking for modified loaded resources
704
int length = paths.length;
705
706         // A rare race condition can occur in the updates of the two arrays
707
// It's totally ok if the latest class added is not checked (it will
708
// be checked the next time
709
int length2 = lastModifiedDates.length;
710         if (length > length2)
711             length = length2;
712
713         for (int i = 0; i < length; i++) {
714             try {
715                 long lastModified =
716                     ((ResourceAttributes) resources.getAttributes(paths[i]))
717                     .getLastModified();
718                 if (lastModified != lastModifiedDates[i]) {
719                     if( log.isDebugEnabled() )
720                         log.debug(" Resource '" + paths[i]
721                                   + "' was modified; Date is now: "
722                                   + new java.util.Date JavaDoc(lastModified) + " Was: "
723                                   + new java.util.Date JavaDoc(lastModifiedDates[i]));
724                     return (true);
725                 }
726             } catch (NamingException JavaDoc e) {
727                 log.error(" Resource '" + paths[i] + "' is missing");
728                 return (true);
729             }
730         }
731
732         length = jarNames.length;
733
734         // Check if JARs have been added or removed
735
if (getJarPath() != null) {
736
737             try {
738                 NamingEnumeration JavaDoc enumeration = resources.listBindings(getJarPath());
739                 int i = 0;
740                 while (enumeration.hasMoreElements() && (i < length)) {
741                     NameClassPair JavaDoc ncPair = (NameClassPair JavaDoc) enumeration.nextElement();
742                     String JavaDoc name = ncPair.getName();
743                     // Ignore non JARs present in the lib folder
744
if (!name.endsWith(".jar"))
745                         continue;
746                     if (!name.equals(jarNames[i])) {
747                         // Missing JAR
748
log.info(" Additional JARs have been added : '"
749                                  + name + "'");
750                         return (true);
751                     }
752                     i++;
753                 }
754                 if (enumeration.hasMoreElements()) {
755                     while (enumeration.hasMoreElements()) {
756                         NameClassPair JavaDoc ncPair =
757                             (NameClassPair JavaDoc) enumeration.nextElement();
758                         String JavaDoc name = ncPair.getName();
759                         // Additional non-JAR files are allowed
760
if (name.endsWith(".jar")) {
761                             // There was more JARs
762
log.info(" Additional JARs have been added");
763                             return (true);
764                         }
765                     }
766                 } else if (i < jarNames.length) {
767                     // There was less JARs
768
log.info(" Additional JARs have been added");
769                     return (true);
770                 }
771             } catch (NamingException JavaDoc e) {
772                 if (log.isDebugEnabled())
773                     log.debug(" Failed tracking modifications of '"
774                         + getJarPath() + "'");
775             } catch (ClassCastException JavaDoc e) {
776                 log.error(" Failed tracking modifications of '"
777                           + getJarPath() + "' : " + e.getMessage());
778             }
779
780         }
781
782         // No classes have been modified
783
return (false);
784
785     }
786
787
788     /**
789      * Render a String representation of this object.
790      */

791     public String JavaDoc toString() {
792
793         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("WebappClassLoader\r\n");
794         sb.append(" delegate: ");
795         sb.append(delegate);
796         sb.append("\r\n");
797         sb.append(" repositories:\r\n");
798         if (repositories != null) {
799             for (int i = 0; i < repositories.length; i++) {
800                 sb.append(" ");
801                 sb.append(repositories[i]);
802                 sb.append("\r\n");
803             }
804         }
805         if (this.parent != null) {
806             sb.append("----------> Parent Classloader:\r\n");
807             sb.append(this.parent.toString());
808             sb.append("\r\n");
809         }
810         return (sb.toString());
811
812     }
813
814
815     // ---------------------------------------------------- ClassLoader Methods
816

817
818      /**
819       * Add the specified URL to the classloader.
820       */

821      protected void addURL(URL JavaDoc url) {
822          super.addURL(url);
823          hasExternalRepositories = true;
824          repositoryURLs = null;
825      }
826
827
828     /**
829      * Find the specified class in our local repositories, if possible. If
830      * not found, throw <code>ClassNotFoundException</code>.
831      *
832      * @param name Name of the class to be loaded
833      *
834      * @exception ClassNotFoundException if the class was not found
835      */

836     public Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
837
838         if (log.isDebugEnabled())
839             log.debug(" findClass(" + name + ")");
840
841         // Cannot load anything from local repositories if class loader is stopped
842
if (!started) {
843             throw new ClassNotFoundException JavaDoc(name);
844         }
845
846         // (1) Permission to define this class when using a SecurityManager
847
if (securityManager != null) {
848             int i = name.lastIndexOf('.');
849             if (i >= 0) {
850                 try {
851                     if (log.isTraceEnabled())
852                         log.trace(" securityManager.checkPackageDefinition");
853                     securityManager.checkPackageDefinition(name.substring(0,i));
854                 } catch (Exception JavaDoc se) {
855                     if (log.isTraceEnabled())
856                         log.trace(" -->Exception-->ClassNotFoundException", se);
857                     throw new ClassNotFoundException JavaDoc(name, se);
858                 }
859             }
860         }
861
862         // Ask our superclass to locate this class, if possible
863
// (throws ClassNotFoundException if it is not found)
864
Class JavaDoc clazz = null;
865         try {
866             if (log.isTraceEnabled())
867                 log.trace(" findClassInternal(" + name + ")");
868             try {
869                 clazz = findClassInternal(name);
870             } catch(ClassNotFoundException JavaDoc cnfe) {
871                 if (!hasExternalRepositories) {
872                     throw cnfe;
873                 }
874             } catch(AccessControlException JavaDoc ace) {
875                 throw new ClassNotFoundException JavaDoc(name, ace);
876             } catch (RuntimeException JavaDoc e) {
877                 if (log.isTraceEnabled())
878                     log.trace(" -->RuntimeException Rethrown", e);
879                 throw e;
880             }
881             if ((clazz == null) && hasExternalRepositories) {
882                 try {
883                     clazz = super.findClass(name);
884                 } catch(AccessControlException JavaDoc ace) {
885                     throw new ClassNotFoundException JavaDoc(name, ace);
886                 } catch (RuntimeException JavaDoc e) {
887                     if (log.isTraceEnabled())
888                         log.trace(" -->RuntimeException Rethrown", e);
889                     throw e;
890                 }
891             }
892             if (clazz == null) {
893                 if (log.isDebugEnabled())
894                     log.debug(" --> Returning ClassNotFoundException");
895                 throw new ClassNotFoundException JavaDoc(name);
896             }
897         } catch (ClassNotFoundException JavaDoc e) {
898             if (log.isTraceEnabled())
899                 log.trace(" --> Passing on ClassNotFoundException");
900             throw e;
901         }
902
903         // Return the class we have located
904
if (log.isTraceEnabled())
905             log.debug(" Returning class " + clazz);
906         if ((log.isTraceEnabled()) && (clazz != null))
907             log.debug(" Loaded by " + clazz.getClassLoader());
908         return (clazz);
909
910     }
911
912
913     /**
914      * Find the specified resource in our local repository, and return a
915      * <code>URL</code> refering to it, or <code>null</code> if this resource
916      * cannot be found.
917      *
918      * @param name Name of the resource to be found
919      */

920     public URL JavaDoc findResource(final String JavaDoc name) {
921
922         if (log.isDebugEnabled())
923             log.debug(" findResource(" + name + ")");
924
925         URL JavaDoc url = null;
926
927         ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
928         if (entry == null) {
929             entry = findResourceInternal(name, name);
930         }
931         if (entry != null) {
932             url = entry.source;
933         }
934
935         if ((url == null) && hasExternalRepositories)
936             url = super.findResource(name);
937
938         if (log.isDebugEnabled()) {
939             if (url != null)
940                 log.debug(" --> Returning '" + url.toString() + "'");
941             else
942                 log.debug(" --> Resource not found, returning null");
943         }
944         return (url);
945
946     }
947
948
949     /**
950      * Return an enumeration of <code>URLs</code> representing all of the
951      * resources with the given name. If no resources with this name are
952      * found, return an empty enumeration.
953      *
954      * @param name Name of the resources to be found
955      *
956      * @exception IOException if an input/output error occurs
957      */

958     public Enumeration JavaDoc findResources(String JavaDoc name) throws IOException JavaDoc {
959
960         if (log.isDebugEnabled())
961             log.debug(" findResources(" + name + ")");
962
963         Vector JavaDoc result = new Vector JavaDoc();
964
965         int jarFilesLength = jarFiles.length;
966         int repositoriesLength = repositories.length;
967
968         int i;
969
970         // Looking at the repositories
971
for (i = 0; i < repositoriesLength; i++) {
972             try {
973                 String JavaDoc fullPath = repositories[i] + name;
974                 resources.lookup(fullPath);
975                 // Note : Not getting an exception here means the resource was
976
// found
977
try {
978                     result.addElement(getURI(new File JavaDoc(files[i], name)));
979                 } catch (MalformedURLException JavaDoc e) {
980                     // Ignore
981
}
982             } catch (NamingException JavaDoc e) {
983             }
984         }
985
986         // Looking at the JAR files
987
synchronized (jarFiles) {
988             if (openJARs()) {
989                 for (i = 0; i < jarFilesLength; i++) {
990                     JarEntry JavaDoc jarEntry = jarFiles[i].getJarEntry(name);
991                     if (jarEntry != null) {
992                         try {
993                             String JavaDoc jarFakeUrl = getURI(jarRealFiles[i]).toString();
994                             jarFakeUrl = "jar:" + jarFakeUrl + "!/" + name;
995                             result.addElement(new URL JavaDoc(jarFakeUrl));
996                         } catch (MalformedURLException JavaDoc e) {
997                             // Ignore
998
}
999                     }
1000                }
1001            }
1002        }
1003
1004        // Adding the results of a call to the superclass
1005
if (hasExternalRepositories) {
1006
1007            Enumeration JavaDoc otherResourcePaths = super.findResources(name);
1008
1009            while (otherResourcePaths.hasMoreElements()) {
1010                result.addElement(otherResourcePaths.nextElement());
1011            }
1012
1013        }
1014
1015        return result.elements();
1016
1017    }
1018
1019
1020    /**
1021     * Find the resource with the given name. A resource is some data
1022     * (images, audio, text, etc.) that can be accessed by class code in a
1023     * way that is independent of the location of the code. The name of a
1024     * resource is a "/"-separated path name that identifies the resource.
1025     * If the resource cannot be found, return <code>null</code>.
1026     * <p>
1027     * This method searches according to the following algorithm, returning
1028     * as soon as it finds the appropriate URL. If the resource cannot be
1029     * found, returns <code>null</code>.
1030     * <ul>
1031     * <li>If the <code>delegate</code> property is set to <code>true</code>,
1032     * call the <code>getResource()</code> method of the parent class
1033     * loader, if any.</li>
1034     * <li>Call <code>findResource()</code> to find this resource in our
1035     * locally defined repositories.</li>
1036     * <li>Call the <code>getResource()</code> method of the parent class
1037     * loader, if any.</li>
1038     * </ul>
1039     *
1040     * @param name Name of the resource to return a URL for
1041     */

1042    public URL JavaDoc getResource(String JavaDoc name) {
1043
1044        if (log.isDebugEnabled())
1045            log.debug("getResource(" + name + ")");
1046        URL JavaDoc url = null;
1047
1048        // (1) Delegate to parent if requested
1049
if (delegate) {
1050            if (log.isDebugEnabled())
1051                log.debug(" Delegating to parent classloader " + parent);
1052            ClassLoader JavaDoc loader = parent;
1053            if (loader == null)
1054                loader = system;
1055            url = loader.getResource(name);
1056            if (url != null) {
1057                if (log.isDebugEnabled())
1058                    log.debug(" --> Returning '" + url.toString() + "'");
1059                return (url);
1060            }
1061        }
1062
1063        // (2) Search local repositories
1064
url = findResource(name);
1065        if (url != null) {
1066            // Locating the repository for special handling in the case
1067
// of a JAR
1068
if (antiJARLocking) {
1069                ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
1070                try {
1071                    String JavaDoc repository = entry.codeBase.toString();
1072                    if ((repository.endsWith(".jar"))
1073                            && (!(name.endsWith(".class")))) {
1074                        // Copy binary content to the work directory if not present
1075
File JavaDoc resourceFile = new File JavaDoc(loaderDir, name);
1076                        url = getURI(resourceFile);
1077                    }
1078                } catch (Exception JavaDoc e) {
1079                    // Ignore
1080
}
1081            }
1082            if (log.isDebugEnabled())
1083                log.debug(" --> Returning '" + url.toString() + "'");
1084            return (url);
1085        }
1086
1087        // (3) Delegate to parent unconditionally if not already attempted
1088
if( !delegate ) {
1089            ClassLoader JavaDoc loader = parent;
1090            if (loader == null)
1091                loader = system;
1092            url = loader.getResource(name);
1093            if (url != null) {
1094                if (log.isDebugEnabled())
1095                    log.debug(" --> Returning '" + url.toString() + "'");
1096                return (url);
1097            }
1098        }
1099
1100        // (4) Resource was not found
1101
if (log.isDebugEnabled())
1102            log.debug(" --> Resource not found, returning null");
1103        return (null);
1104
1105    }
1106
1107
1108    /**
1109     * Find the resource with the given name, and return an input stream
1110     * that can be used for reading it. The search order is as described
1111     * for <code>getResource()</code>, after checking to see if the resource
1112     * data has been previously cached. If the resource cannot be found,
1113     * return <code>null</code>.
1114     *
1115     * @param name Name of the resource to return an input stream for
1116     */

1117    public InputStream JavaDoc getResourceAsStream(String JavaDoc name) {
1118
1119        if (log.isDebugEnabled())
1120            log.debug("getResourceAsStream(" + name + ")");
1121        InputStream JavaDoc stream = null;
1122
1123        // (0) Check for a cached copy of this resource
1124
stream = findLoadedResource(name);
1125        if (stream != null) {
1126            if (log.isDebugEnabled())
1127                log.debug(" --> Returning stream from cache");
1128            return (stream);
1129        }
1130
1131        // (1) Delegate to parent if requested
1132
if (delegate) {
1133            if (log.isDebugEnabled())
1134                log.debug(" Delegating to parent classloader " + parent);
1135            ClassLoader JavaDoc loader = parent;
1136            if (loader == null)
1137                loader = system;
1138            stream = loader.getResourceAsStream(name);
1139            if (stream != null) {
1140                // FIXME - cache???
1141
if (log.isDebugEnabled())
1142                    log.debug(" --> Returning stream from parent");
1143                return (stream);
1144            }
1145        }
1146
1147        // (2) Search local repositories
1148
if (log.isDebugEnabled())
1149            log.debug(" Searching local repositories");
1150        URL JavaDoc url = findResource(name);
1151        if (url != null) {
1152            // FIXME - cache???
1153
if (log.isDebugEnabled())
1154                log.debug(" --> Returning stream from local");
1155            stream = findLoadedResource(name);
1156            try {
1157                if (hasExternalRepositories && (stream == null))
1158                    stream = url.openStream();
1159            } catch (IOException JavaDoc e) {
1160                ; // Ignore
1161
}
1162            if (stream != null)
1163                return (stream);
1164        }
1165
1166        // (3) Delegate to parent unconditionally
1167
if (!delegate) {
1168            if (log.isDebugEnabled())
1169                log.debug(" Delegating to parent classloader unconditionally " + parent);
1170            ClassLoader JavaDoc loader = parent;
1171            if (loader == null)
1172                loader = system;
1173            stream = loader.getResourceAsStream(name);
1174            if (stream != null) {
1175                // FIXME - cache???
1176
if (log.isDebugEnabled())
1177                    log.debug(" --> Returning stream from parent");
1178                return (stream);
1179            }
1180        }
1181
1182        // (4) Resource was not found
1183
if (log.isDebugEnabled())
1184            log.debug(" --> Resource not found, returning null");
1185        return (null);
1186
1187    }
1188
1189
1190    /**
1191     * Load the class with the specified name. This method searches for
1192     * classes in the same manner as <code>loadClass(String, boolean)</code>
1193     * with <code>false</code> as the second argument.
1194     *
1195     * @param name Name of the class to be loaded
1196     *
1197     * @exception ClassNotFoundException if the class was not found
1198     */

1199    public Class JavaDoc loadClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
1200
1201        return (loadClass(name, false));
1202
1203    }
1204
1205
1206    /**
1207     * Load the class with the specified name, searching using the following
1208     * algorithm until it finds and returns the class. If the class cannot
1209     * be found, returns <code>ClassNotFoundException</code>.
1210     * <ul>
1211     * <li>Call <code>findLoadedClass(String)</code> to check if the
1212     * class has already been loaded. If it has, the same
1213     * <code>Class</code> object is returned.</li>
1214     * <li>If the <code>delegate</code> property is set to <code>true</code>,
1215     * call the <code>loadClass()</code> method of the parent class
1216     * loader, if any.</li>
1217     * <li>Call <code>findClass()</code> to find this class in our locally
1218     * defined repositories.</li>
1219     * <li>Call the <code>loadClass()</code> method of our parent
1220     * class loader, if any.</li>
1221     * </ul>
1222     * If the class was found using the above steps, and the
1223     * <code>resolve</code> flag is <code>true</code>, this method will then
1224     * call <code>resolveClass(Class)</code> on the resulting Class object.
1225     *
1226     * @param name Name of the class to be loaded
1227     * @param resolve If <code>true</code> then resolve the class
1228     *
1229     * @exception ClassNotFoundException if the class was not found
1230     */

1231    public Class JavaDoc loadClass(String JavaDoc name, boolean resolve)
1232        throws ClassNotFoundException JavaDoc {
1233
1234        if (log.isDebugEnabled())
1235            log.debug("loadClass(" + name + ", " + resolve + ")");
1236        Class JavaDoc clazz = null;
1237
1238        // Log access to stopped classloader
1239
if (!started) {
1240            try {
1241                throw new IllegalStateException JavaDoc();
1242            } catch (IllegalStateException JavaDoc e) {
1243                log.info(sm.getString("webappClassLoader.stopped", name), e);
1244            }
1245        }
1246
1247        // (0) Check our previously loaded local class cache
1248
clazz = findLoadedClass0(name);
1249        if (clazz != null) {
1250            if (log.isDebugEnabled())
1251                log.debug(" Returning class from cache");
1252            if (resolve)
1253                resolveClass(clazz);
1254            return (clazz);
1255        }
1256
1257        // (0.1) Check our previously loaded class cache
1258
clazz = findLoadedClass(name);
1259        if (clazz != null) {
1260            if (log.isDebugEnabled())
1261                log.debug(" Returning class from cache");
1262            if (resolve)
1263                resolveClass(clazz);
1264            return (clazz);
1265        }
1266
1267        // (0.2) Try loading the class with the system class loader, to prevent
1268
// the webapp from overriding J2SE classes
1269
try {
1270            clazz = system.loadClass(name);
1271            if (clazz != null) {
1272                if (resolve)
1273                    resolveClass(clazz);
1274                return (clazz);
1275            }
1276        } catch (ClassNotFoundException JavaDoc e) {
1277            // Ignore
1278
}
1279
1280        // (0.5) Permission to access this class when using a SecurityManager
1281
if (securityManager != null) {
1282            int i = name.lastIndexOf('.');
1283            if (i >= 0) {
1284                try {
1285                    securityManager.checkPackageAccess(name.substring(0,i));
1286                } catch (SecurityException JavaDoc se) {
1287                    String JavaDoc error = "Security Violation, attempt to use " +
1288                        "Restricted Class: " + name;
1289                    log.info(error, se);
1290                    throw new ClassNotFoundException JavaDoc(error, se);
1291                }
1292            }
1293        }
1294
1295        boolean delegateLoad = delegate || filter(name);
1296
1297        // (1) Delegate to our parent if requested
1298
if (delegateLoad) {
1299            if (log.isDebugEnabled())
1300                log.debug(" Delegating to parent classloader1 " + parent);
1301            ClassLoader JavaDoc loader = parent;
1302            if (loader == null)
1303                loader = system;
1304            try {
1305                clazz = loader.loadClass(name);
1306                if (clazz != null) {
1307                    if (log.isDebugEnabled())
1308                        log.debug(" Loading class from parent");
1309                    if (resolve)
1310                        resolveClass(clazz);
1311                    return (clazz);
1312                }
1313            } catch (ClassNotFoundException JavaDoc e) {
1314                ;
1315            }
1316        }
1317
1318        // (2) Search local repositories
1319
if (log.isDebugEnabled())
1320            log.debug(" Searching local repositories");
1321        try {
1322            clazz = findClass(name);
1323            if (clazz != null) {
1324                if (log.isDebugEnabled())
1325                    log.debug(" Loading class from local repository");
1326                if (resolve)
1327                    resolveClass(clazz);
1328                return (clazz);
1329            }
1330        } catch (ClassNotFoundException JavaDoc e) {
1331            ;
1332        }
1333
1334        // (3) Delegate to parent unconditionally
1335
if (!delegateLoad) {
1336            if (log.isDebugEnabled())
1337                log.debug(" Delegating to parent classloader at end: " + parent);
1338            ClassLoader JavaDoc loader = parent;
1339            if (loader == null)
1340                loader = system;
1341            try {
1342                clazz = loader.loadClass(name);
1343                if (clazz != null) {
1344                    if (log.isDebugEnabled())
1345                        log.debug(" Loading class from parent");
1346                    if (resolve)
1347                        resolveClass(clazz);
1348                    return (clazz);
1349                }
1350            } catch (ClassNotFoundException JavaDoc e) {
1351                ;
1352            }
1353        }
1354
1355        throw new ClassNotFoundException JavaDoc(name);
1356    }
1357
1358
1359    /**
1360     * Get the Permissions for a CodeSource. If this instance
1361     * of WebappClassLoader is for a web application context,
1362     * add read FilePermission or JndiPermissions for the base
1363     * directory (if unpacked),
1364     * the context URL, and jar file resources.
1365     *
1366     * @param codeSource where the code was loaded from
1367     * @return PermissionCollection for CodeSource
1368     */

1369    protected PermissionCollection JavaDoc getPermissions(CodeSource JavaDoc codeSource) {
1370
1371        String JavaDoc codeUrl = codeSource.getLocation().toString();
1372        PermissionCollection JavaDoc pc;
1373        if ((pc = (PermissionCollection JavaDoc)loaderPC.get(codeUrl)) == null) {
1374            pc = super.getPermissions(codeSource);
1375            if (pc != null) {
1376                Iterator JavaDoc perms = permissionList.iterator();
1377                while (perms.hasNext()) {
1378                    Permission JavaDoc p = (Permission JavaDoc)perms.next();
1379                    pc.add(p);
1380                }
1381                loaderPC.put(codeUrl,pc);
1382            }
1383        }
1384        return (pc);
1385
1386    }
1387
1388
1389    /**
1390     * Returns the search path of URLs for loading classes and resources.
1391     * This includes the original list of URLs specified to the constructor,
1392     * along with any URLs subsequently appended by the addURL() method.
1393     * @return the search path of URLs for loading classes and resources.
1394     */

1395    public URL JavaDoc[] getURLs() {
1396
1397        if (repositoryURLs != null) {
1398            return repositoryURLs;
1399        }
1400
1401        URL JavaDoc[] external = super.getURLs();
1402
1403        int filesLength = files.length;
1404        int jarFilesLength = jarRealFiles.length;
1405        int length = filesLength + jarFilesLength + external.length;
1406        int i;
1407
1408        try {
1409
1410            URL JavaDoc[] urls = new URL JavaDoc[length];
1411            for (i = 0; i < length; i++) {
1412                if (i < filesLength) {
1413                    urls[i] = getURL(files[i], true);
1414                } else if (i < filesLength + jarFilesLength) {
1415                    urls[i] = getURL(jarRealFiles[i - filesLength], true);
1416                } else {
1417                    urls[i] = external[i - filesLength - jarFilesLength];
1418                }
1419            }
1420
1421            repositoryURLs = urls;
1422
1423        } catch (MalformedURLException JavaDoc e) {
1424            repositoryURLs = new URL JavaDoc[0];
1425        }
1426
1427        return repositoryURLs;
1428
1429    }
1430
1431
1432    // ------------------------------------------------------ Lifecycle Methods
1433

1434
1435    /**
1436     * Add a lifecycle event listener to this component.
1437     *
1438     * @param listener The listener to add
1439     */

1440    public void addLifecycleListener(LifecycleListener listener) {
1441    }
1442
1443
1444    /**
1445     * Get the lifecycle listeners associated with this lifecycle. If this
1446     * Lifecycle has no listeners registered, a zero-length array is returned.
1447     */

1448    public LifecycleListener[] findLifecycleListeners() {
1449        return new LifecycleListener[0];
1450    }
1451
1452
1453    /**
1454     * Remove a lifecycle event listener from this component.
1455     *
1456     * @param listener The listener to remove
1457     */

1458    public void removeLifecycleListener(LifecycleListener listener) {
1459    }
1460
1461
1462    /**
1463     * Start the class loader.
1464     *
1465     * @exception LifecycleException if a lifecycle error occurs
1466     */

1467    public void start() throws LifecycleException {
1468
1469        started = true;
1470        String JavaDoc encoding = null;
1471        try {
1472            encoding = System.getProperty("file.encoding");
1473        } catch (Exception JavaDoc e) {
1474            return;
1475        }
1476        if (encoding.indexOf("EBCDIC")!=-1) {
1477            needConvert = true;
1478        }
1479
1480    }
1481
1482
1483    /**
1484     * Stop the class loader.
1485     *
1486     * @exception LifecycleException if a lifecycle error occurs
1487     */

1488    public void stop() throws LifecycleException {
1489
1490        // Clearing references should be done before setting started to
1491
// false, due to possible side effects
1492
clearReferences();
1493
1494        started = false;
1495
1496        int length = files.length;
1497        for (int i = 0; i < length; i++) {
1498            files[i] = null;
1499        }
1500
1501        length = jarFiles.length;
1502        for (int i = 0; i < length; i++) {
1503            try {
1504                if (jarFiles[i] != null) {
1505                    jarFiles[i].close();
1506                }
1507            } catch (IOException JavaDoc e) {
1508                // Ignore
1509
}
1510            jarFiles[i] = null;
1511        }
1512
1513        notFoundResources.clear();
1514        resourceEntries.clear();
1515        resources = null;
1516        repositories = null;
1517        repositoryURLs = null;
1518        files = null;
1519        jarFiles = null;
1520        jarRealFiles = null;
1521        jarPath = null;
1522        jarNames = null;
1523        lastModifiedDates = null;
1524        paths = null;
1525        hasExternalRepositories = false;
1526        parent = null;
1527
1528        permissionList.clear();
1529        loaderPC.clear();
1530
1531        if (loaderDir != null) {
1532            deleteDir(loaderDir);
1533        }
1534
1535    }
1536
1537
1538    /**
1539     * Used to periodically signal to the classloader to release
1540     * JAR resources.
1541     */

1542    public void closeJARs(boolean force) {
1543        if (jarFiles.length > 0) {
1544                synchronized (jarFiles) {
1545                    if (force || (System.currentTimeMillis()
1546                                  > (lastJarAccessed + 90000))) {
1547                        for (int i = 0; i < jarFiles.length; i++) {
1548                            try {
1549                                if (jarFiles[i] != null) {
1550                                    jarFiles[i].close();
1551                                    jarFiles[i] = null;
1552                                }
1553                            } catch (IOException JavaDoc e) {
1554                                if (log.isDebugEnabled()) {
1555                                    log.debug("Failed to close JAR", e);
1556                                }
1557                            }
1558                        }
1559                    }
1560                }
1561        }
1562    }
1563
1564
1565    // ------------------------------------------------------ Protected Methods
1566

1567    
1568    /**
1569     * Clear references.
1570     */

1571    protected void clearReferences() {
1572
1573        // Unregister any JDBC drivers loaded by this classloader
1574
Enumeration JavaDoc drivers = DriverManager.getDrivers();
1575        while (drivers.hasMoreElements()) {
1576            Driver JavaDoc driver = (Driver JavaDoc) drivers.nextElement();
1577            if (driver.getClass().getClassLoader() == this) {
1578                try {
1579                    DriverManager.deregisterDriver(driver);
1580                } catch (SQLException JavaDoc e) {
1581                    log.warn("SQL driver deregistration failed", e);
1582                }
1583            }
1584        }
1585        
1586        // Null out any static or final fields from loaded classes,
1587
// as a workaround for apparent garbage collection bugs
1588
Iterator JavaDoc loadedClasses = ((HashMap JavaDoc) resourceEntries.clone()).values().iterator();
1589        while (loadedClasses.hasNext()) {
1590            ResourceEntry entry = (ResourceEntry) loadedClasses.next();
1591            if (entry.loadedClass != null) {
1592                Class JavaDoc clazz = entry.loadedClass;
1593                try {
1594                    Field JavaDoc[] fields = clazz.getDeclaredFields();
1595                    for (int i = 0; i < fields.length; i++) {
1596                        Field JavaDoc field = fields[i];
1597                        int mods = field.getModifiers();
1598                        if (field.getType().isPrimitive()
1599                                || (field.getName().indexOf("$") != -1)) {
1600                            continue;
1601                        }
1602                        if (Modifier.isStatic(mods)) {
1603                            try {
1604                                field.setAccessible(true);
1605                                if (Modifier.isFinal(mods)) {
1606                                    if (!((field.getType().getName().startsWith("java."))
1607                                            || (field.getType().getName().startsWith("javax.")))) {
1608                                        nullInstance(field.get(null));
1609                                    }
1610                                } else {
1611                                    field.set(null, null);
1612                                    if (log.isDebugEnabled()) {
1613                                        log.debug("Set field " + field.getName()
1614                                                + " to null in class " + clazz.getName());
1615                                    }
1616                                }
1617                            } catch (Throwable JavaDoc t) {
1618                                if (log.isDebugEnabled()) {
1619                                    log.debug("Could not set field " + field.getName()
1620                                            + " to null in class " + clazz.getName(), t);
1621                                }
1622                            }
1623                        }
1624                    }
1625                } catch (Throwable JavaDoc t) {
1626                    if (log.isDebugEnabled()) {
1627                        log.debug("Could not clean fields for class " + clazz.getName(), t);
1628                    }
1629                }
1630            }
1631        }
1632        
1633         // Clear the IntrospectionUtils cache.
1634
IntrospectionUtils.clear();
1635        
1636        // Clear the classloader reference in common-logging
1637
org.apache.commons.logging.LogFactory.release(this);
1638        
1639        // Clear the classloader reference in the VM's bean introspector
1640
java.beans.Introspector.flushCaches();
1641
1642    }
1643
1644
1645    protected void nullInstance(Object JavaDoc instance) {
1646        if (instance == null) {
1647            return;
1648        }
1649        Field JavaDoc[] fields = instance.getClass().getDeclaredFields();
1650        for (int i = 0; i < fields.length; i++) {
1651            Field JavaDoc field = fields[i];
1652            int mods = field.getModifiers();
1653            if (field.getType().isPrimitive()
1654                    || (field.getName().indexOf("$") != -1)) {
1655                continue;
1656            }
1657            try {
1658                field.setAccessible(true);
1659                if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) {
1660                    // Doing something recursively is too risky
1661
continue;
1662                } else {
1663                    Object JavaDoc value = field.get(instance);
1664                    if (null != value) {
1665                        Class JavaDoc valueClass = value.getClass();
1666                        if (!loadedByThisOrChild(valueClass)) {
1667                            if (log.isDebugEnabled()) {
1668                                log.debug("Not setting field " + field.getName() +
1669                                        " to null in object of class " +
1670                                        instance.getClass().getName() +
1671                                        " because the referenced object was of type " +
1672                                        valueClass.getName() +
1673                                        " which was not loaded by this WebappClassLoader.");
1674                            }
1675                        } else {
1676                            field.set(instance, null);
1677                            if (log.isDebugEnabled()) {
1678                                log.debug("Set field " + field.getName()
1679                                        + " to null in class " + instance.getClass().getName());
1680                            }
1681                        }
1682                    }
1683                }
1684            } catch (Throwable JavaDoc t) {
1685                if (log.isDebugEnabled()) {
1686                    log.debug("Could not set field " + field.getName()
1687                            + " to null in object instance of class "
1688                            + instance.getClass().getName(), t);
1689                }
1690            }
1691        }
1692    }
1693
1694
1695    /**
1696     * Determine whether a class was loaded by this class loader or one of
1697     * its child class loaders.
1698     */

1699    protected boolean loadedByThisOrChild(Class JavaDoc clazz)
1700    {
1701        boolean result = false;
1702        for (ClassLoader JavaDoc classLoader = clazz.getClassLoader();
1703                null != classLoader; classLoader = classLoader.getParent()) {
1704            if (classLoader.equals(this)) {
1705                result = true;
1706                break;
1707            }
1708        }
1709        return result;
1710    }
1711
1712
1713    /**
1714     * Used to periodically signal to the classloader to release JAR resources.
1715     */

1716    protected boolean openJARs() {
1717        if (started && (jarFiles.length > 0)) {
1718            lastJarAccessed = System.currentTimeMillis();
1719            if (jarFiles[0] == null) {
1720                for (int i = 0; i < jarFiles.length; i++) {
1721                    try {
1722                        jarFiles[i] = new JarFile JavaDoc(jarRealFiles[i]);
1723                    } catch (IOException JavaDoc e) {
1724                        if (log.isDebugEnabled()) {
1725                            log.debug("Failed to open JAR", e);
1726                        }
1727                        return false;
1728                    }
1729                }
1730            }
1731        }
1732        return true;
1733    }
1734
1735
1736    /**
1737     * Find specified class in local repositories.
1738     *
1739     * @return the loaded class, or null if the class isn't found
1740     */

1741    protected Class JavaDoc findClassInternal(String JavaDoc name)
1742        throws ClassNotFoundException JavaDoc {
1743
1744        if (!validate(name))
1745            throw new ClassNotFoundException JavaDoc(name);
1746
1747        String JavaDoc tempPath = name.replace('.', '/');
1748        String JavaDoc classPath = tempPath + ".class";
1749
1750        ResourceEntry entry = null;
1751
1752        entry = findResourceInternal(name, classPath);
1753
1754        if (entry == null)
1755            throw new ClassNotFoundException JavaDoc(name);
1756
1757        Class JavaDoc clazz = entry.loadedClass;
1758        if (clazz != null)
1759            return clazz;
1760
1761        synchronized (entry) {
1762            if (entry.binaryContent == null && entry.loadedClass == null)
1763                throw new ClassNotFoundException JavaDoc(name);
1764
1765            // Looking up the package
1766
String JavaDoc packageName = null;
1767            int pos = name.lastIndexOf('.');
1768            if (pos != -1)
1769                packageName = name.substring(0, pos);
1770        
1771            Package JavaDoc pkg = null;
1772        
1773            if (packageName != null) {
1774                synchronized (this) {
1775                    pkg = getPackage(packageName);
1776            
1777                    // Define the package (if null)
1778
if (pkg == null) {
1779                        if (entry.manifest == null) {
1780                            definePackage(packageName, null, null, null, null,
1781                                    null, null, null);
1782                        } else {
1783                            definePackage(packageName, entry.manifest,
1784                                    entry.codeBase);
1785                        }
1786                    }
1787                }
1788            }
1789    
1790            if (securityManager != null) {
1791
1792                // Checking sealing
1793
if (pkg != null) {
1794                    boolean sealCheck = true;
1795                    if (pkg.isSealed()) {
1796                        sealCheck = pkg.isSealed(entry.codeBase);
1797                    } else {
1798                        sealCheck = (entry.manifest == null)
1799                            || !isPackageSealed(packageName, entry.manifest);
1800                    }
1801                    if (!sealCheck)
1802                        throw new SecurityException JavaDoc
1803                            ("Sealing violation loading " + name + " : Package "
1804                             + packageName + " is sealed.");
1805                }
1806    
1807            }
1808
1809            if (entry.loadedClass == null) {
1810                clazz = defineClass(name, entry.binaryContent, 0,
1811                        entry.binaryContent.length,
1812                        new CodeSource JavaDoc(entry.codeBase, entry.certificates));
1813                entry.loadedClass = clazz;
1814                entry.binaryContent = null;
1815                entry.source = null;
1816                entry.codeBase = null;
1817                entry.manifest = null;
1818                entry.certificates = null;
1819            } else {
1820                clazz = entry.loadedClass;
1821            }
1822        }
1823        
1824        return clazz;
1825
1826    }
1827
1828    /**
1829     * Find specified resource in local repositories. This block
1830     * will execute under an AccessControl.doPrivilege block.
1831     *
1832     * @return the loaded resource, or null if the resource isn't found
1833     */

1834    protected ResourceEntry findResourceInternal(File JavaDoc file, String JavaDoc path){
1835        ResourceEntry entry = new ResourceEntry();
1836        try {
1837            entry.source = getURI(new File JavaDoc(file, path));
1838            entry.codeBase = getURL(new File JavaDoc(file, path), false);
1839        } catch (MalformedURLException JavaDoc e) {
1840            return null;
1841        }
1842        return entry;
1843    }
1844    
1845
1846    /**
1847     * Find specified resource in local repositories.
1848     *
1849     * @return the loaded resource, or null if the resource isn't found
1850     */

1851    protected ResourceEntry findResourceInternal(String JavaDoc name, String JavaDoc path) {
1852
1853        if (!started) {
1854            log.info(sm.getString("webappClassLoader.stopped", name));
1855            return null;
1856        }
1857
1858        if ((name == null) || (path == null))
1859            return null;
1860
1861        ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
1862        if (entry != null)
1863            return entry;
1864
1865        int contentLength = -1;
1866        InputStream JavaDoc binaryStream = null;
1867
1868        int jarFilesLength = jarFiles.length;
1869        int repositoriesLength = repositories.length;
1870
1871        int i;
1872
1873        Resource resource = null;
1874
1875        boolean fileNeedConvert = false;
1876
1877        for (i = 0; (entry == null) && (i < repositoriesLength); i++) {
1878            try {
1879
1880                String JavaDoc fullPath = repositories[i] + path;
1881
1882                Object JavaDoc lookupResult = resources.lookup(fullPath);
1883                if (lookupResult instanceof Resource) {
1884                    resource = (Resource) lookupResult;
1885                }
1886
1887                // Note : Not getting an exception here means the resource was
1888
// found
1889
if (securityManager != null) {
1890                    PrivilegedAction JavaDoc dp =
1891                        new PrivilegedFindResource(files[i], path);
1892                    entry = (ResourceEntry)AccessController.doPrivileged(dp);
1893                 } else {
1894                    entry = findResourceInternal(files[i], path);
1895                 }
1896
1897                ResourceAttributes attributes =
1898                    (ResourceAttributes) resources.getAttributes(fullPath);
1899                contentLength = (int) attributes.getContentLength();
1900                entry.lastModified = attributes.getLastModified();
1901
1902                if (resource != null) {
1903
1904
1905                    try {
1906                        binaryStream = resource.streamContent();
1907                    } catch (IOException JavaDoc e) {
1908                        return null;
1909                    }
1910
1911                    if (needConvert) {
1912                        if (path.endsWith(".properties")) {
1913                            fileNeedConvert = true;
1914                        }
1915                    }
1916
1917                    // Register the full path for modification checking
1918
// Note: Only syncing on a 'constant' object is needed
1919
synchronized (allPermission) {
1920
1921                        int j;
1922
1923                        long[] result2 =
1924                            new long[lastModifiedDates.length + 1];
1925                        for (j = 0; j < lastModifiedDates.length; j++) {
1926                            result2[j] = lastModifiedDates[j];
1927                        }
1928                        result2[lastModifiedDates.length] = entry.lastModified;
1929                        lastModifiedDates = result2;
1930
1931                        String JavaDoc[] result = new String JavaDoc[paths.length + 1];
1932                        for (j = 0; j < paths.length; j++) {
1933                            result[j] = paths[j];
1934                        }
1935                        result[paths.length] = fullPath;
1936                        paths = result;
1937
1938                    }
1939
1940                }
1941
1942            } catch (NamingException JavaDoc e) {
1943            }
1944        }
1945
1946        if ((entry == null) && (notFoundResources.containsKey(name)))
1947            return null;
1948
1949        JarEntry JavaDoc jarEntry = null;
1950
1951        synchronized (jarFiles) {
1952
1953            if (!openJARs()) {
1954                return null;
1955            }
1956            for (i = 0; (entry == null) && (i < jarFilesLength); i++) {
1957
1958                jarEntry = jarFiles[i].getJarEntry(path);
1959
1960                if (jarEntry != null) {
1961
1962                    entry = new ResourceEntry();
1963                    try {
1964                        entry.codeBase = getURL(jarRealFiles[i], false);
1965                        String JavaDoc jarFakeUrl = getURI(jarRealFiles[i]).toString();
1966                        jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path;
1967                        entry.source = new URL JavaDoc(jarFakeUrl);
1968                        entry.lastModified = jarRealFiles[i].lastModified();
1969                    } catch (MalformedURLException JavaDoc e) {
1970                        return null;
1971                    }
1972                    contentLength = (int) jarEntry.getSize();
1973                    try {
1974                        entry.manifest = jarFiles[i].getManifest();
1975                        binaryStream = jarFiles[i].getInputStream(jarEntry);
1976                    } catch (IOException JavaDoc e) {
1977                        return null;
1978                    }
1979
1980                    // Extract resources contained in JAR to the workdir
1981
if (antiJARLocking && !(path.endsWith(".class"))) {
1982                        byte[] buf = new byte[1024];
1983                        File JavaDoc resourceFile = new File JavaDoc
1984                            (loaderDir, jarEntry.getName());
1985                        if (!resourceFile.exists()) {
1986                            Enumeration JavaDoc entries = jarFiles[i].entries();
1987                            while (entries.hasMoreElements()) {
1988                                JarEntry JavaDoc jarEntry2 =
1989                                    (JarEntry JavaDoc) entries.nextElement();
1990                                if (!(jarEntry2.isDirectory())
1991                                    && (!jarEntry2.getName().endsWith
1992                                        (".class"))) {
1993                                    resourceFile = new File JavaDoc
1994                                        (loaderDir, jarEntry2.getName());
1995                                    resourceFile.getParentFile().mkdirs();
1996                                    FileOutputStream JavaDoc os = null;
1997                                    InputStream JavaDoc is = null;
1998                                    try {
1999                                        is = jarFiles[i].getInputStream
2000                                            (jarEntry2);
2001                                        os = new FileOutputStream JavaDoc
2002                                            (resourceFile);
2003                                        while (true) {
2004                                            int n = is.read(buf);
2005                                            if (n <= 0) {
2006                                                break;
2007                                            }
2008                                            os.write(buf, 0, n);
2009                                        }
2010                                    } catch (IOException JavaDoc e) {
2011                                        // Ignore
2012
} finally {
2013                                        try {
2014                                            if (is != null) {
2015                                                is.close();
2016                                            }
2017                                        } catch (IOException JavaDoc e) {
2018                                        }
2019                                        try {
2020                                            if (os != null) {
2021                                                os.close();
2022                                            }
2023                                        } catch (IOException JavaDoc e) {
2024                                        }
2025                                    }
2026                                }
2027                            }
2028                        }
2029                    }
2030
2031                }
2032
2033            }
2034
2035            if (entry == null) {
2036                synchronized (notFoundResources) {
2037                    notFoundResources.put(name, name);
2038                }
2039                return null;
2040            }
2041
2042            if (binaryStream != null) {
2043
2044                byte[] binaryContent = new byte[contentLength];
2045
2046                int pos = 0;
2047                try {
2048
2049                    while (true) {
2050                        int n = binaryStream.read(binaryContent, pos,
2051                                                  binaryContent.length - pos);
2052                        if (n <= 0)
2053                            break;
2054                        pos += n;
2055                    }
2056                    binaryStream.close();
2057                } catch (IOException JavaDoc e) {
2058                    e.printStackTrace();
2059                    return null;
2060                } catch (Exception JavaDoc e) {
2061                    e.printStackTrace();
2062                    return null;
2063                }
2064
2065                if (fileNeedConvert) {
2066                    String JavaDoc str = new String JavaDoc(binaryContent,0,pos);
2067                    try {
2068                        binaryContent = str.getBytes("UTF-8");
2069                    } catch (Exception JavaDoc e) {
2070                        return null;
2071                    }
2072                }
2073                entry.binaryContent = binaryContent;
2074
2075                // The certificates are only available after the JarEntry
2076
// associated input stream has been fully read
2077
if (jarEntry != null) {
2078                    entry.certificates = jarEntry.getCertificates();
2079                }
2080
2081            }
2082
2083        }
2084
2085        // Add the entry in the local resource repository
2086
synchronized (resourceEntries) {
2087            // Ensures that all the threads which may be in a race to load
2088
// a particular class all end up with the same ResourceEntry
2089
// instance
2090
ResourceEntry entry2 = (ResourceEntry) resourceEntries.get(name);
2091            if (entry2 == null) {
2092                resourceEntries.put(name, entry);
2093            } else {
2094                entry = entry2;
2095            }
2096        }
2097
2098        return entry;
2099
2100    }
2101
2102
2103    /**
2104     * Returns true if the specified package name is sealed according to the
2105     * given manifest.
2106     */

2107    protected boolean isPackageSealed(String JavaDoc name, Manifest JavaDoc man) {
2108
2109        String JavaDoc path = name.replace('.', '/') + '/';
2110        Attributes JavaDoc attr = man.getAttributes(path);
2111        String JavaDoc sealed = null;
2112        if (attr != null) {
2113            sealed = attr.getValue(Name.SEALED);
2114        }
2115        if (sealed == null) {
2116            if ((attr = man.getMainAttributes()) != null) {
2117                sealed = attr.getValue(Name.SEALED);
2118            }
2119        }
2120        return "true".equalsIgnoreCase(sealed);
2121
2122    }
2123
2124
2125    /**
2126     * Finds the resource with the given name if it has previously been
2127     * loaded and cached by this class loader, and return an input stream
2128     * to the resource data. If this resource has not been cached, return
2129     * <code>null</code>.
2130     *
2131     * @param name Name of the resource to return
2132     */

2133    protected InputStream JavaDoc findLoadedResource(String JavaDoc name) {
2134
2135        ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
2136        if (entry != null) {
2137            if (entry.binaryContent != null)
2138                return new ByteArrayInputStream JavaDoc(entry.binaryContent);
2139        }
2140        return (null);
2141
2142    }
2143
2144
2145    /**
2146     * Finds the class with the given name if it has previously been
2147     * loaded and cached by this class loader, and return the Class object.
2148     * If this class has not been cached, return <code>null</code>.
2149     *
2150     * @param name Name of the resource to return
2151     */

2152    protected Class JavaDoc findLoadedClass0(String JavaDoc name) {
2153
2154        ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
2155        if (entry != null) {
2156            return entry.loadedClass;
2157        }
2158        return (null); // FIXME - findLoadedResource()
2159

2160    }
2161
2162
2163    /**
2164     * Refresh the system policy file, to pick up eventual changes.
2165     */

2166    protected void refreshPolicy() {
2167
2168        try {
2169            // The policy file may have been modified to adjust
2170
// permissions, so we're reloading it when loading or
2171
// reloading a Context
2172
Policy JavaDoc policy = Policy.getPolicy();
2173            policy.refresh();
2174        } catch (AccessControlException JavaDoc e) {
2175            // Some policy files may restrict this, even for the core,
2176
// so this exception is ignored
2177
}
2178
2179    }
2180
2181
2182    /**
2183     * Filter classes.
2184     *
2185     * @param name class name
2186     * @return true if the class should be filtered
2187     */

2188    protected boolean filter(String JavaDoc name) {
2189
2190        if (name == null)
2191            return false;
2192
2193        // Looking up the package
2194
String JavaDoc packageName = null;
2195        int pos = name.lastIndexOf('.');
2196        if (pos != -1)
2197            packageName = name.substring(0, pos);
2198        else
2199            return false;
2200
2201        for (int i = 0; i < packageTriggers.length; i++) {
2202            if (packageName.startsWith(packageTriggers[i]))
2203                return true;
2204        }
2205
2206        return false;
2207
2208    }
2209
2210
2211    /**
2212     * Validate a classname. As per SRV.9.7.2, we must restict loading of
2213     * classes from J2SE (java.*) and classes of the servlet API
2214     * (javax.servlet.*). That should enhance robustness and prevent a number
2215     * of user error (where an older version of servlet.jar would be present
2216     * in /WEB-INF/lib).
2217     *
2218     * @param name class name
2219     * @return true if the name is valid
2220     */

2221    protected boolean validate(String JavaDoc name) {
2222
2223        if (name == null)
2224            return false;
2225        if (name.startsWith("java."))
2226            return false;
2227
2228        return true;
2229
2230    }
2231
2232
2233    /**
2234     * Check the specified JAR file, and return <code>true</code> if it does
2235     * not contain any of the trigger classes.
2236     *
2237     * @param jarfile The JAR file to be checked
2238     *
2239     * @exception IOException if an input/output error occurs
2240     */

2241    protected boolean validateJarFile(File JavaDoc jarfile)
2242        throws IOException JavaDoc {
2243
2244        if (triggers == null)
2245            return (true);
2246        JarFile JavaDoc jarFile = new JarFile JavaDoc(jarfile);
2247        for (int i = 0; i < triggers.length; i++) {
2248            Class JavaDoc clazz = null;
2249            try {
2250                if (parent != null) {
2251                    clazz = parent.loadClass(triggers[i]);
2252                } else {
2253                    clazz = Class.forName(triggers[i]);
2254                }
2255            } catch (Throwable JavaDoc t) {
2256                clazz = null;
2257            }
2258            if (clazz == null)
2259                continue;
2260            String JavaDoc name = triggers[i].replace('.', '/') + ".class";
2261            if (log.isDebugEnabled())
2262                log.debug(" Checking for " + name);
2263            JarEntry JavaDoc jarEntry = jarFile.getJarEntry(name);
2264            if (jarEntry != null) {
2265                log.info("validateJarFile(" + jarfile +
2266                    ") - jar not loaded. See Servlet Spec 2.3, "
2267                    + "section 9.7.2. Offending class: " + name);
2268                jarFile.close();
2269                return (false);
2270            }
2271        }
2272        jarFile.close();
2273        return (true);
2274
2275    }
2276
2277
2278    /**
2279     * Get URL.
2280     */

2281    protected URL JavaDoc getURL(File JavaDoc file, boolean encoded)
2282        throws MalformedURLException JavaDoc {
2283
2284        File JavaDoc realFile = file;
2285        try {
2286            realFile = realFile.getCanonicalFile();
2287        } catch (IOException JavaDoc e) {
2288            // Ignore
2289
}
2290        if(encoded) {
2291            return getURI(realFile);
2292        } else {
2293            return realFile.toURL();
2294        }
2295
2296    }
2297
2298
2299    /**
2300     * Get URL.
2301     */

2302    protected URL JavaDoc getURI(File JavaDoc file)
2303        throws MalformedURLException JavaDoc {
2304
2305
2306        File JavaDoc realFile = file;
2307        try {
2308            realFile = realFile.getCanonicalFile();
2309        } catch (IOException JavaDoc e) {
2310            // Ignore
2311
}
2312        return realFile.toURI().toURL();
2313
2314    }
2315
2316
2317    /**
2318     * Delete the specified directory, including all of its contents and
2319     * subdirectories recursively.
2320     *
2321     * @param dir File object representing the directory to be deleted
2322     */

2323    protected static void deleteDir(File JavaDoc dir) {
2324
2325        String JavaDoc files[] = dir.list();
2326        if (files == null) {
2327            files = new String JavaDoc[0];
2328        }
2329        for (int i = 0; i < files.length; i++) {
2330            File JavaDoc file = new File JavaDoc(dir, files[i]);
2331            if (file.isDirectory()) {
2332                deleteDir(file);
2333            } else {
2334                file.delete();
2335            }
2336        }
2337        dir.delete();
2338
2339    }
2340
2341
2342}
2343
2344
Popular Tags