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