PageRenderTime 90ms CodeModel.GetById 79ms app.highlight 7ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-0-pre3/bsh/BshClassManager.java

#
Java | 332 lines | 107 code | 36 blank | 189 comment | 18 complexity | 0c43b46498b4e99d7219235a91e7d0f3 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() 
167		This simply wraps that method call and provides a central point for
168		monitoring and handling certain Java version dependent bugs, etc.
169		Note: this used to be called loadSystemClass()
170		@return the class
171	*/
172	public static Class plainClassForName( String name ) 
173		throws ClassNotFoundException 
174	{
175		try {
176			Class c;
177			if ( externalClassLoader != null ) {
178				c = externalClassLoader.loadClass( name );
179			}else
180				c = Class.forName(name);
181
182			cacheClassInfo( name, c );
183			return c;
184		/*
185			This is weird... jdk under Win is throwing these to
186			warn about lower case / upper case possible mismatch.
187			e.g. bsh.console bsh.Console
188		*/
189		} catch ( NoClassDefFoundError e ) {
190			cacheClassInfo( name, null ); // non-class
191			throw new ClassNotFoundException( e.toString() );
192		}
193	}
194
195	/**
196		Cache info about whether name is a class or not.
197		@param value 
198			if value is non-null, cache the class
199			if value is null, set the flag that it is *not* a class to
200			speed later resolution
201	*/
202	public static void cacheClassInfo( String name, Class value ) {
203		if ( value != null )
204			absoluteClassCache.put( name, value );
205		else
206			absoluteNonClasses.put( name, NOVALUE );
207	}
208
209	/**
210		Clear the static caches in BshClassManager
211	*/
212	protected void clearCaches() {
213    	absoluteNonClasses = new Hashtable();
214    	absoluteClassCache = new Hashtable();
215	}
216
217	/**
218		Add a BshClassManager.Listener to the class manager.
219		The listener is informed upon changes to the classpath.
220		This is a static convenience form of BshClassManager addListener().
221		If there is no class manager the listener will be ignored.
222	*/
223	public static void addCMListener( Listener l ) {
224		getClassManager(); // prime it
225		if ( manager != null )
226			manager.addListener( l );
227	}
228
229	/**
230		Set an external class loader.  BeanShell will use this at the same 
231		point it would otherwise use the plain Class.forName().
232		i.e. if no explicit classpath management is done from the script
233		(addClassPath(), setClassPath(), reloadClasses()) then BeanShell will
234		only use the supplied classloader.  If additional classpath management
235		is done then BeanShell will perform that in addition to the supplied
236		external classloader.
237		However BeanShell is not currently able to reload
238		classes supplied through the external classloader.
239	*/
240	public static void setClassLoader( ClassLoader externalCL ) 
241	{
242		externalClassLoader = externalCL;
243		BshClassManager bcm = getClassManager();
244		if ( bcm != null )
245			bcm.classLoaderChanged();
246	}
247
248	// end static methods
249
250	public static interface Listener 
251	{
252		public void classLoaderChanged();
253	}
254
255	// Begin interface methods
256
257	public abstract Class getClassForName( String name );
258
259	public abstract ClassLoader getBaseLoader();
260
261	public abstract ClassLoader getLoaderForClass( String name );
262
263	public abstract void addClassPath( URL path )
264		throws IOException;
265
266	/**
267		Clear all loaders and start over.  No class loading.
268	*/
269	public abstract void reset();
270
271	/**
272		Set a new base classpath and create a new base classloader.
273		This means all types change. 
274	*/
275	public abstract void setClassPath( URL [] cp );
276
277	/**
278		Overlay the entire path with a new class loader.
279		Set the base path to the user path + base path.
280
281		No point in including the boot class path (can't reload thos).
282	*/
283	public abstract void reloadAllClasses() throws ClassPathException;
284
285	/**
286		Reloading classes means creating a new classloader and using it
287		whenever we are asked for classes in the appropriate space.
288		For this we use a DiscreteFilesClassLoader
289	*/
290	public abstract void reloadClasses( String [] classNames )
291		throws ClassPathException;
292
293	/**
294		Reload all classes in the specified package: e.g. "com.sun.tools"
295
296		The special package name "<unpackaged>" can be used to refer 
297		to unpackaged classes.
298	*/
299	public abstract void reloadPackage( String pack ) 
300		throws ClassPathException ;
301
302	/**
303		This has been removed from the interface to shield the core from the
304		rest of the classpath package. If you need the classpath you will have
305		to cast the classmanager to its impl.
306
307		public abstract BshClassPath getClassPath() throws ClassPathException;
308	*/
309
310	/**
311		Support for "import *;"
312		Hide details in here as opposed to NameSpace.
313	Note: this used to be package private...
314	*/
315	public abstract void doSuperImport() throws EvalError;
316
317	/**
318		Return the name or null if none is found,
319		Throw an ClassPathException containing detail if name is ambigous.
320	Note: this used to be package private...
321	*/
322	public abstract String getClassNameByUnqName( String name ) 
323		throws ClassPathException;
324
325	public abstract void addListener( Listener l );
326
327	public abstract void removeListener( Listener l );
328
329	public abstract void dump( PrintWriter pw );
330
331	protected abstract void classLoaderChanged();
332}