001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Contact: sequoia@continuent.org
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: *
019: * Initial developer(s): Marc Wick.
020: * Contributor(s): Emmanuel Cecchet.
021: */package org.continuent.sequoia.controller.connection;
022:
023: import java.io.BufferedInputStream;
024: import java.io.File;
025: import java.io.FileInputStream;
026: import java.io.FilenameFilter;
027: import java.io.IOException;
028: import java.net.MalformedURLException;
029: import java.net.URL;
030: import java.util.Enumeration;
031: import java.util.Hashtable;
032: import java.util.jar.JarFile;
033: import java.util.zip.ZipEntry;
034: import java.util.zip.ZipFile;
035: import java.util.zip.ZipInputStream;
036:
037: /**
038: * This class defines a DriverClassLoader used to load drivers with their own
039: * classloder to be able to handle different implementations of drivers sharing
040: * the same class name. For example if you want to connect to two backends of
041: * the same vendor, but running with different releases and requiring a driver
042: * compatible with the respective database release
043: *
044: * @author <a href="mailto:marc.wick@monte-bre.ch">Marc Wick </a>
045: * @author <a href="mailto:emmanuel.cecchet@emicnetworks.com">Emmanuel Cecchet</a>
046: * @version 1.0
047: */
048: public class DriverClassLoader extends ClassLoader {
049:
050: /** path on filesystem where the driver is located */
051: private File path = null;
052:
053: /**
054: * Creates a new <code>DriverClassLoader.java</code> object
055: *
056: * @param parent classloader, null if no parent classloader should be used
057: * @param pPath path where the driver classfiles of jar files are located
058: */
059: public DriverClassLoader(ClassLoader parent, File pPath) {
060: super (parent);
061: path = pPath;
062: if (path == null)
063: path = new File("");
064: }
065:
066: /**
067: * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
068: */
069: protected synchronized Class loadClass(String name, boolean resolve)
070: throws ClassNotFoundException {
071: try {
072: return super .loadClass(name, resolve);
073: } catch (ClassNotFoundException e) {
074: Class c = findClass(name);
075: if (resolve)
076: resolveClass(c);
077: return c;
078: }
079: }
080:
081: /**
082: * Finds the specified class including in jar files in the classpath
083: *
084: * @see java.lang.ClassLoader#findClass(java.lang.String)
085: */
086: protected Class findClass(String className)
087: throws ClassNotFoundException {
088: FileInputStream fis = null;
089:
090: try {
091: byte[] classBytes = null;
092:
093: // first we try to locate a class file.
094: String pathName = className
095: .replace('.', File.separatorChar);
096: File file = new File(path.getAbsolutePath(), pathName
097: + ".class");
098: if (file.exists()) {
099: // we have found a class file and read it
100: fis = new FileInputStream(file);
101: classBytes = new byte[fis.available()];
102: fis.read(classBytes);
103: } else {
104: // no class file exists we have to check jar files
105: classBytes = findClassInJarFile(path, className);
106: }
107:
108: // we convert the bytes into the specified class
109: Class clazz = defineClass(className, classBytes, 0,
110: classBytes.length);
111: return clazz;
112: } catch (Exception e) {
113: // We could not find the class, so indicate the problem with an exception
114: throw new ClassNotFoundException(className, e);
115: } finally {
116: if (null != fis) {
117: try {
118: fis.close();
119: } catch (Exception e) {
120: }
121: }
122: }
123: }
124:
125: /**
126: * we cache the contents of the jar files, as we don't want to have to read
127: * the file for every single class we are going to need
128: */
129: private Hashtable htJarContents = new Hashtable();
130:
131: /**
132: * Find the first jar file containing the className and load it
133: *
134: * @param dir directory where we are looking for jar files
135: * @param className name of the class we are looking for
136: * @return the class as byte[]
137: * @throws IOException if an error occurs
138: */
139: private byte[] findClassInJarFile(File dir, String className)
140: throws IOException {
141: // is the class already cached ?
142: String resourceName = convertClassNameToResourceName(className);
143: byte[] classBytes = (byte[]) htJarContents.get(resourceName);
144: if (classBytes != null) {
145: // it has been cached, we return
146: return classBytes;
147: }
148:
149: if (!dir.canRead())
150: throw new IOException(dir + " is not readable.");
151:
152: if (dir.isFile()) {
153: // driverPath specified a jar file
154: loadJarFile(dir.getAbsolutePath());
155: // after loading the jar file the class bytes are in the cache
156: return (byte[]) htJarContents.get(resourceName);
157: }
158:
159: // the class is not yet cached we have to find the right jar file
160:
161: // find all jar files in the directory
162: String[] jarFiles = dir.list(new FilenameFilter() {
163: public boolean accept(File dir, String name) {
164: return name.endsWith(".jar");
165: }
166: });
167:
168: if (jarFiles == null)
169: throw new IOException("Invalid path " + dir);
170:
171: // loop over jar files
172: for (int i = 0; i < jarFiles.length; i++) {
173: File file = new File(dir, jarFiles[i]);
174: JarFile jarFile = new JarFile(file);
175:
176: // we see whether the jar file contains the class we are looking for
177: // no need in loading jar files as long as we don't really need the
178: // content.
179: if (jarFile.getEntry(resourceName) != null) {
180: // we have found the right jar file and are loading it now
181: loadJarFile(jarFile.getName());
182:
183: // after loading the jar file the class bytes are in the cache
184: classBytes = (byte[]) htJarContents.get(resourceName);
185: }
186: }
187: return classBytes;
188: }
189:
190: /**
191: * @see java.lang.ClassLoader#findResource(java.lang.String)
192: */
193: protected URL findResource(String name) {
194:
195: // we try to locate the resource unjarred
196: if (path.isDirectory()) {
197: File searchResource = new File(path, name);
198: if (searchResource.exists()) {
199: try {
200: return searchResource.toURL();
201: } catch (MalformedURLException mfe) {
202: }
203: }
204: } else if (path.isFile()) {
205: // try getting the resource from the file
206: try {
207: new JarFile(path);
208: // convert the jar entry into URL format
209: return new URL("jar:" + path.toURL() + "!/" + name);
210: } catch (Exception e) {
211: // we couldn't find resource in file
212: return null;
213: }
214: }
215:
216: // now we are checking the jar files
217: try {
218: // find all jar files in the directory
219: String[] jarFiles = path.list(new FilenameFilter() {
220: public boolean accept(File dir, String name) {
221: return name.endsWith(".jar");
222: }
223: });
224: // loop over jar files
225: for (int i = 0; i < jarFiles.length; i++) {
226: File file = new File(path, jarFiles[i]);
227: JarFile jarFile = new JarFile(file);
228:
229: // we see whether the jar file contains the resource we are looking for
230: if (jarFile.getJarEntry(name) != null) {
231: // convert the jar entry into URL format
232: return new URL("jar:" + file.toURL() + "!/" + name);
233: }
234: }
235: } catch (Exception e) {
236: e.printStackTrace();
237: }
238: return null;
239: }
240:
241: /**
242: * convert the class name into the rescource name. This method is just
243: * replacing the '.' in the name with a '/' and adding the suffix '.class'
244: *
245: * @param className
246: * @return resource name
247: */
248: private String convertClassNameToResourceName(String className) {
249: String resourceName = className;
250: resourceName = resourceName.replace('.', '/');
251: resourceName = resourceName + ".class";
252: return resourceName;
253: }
254:
255: /**
256: * Load the contents of jar file in the cache
257: *
258: * @param jarFileName name of the jar file we want to load
259: * @throws IOException
260: */
261: private void loadJarFile(String jarFileName) throws IOException {
262: Hashtable htSizes = new Hashtable();
263: // extracts just sizes only.
264: // For a reason I dont' understand not all files return the size in the loop
265: // below (using ZipInputStream). So we cache the sizes here in case the loop
266: // below does not give us the file size
267: ZipFile zf = new ZipFile(jarFileName);
268: Enumeration e = zf.entries();
269: while (e.hasMoreElements()) {
270: ZipEntry ze = (ZipEntry) e.nextElement();
271:
272: htSizes.put(ze.getName(), new Integer((int) ze.getSize()));
273: }
274: zf.close();
275:
276: // extract resources and put them into the hashtable.
277: FileInputStream fis = new FileInputStream(jarFileName);
278: BufferedInputStream bis = new BufferedInputStream(fis);
279: ZipInputStream zis = new ZipInputStream(bis);
280: ZipEntry ze = null;
281: while ((ze = zis.getNextEntry()) != null) {
282: if (ze.isDirectory()) {
283: continue;
284: }
285:
286: int size = (int) ze.getSize();
287: // -1 means unknown size.
288: if (size == -1) {
289: // that is the reason we have cached the file size above.
290: size = ((Integer) htSizes.get(ze.getName())).intValue();
291: }
292:
293: byte[] b = new byte[size];
294: int rb = 0;
295: int chunk = 0;
296: while ((size - rb) > 0) {
297: chunk = zis.read(b, rb, size - rb);
298: if (chunk == -1) {
299: break;
300: }
301: rb += chunk;
302: }
303:
304: // add to internal resource hashtable
305: htJarContents.put(ze.getName(), b);
306: }
307:
308: }
309: }
|