PageRenderTime 115ms CodeModel.GetById 91ms app.highlight 19ms RepoModel.GetById 2ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/JARClassLoader.java

#
Java | 439 lines | 301 code | 54 blank | 84 comment | 51 complexity | 2231d81ab28d2c340b01d805dcde7937 MD5 | raw file
  1/*
  2 * JARClassLoader.java - Loads classes from JAR files
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 1999, 2003 Slava Pestov
  7 * Portions copyright (C) 1999 mike dillon
  8 *
  9 * This program is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU General Public License
 11 * as published by the Free Software Foundation; either version 2
 12 * of the License, or any later version.
 13 *
 14 * This program 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.  See the
 17 * GNU General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU General Public License
 20 * along with this program; if not, write to the Free Software
 21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 22 */
 23
 24package org.gjt.sp.jedit;
 25
 26//{{{ Imports
 27import java.io.InputStream;
 28import java.io.IOException;
 29import java.net.URL;
 30import java.util.*;
 31import java.util.zip.ZipEntry;
 32import java.util.zip.ZipFile;
 33import org.gjt.sp.util.Log;
 34
 35import java.util.jar.Manifest;
 36import java.util.jar.JarFile;
 37import java.net.MalformedURLException;
 38import java.util.jar.Attributes;
 39import java.util.jar.Attributes.Name;
 40//}}}
 41
 42/**
 43 * A class loader implementation that loads classes from JAR files. All
 44 * instances share the same set of classes.
 45 * @author Slava Pestov
 46 * @version $Id: JARClassLoader.java 5327 2006-01-01 18:40:29Z ezust $
 47 */
 48public class JARClassLoader extends ClassLoader
 49{
 50	//{{{ JARClassLoader constructor
 51	/**
 52	 * This constructor creates a class loader for loading classes from all
 53	 * plugins. For example BeanShell uses one of these so that scripts can
 54	 * use plugin classes.
 55	 */
 56	public JARClassLoader()
 57	{
 58		// for debugging
 59		id = INDEX++;
 60		live++;
 61	} //}}}
 62
 63	//{{{ loadClass() method
 64	/**
 65	 * @exception ClassNotFoundException if the class could not be found
 66	 */
 67	public Class loadClass(String clazz, boolean resolveIt)
 68		throws ClassNotFoundException
 69	{
 70		// see what JARClassLoader this class is in
 71		Object obj = classHash.get(clazz);
 72		if(obj == NO_CLASS)
 73		{
 74			// we remember which classes we don't exist
 75			// because BeanShell tries loading all possible
 76			// <imported prefix>.<class name> combinations
 77			throw new ClassNotFoundException(clazz);
 78		}
 79		else if(obj instanceof JARClassLoader)
 80		{
 81			JARClassLoader classLoader = (JARClassLoader)obj;
 82			return classLoader._loadClass(clazz,resolveIt);
 83		}
 84
 85		// if it's not in the class hash, and not marked as
 86		// non-existent, try loading it from the CLASSPATH
 87		try
 88		{
 89			Class cls;
 90
 91			/* Defer to whoever loaded us (such as JShell,
 92			 * Echidna, etc) */
 93			ClassLoader parentLoader = getClass().getClassLoader();
 94			if (parentLoader != null)
 95				cls = parentLoader.loadClass(clazz);
 96			else
 97				cls = findSystemClass(clazz);
 98
 99			return cls;
100		}
101		catch(ClassNotFoundException cnf)
102		{
103			// remember that this class doesn't exist for
104			// future reference
105			classHash.put(clazz,NO_CLASS);
106
107			throw cnf;
108		}
109	} //}}}
110
111	//{{{ getResourceAsStream() method
112	public InputStream getResourceAsStream(String name)
113	{
114		if(jar == null)
115			return null;
116
117		try
118		{
119			ZipFile zipFile = jar.getZipFile();
120			ZipEntry entry = zipFile.getEntry(name);
121			if(entry == null)
122				return getSystemResourceAsStream(name);
123			else
124				return zipFile.getInputStream(entry);
125		}
126		catch(IOException io)
127		{
128			Log.log(Log.ERROR,this,io);
129
130			return null;
131		}
132	} //}}}
133
134	//{{{ getResource() method
135	public URL getResource(String name)
136	{
137		if(jar == null)
138			return null;
139
140		try
141		{
142			ZipFile zipFile = jar.getZipFile();
143			ZipEntry entry = zipFile.getEntry(name);
144			if(entry == null)
145				return getSystemResource(name);
146			else
147				return new URL(getResourceAsPath(name));
148		}
149		catch(IOException io)
150		{
151			Log.log(Log.ERROR,this,io);
152			return null;
153		}
154	} //}}}
155
156	//{{{ getResourceAsPath() method
157	public String getResourceAsPath(String name)
158	{
159		if(jar == null)
160			return null;
161
162		if(!name.startsWith("/"))
163			name = "/" + name;
164
165		return "jeditresource:/" + MiscUtilities.getFileName(
166			jar.getPath()) + "!" + name;
167	} //}}}
168
169	//{{{ getZipFile() method
170	/**
171	 * @deprecated Call <code>PluginJAR.getZipFile()</code> instead.
172	 */
173	public ZipFile getZipFile()
174	{
175		try
176		{
177			return jar.getZipFile();
178		}
179		catch(IOException io)
180		{
181			Log.log(Log.ERROR,this,io);
182			return null;
183		}
184	} //}}}
185
186	//{{{ dump() method
187	/**
188	 * For debugging.
189	 */
190	public static void dump()
191	{
192		Log.log(Log.DEBUG,JARClassLoader.class,
193			"Total instances created: " + INDEX);
194		Log.log(Log.DEBUG,JARClassLoader.class,
195			"Live instances: " + live);
196		synchronized(classHash)
197		{
198			Iterator entries = classHash.entrySet().iterator();
199			while(entries.hasNext())
200			{
201				Map.Entry entry = (Map.Entry)entries.next();
202				if(entry.getValue() != NO_CLASS)
203				{
204					Log.log(Log.DEBUG,JARClassLoader.class,
205						entry.getKey() + " ==> "
206						+ entry.getValue());
207				}
208			}
209		}
210	} //}}}
211
212	//{{{ toString() method
213	public String toString()
214	{
215		if(jar == null)
216			return "<anonymous>(" + id + ")";
217		else
218			return jar.getPath() + " (" + id + ")";
219	} //}}}
220
221	//{{{ findResources() method
222	protected Enumeration findResources(String name) throws IOException
223	{
224		class SingleElementEnumeration implements Enumeration
225		{
226			private Object element;
227
228			public SingleElementEnumeration(Object element)
229			{
230				this.element = element;
231			}
232
233			public boolean hasMoreElements()
234			{
235				return (element != null);
236			}
237
238			public Object nextElement()
239			{
240				if(element != null)
241				{
242					Object retval = element;
243					element = null;
244					return retval;
245				}
246				else
247					throw new NoSuchElementException();
248			}
249		}
250
251		URL resource = getResource(name);
252		return new SingleElementEnumeration(resource);
253	} //}}}
254
255	//{{{ finalize() method
256	protected void finalize()
257	{
258		live--;
259	} //}}}
260
261	//{{{ Package-private members
262
263	//{{{ JARClassLoader constructor
264	/**
265	 * @since jEdit 4.2pre1
266	 */
267	JARClassLoader(PluginJAR jar)
268	{
269		this();
270		this.jar = jar;
271	} //}}}
272
273	//{{{ activate() method
274	void activate()
275	{
276		String[] classes = jar.getClasses();
277		if(classes != null)
278		{
279			for(int i = 0; i < classes.length; i++)
280			{
281				classHash.put(classes[i],this);
282			}
283		}
284	} //}}}
285
286	//{{{ deactivate() method
287	void deactivate()
288	{
289		String[] classes = jar.getClasses();
290		if(classes == null)
291			return;
292
293		for(int i = 0; i < classes.length; i++)
294		{
295			Object loader = classHash.get(classes[i]);
296			if(loader == this)
297				classHash.remove(classes[i]);
298			else
299				/* two plugins provide same class! */;
300		}
301	} //}}}
302
303	//}}}
304
305	//{{{ Private members
306
307	// used to mark non-existent classes in class hash
308	private static final Object NO_CLASS = new Object();
309
310	private static int INDEX;
311	private static int live;
312	private static Hashtable classHash = new Hashtable();
313
314	private int id;
315	private PluginJAR jar;
316
317	//{{{ _loadClass() method
318	/**
319	 * Load class from this JAR only.
320	 */
321	private synchronized Class _loadClass(String clazz, boolean resolveIt)
322		throws ClassNotFoundException
323	{
324		jar.activatePlugin();
325
326		synchronized(this)
327		{
328			Class cls = findLoadedClass(clazz);
329			if(cls != null)
330			{
331				if(resolveIt)
332					resolveClass(cls);
333				return cls;
334			}
335
336			String name = MiscUtilities.classToFile(clazz);
337
338			try
339			{
340				definePackage(clazz);
341				ZipFile zipFile = jar.getZipFile();
342				ZipEntry entry = zipFile.getEntry(name);
343
344				if(entry == null)
345					throw new ClassNotFoundException(clazz);
346
347				InputStream in = zipFile.getInputStream(entry);
348
349				int len = (int)entry.getSize();
350				byte[] data = new byte[len];
351				int success = 0;
352				int offset = 0;
353				while(success < len)
354				{
355					len -= success;
356					offset += success;
357					success = in.read(data,offset,len);
358					if(success == -1)
359					{
360						Log.log(Log.ERROR,this,"Failed to load class "
361							+ clazz + " from " + zipFile.getName());
362						throw new ClassNotFoundException(clazz);
363					}
364				}
365
366				cls = defineClass(clazz,data,0,data.length);
367
368				if(resolveIt)
369					resolveClass(cls);
370
371				return cls;
372			}
373			catch(IOException io)
374			{
375				Log.log(Log.ERROR,this,io);
376
377				throw new ClassNotFoundException(clazz);
378			}
379		}
380	} //}}}
381
382	//{{{ definePackage(clazz) method
383	private void definePackage(String clazz) throws IOException 
384	{
385		int idx = clazz.lastIndexOf('.');
386		if (idx != -1) {		
387			String name = clazz.substring(0, idx);
388			if (getPackage(name) == null) definePackage(name, new JarFile(jar.getFile()).getManifest());
389		}
390	} //}}}
391	
392	//{{{ getMfValue() method
393	private String getMfValue(Attributes sectionAttrs, Attributes mainAttrs, Attributes.Name name) 
394	{
395		String value=null;
396		if (sectionAttrs != null)
397			value = sectionAttrs.getValue(name);
398		else if (mainAttrs != null) {
399			value = mainAttrs.getValue(name);
400		}
401		return value;
402	}
403	//}}}
404	
405	//{{{ definePackage(packageName, manifest) method
406	private void definePackage(String name, Manifest mf) 
407	{
408		if (mf==null) 
409		{
410			definePackage(name, null, null, null, null, null,
411			null, null);
412			return;
413		}
414
415		Attributes sa = mf.getAttributes(name.replace('.', '/') + "/");
416		Attributes ma = mf.getMainAttributes();
417		
418		URL sealBase = null;		
419		if (Boolean.valueOf(getMfValue(sa, ma, Name.SEALED)).booleanValue()) 
420		{
421			try 
422			{
423				sealBase = jar.getFile().toURL();
424			} catch (MalformedURLException e) { }
425		}
426		
427		Package pkg=definePackage(
428			name, 
429			getMfValue(sa, ma, Name.SPECIFICATION_TITLE), 
430			getMfValue(sa, ma, Name.SPECIFICATION_VERSION), 
431			getMfValue(sa, ma, Name.SPECIFICATION_VENDOR), 
432			getMfValue(sa, ma, Name.IMPLEMENTATION_TITLE), 
433			getMfValue(sa, ma, Name.IMPLEMENTATION_VERSION), 
434			getMfValue(sa, ma, Name.IMPLEMENTATION_VENDOR), 
435			sealBase);
436	} //}}}
437	
438	//}}}
439}