Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

DriverClassLoader.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2004 French National Institute For Research In Computer
00004  * Science And Control (INRIA).
00005  * Contact: c-jdbc@objectweb.org
00006  * 
00007  * This library is free software; you can redistribute it and/or modify it
00008  * under the terms of the GNU Lesser General Public License as published by the
00009  * Free Software Foundation; either version 2.1 of the License, or any later
00010  * version.
00011  * 
00012  * This library is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this library; if not, write to the Free Software Foundation,
00019  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00020  *
00021  * Initial developer(s): Marc Wick.
00022  * Contributor(s): ______________________.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.connection;
00026 
00027 import java.io.BufferedInputStream;
00028 import java.io.File;
00029 import java.io.FileInputStream;
00030 import java.io.FilenameFilter;
00031 import java.io.IOException;
00032 import java.net.MalformedURLException;
00033 import java.net.URL;
00034 import java.util.Enumeration;
00035 import java.util.Hashtable;
00036 import java.util.jar.JarFile;
00037 import java.util.zip.ZipEntry;
00038 import java.util.zip.ZipFile;
00039 import java.util.zip.ZipInputStream;
00040 
00041 /**
00042  * This class defines a DriverClassLoader
00043  * 
00044  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00045  * @version 1.0
00046  */
00047 /**
00048  * This class defines a DriverClassLoader used to load drivers with their own
00049  * classloder to be able to handle different implementations of drivers sharing
00050  * the same class name. For example if you want to connect to two backends of
00051  * the same vendor, but running with different releases and requiring a driver
00052  * compatible with the respective database release
00053  * 
00054  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00055  * @version 1.0
00056  */
00057 public class DriverClassLoader extends ClassLoader
00058 {
00059 
00060   /** path on filesystem where the driver is located */
00061   private File path = null;
00062 
00063   /**
00064    * Creates a new <code>DriverClassLoader.java</code> object
00065    * 
00066    * @param parent classloader, null if no parent classloader should be used
00067    * @param pPath path where the driver classfiles of jar files are located
00068    */
00069   DriverClassLoader(ClassLoader parent, File pPath)
00070   {
00071     super(parent);
00072     path = pPath;
00073     if (path == null)
00074       path = new File("");
00075 
00076   }
00077 
00078   /**
00079    * finds the specified class
00080    * 
00081    * @see java.lang.ClassLoader#findClass(java.lang.String)
00082    */
00083   protected Class findClass(String className) throws ClassNotFoundException
00084   {
00085 
00086     FileInputStream fis = null;
00087 
00088     try
00089     {
00090       byte[] classBytes = null;
00091 
00092       // first we try to locate a class file.
00093       String pathName = className.replace('.', File.separatorChar);
00094       File file = new File(path.getAbsolutePath(), pathName + ".class");
00095       if (file.exists())
00096       {
00097         // we have found a class file and read it
00098         fis = new FileInputStream(file);
00099         classBytes = new byte[fis.available()];
00100         fis.read(classBytes);
00101       }
00102       else
00103       {
00104         // no class file exists we have to check jar files
00105         classBytes = findClassInJarFile(path, className);
00106       }
00107 
00108       // we convert the bytes into the specified class
00109       Class clazz = defineClass(null, classBytes, 0, classBytes.length);
00110       return clazz;
00111     }
00112     catch (Exception e)
00113     {
00114       // We could not find the class, so indicate the problem with an exception
00115       throw new ClassNotFoundException(className, e);
00116     }
00117     finally
00118     {
00119       if (null != fis)
00120       {
00121         try
00122         {
00123           fis.close();
00124         }
00125         catch (Exception e)
00126         {
00127         }
00128       }
00129     }
00130   }
00131 
00132   /**
00133    * we cache the contents of the jar files, as we don't want to have to read
00134    * the file for every single class we are going to need
00135    */
00136   private Hashtable htJarContents = new Hashtable();
00137 
00138   /**
00139    * Find the first jar file containing the className and load it
00140    * 
00141    * @param dir directory where we are looking for jar files
00142    * @param className name of the class we are looking for
00143    * @return the class as byte[]
00144    * @throws IOException if an error occurs
00145    */
00146   private byte[] findClassInJarFile(File dir, String className)
00147       throws IOException
00148   {
00149     // is the class already cached ?
00150     String resourceName = convertClassNameToResourceName(className);
00151     byte[] classBytes = (byte[]) htJarContents.get(resourceName);
00152     if (classBytes != null)
00153     {
00154       // it has been cached, we return
00155       return classBytes;
00156     }
00157 
00158     if (!dir.canRead())
00159       throw new IOException(dir + " is not readable.");
00160 
00161     if (dir.isFile())
00162     {
00163       // driverPath specified a jar file
00164       loadJarFile(dir.getAbsolutePath());
00165       // after loading the jar file the class bytes are in the cache
00166       return (byte[]) htJarContents.get(resourceName);
00167     }
00168 
00169     // the class is not yet cached we have to find the right jar file
00170 
00171     // find all jar files in the directory
00172     String[] jarFiles = dir.list(new FilenameFilter()
00173     {
00174       public boolean accept(File dir, String name)
00175       {
00176         return name.endsWith(".jar");
00177       }
00178     });
00179 
00180     if (jarFiles == null)
00181       throw new IOException("Invalid path " + dir);
00182 
00183     // loop over jar files
00184     for (int i = 0; i < jarFiles.length; i++)
00185     {
00186       File file = new File(dir, jarFiles[i]);
00187       JarFile jarFile = new JarFile(file);
00188 
00189       // we see whether the jar file contains the class we are looking for
00190       // no need in loading jar files as long as we don't really need the
00191       // content.
00192       if (jarFile.getEntry(resourceName) != null)
00193       {
00194         // we have found the right jar file and are loading it now
00195         loadJarFile(jarFile.getName());
00196 
00197         // after loading the jar file the class bytes are in the cache
00198         classBytes = (byte[]) htJarContents.get(resourceName);
00199       }
00200     }
00201     return classBytes;
00202   }
00203 
00204   /**
00205    * @see java.lang.ClassLoader#findResource(java.lang.String)
00206    */
00207   protected URL findResource(String name)
00208   {
00209 
00210     // we try to locate the resource unjarred
00211     if (path.isDirectory())
00212     {
00213       File searchResource = new File(path, name);
00214       if (searchResource.exists())
00215       {
00216         try
00217         {
00218           return searchResource.toURL();
00219         }
00220         catch (MalformedURLException mfe)
00221         {
00222         }
00223       }
00224     }
00225     else if (path.isFile())
00226     {
00227       // try getting the resource from the file
00228       try
00229       {
00230         new JarFile(path);
00231         // convert the jar entry into URL format
00232         return new URL("jar:" + path.toURL() + "!/" + name);
00233       }
00234       catch (Exception e)
00235       {
00236         // we couldn't find resource in file
00237         return null;
00238       }
00239     }
00240 
00241     //now we are checking the jar files
00242     try
00243     {
00244       // find all jar files in the directory
00245       String[] jarFiles = path.list(new FilenameFilter()
00246       {
00247         public boolean accept(File dir, String name)
00248         {
00249           return name.endsWith(".jar");
00250         }
00251       });
00252       // loop over jar files
00253       for (int i = 0; i < jarFiles.length; i++)
00254       {
00255         File file = new File(path, jarFiles[i]);
00256         JarFile jarFile = new JarFile(file);
00257 
00258         // we see whether the jar file contains the resource we are looking for
00259         if (jarFile.getJarEntry(name) != null)
00260         {
00261           // convert the jar entry into URL format
00262           return new URL("jar:" + file.toURL() + "!/" + name);
00263         }
00264       }
00265     }
00266     catch (Exception e)
00267     {
00268       e.printStackTrace();
00269     }
00270     return null;
00271   }
00272 
00273   /**
00274    * convert the class name into the rescource name. This method is just
00275    * replacing the '.' in the name with a '/' and adding the suffix '.class'
00276    * 
00277    * @param className
00278    * @return resource name
00279    */
00280   private String convertClassNameToResourceName(String className)
00281   {
00282     String resourceName = className;
00283     resourceName = resourceName.replace('.', '/');
00284     resourceName = resourceName + ".class";
00285     return resourceName;
00286   }
00287 
00288   /**
00289    * Load the contents of jar file in the cache
00290    * 
00291    * @param jarFileName name of the jar file we want to load
00292    * @throws IOException
00293    */
00294   private void loadJarFile(String jarFileName) throws IOException
00295   {
00296     Hashtable htSizes = new Hashtable();
00297     // extracts just sizes only.
00298     // For a reason I dont' understand not all files return the size in the loop
00299     // below (using ZipInputStream). So we cache the sizes here in case the loop
00300     // below does not give us the file size
00301     ZipFile zf = new ZipFile(jarFileName);
00302     Enumeration e = zf.entries();
00303     while (e.hasMoreElements())
00304     {
00305       ZipEntry ze = (ZipEntry) e.nextElement();
00306 
00307       htSizes.put(ze.getName(), new Integer((int) ze.getSize()));
00308     }
00309     zf.close();
00310 
00311     // extract resources and put them into the hashtable.
00312     FileInputStream fis = new FileInputStream(jarFileName);
00313     BufferedInputStream bis = new BufferedInputStream(fis);
00314     ZipInputStream zis = new ZipInputStream(bis);
00315     ZipEntry ze = null;
00316     while ((ze = zis.getNextEntry()) != null)
00317     {
00318       if (ze.isDirectory())
00319       {
00320         continue;
00321       }
00322 
00323       int size = (int) ze.getSize();
00324       // -1 means unknown size.
00325       if (size == -1)
00326       {
00327         // that is the reason we have cached the file size above.
00328         size = ((Integer) htSizes.get(ze.getName())).intValue();
00329       }
00330 
00331       byte[] b = new byte[size];
00332       int rb = 0;
00333       int chunk = 0;
00334       while ((size - rb) > 0)
00335       {
00336         chunk = zis.read(b, rb, size - rb);
00337         if (chunk == -1)
00338         {
00339           break;
00340         }
00341         rb += chunk;
00342       }
00343 
00344       // add to internal resource hashtable
00345       htJarContents.put(ze.getName(), b);
00346     }
00347 
00348   }
00349 }

Generated on Mon Apr 11 22:01:31 2005 for C-JDBC by  doxygen 1.3.9.1