PageRenderTime 57ms CodeModel.GetById 43ms app.highlight 7ms RepoModel.GetById 4ms app.codeStats 0ms

/jEdit/tags/jedit-4-1-pre5/bsh/BshClassManager.java

#
Java | 352 lines | 114 code | 37 blank | 201 comment | 21 complexity | 648736a259e909c16a538b0c4441c35c MD5 | raw file
  1/*****************************************************************************
  2 *                                                                           *
  3 *  This file is part of the BeanShell Java Scripting distribution.          *
  4 *  Documentation and updates may be found at http://www.beanshell.org/      *
  5 *                                                                           *
  6 *  Sun Public License Notice:                                               *
  7 *                                                                           *
  8 *  The contents of this file are subject to the Sun Public License Version  *
  9 *  1.0 (the "License"); you may not use this file except in compliance with *
 10 *  the License. A copy of the License is available at http://www.sun.com    * 
 11 *                                                                           *
 12 *  The Original Code is BeanShell. The Initial Developer of the Original    *
 13 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
 14 *  (C) 2000.  All Rights Reserved.                                          *
 15 *                                                                           *
 16 *  GNU Public License Notice:                                               *
 17 *                                                                           *
 18 *  Alternatively, the contents of this file may be used under the terms of  *
 19 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 20 *  provisions of LGPL are applicable instead of those above. If you wish to *
 21 *  allow use of your version of this file only under the  terms of the LGPL *
 22 *  and not to allow others to use your version of this file under the SPL,  *
 23 *  indicate your decision by deleting the provisions above and replace      *
 24 *  them with the notice and other provisions required by the LGPL.  If you  *
 25 *  do not delete the provisions above, a recipient may use your version of  *
 26 *  this file under either the SPL or the LGPL.                              *
 27 *                                                                           *
 28 *  Patrick Niemeyer (pat@pat.net)                                           *
 29 *  Author of Learning Java, O'Reilly & Associates                           *
 30 *  http://www.pat.net/~pat/                                                 *
 31 *                                                                           *
 32 *****************************************************************************/
 33
 34package bsh;
 35
 36import java.net.*;
 37import java.util.*;
 38import java.io.IOException;
 39import java.io.*;
 40
 41/**
 42	BshClassManager manages all classloading in BeanShell.
 43	It also supports a dynamically loaded extension (bsh.classpath package)
 44	which allows classpath extension and class file reloading.
 45
 46	Currently the extension relies on 1.2 for BshClassLoader and weak 
 47	references.  
 48
 49	See http://www.beanshell.org/manual/classloading.html for details
 50	on the bsh classloader architecture.
 51	<p>
 52
 53	Bsh has a multi-tiered class loading architecture.  No class loader is
 54	used unless/until the classpath is modified or a class is reloaded.
 55	<p>
 56
 57	Note: currently class loading features affect all instances of the
 58	Interpreter.  However the basic design of this class will allow for
 59	per instance class management in the future if it is desired.
 60*/
 61/*
 62	Implementation notes:
 63
 64	Note: we may need some synchronization in here
 65
 66	Note on jdk1.2 dependency:
 67	<p>
 68
 69	We are forced to use weak references here to accomodate all of the 
 70	fleeting namespace listeners as they fall out of scope.  (NameSpaces must 
 71	be informed if the class space changes so that they can un-cache names).  
 72	I had the thought that a way around this would be to implement BeanShell's 
 73	own garbage collector...  Then I came to my senses.
 74	<p>
 75
 76	Perhaps a simpler idea would be to have entities that reference cached
 77	types always perform a light weight check with a counter / reference
 78	value and use that to detect changes in the namespace.  This puts the 
 79	burden on the consumer to check at appropriate times, but could eliminate
 80	the need for the listener system in many places and the necessity of weak 
 81	references in this package.
 82	<p>
 83*/
 84public abstract class BshClassManager
 85{
 86	/** Singleton class manager */
 87	private static BshClassManager manager;
 88	private static boolean checkedForManager;
 89	// Use a hashtable as a Set...
 90	private static Object NOVALUE = new Object(); 
 91	
 92	/**
 93		An external classloader supplied by the setClassLoader() command.
 94	*/
 95	private static ClassLoader externalClassLoader;
 96
 97	/**
 98		Global cache for things we know are classes.
 99		Note: these should probably be re-implemented with Soft references.
100		(as opposed to strong or Weak)
101	*/
102    protected transient static Hashtable absoluteClassCache = new Hashtable();
103	/**
104		Global cache for things we know are *not* classes.
105		Note: these should probably be re-implemented with Soft references.
106		(as opposed to strong or Weak)
107	*/
108    protected transient static Hashtable absoluteNonClasses = new Hashtable();
109
110	// Begin static methods
111
112	/**
113		@return the BshClassManager singleton or null, indicating no
114		class manager is available.
115	*/
116// Note: this should probably throw Capabilities.Unavailable instead of
117// returning null
118	public static BshClassManager getClassManager() 
119	{
120		// Bootstrap the class manager if it exists
121
122		// have we loaded it before?
123		if ( !checkedForManager && manager == null )
124			// Do we have the necessary jdk1.2 packages?
125			try {
126				if ( plainClassForName("java.lang.ref.WeakReference") != null
127					&& plainClassForName("java.util.HashMap")  != null )
128				{
129					// try to load the implementation
130					Class bcm = plainClassForName(
131						"bsh.classpath.ClassManagerImpl");
132					manager = (BshClassManager)bcm.newInstance();
133				}
134			} catch ( ClassNotFoundException e ) {
135				//System.err.println("No class manager available.");
136			} catch ( Exception e ) {
137				System.err.println("Error loading classmanager: "+e);
138			}
139
140		checkedForManager = true;
141		return manager;
142	}
143
144	public static boolean classExists( String name ) {
145		return ( classForName( name ) != null );
146	}
147
148	/**
149		Load the specified class by name, taking into account added classpath
150		and reloaded classes, etc.
151		@return the class or null
152	*/
153	public static Class classForName( String name ) {
154		BshClassManager manager = getClassManager(); // prime the singleton
155		if ( manager != null )
156			return manager.getClassForName( name );
157		else
158			try {
159				return plainClassForName( name );
160			} catch ( ClassNotFoundException e ) {
161				return null;
162			}
163	}
164
165	/**
166		Perform a plain Class.forName() or call the externally provided
167		class loader.
168		If a BshClassManager implementation is loaded the call will be 
169		delegated to it, to allow for additional hooks.
170
171		This simply wraps that bottom level class lookup call and provides a 
172		central point for monitoring and handling certain Java version 
173		dependent bugs, etc.
174
175		@see #getPlainClassForName( String )
176		@return the class
177	*/
178	public static Class plainClassForName( String name ) 
179		throws ClassNotFoundException 
180	{
181		try {
182			Class c;
183			if ( externalClassLoader != null )
184				c = externalClassLoader.loadClass( name );
185			else {
186				// If BCM exists, delegate to it.
187				BshClassManager bcm = manager; // Don't create if not there yet.
188				if ( bcm != null )
189					c = bcm.getPlainClassForName( name );				
190				else
191					c = Class.forName( name );
192			}
193
194			cacheClassInfo( name, c );
195			return c;
196		/*
197			This is weird... jdk under Win is throwing these to
198			warn about lower case / upper case possible mismatch.
199			e.g. bsh.console bsh.Console
200		*/
201		} catch ( NoClassDefFoundError e ) {
202			cacheClassInfo( name, null ); // non-class
203			throw new ClassNotFoundException( e.toString() );
204		}
205	}
206
207	/**
208		Cache info about whether name is a class or not.
209		@param value 
210			if value is non-null, cache the class
211			if value is null, set the flag that it is *not* a class to
212			speed later resolution
213	*/
214	public static void cacheClassInfo( String name, Class value ) {
215		if ( value != null )
216			absoluteClassCache.put( name, value );
217		else
218			absoluteNonClasses.put( name, NOVALUE );
219	}
220
221	/**
222		Clear the static caches in BshClassManager
223	*/
224	protected void clearCaches() {
225    	absoluteNonClasses = new Hashtable();
226    	absoluteClassCache = new Hashtable();
227	}
228
229	/**
230		Add a BshClassManager.Listener to the class manager.
231		The listener is informed upon changes to the classpath.
232		This is a static convenience form of BshClassManager addListener().
233		If there is no class manager the listener will be ignored.
234	*/
235	public static void addCMListener( Listener l ) {
236		getClassManager(); // prime it
237		if ( manager != null )
238			manager.addListener( l );
239	}
240
241	/**
242		Set an external class loader.  BeanShell will use this at the same 
243		point it would otherwise use the plain Class.forName().
244		i.e. if no explicit classpath management is done from the script
245		(addClassPath(), setClassPath(), reloadClasses()) then BeanShell will
246		only use the supplied classloader.  If additional classpath management
247		is done then BeanShell will perform that in addition to the supplied
248		external classloader.
249		However BeanShell is not currently able to reload
250		classes supplied through the external classloader.
251	*/
252	public static void setClassLoader( ClassLoader externalCL ) 
253	{
254		externalClassLoader = externalCL;
255		BshClassManager bcm = getClassManager();
256		if ( bcm != null )
257			bcm.classLoaderChanged();
258	}
259
260	// end static methods
261
262	public static interface Listener 
263	{
264		public void classLoaderChanged();
265	}
266
267	// Begin interface methods
268
269	/** @see #classForName( String ) */
270	public abstract Class getClassForName( String name );
271
272	/** 
273		Delegate for plainClassForName.
274		@see #plainClassForName( String )
275	 */
276	public abstract Class getPlainClassForName( String name ) 
277		throws ClassNotFoundException ;
278
279	public abstract ClassLoader getBaseLoader();
280
281	public abstract ClassLoader getLoaderForClass( String name );
282
283	public abstract void addClassPath( URL path )
284		throws IOException;
285
286	/**
287		Clear all loaders and start over.  No class loading.
288	*/
289	public abstract void reset();
290
291	/**
292		Set a new base classpath and create a new base classloader.
293		This means all types change. 
294	*/
295	public abstract void setClassPath( URL [] cp );
296
297	/**
298		Overlay the entire path with a new class loader.
299		Set the base path to the user path + base path.
300
301		No point in including the boot class path (can't reload thos).
302	*/
303	public abstract void reloadAllClasses() throws ClassPathException;
304
305	/**
306		Reloading classes means creating a new classloader and using it
307		whenever we are asked for classes in the appropriate space.
308		For this we use a DiscreteFilesClassLoader
309	*/
310	public abstract void reloadClasses( String [] classNames )
311		throws ClassPathException;
312
313	/**
314		Reload all classes in the specified package: e.g. "com.sun.tools"
315
316		The special package name "<unpackaged>" can be used to refer 
317		to unpackaged classes.
318	*/
319	public abstract void reloadPackage( String pack ) 
320		throws ClassPathException ;
321
322	/**
323		This has been removed from the interface to shield the core from the
324		rest of the classpath package. If you need the classpath you will have
325		to cast the classmanager to its impl.
326
327		public abstract BshClassPath getClassPath() throws ClassPathException;
328	*/
329
330	/**
331		Support for "import *;"
332		Hide details in here as opposed to NameSpace.
333	Note: this used to be package private...
334	*/
335	public abstract void doSuperImport() throws EvalError;
336
337	/**
338		Return the name or null if none is found,
339		Throw an ClassPathException containing detail if name is ambigous.
340	Note: this used to be package private...
341	*/
342	public abstract String getClassNameByUnqName( String name ) 
343		throws ClassPathException;
344
345	public abstract void addListener( Listener l );
346
347	public abstract void removeListener( Listener l );
348
349	public abstract void dump( PrintWriter pw );
350
351	protected abstract void classLoaderChanged();
352}