KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > loader > DynamicClassLoader


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.loader;
31
32 import com.caucho.config.ConfigException;
33 import com.caucho.lifecycle.Lifecycle;
34 import com.caucho.loader.enhancer.ByteCodeEnhancer;
35 import com.caucho.loader.enhancer.EnhancerRuntimeException;
36 import com.caucho.make.AlwaysModified;
37 import com.caucho.make.DependencyContainer;
38 import com.caucho.make.Make;
39 import com.caucho.make.MakeContainer;
40 import com.caucho.server.util.CauchoSystem;
41 import com.caucho.util.ByteBuffer;
42 import com.caucho.util.L10N;
43 import com.caucho.util.TimedCache;
44 import com.caucho.vfs.Dependency;
45 import com.caucho.vfs.JarPath;
46 import com.caucho.vfs.Path;
47 import com.caucho.vfs.ReadStream;
48
49 import javax.annotation.PostConstruct;
50 import java.io.FilePermission JavaDoc;
51 import java.io.IOException JavaDoc;
52 import java.io.InputStream JavaDoc;
53 import java.net.URL JavaDoc;
54 import java.security.*;
55 import java.lang.instrument.*;
56 import java.util.ArrayList JavaDoc;
57 import java.util.Enumeration JavaDoc;
58 import java.util.Hashtable JavaDoc;
59 import java.util.Vector JavaDoc;
60 import java.util.jar.Attributes JavaDoc;
61 import java.util.jar.Manifest JavaDoc;
62 import java.util.logging.Level JavaDoc;
63 import java.util.logging.Logger JavaDoc;
64 import java.util.regex.Pattern JavaDoc;
65
66 /**
67  * Class loader which checks for changes in class files and automatically
68  * picks up new jars.
69  *
70  * <p>DynamicClassLoaders can be chained creating one virtual class loader.
71  * From the perspective of the JDK, it's all one classloader. Internally,
72  * the class loader chain searches like a classpath.
73  */

74 public class DynamicClassLoader extends java.net.URLClassLoader JavaDoc
75   implements Dependency, Make
76 {
77   private static L10N _L;
78   private static Logger JavaDoc _log;
79
80   private final static URL JavaDoc NULL_URL;
81   private final static URL JavaDoc []NULL_URL_ARRAY = new URL JavaDoc[0];
82
83   private static long _globalDependencyCheckInterval = 2000L;
84
85   private String JavaDoc _id;
86
87   private final boolean _isVerbose;
88   private int _verboseDepth;
89
90   // List of resource loaders
91
private ArrayList JavaDoc<Loader> _loaders = new ArrayList JavaDoc<Loader>();
92
93   private JarLoader _jarLoader;
94   private PathLoader _pathLoader;
95
96   // List of cached classes
97
private Hashtable JavaDoc<String JavaDoc,ClassEntry> _entryCache;
98
99   private TimedCache<String JavaDoc,URL JavaDoc> _resourceCache;
100
101   // Dependencies
102
private DependencyContainer _dependencies = new DependencyContainer();
103   private boolean _isEnableDependencyCheck = false;
104
105   // Makes
106
private MakeContainer _makeList;
107
108   // If true, use the servlet spec's hack to give the parent priority
109
// for some classes, but the child priority for other classes.
110
private boolean _useServletHack;
111
112   // List of packages where the parent has priority.
113
private String JavaDoc []_parentPriorityPackages;
114
115   // List of packages where this has priority.
116
private String JavaDoc []_priorityPackages;
117
118   // Array of listeners
119
// XXX: There's still some questions of what kind of reference this
120
// should be. It either needs to be a WeakReference or
121
// a normal reference. Anything in between makes no sense.
122
//
123
// server/10w3 indicates that it needs to be a regular reference
124
private ArrayList JavaDoc<ClassLoaderListener> _listeners;
125
126   // The security manager for the loader
127
private SecurityManager JavaDoc _securityManager;
128
129   // List of permissions allowed in this context
130
private ArrayList JavaDoc<Permission JavaDoc> _permissions;
131
132   // The security code source for the loader
133
private CodeSource _codeSource;
134
135   // Any enhancer
136
private ArrayList JavaDoc<ClassFileTransformer> _classFileTransformerList;
137
138   private URL JavaDoc []_urls = NULL_URL_ARRAY;
139
140   private WeakCloseListener _closeListener;
141
142   // Lifecycle
143
protected final Lifecycle _lifecycle = new Lifecycle();
144
145   private long _lastNullCheck;
146
147   /**
148    * Create a new class loader.
149    *
150    * @param parent parent class loader
151    */

152   public DynamicClassLoader(ClassLoader JavaDoc parent)
153   {
154     this(parent, true);
155   }
156
157   /**
158    * Create a new class loader.
159    *
160    * @param parent parent class loader
161    */

162   public DynamicClassLoader(ClassLoader JavaDoc parent, boolean enableDependencyCheck)
163   {
164     super(NULL_URL_ARRAY, getInitParent(parent));
165
166     parent = getParent();
167
168     _securityManager = System.getSecurityManager();
169
170     _isEnableDependencyCheck = enableDependencyCheck;
171
172     _dependencies.setCheckInterval(_globalDependencyCheckInterval);
173
174     for (; parent != null; parent = parent.getParent()) {
175       if (parent instanceof DynamicClassLoader) {
176         DynamicClassLoader loader = (DynamicClassLoader) parent;
177
178         loader.init();
179
180         addPermissions(loader.getPermissions());
181
182         // loader.addListener(this);
183

184         _dependencies.add(loader);
185         _dependencies.setCheckInterval(loader.getDependencyCheckInterval());
186
187         _useServletHack = loader._useServletHack;
188
189         break;
190       }
191     }
192
193     if (System.getProperty("resin.verbose.classpath") != null) {
194       _isVerbose = true;
195
196       int depth = 0;
197
198       while (parent != null) {
199         depth++;
200         parent = parent.getParent();
201       }
202
203       _verboseDepth = depth;
204     }
205     else
206       _isVerbose = false;
207   }
208
209   /**
210    * Returns the initialization parent, i.e. the parent if given
211    * or the context class loader if not given.
212    */

213   private static ClassLoader JavaDoc getInitParent(ClassLoader JavaDoc parent)
214   {
215     if (parent != null)
216       return parent;
217     else
218       return Thread.currentThread().getContextClassLoader();
219   }
220
221   private void verbose(String JavaDoc name, String JavaDoc msg)
222   {
223     if (_isVerbose) {
224       for (int i = _verboseDepth; _verboseDepth > 0; _verboseDepth--)
225         System.err.print(' ');
226
227       System.err.println(toString() + " " + name + " " + msg);
228     }
229   }
230
231   /**
232    * Returns the global dependency check interval.
233    */

234   public static long getGlobalDependencyCheckInterval()
235   {
236     return _globalDependencyCheckInterval;
237   }
238
239   /**
240    * Sets the global dependency check interval.
241    */

242   public static void setGlobalDependencyCheckInterval(long interval)
243   {
244     _globalDependencyCheckInterval = interval;
245   }
246
247   /**
248    * Sets the name.
249    */

250   public void setId(String JavaDoc id)
251   {
252     _id = id;
253   }
254
255   /**
256    * Gets the name.
257    */

258   public String JavaDoc getId()
259   {
260     return _id;
261   }
262
263   /**
264    * Returns true if the class loader is closed.
265    */

266   public boolean isDestroyed()
267   {
268     return _lifecycle.isDestroyed();
269   }
270
271   /**
272    * Adds a resource loader to the end of the list.
273    */

274   public void addLoader(Loader loader)
275   {
276     int p = _loaders.indexOf(loader);
277
278     // overriding loaders are inserted before the loader they override
279
// server/10ih
280
if (p >= 0) {
281       Loader oldLoader = _loaders.get(p);
282
283       if (oldLoader != loader)
284         addLoader(loader, p);
285     }
286     else
287       addLoader(loader, _loaders.size());
288
289   }
290
291   /**
292    * Adds a resource loader.
293    */

294   public void addLoader(Loader loader, int offset)
295   {
296     if (_lifecycle.isDestroyed())
297       throw new IllegalStateException JavaDoc(L().l("can't add loaders after initialization"));
298
299     _loaders.add(offset, loader);
300
301     if (loader.getLoader() == null)
302       loader.setLoader(this);
303
304     if (loader instanceof Dependency)
305       _dependencies.add((Dependency) loader);
306
307     if (loader instanceof Make) {
308       if (_makeList == null)
309         _makeList = new MakeContainer();
310
311       _makeList.add((Make) loader);
312     }
313
314     if (loader instanceof ClassLoaderListener)
315       addListener(new WeakLoaderListener((ClassLoaderListener) loader));
316   }
317
318   public ArrayList JavaDoc<Loader> getLoaders()
319   {
320     return _loaders;
321   }
322
323   /**
324    * Adds jars based on a manifest classpath.
325    */

326   public void addJarManifestClassPath(Path path)
327     throws IOException JavaDoc
328   {
329     JarPath jar = JarPath.create(path);
330     Path manifestPath = jar.lookup("META-INF/MANIFEST.MF");
331
332     if (manifestPath.canRead()) {
333       ReadStream is = manifestPath.openRead();
334       try {
335         Manifest JavaDoc manifest = new Manifest JavaDoc(is);
336
337         Attributes JavaDoc main = manifest.getMainAttributes();
338
339         String JavaDoc classPath = main.getValue("Class-Path");
340
341         addManifestClassPath(classPath, path.getParent());
342       } catch (IOException JavaDoc e) {
343         log().log(Level.WARNING, e.toString(), e);
344
345         return;
346       } finally {
347         is.close();
348       }
349     }
350   }
351
352   /**
353    * Adds jars based on a manifest classpath.
354    */

355   public void addManifestClassPath(String JavaDoc classPath, Path pwd)
356   {
357     if (classPath == null)
358       return;
359
360     String JavaDoc []urls = Pattern.compile("[\\s,]+").split(classPath);
361     if (urls == null)
362       return;
363
364     for (int i = 0; i < urls.length; i++) {
365       String JavaDoc url = urls[i];
366
367       if (url.equals(""))
368         continue;
369
370       addJar(pwd.lookup(url));
371     }
372   }
373
374   /**
375    * Adds a jar loader.
376    */

377   public void addJar(Path jar)
378   {
379     if (_lifecycle.isDestroyed())
380       throw new IllegalStateException JavaDoc(L().l("can't add jars after closing"));
381
382     if (jar.isDirectory()) {
383       SimpleLoader loader = new SimpleLoader();
384       loader.setPath(jar);
385       addLoader(loader);
386     }
387     else {
388       if (_jarLoader == null) {
389         _jarLoader = new JarLoader();
390         addLoader(_jarLoader);
391       }
392
393       _jarLoader.addJar(jar);
394     }
395   }
396
397   /**
398    * Adds a jar loader.
399    */

400   public void addPathClass(String JavaDoc className, Path path)
401   {
402     if (_pathLoader == null) {
403       _pathLoader = new PathLoader();
404       // ejb/0i00
405
_loaders.add(0, _pathLoader);
406     }
407
408     _pathLoader.put(className, path);
409   }
410
411   /**
412    * Adds the URL to the URLClassLoader.
413    */

414   public void addURL(Path path)
415   {
416     try {
417       if (path.getScheme().equals("memory"))
418         return;
419
420       if (! path.getScheme().equals("jar") && ! path.getURL().endsWith("/"))
421         path = path.lookup("./");
422
423       addURL(new URL JavaDoc(path.getURL()));
424     } catch (Exception JavaDoc e) {
425       log().log(Level.WARNING, e.toString(), e);
426     }
427   }
428
429   /**
430    * Adds the URL to the URLClassLoader.
431    */

432   public void addURL(URL JavaDoc url)
433   {
434     addURL(_urls.length, url);
435   }
436
437   /**
438    * Adds the URL to the URLClassLoader.
439    */

440   public void addURL(int index, URL JavaDoc url)
441   {
442     super.addURL(url);
443
444     URL JavaDoc []newURLs = new URL JavaDoc[_urls.length + 1];
445
446     for (int i = 0; i < index; i++)
447       newURLs[i] = _urls[i];
448
449     newURLs[index] = url;
450
451     for (int i = index + 1; i < newURLs.length; i++)
452       newURLs[i] = _urls[i - 1];
453
454     _urls = newURLs;
455   }
456
457   /**
458    * Returns the URLs.
459    */

460   public URL JavaDoc []getURLs()
461   {
462     return _urls;
463   }
464
465   /**
466    * Sets the dependency check interval.
467    */

468   public void setDependencyCheckInterval(long interval)
469   {
470     _dependencies.setCheckInterval(interval);
471   }
472
473   /**
474    * Gets the dependency check interval.
475    */

476   public long getDependencyCheckInterval()
477   {
478     if (_dependencies != null)
479       return _dependencies.getCheckInterval();
480     else
481       return 0;
482   }
483
484   /**
485    * Enables the dependency checking.
486    */

487   public void setEnableDependencyCheck(boolean enable)
488   {
489     _isEnableDependencyCheck = enable;
490   }
491
492   /**
493    * Adds a dependency.
494    */

495   public void addDependency(Dependency dependency)
496   {
497     _dependencies.add(dependency);
498   }
499
500   public void addPermission(String JavaDoc path, String JavaDoc actions)
501   {
502     addPermission(new FilePermission JavaDoc(path, actions));
503   }
504
505   /**
506    * Adds a permission to the loader.
507    */

508   public void addPermission(Permission JavaDoc permission)
509   {
510     if (_permissions == null)
511       _permissions = new ArrayList JavaDoc<Permission JavaDoc>();
512
513     _permissions.add(permission);
514   }
515
516   public ArrayList JavaDoc<Permission JavaDoc> getPermissions()
517   {
518     return _permissions;
519   }
520
521   public void addPermissions(ArrayList JavaDoc<Permission JavaDoc> perms)
522   {
523     if (perms == null)
524       return;
525
526     if (_permissions == null)
527       _permissions = new ArrayList JavaDoc<Permission JavaDoc>();
528
529     _permissions.addAll(perms);
530   }
531
532   /**
533    * Returns the security manager.
534    */

535   public SecurityManager JavaDoc getSecurityManager()
536   {
537     return _securityManager;
538   }
539
540   /**
541    * Set true if the loader should use the servlet spec's hack.
542    */

543   public void setServletHack(boolean servletHack)
544   {
545     _useServletHack = servletHack;
546
547     if (_parentPriorityPackages == null)
548       _parentPriorityPackages = new String JavaDoc[0];
549   }
550
551   /**
552    * Adds a listener to detect class loader changes.
553    */

554   public final void addListener(ClassLoaderListener listener)
555   {
556     if (_lifecycle.isDestroyed()) {
557       IllegalStateException JavaDoc e = new IllegalStateException JavaDoc(L().l("attempted to add listener to a closed classloader {0}", this));
558
559       log().log(Level.WARNING, e.toString(), e);
560
561       return;
562     }
563
564     ArrayList JavaDoc<ClassLoaderListener> listeners;
565     WeakCloseListener closeListener = null;
566
567     synchronized (this) {
568       if (_listeners == null) {
569         _listeners = new ArrayList JavaDoc<ClassLoaderListener>();
570         closeListener = new WeakCloseListener(this);
571         //_closeListener = closeListener;
572
}
573       listeners = _listeners;
574     }
575
576     if (closeListener != null) {
577       for (ClassLoader JavaDoc parent = getParent();
578            parent != null;
579            parent = parent.getParent()) {
580         if (parent instanceof DynamicClassLoader) {
581           ((DynamicClassLoader) parent).addListener(closeListener);
582           break;
583         }
584       }
585     }
586
587     synchronized (listeners) {
588       for (int i = listeners.size() - 1; i >= 0; i--) {
589         ClassLoaderListener oldListener = listeners.get(i);
590
591         if (listener == oldListener) {
592           return;
593         }
594         else if (oldListener == null)
595           listeners.remove(i);
596       }
597
598       listeners.add(listener);
599     }
600
601     if (_lifecycle.isActive())
602       listener.classLoaderInit(this);
603   }
604
605   /**
606    * Adds a listener to detect class loader changes.
607    */

608   public final void removeListener(ClassLoaderListener listener)
609   {
610     ArrayList JavaDoc<ClassLoaderListener> listeners = _listeners;
611
612     if (listeners == null)
613       return;
614
615     synchronized (listeners) {
616       for (int i = listeners.size() - 1; i >= 0; i--) {
617         ClassLoaderListener oldListener = listeners.get(i);
618
619         if (listener == oldListener) {
620           listeners.remove(i);
621           return;
622         }
623         else if (oldListener == null)
624           listeners.remove(i);
625       }
626     }
627   }
628
629   /**
630    * Returns the listeners.
631    */

632   protected ArrayList JavaDoc<ClassLoaderListener> getListeners()
633   {
634     ArrayList JavaDoc<ClassLoaderListener> listeners;
635     listeners = new ArrayList JavaDoc<ClassLoaderListener>();
636
637     ArrayList JavaDoc<ClassLoaderListener> listenerList;
638     listenerList = _listeners;
639
640     if (listenerList != null) {
641       synchronized (listenerList) {
642         for (int i = 0; i < listenerList.size(); i++) {
643           ClassLoaderListener listener = listenerList.get(i);
644
645           if (listener != null)
646             listeners.add(listener);
647           else {
648             listenerList.remove(i);
649             i--;
650           }
651         }
652       }
653     }
654
655     return listeners;
656   }
657
658   /**
659    * Add to the list of packages that don't use the hack.
660    */

661   public void addParentPriorityPackages(String JavaDoc []pkg)
662   {
663     for (int i = 0; pkg != null && i < pkg.length; i++) {
664       addParentPriorityPackage(pkg[i]);
665     }
666   }
667
668   /**
669    * Add to the list of packages that don't use the {@link #setServletHack(boolean)}.
670    */

671   public void addParentPriorityPackage(String JavaDoc pkg)
672   {
673     if (_parentPriorityPackages == null)
674       _parentPriorityPackages = new String JavaDoc[0];
675
676     int oldLength = _parentPriorityPackages.length;
677     String JavaDoc []newPkgs = new String JavaDoc[oldLength + 1];
678
679     System.arraycopy(_parentPriorityPackages, 0, newPkgs, 0, oldLength);
680
681     if (! pkg.endsWith("."))
682       pkg = pkg + '.';
683
684     newPkgs[oldLength] = pkg;
685     _parentPriorityPackages = newPkgs;
686   }
687
688   /**
689    * Add to the list of packages that take priority over the parent
690    */

691   public void addPriorityPackage(String JavaDoc pkg)
692   {
693     if (_priorityPackages == null)
694       _priorityPackages = new String JavaDoc[0];
695
696     int oldLength = _priorityPackages.length;
697     String JavaDoc []newPkgs = new String JavaDoc[oldLength + 1];
698
699     System.arraycopy(_priorityPackages, 0, newPkgs, 0, oldLength);
700
701     if (! pkg.endsWith("."))
702       pkg = pkg + '.';
703
704     newPkgs[oldLength] = pkg;
705     _priorityPackages = newPkgs;
706   }
707
708   /**
709    * Returns the permission collection for the given code source.
710    */

711   protected PermissionCollection getPermissions(CodeSource codeSource)
712   {
713     PermissionCollection perms = super.getPermissions(codeSource);
714
715     ArrayList JavaDoc<Permission JavaDoc> permissions = _permissions;
716
717     for (int i = 0; permissions != null && i < permissions.size(); i++) {
718       Permission JavaDoc permission = permissions.get(i);
719
720       perms.add(permission);
721     }
722
723     return perms;
724   }
725
726   protected void addCodeBasePath(String JavaDoc path)
727   {
728   }
729
730   /**
731    * Sets any enhancer.
732    */

733   public void addTransformer(ClassFileTransformer transformer)
734   {
735     if (_classFileTransformerList == null)
736       _classFileTransformerList = new ArrayList JavaDoc<ClassFileTransformer>();
737     
738     _classFileTransformerList.add(transformer);
739   }
740
741   protected ArrayList JavaDoc<ClassFileTransformer> getTransformerList()
742   {
743     return _classFileTransformerList;
744   }
745
746   /**
747    * Fill data for the class path. fillClassPath() will add all
748    * .jar and .zip files in the directory list.
749    */

750   public final String JavaDoc getClassPath()
751   {
752     ClassLoader JavaDoc parent = getParent();
753
754     String JavaDoc head;
755
756     if (parent instanceof DynamicClassLoader)
757       head = (((DynamicClassLoader) parent).getClassPath());
758     else
759       head = CauchoSystem.getClassPath();
760
761     for (int i = 0; i < _loaders.size(); i++) {
762       Loader loader = _loaders.get(i);
763
764       head = loader.getClassPath(head);
765     }
766
767     return head;
768   }
769
770   /**
771    * Fill data for the class path. fillClassPath() will add all
772    * .jar and .zip files in the directory list.
773    */

774   public final String JavaDoc getLocalClassPath()
775   {
776     String JavaDoc head = "";
777
778     for (int i = 0; i < _loaders.size(); i++) {
779       Loader loader = _loaders.get(i);
780
781       head = loader.getClassPath(head);
782     }
783
784     return head;
785   }
786
787   /**
788    * Returns the source path. The source path is used for looking up
789    * resources.
790    */

791   public final String JavaDoc getSourcePath()
792   {
793     ClassLoader JavaDoc parent = getParent();
794
795     String JavaDoc head;
796
797     if (parent instanceof DynamicClassLoader)
798       head = (((DynamicClassLoader) parent).getSourcePath());
799     else
800       head = CauchoSystem.getClassPath();
801
802     for (int i = 0; i < _loaders.size(); i++) {
803       Loader loader = _loaders.get(i);
804
805       head = loader.getSourcePath(head);
806     }
807
808     return head;
809   }
810
811   /**
812    * Returns the resource path with most specific first.
813    */

814   public final String JavaDoc getResourcePathSpecificFirst()
815   {
816     ClassLoader JavaDoc parent = getParent();
817
818     String JavaDoc head = "";
819     int size = _loaders != null ? _loaders.size() : 0;
820     for (int i = 0; i < size; i++) {
821       Loader loader = _loaders.get(i);
822
823       head = loader.getSourcePath(head);
824     }
825
826     String JavaDoc tail;
827     if (parent instanceof DynamicClassLoader)
828       tail = (((DynamicClassLoader) parent).getResourcePathSpecificFirst());
829     else
830       tail = CauchoSystem.getClassPath();
831
832     if (head == null || head.equals(""))
833       return tail;
834     if (tail == null || tail.equals(""))
835       return head;
836     else
837       return head + CauchoSystem.getPathSeparatorChar() + tail;
838   }
839
840   /**
841    * Returns true if any of the classes have been modified.
842    */

843   public final boolean isModified()
844   {
845     return isModified(_isEnableDependencyCheck);
846   }
847
848   /**
849    * Returns true if any of the classes have been modified.
850    */

851   public final boolean isModified(boolean enable)
852   {
853     if (_lifecycle.isDestroyed()) {
854       return true;
855     }
856
857     DependencyContainer dependencies = _dependencies;
858
859     if (dependencies == null) {
860       return true;
861     }
862
863     if (enable) {
864       boolean isModified = dependencies.isModified();
865
866       return isModified;
867     }
868     else {
869       boolean isModified = isModified(getParent());
870
871       return isModified;
872     }
873   }
874
875   /**
876    * Returns true if any of the classes have been modified, forcing a check.
877    */

878   public final boolean isModifiedNow()
879   {
880     if (_lifecycle.isDestroyed())
881       return true;
882
883     DependencyContainer dependencies = _dependencies;
884
885     if (dependencies == null)
886       return true;
887
888     return dependencies.isModifiedNow();
889   }
890
891   /**
892    * Returns true if any of the classes have been modified.
893    */

894   public final void resetDependencyCheckInterval()
895   {
896     if (_lifecycle.isDestroyed())
897       return;
898
899     DependencyContainer dependencies = _dependencies;
900
901     if (dependencies == null)
902       return;
903
904     dependencies.resetDependencyCheckInterval();
905   }
906
907   /**
908    * Returns true if any of the classes have been modified.
909    */

910   public final void clearModified()
911   {
912     if (_lifecycle.isDestroyed())
913       return;
914
915     DependencyContainer dependencies = _dependencies;
916
917     if (dependencies == null)
918       return;
919
920     dependencies.clearModified();
921   }
922
923   /**
924    * Returns true if any of the classes have been modified.
925    */

926   public static boolean isModified(ClassLoader JavaDoc loader)
927   {
928     for (; loader != null; loader = loader.getParent()) {
929       if (loader instanceof DynamicClassLoader)
930         return ((DynamicClassLoader) loader).isModified();
931     }
932
933     return false;
934   }
935
936   /**
937    * Makes any changed classes for the virtual class loader.
938    */

939   public final void make()
940     throws Exception JavaDoc
941   {
942     for (ClassLoader JavaDoc loader = getParent();
943          loader != null;
944          loader = loader.getParent()) {
945       if (loader instanceof Make) {
946         ((Make) loader).make();
947         break;
948       }
949     }
950
951     if (_makeList != null)
952       _makeList.make();
953   }
954
955   /**
956    * Defines a new package.
957    */

958   protected Package JavaDoc definePackage(String JavaDoc name,
959                                   String JavaDoc a1, String JavaDoc a2, String JavaDoc a3,
960                                   String JavaDoc b1, String JavaDoc b2, String JavaDoc b3,
961                                   URL JavaDoc url)
962   {
963     name = name.replace('/', '.');
964
965     if (name.endsWith("."))
966       name = name.substring(0, name.length() - 1);
967
968     return super.definePackage(name, a1, a2, a3, b1, b2, b3, url);
969   }
970
971   /**
972    * Initialize the class loader.
973    */

974   @PostConstruct
975   public void init()
976   {
977     if (! _lifecycle.toActive())
978       return;
979
980     try {
981       ArrayList JavaDoc<ClassLoaderListener> listeners = getListeners();
982
983       if (listeners != null) {
984         for (int i = 0; i < listeners.size(); i++) {
985           ClassLoaderListener listener = listeners.get(i);
986
987           listener.classLoaderInit(this);
988         }
989       }
990     } catch (Throwable JavaDoc e) {
991       log().log(Level.WARNING, e.toString(), e);
992     }
993   }
994
995   /**
996    * Validates the class loader.
997    */

998   public void validate()
999     throws ConfigException
1000  {
1001    ArrayList JavaDoc<Loader> loaders = _loaders;
1002
1003    if (loaders == null)
1004      throw new IllegalStateException JavaDoc(_L.l("Class loader {0} is closed during initialization.", this));
1005
1006    for (int i = 0; i < loaders.size(); i++)
1007      loaders.get(i).validate();
1008  }
1009
1010  public Class JavaDoc<?> loadClass(String JavaDoc name) throws ClassNotFoundException JavaDoc
1011  {
1012    // the Sun JDK implementation of ClassLoader delegates this call
1013
// to loadClass(name, false), but there is no guarantee that other
1014
// implementations do.
1015
return loadClass(name, false);
1016  }
1017
1018  /**
1019   * Load a class using this class loader
1020   *
1021   * @param name the classname to load
1022   * @param resolve if true, resolve the class
1023   *
1024   * @return the loaded classes
1025   */

1026  // XXX: added synchronized for RSN-373
1027
protected synchronized Class JavaDoc loadClass(String JavaDoc name, boolean resolve)
1028    throws ClassNotFoundException JavaDoc
1029  {
1030    /*
1031    if (! _useServletHack) {
1032      // XXX: sync issues with loading and compiling?
1033      return super.loadClass(name, resolve);
1034    }
1035    */

1036
1037    // The JVM has already cached the classes, so we don't need to
1038
Class JavaDoc cl = findLoadedClass(name);
1039
1040    if (cl != null) {
1041      if (resolve)
1042        resolveClass(cl);
1043      return cl;
1044    }
1045
1046    if (_lifecycle.isDestroyed()) {
1047      log().fine(L().l("Loading class {0} when class loader {1} has been closed.",
1048                       name, this));
1049
1050      return super.loadClass(name, resolve);
1051    }
1052
1053    boolean normalJdkOrder = isNormalJdkOrder(name);
1054
1055    if (_lifecycle.isBeforeInit())
1056      init();
1057
1058    if (normalJdkOrder) {
1059      try {
1060        ClassLoader JavaDoc parent = getParent();
1061        if (parent != null)
1062          cl = Class.forName(name, false, parent);
1063        else
1064          cl = findSystemClass(name);
1065      } catch (ClassNotFoundException JavaDoc e) {
1066        cl = findClass(name);
1067      }
1068    }
1069    else {
1070
1071      try {
1072        cl = findClass(name);
1073      } catch (ClassNotFoundException JavaDoc e) {
1074        ClassLoader JavaDoc parent = getParent();
1075        if (parent != null)
1076          cl = Class.forName(name, false, parent);
1077        else
1078          cl = findSystemClass(name);
1079      }
1080    }
1081
1082    if (resolve)
1083      resolveClass(cl);
1084
1085    return cl;
1086  }
1087
1088  /**
1089   * Load a class using this class loader
1090   *
1091   * @param name the classname using either '/' or '.'
1092   *
1093   * @return the loaded class
1094   */

1095  protected Class JavaDoc findClass(String JavaDoc name)
1096    throws ClassNotFoundException JavaDoc
1097  {
1098    if (_isVerbose)
1099      verbose(name, "findClass");
1100
1101    if (_lifecycle.isDestroyed()) {
1102      log().fine("Class loader has been closed.");
1103      return super.findClass(name);
1104    }
1105
1106    if (_lifecycle.isBeforeInit())
1107      init();
1108    /*
1109    if (! _lifecycle.isActive())
1110      return super.findClass(name);
1111    */

1112
1113    name = name.replace('/', '.');
1114
1115    ClassEntry entry = null;
1116
1117    synchronized (this) {
1118      entry = _entryCache == null ? null : _entryCache.get(name);
1119
1120      if (entry == null)
1121        entry = getClassEntry(name);
1122
1123      if (entry == null)
1124        throw new ClassNotFoundException JavaDoc(name);
1125
1126      if (entry != null && _isVerbose)
1127        verbose(name, (isNormalJdkOrder(name) ? "found" : "found (took priority from parent)"));
1128
1129      if (_isEnableDependencyCheck)
1130        entry.addDependencies(_dependencies);
1131
1132      // Currently, the entry must be in the entry cache for synchronization
1133
// to work. The same entry must be returned for two separate threads
1134
// trying to load the class at the same time.
1135

1136      if (_entryCache == null)
1137        _entryCache = new Hashtable JavaDoc<String JavaDoc,ClassEntry>(8);
1138
1139      _entryCache.put(name, entry);
1140    }
1141
1142    try {
1143      return loadClass(entry);
1144    } catch (RuntimeException JavaDoc e) {
1145      throw e;
1146    } catch (ClassNotFoundException JavaDoc e) {
1147      throw e;
1148    } catch (Exception JavaDoc e) {
1149      log().log(Level.FINE, e.toString(), e);
1150
1151      throw new ClassNotFoundException JavaDoc(name + " [" + e + "]", e);
1152    }
1153  }
1154
1155  /**
1156   * Returns the matching class entry.
1157   */

1158  protected ClassEntry getClassEntry(String JavaDoc name)
1159    throws ClassNotFoundException JavaDoc
1160  {
1161    for (int i = 0; i < _loaders.size(); i++) {
1162      Loader loader = _loaders.get(i);
1163
1164      ClassEntry entry = loader.getClassEntry(name);
1165
1166      if (entry != null)
1167        return entry;
1168    }
1169
1170    return null;
1171  }
1172
1173  /**
1174   * Loads the class from the loader. The loadClass must be in the
1175   * top classLoader because the defineClass must be owned by the top.
1176   */

1177  protected Class JavaDoc loadClass(ClassEntry entry)
1178      throws IOException JavaDoc, ClassNotFoundException JavaDoc
1179  {
1180    synchronized (entry) {
1181      Class JavaDoc cl = entry.getEntryClass();
1182
1183      if (cl != null)
1184        return cl;
1185
1186      entry.preLoad();
1187
1188      String JavaDoc name = entry.getName();
1189      int p = name.lastIndexOf('.');
1190      if (p > 0) {
1191        String JavaDoc packageName = name.substring(0, p);
1192        Package JavaDoc pkg = getPackage(packageName);
1193
1194        ClassPackage classPackage = entry.getClassPackage();
1195
1196        if (pkg != null) {
1197        }
1198        else if (classPackage != null) {
1199          definePackage(packageName,
1200                        classPackage.getSpecificationTitle(),
1201                        classPackage.getSpecificationVersion(),
1202                        classPackage.getSpecificationVendor(),
1203                        classPackage.getImplementationTitle(),
1204                        classPackage.getImplementationVersion(),
1205                        classPackage.getImplementationVendor(),
1206                        null);
1207        }
1208        else {
1209          definePackage(packageName,
1210                        null,
1211                        null,
1212                        null,
1213                        null,
1214                        null,
1215                        null,
1216                        null);
1217        }
1218      }
1219
1220      ByteBuffer buffer = new ByteBuffer();
1221
1222      entry.load(buffer);
1223
1224      byte []bBuf = buffer.getBuffer();
1225      int bLen = buffer.length();
1226
1227      if (_classFileTransformerList != null) {
1228    Class JavaDoc redefineClass = null;
1229    String JavaDoc className = name.replace('.', '/');
1230
1231    if (bBuf.length != bLen) {
1232      byte []tempBuf = new byte[bLen];
1233      System.arraycopy(bBuf, 0, tempBuf, 0, bLen);
1234      bBuf = tempBuf;
1235    }
1236
1237    ProtectionDomain protectionDomain = null;
1238    
1239    for (int i = 0; i < _classFileTransformerList.size(); i++) {
1240      ClassFileTransformer transformer = _classFileTransformerList.get(i);
1241      
1242      try {
1243        byte []enhancedBuffer = transformer.transform(this,
1244                              className,
1245                              redefineClass,
1246                              protectionDomain,
1247                              bBuf);
1248
1249        if (enhancedBuffer != null) {
1250          bBuf = enhancedBuffer;
1251          bLen = enhancedBuffer.length;
1252
1253          if (_isVerbose || true)
1254        verbose(name, String.valueOf(transformer));
1255        }
1256        /* RSN-109
1257           } catch (RuntimeException e) {
1258           throw e;
1259           } catch (Error e) {
1260           throw e;
1261        */

1262      } catch (EnhancerRuntimeException e) {
1263        throw e;
1264      } catch (Throwable JavaDoc e) {
1265        log().log(Level.WARNING, e.toString(), e);
1266      }
1267    }
1268      }
1269
1270      try {
1271        cl = defineClass(entry.getName(),
1272             bBuf, 0, bLen,
1273             entry.getCodeSource());
1274      } catch (RuntimeException JavaDoc e) {
1275        log().log(Level.FINE, entry.getName() + " [" + e.toString() + "]", e);
1276
1277    throw e;
1278      } catch (Exception JavaDoc e) {
1279        log().log(Level.FINE, entry.getName() + " [" + e.toString() + "]", e);
1280
1281        ClassNotFoundException JavaDoc exn;
1282        exn = new ClassNotFoundException JavaDoc(entry.getName() + " [" + e + "]", e);
1283        //exn.initCause(e);
1284

1285        throw exn;
1286      }
1287
1288      entry.setEntryClass(cl);
1289
1290      if (entry.postLoad()) {
1291        _dependencies.add(AlwaysModified.create());
1292        _dependencies.setModified(true);
1293      }
1294
1295      return cl;
1296    }
1297  }
1298
1299  /**
1300   * Gets the named resource
1301   *
1302   * @param name name of the resource
1303   */

1304  public URL JavaDoc getResource(String JavaDoc name)
1305  {
1306    if (_resourceCache == null) {
1307      long expireInterval = getDependencyCheckInterval();
1308
1309      _resourceCache = new TimedCache<String JavaDoc,URL JavaDoc>(256, expireInterval);
1310    }
1311
1312    URL JavaDoc url = _resourceCache.get(name);
1313
1314    if (url == NULL_URL)
1315      return null;
1316    else if (url != null)
1317      return url;
1318
1319    boolean isNormalJdkOrder = isNormalJdkOrder(name);
1320
1321    if (isNormalJdkOrder) {
1322      url = getParentResource(name);
1323
1324      if (url != null)
1325        return url;
1326    }
1327
1328    ArrayList JavaDoc<Loader> loaders = _loaders;
1329
1330    for (int i = 0; loaders != null && i < loaders.size(); i++) {
1331      Loader loader = loaders.get(i);
1332
1333      url = loader.getResource(name);
1334
1335      if (url != null) {
1336        _resourceCache.put(name, url);
1337
1338        return url;
1339      }
1340    }
1341
1342    if (! isNormalJdkOrder) {
1343      url = getParentResource(name);
1344
1345      if (url != null)
1346        return url;
1347    }
1348
1349    _resourceCache.put(name, NULL_URL);
1350
1351    return null;
1352  }
1353
1354  private URL JavaDoc getParentResource(String JavaDoc name)
1355  {
1356    ClassLoader JavaDoc parent = getParent();
1357
1358    ClassLoader JavaDoc systemLoader = ClassLoader.getSystemClassLoader();
1359
1360    URL JavaDoc url;
1361
1362    if (parent != null) {
1363      url = parent.getResource(name);
1364
1365      if (url != null) {
1366        _resourceCache.put(name, url);
1367
1368        return url;
1369      }
1370    }
1371    else if (this != systemLoader) {
1372      url = getSystemResource(name);
1373
1374      if (url != null) {
1375        _resourceCache.put(name, url);
1376
1377        return url;
1378      }
1379    }
1380
1381    return null;
1382  }
1383
1384
1385  /**
1386   * Opens a stream to a resource somewhere in the classpath
1387   *
1388   * @param name the path to the resource
1389   *
1390   * @return an input stream to the resource
1391   */

1392  public InputStream JavaDoc getResourceAsStream(String JavaDoc name)
1393  {
1394    boolean isNormalJdkOrder = isNormalJdkOrder(name);
1395    InputStream JavaDoc is = null;
1396
1397    if (isNormalJdkOrder) {
1398      is = getParentResourceAsStream(name);
1399
1400      if (is != null)
1401        return is;
1402    }
1403
1404    ArrayList JavaDoc<Loader> loaders = _loaders;
1405
1406    for (int i = 0; loaders != null && i < loaders.size(); i++) {
1407      Loader loader = loaders.get(i);
1408
1409      is = loader.getResourceAsStream(name);
1410
1411      if (is != null)
1412        return is;
1413    }
1414
1415    if (! isNormalJdkOrder) {
1416      is = getParentResourceAsStream(name);
1417    }
1418
1419    return is;
1420  }
1421
1422  private InputStream JavaDoc getParentResourceAsStream(String JavaDoc name)
1423  {
1424    ClassLoader JavaDoc parent = getParent();
1425
1426    ClassLoader JavaDoc systemLoader = ClassLoader.getSystemClassLoader();
1427
1428    InputStream JavaDoc is;
1429
1430    if (parent != null) {
1431      is = parent.getResourceAsStream(name);
1432
1433      if (is != null)
1434        return is;
1435    }
1436    else if (this != systemLoader) {
1437      is = getSystemResourceAsStream(name);
1438
1439      if (is != null) {
1440        return is;
1441      }
1442    }
1443
1444    return null;
1445  }
1446
1447  /**
1448   * Returns an enumeration of matching resources.
1449   */

1450  public Enumeration JavaDoc<URL JavaDoc> findResources(String JavaDoc name)
1451  {
1452    Vector JavaDoc<URL JavaDoc> resources = new Vector JavaDoc<URL JavaDoc>();
1453
1454    for (int i = 0; i < _loaders.size(); i++) {
1455      Loader loader = _loaders.get(i);
1456
1457      loader.getResources(resources, name);
1458    }
1459
1460    return resources.elements();
1461  }
1462
1463  /**
1464   * Returns the full library path for the name.
1465   */

1466  public String JavaDoc findLibrary(String JavaDoc name)
1467  {
1468    String JavaDoc systemName = System.mapLibraryName(name);
1469
1470    for (int i = 0; i < _loaders.size(); i++) {
1471      Loader loader = _loaders.get(i);
1472
1473      Path path = loader.getPath(systemName);
1474
1475      if (path != null && path.canRead()) {
1476        return path.getNativePath();
1477      }
1478    }
1479
1480    return super.findLibrary(name);
1481  }
1482
1483  /**
1484   * Returns the matching single-level path.
1485   */

1486  public Path findPath(String JavaDoc name)
1487  {
1488    for (int i = 0; i < _loaders.size(); i++) {
1489      Loader loader = _loaders.get(i);
1490
1491      Path path = loader.getPath(name);
1492
1493      if (path != null && path.canRead()) {
1494        return path;
1495      }
1496    }
1497
1498    return null;
1499  }
1500
1501  /**
1502   * Returns true if the class loader should use the normal order,
1503   * i.e. looking at the parents first.
1504   */

1505  private boolean isNormalJdkOrder(String JavaDoc className)
1506  {
1507    if (_priorityPackages != null) {
1508      String JavaDoc canonName = className.replace('/', '.');
1509
1510      for (String JavaDoc priorityPackage : _priorityPackages) {
1511        if (canonName.startsWith(priorityPackage))
1512          return false;
1513      }
1514    }
1515
1516    if (! _useServletHack)
1517      return true;
1518
1519    String JavaDoc canonName = className.replace('/', '.');
1520    String JavaDoc []pkgs = _parentPriorityPackages;
1521
1522    for (int i = 0; pkgs != null && i < pkgs.length; i++) {
1523      if (canonName.startsWith(pkgs[i]))
1524        return true;
1525    }
1526
1527    return false;
1528  }
1529
1530  /**
1531   * stops the class loader.
1532   */

1533  public void stop()
1534  {
1535  }
1536
1537  /**
1538   * Destroys the class loader.
1539   */

1540  public void destroy()
1541  {
1542    try {
1543      stop();
1544    } catch (Throwable JavaDoc e) {
1545    }
1546
1547    if (! _lifecycle.toDestroying())
1548      return;
1549
1550    try {
1551      ClassLoader JavaDoc parent = getParent();
1552      for (; parent != null; parent = parent.getParent()) {
1553        if (parent instanceof DynamicClassLoader) {
1554          DynamicClassLoader loader = (DynamicClassLoader) parent;
1555
1556          if (_closeListener != null)
1557            loader.removeListener(_closeListener);
1558        }
1559      }
1560
1561      ArrayList JavaDoc<ClassLoaderListener> listeners = _listeners;
1562      _listeners = null;
1563
1564      Thread JavaDoc thread = Thread.currentThread();
1565      ClassLoader JavaDoc oldLoader = thread.getContextClassLoader();
1566
1567      try {
1568        // Use reverse order so the last resources will be destroyed first.
1569
if (listeners != null) {
1570          for (int i = listeners.size() - 1; i >= 0; i--) {
1571            ClassLoaderListener listener = listeners.get(i);
1572
1573            try {
1574              thread.setContextClassLoader(this);
1575
1576              listener.classLoaderDestroy(this);
1577            } catch (Throwable JavaDoc e) {
1578              log().log(Level.WARNING, e.toString(), e);
1579            }
1580          }
1581        }
1582      } finally {
1583        thread.setContextClassLoader(oldLoader);
1584      }
1585
1586      for (int i = _loaders.size() - 1; i >= 0; i--) {
1587        Loader loader = _loaders.get(i);
1588
1589        try {
1590          loader.destroy();
1591        } catch (Throwable JavaDoc e) {
1592          log().log(Level.WARNING, e.toString(), e);
1593        }
1594      }
1595    } finally {
1596      _closeListener = null;
1597      _listeners = null;
1598      _entryCache = null;
1599      _makeList = null;
1600      _loaders = null;
1601      _jarLoader = null;
1602      _dependencies = null;
1603      _permissions = null;
1604      _securityManager = null;
1605      _codeSource = null;
1606      _classFileTransformerList = null;
1607
1608      _lifecycle.toDestroy();
1609    }
1610  }
1611
1612  /**
1613   * Sets the old loader if not destroyed.
1614   */

1615  public static void setOldLoader(Thread JavaDoc thread, ClassLoader JavaDoc oldLoader)
1616  {
1617    if (! (oldLoader instanceof DynamicClassLoader)) {
1618      thread.setContextClassLoader(oldLoader);
1619      return;
1620    }
1621
1622    DynamicClassLoader dynLoader = (DynamicClassLoader) oldLoader;
1623
1624    if (! dynLoader.isDestroyed())
1625      thread.setContextClassLoader(oldLoader);
1626    else
1627      thread.setContextClassLoader(ClassLoader.getSystemClassLoader());
1628  }
1629
1630  public ClassLoader JavaDoc getInstrumentableClassLoader()
1631  {
1632    return this;
1633  }
1634  
1635  public ClassLoader JavaDoc getThrowawayClassLoader()
1636  {
1637    DynamicClassLoader dynLoader = new DynamicClassLoader(getParent());
1638
1639    for (int i = 0; i < _loaders.size(); i++) {
1640      dynLoader.addLoader(_loaders.get(i));
1641    }
1642
1643    return dynLoader;
1644  }
1645
1646  /**
1647   * Copies the loader.
1648   */

1649  protected void replace(DynamicClassLoader source)
1650  {
1651    _id = source._id;
1652
1653    _loaders.addAll(source._loaders);
1654    _jarLoader = source._jarLoader;
1655
1656    _dependencies = source._dependencies;
1657
1658    _makeList = source._makeList;
1659    _useServletHack = source._useServletHack;
1660    _parentPriorityPackages = source._parentPriorityPackages;
1661
1662    if (source._listeners != null) {
1663      if (_listeners == null)
1664        _listeners = new ArrayList JavaDoc<ClassLoaderListener>();
1665
1666      _listeners.addAll(source._listeners);
1667      source._listeners.clear();
1668    }
1669
1670    _securityManager = source._securityManager;
1671    if (source._permissions != null) {
1672      if (_permissions == null)
1673        _permissions = new ArrayList JavaDoc<Permission JavaDoc>();
1674
1675      _permissions.addAll(source._permissions);
1676    }
1677
1678    _codeSource = source._codeSource;
1679
1680    _lifecycle.copyState(source._lifecycle);
1681  }
1682
1683  public String JavaDoc toString()
1684  {
1685    if (_id != null)
1686      return "DynamicClassLoader[" + _id + "]";
1687    else
1688      return "DynamicClassLoader" + _loaders;
1689  }
1690
1691  private static L10N L()
1692  {
1693    if (_L == null)
1694      _L = new L10N(DynamicClassLoader.class);
1695
1696    return _L;
1697  }
1698
1699  protected static Logger JavaDoc log()
1700  {
1701    if (_log == null)
1702      _log = Logger.getLogger(DynamicClassLoader.class.getName());
1703
1704    return _log;
1705  }
1706
1707  // XXX: GC issues
1708
/*n
1709  protected void finalize()
1710  {
1711    destroy();
1712  }
1713  */

1714
1715  static {
1716    URL JavaDoc url = null;
1717
1718    try {
1719      url = new URL JavaDoc("file:/caucho/null");
1720    } catch (Throwable JavaDoc e) {
1721    }
1722
1723    NULL_URL = url;
1724  }
1725}
1726
Popular Tags