001: /*
002: * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
003: * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to license terms.
004: */
005: package com.sun.portal.desktop.context;
006:
007: import java.io.ByteArrayOutputStream;
008: import java.io.FileInputStream;
009: import java.io.InputStream;
010: import java.io.IOException;
011: import java.io.File;
012:
013: import java.net.URL;
014: import java.net.MalformedURLException;
015: import java.net.URLClassLoader;
016:
017: import java.util.Collection;
018: import java.util.HashSet;
019: import java.util.ArrayList;
020: import java.util.List;
021: import java.util.logging.Level;
022: import java.util.logging.Logger;
023: import java.util.zip.ZipFile;
024: import java.util.zip.ZipEntry;
025:
026: import java.lang.ClassLoader;
027:
028: import com.sun.portal.log.common.PortalLogger;
029:
030: /**
031: * The ProviderClassLoader is responsible for loading new or modified
032: * provider classes.
033: *
034: */
035: public class ProviderClassLoader extends ClassLoader {
036:
037: private static ClassInfoCache cic = null;
038: private static String baseSearchDir = null;
039:
040: private static ProviderClassLoader instance;
041: private static URLClassLoader urlClassLoader;
042: private ClassLoader parent = null;
043: private Collection failedToLoadClassSet;
044: private static Logger logger = PortalLogger
045: .getLogger(ProviderClassLoader.class);
046:
047: private List listeners;
048:
049: public static synchronized ProviderClassLoader getInstance(
050: String providerBaseDir) {
051:
052: // create a new ProviderClassLoader instance when the loader is
053: // out of date.
054: if (instance == null) {
055: instance = new ProviderClassLoader(providerBaseDir);
056: }
057: return instance;
058: }
059:
060: public static synchronized ProviderClassLoader getInstance(
061: String providerClassName, String providerBaseDir) {
062:
063: // create a new ProviderClassLoader instance when the loader is
064: // out of date.
065:
066: if (instance == null
067: || instance.isProviderClassOutOfDate(providerClassName)) {
068: instance = new ProviderClassLoader(providerBaseDir);
069: }
070: return instance;
071: }
072:
073: /**
074: * This Constructor ensures that a values is set to the member variable
075: * <CODE>baseSearchDir</CODE>. It calls the loadClass() method of the parent
076: * to load the provider class
077: * @param providerBaseDir Directory from which provider classes will be
078: * loaded as String
079: */
080: private ProviderClassLoader(String providerBaseDir) {
081: super (ProviderClassLoader.class.getClassLoader());
082:
083: parent = ProviderClassLoader.class.getClassLoader();
084: cic = ClassInfoCache.getInstance(providerBaseDir);
085: if (cic != null) {
086: baseSearchDir = providerBaseDir;
087: }
088: failedToLoadClassSet = new HashSet();
089: listeners = new ArrayList();
090:
091: try {
092: if (System.getProperty("os.name").indexOf("indows") != -1)
093: urlClassLoader = URLClassLoader
094: .newInstance(new URL[] { new URL("file:///"
095: + providerBaseDir + "/") });
096: else
097: urlClassLoader = URLClassLoader
098: .newInstance(new URL[] { new URL("file://"
099: + providerBaseDir + "/") });
100: } catch (MalformedURLException e) {
101: logger.log(Level.SEVERE, "PSDT_CSPDC0003", e);
102: }
103:
104: }
105:
106: public synchronized void addListener(
107: ProviderClassLoaderListener pcll) {
108: listeners.add(pcll);
109: }
110:
111: public synchronized void notifyListeners() {
112: for (int i = 0; i < listeners.size(); i++) {
113: ((ProviderClassLoaderListener) listeners.get(i))
114: .providerClassLoaderOutdated();
115: }
116: listeners.clear();
117: }
118:
119: /**
120: * Checks if the provider class has been modified on the file system
121: * @return <I>true</I> if the class has been modified since it was loaded
122: */
123: public boolean isProviderClassOutOfDate(String theClassName) {
124: boolean outdated = false;
125:
126: File fileHandle = cic.getHandle(theClassName);
127: if (fileHandle != null) {
128: // If class information exists, this class was loaded from the
129: // Provider classpath either from a class file or a Jar
130: long lastModTime = cic.getModifiedTime(theClassName);
131: if (fileHandle != null && fileHandle.exists()) {
132: // If lastmodified time of this classfile or Jar file to which
133: // this class belongs to is greater than the value this class
134: // information is stored in the cache then set the state to true
135: if (fileHandle.lastModified() > lastModTime) {
136: outdated = true;
137: cic.removeClassInfo(theClassName);
138: }
139:
140: } else {
141: // If file handle is not more a valid file class file could have
142: // could have been moved into a Jar or if it was previously
143: // loaded from a Jar the Jarfile could have been renamed
144: outdated = true;
145: // Remove information about its class as the handle is invalid
146: cic.removeClassInfo(theClassName);
147: }
148: } else {
149: // If classInfo is null the provider was loaded initially by the
150: // system classloader. Or the Provider Class loader failed to load
151: // this class. If it failed to load the class then an attempt should
152: // be made to load it again. Hence set outdated flag to true.
153: if (failedToLoadClassSet.contains(theClassName)) {
154: outdated = true;
155: }
156: }
157:
158: if (outdated == true) {
159: notifyListeners();
160: }
161:
162: return outdated;
163: }
164:
165: /**
166: * This method overriddes the findClass method in the parent. It is called
167: * by the loadclass method of the parent - java.lang.ClassLoader.
168: * The parent calls this method at step (3) in the following
169: * list of steps:
170: * (1) Call <CODE>findLoadedClass(String)</CODE> to check if the class has
171: * already been loaded.
172: * (2) Call the <CODE>loadClass</CODE> method on the parent class loader.
173: * (3) Call the <CODE>findClass(String)</CODE> method to find the class.
174: *
175: * @param className the class to load
176: * @return Class representing the loaded class
177: * @exception ClassNotFoundException Throws Class Not Found exception
178: */
179:
180: public Class findClass(String className)
181: throws ClassNotFoundException {
182: Class rclass = null;
183: byte[] classBytes = null;
184:
185: try {
186: classBytes = findLocalClass(className);
187: } catch (IOException ioe) {
188: throw new ClassNotFoundException(
189: "ProviderClassLoader.findClass() : not found class Name = "
190: + className, ioe);
191: }
192:
193: if (classBytes != null) {
194:
195: rclass = defineClass(className, classBytes, 0,
196: classBytes.length);
197: if (rclass == null) {
198: failedToLoadClassSet.add(className);
199: throw new ClassNotFoundException(
200: "ProviderClassLoader.findClass() : not found class Name = "
201: + className);
202: }
203:
204: } else {
205: failedToLoadClassSet.add(className);
206: throw new ClassNotFoundException(
207: "ProviderClassLoader.findClass() : not found class Name = "
208: + className);
209: }
210:
211: return rclass;
212: }
213:
214: /**
215: * This method is responsible for finding the local class file and
216: * returning its byte representation.
217: *
218: * @param className the class to load
219: * @return byte[] representing the class bytes
220: * @exception IOException Throws I/O exception
221: */
222: public byte[] findLocalClass(String className) throws IOException {
223:
224: byte[] result = null;
225: String cString = className.replace('.', '/') + ".class";
226: String fullFileLocation = baseSearchDir + '/' + cString;
227: File classFile = new File(fullFileLocation);
228:
229: if (!classFile.exists()) {
230: File jarFile = cic.getJarFileHandle(className);
231: if (jarFile != null && jarFile.isFile()) {
232: ZipFile zip = new ZipFile(jarFile);
233: ZipEntry entryInJar = zip.getEntry(cString);
234: InputStream istream = zip.getInputStream(entryInJar);
235: result = getClassData(istream);
236: cic.setClassInfo(className, true, jarFile);
237: istream.close();
238: zip.close();
239:
240: }
241:
242: } else {
243: FileInputStream fi = new FileInputStream(classFile);
244: result = getClassData(fi);
245: cic.setClassInfo(className, false, classFile);
246: fi.close();
247:
248: }
249:
250: return result;
251: }
252:
253: /**
254: * Gets a byte array from an InputStream
255: *
256: * @return byte bytearray specifying data from an InputStream
257: * @exception IOException if any InputOutput exception occurs while reading the Stream
258: */
259: private byte[] getClassData(InputStream cistream)
260: throws IOException {
261:
262: if (cistream == null) {
263: return null;
264: }
265: byte[] byteBuf = new byte[1024];
266:
267: ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
268: int len = 0;
269:
270: while ((len = cistream.read(byteBuf)) != -1) {
271: byteOut.write(byteBuf, 0, len);
272: }
273:
274: return byteOut.toByteArray();
275: }
276:
277: /**
278: * This method overrides the one in the the parent java.lang.ClassLoader
279: * It is called to get the URL of a resources like property files
280: *
281: * @return A <tt>URL</tt> object for reading the resource, or
282: * <tt>null</tt> if the resource could not be found
283: *
284: * @param name Name of the resource that the classloader is doing the lookup
285: * for.
286: */
287:
288: protected URL findResource(String name) {
289: URL resURL = null;
290: if (urlClassLoader != null) {
291: resURL = urlClassLoader.getResource(name);
292: } else {
293: resURL = super.findResource(name);
294: }
295: return resURL;
296: }
297: }
|