Package org.xmlBlaster.util.classloader

Source Code of org.xmlBlaster.util.classloader.StandaloneClassLoaderFactory

/*------------------------------------------------------------------------------
Name:      ClassLoaderFactory.java
Project:   xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
------------------------------------------------------------------------------*/
package org.xmlBlaster.util.classloader;

import java.util.logging.Logger;
import java.util.logging.Level;
import org.xmlBlaster.util.Global;
import org.xmlBlaster.util.plugin.PluginInfo;
import org.xmlBlaster.util.XmlBlasterException;
import org.xmlBlaster.util.def.ErrorCode;

import org.xmlBlaster.util.ReplaceVariable;
import java.io.File;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.MalformedURLException;

public class StandaloneClassLoaderFactory implements ClassLoaderFactory {
   public String ME;
   private Global glob;
   private static Logger log = Logger.getLogger(StandaloneClassLoaderFactory.class.getName());
   private int instanceCounter = 0;

   public StandaloneClassLoaderFactory() {

   }
   /**
    * We are a singleton in respect to a Global instance.
    */
   public StandaloneClassLoaderFactory (Global glob) {
      init(glob);
   }

   public void init(Global glob) {
     ++instanceCounter;
     this.ME = "ClassLoaderFactory-" + instanceCounter;
     this.glob = glob;

     if (log.isLoggable(Level.FINER)) log.finer("ClassLoaderFactory constructor #" + instanceCounter);
   }

   /**
    * Creates and returns a new URL class loader based on the callers class loader and the
    * callers related additional classes which may exist in a specified path.
    */
   public URLClassLoader getPluginClassLoader(PluginInfo pluginInfo) throws XmlBlasterException {
      if (log.isLoggable(Level.FINER)) log.finer("Entering getPluginClassLoader for plugin=" + pluginInfo.getClassName());

      java.util.Properties pluginParams = pluginInfo.getParameters();
      LoaderInfo loaderInfo = getLoaderInfo(this, pluginInfo.getClassName(), false);
      if (log.isLoggable(Level.FINE)) log.fine(loaderInfo.toString());

      // In xmlBlaster.properties e.g.
      // ProtocolPlugin[IOR][1.0]=org.xmlBlaster.protocol.soap.SoapDriver,classpath=soap.jar:xerces.jar
      String classPathStr = (String)pluginParams.get(PluginInfo.KEY_CLASSPATH);
      ArrayList classPath = new ArrayList();
      if (classPathStr != null) {
         log.info("Analyzing classpath=" + classPathStr + " for plugin " + pluginInfo.getClassName());
         StringTokenizer st = new StringTokenizer(classPathStr, ";:");
         while (st.hasMoreElements()) {
            String jar = (String)st.nextElement()// e.g. "soap/soap.jar"
            if (log.isLoggable(Level.FINE)) log.fine("Looking for jar '" + jar + "' ...");
            File f = new File(jar); // 1. check absolute path
            if (f.canRead()) {
               classPath.add(jar);
               continue;
            }
            String jarStripped = f.getName();      // e.g. "soap.jar"
            if (log.isLoggable(Level.FINE)) log.fine("Looking for jarStripped '" + jarStripped + "' ...");
            f = new File(jarStripped);      // 2. check local directory
            if (f.canRead()) {
               classPath.add(jarStripped);
               continue;
            }
            String resourceJar = loaderInfo.rootPath +jar; // e.g. "/home/xmlblast/xmlBlaster/lib/soap/soap.jar"
            if (log.isLoggable(Level.FINE)) log.fine("Looking for resourceJar=" + resourceJar + " ...");
            f = new File(resourceJar);      // 3. check resource path of this instance
            if (f.canRead()) {
               classPath.add(resourceJar);
               continue;
            }
            String resourceJarStripped = loaderInfo.rootPath +jar;   // e.g. "/home/xmlblast/xmlBlaster/lib/soap.jar"
            if (log.isLoggable(Level.FINE)) log.fine("Looking for resourceJarStripped=" + resourceJarStripped + " ...");
            f = new File(resourceJarStripped);      // 3. check resource path of this instance
            if (f.canRead()) {
               classPath.add(resourceJarStripped);
               continue;
            }
            log.info("Plugin '" + pluginInfo.getClassName() + "' specific jar file '" + jar + "' not found, using JVM default CLASSPATH");

                                    // 4. check JVM classpath
            URL[] urls = ((URLClassLoader)this.getClass().getClassLoader()).getURLs();
            for (int j=0; j<urls.length; j++) {
               if (urls[j].getFile().equalsIgnoreCase(jar)) {
                  classPath.add(urls[j].toString());
                  continue;
               }
            }
         }
      }

      // The plugin itself needs to be loaded by our ClassLoader to inherit it
      // to all children classes - add the classpath to the plugin class:
      if (classPath.size() > 0) {
         if (loaderInfo.jarPath != null)
            classPath.add(loaderInfo.jarPath); // Attach to end e.g. xmlBlaster.jar
         else
            classPath.add(loaderInfo.rootPath); // Attach to end e.g. xmlBlaster/classes
      }

      if (log.isLoggable(Level.FINE)) log.fine("Found " + classPath.size() + " plugin specific jar files");
      return new PluginClassLoader(glob, stringToUrl(classPath), pluginInfo );
   }


   /**
    * Creates and returns a new URL class loader based on the callers class loader and the
    * callers related additional classes which may exist in a specified path.
    */
   public URLClassLoader getXmlBlasterClassLoader() throws XmlBlasterException {
      if (log.isLoggable(Level.FINER)) log.finer("Entering getXmlBlasterClassLoader ...");

      LoaderInfo loaderInfo = getLoaderInfo(this, "org.xmlBlaster.Main", true);
      if (log.isLoggable(Level.FINE)) log.fine(loaderInfo.toString());

      ArrayList classPath = new ArrayList();

      if (loaderInfo.jarPath != null)
         classPath.add(loaderInfo.jarPath); // Attach to end e.g. xmlBlaster.jar
      else
         classPath.add(loaderInfo.rootPath); // Attach to end e.g. xmlBlaster/classes

      if (this.getClass().getClassLoader() instanceof URLClassLoader) {
         URL[] urls = ((URLClassLoader)this.getClass().getClassLoader()).getURLs();
         for (int i=0; i<urls.length; i++) {
            String xmlBlasterJar = (String)classPath.get(0);
            if( urls[i].getFile().indexOf(xmlBlasterJar) < 0 ) {
               classPath.add(urls[i].getFile());
            }
         }
      }

      if (log.isLoggable(Level.FINE)) {
         String text = "Build new classpath with " + classPath.size() + " entries:";
         for(int i = 0; i < classPath.size(); i++ ) {
            text += (String)classPath.get(i);
            if(i < (classPath.size() - 1) )
               text += ":";
         }
         log.fine(text);
      }
      return new XmlBlasterClassLoader(this.glob, stringToUrl(classPath) );
   }
   /**
    * Retrievs the base path for the object related classpath.
    * Taking the base path from the line in the environment classpath
    * which contains the xmlBlaster.jar first!
    *
    * Adding the name of the calling class as path to the basepath.
    *
    * @param caller Type of the calling class
    * @param plugin The plugin name e.g. "org.xmlBlaster.protocol.corba.CorbaDriver"
    *               or null
    * @param exceptionOnFailure TODO
    * @return The base path for the caller specific additional classes, is never null
    * @exception On failure
    */
   public static LoaderInfo getLoaderInfo(Object caller, String plugin, boolean exceptionOnFailure) throws XmlBlasterException {
      String ME = "ClassLoaderFactory";
      //if (log.isLoggable(Level.FINER)) log.call(ME, "Entering getLoaderInfo");
      if (plugin == null || plugin.length() < 1) {
         Thread.dumpStack();
         throw new XmlBlasterException(Global.instance(), ErrorCode.INTERNAL_ILLEGALARGUMENT, ME, "getLoaderInfo() with plugin=null");
      }

      String classResource = which(caller, plugin); // e.g. "/home/xmlblast/xmlBlaster/classes/org/xmlBlaster/protocol/corba/CorbaDriver.class"
      if (classResource == null) {
         if (exceptionOnFailure) {
            String text = "Can't find class " + plugin + ", please check your plugin name and your CLASSPATH";
            // if (log.isLoggable(Level.FINE)) log.trace(ME, text);
            throw new XmlBlasterException(Global.instance(), ErrorCode.RESOURCE_CONFIGURATION_PLUGINFAILED, ME, text);
         }
         return new LoaderInfo(plugin, "", "", "", "");
      }
      //if (log.isLoggable(Level.FINE)) log.trace(ME, "plugin '" + plugin + "' has resource path " + classResource );

      String pluginSlashed = ReplaceVariable.replaceAll(plugin, ".", "/"); // replace '.' by fileSeperator (windows wants "/" as well)
      // plugin.replaceAll("\\.", "/"); // since JDK 1.4 :-(

      String jarPath = null;
      String jarName = null;
      String rootPath = ""; // i.e. '/home/developer/java/xmlBlaster/lib/'

      if(classResource.indexOf('!') == -1) {
         // Determine the BasePath from classes
         // log.warn(ME, "Class not loaded from jar, don't know how to determine rootPath");
         int index = classResource.lastIndexOf(pluginSlashed);
         if (index == -1)
            rootPath = classResource;
         else
            rootPath = classResource.substring(0, classResource.lastIndexOf(pluginSlashed));
      }
      else {
         // Determine the BasePath from jar
         // 'file:/home/xmlblaster/work/xmlBlaster/lib/xmlBlaster.jar!/org/xmlBlaster/engine/cluster/simpledomain/RoundRobin.class'
         jarPath = classResource.substring(classResource.indexOf("/"), classResource.indexOf("!"));
         jarName = jarPath.substring(jarPath.lastIndexOf("/") + 1, jarPath.length()      );
         rootPath = jarPath.substring(0, jarPath.lastIndexOf(jarName));
         // jarPath = '/home/xmlblast/xmlBlaster/lib/xmlBlaster.jar' jarName = 'xmlBlaster.jar'
      }

      LoaderInfo loaderInfo = new LoaderInfo(plugin, rootPath, jarPath, jarName, pluginSlashed);
      //if (log.isLoggable(Level.FINE)) log.trace(ME, loaderInfo.toString());
      return loaderInfo;
   }

   /*
    * Change String class names to URL objects.
    * @param stringUrls The array containing the array of URL for the specified class' class loader.
    * @return an array of URL containig the stringUrls array
    * @exception XmlBlasterException if the array of URLs can not be formed.
    */
   private URL[] stringToUrl(ArrayList stringUrls) throws XmlBlasterException {
      if (stringUrls == null)
         return new URL[0];
      URL[] url = new URL[stringUrls.size()];

      try {
         for(int ii=0; ii < stringUrls.size(); ii++) {
            url[ii] = new URL( "file", null, (String)stringUrls.get(ii) );
         }
      } catch (MalformedURLException e) {
         throw new XmlBlasterException(glob, ErrorCode.INTERNAL_UNKNOWN, ME, "Malformed Url Exception occured: ", e);
      }

      if (log.isLoggable(Level.FINE)) {
         log.fine("New Classpath as URL before creating classloader:");
         for (int ii = 0; ii < url.length; ii++) {
              log.fine(">>" + ii +": " + url[ii].toString() + "<<");
        }
      }

      return (url);
   }

   /**
    * Prints the absolute pathname of the class file
    * containing the specified class name, as prescribed
    * by the current classpath.
    * <pre>
    * author <a href="mailto:mike@clarkware.com">Mike Clark</a>
    * author <a href="http://www.clarkware.com">Clarkware Consulting</a>
    * </pre>
    *
    * @param caller
    * @param className Name of the class, e.g. "org.xmlBlaster.protocol.corba.CorbaDriver"
    * @return Url of resource of className.
    *   e.g. "/home/xmlblast/xmlBlaster/classes/org/xmlBlaster/protocol/corba/CorbaDriver.class"
    */
   public static String which(Object caller, String className) {

      if (!className.startsWith("/")) {
         className = "/" + className;
      }
      className = className.replace('.', '/');
      className = className + ".class";

      java.net.URL classUrl = caller.getClass().getResource(className);

      if (classUrl != null) {
         //if (log.isLoggable(Level.FINE)) log.trace(ME, "Class '" + className + "' found in '" + classUrl.getFile() + "'");
         return classUrl.getFile().toString();
      }
      else {
         //if (log.isLoggable(Level.FINE)) log.trace(ME, "Class '" + className + "' not found in '" + System.getProperty("java.class.path") + "'");
         return getDirectoryForWrite();
      }
   }
  
   /**
    * Navigate up the absolute file system path (where this class came from) until we find a writeable directory.
    * @return for example "/home/project/lib"
    */
   public static String getDirectoryForWrite() {
      java.net.URL classUrl = StandaloneClassLoaderFactory.class.getResource("StandaloneClassLoaderFactory.class");
      // classUrl.toString():
      // jar:file:/home/xmlblast/xmlBlaster/lib/xmlBlaster.jar!/org/xmlBlaster/util/classloader/StandaloneClassLoaderFactory.class
     
      String str = classUrl.getFile(); // strips "jar:", of if starting with "file:" the "file:" which we don't want
      if (!str.startsWith("file:")) str = classUrl.toString();
      log.fine("Looking up CLASSPATH directory with write access for '" + str + "'");
      try {
         File f = new File(new URI(str));
         // org.xmlBlaster.util.classloader.StandaloneClassLoaderFactory
         if (f.getAbsolutePath().endsWith("org/xmlBlaster/util/classloader/StandaloneClassLoaderFactory.class"))
            f = f.getParentFile().getParentFile().getParentFile().getParentFile().getParentFile();
         return directoryForWrite(f.getAbsolutePath());
      } catch (Throwable e) { // IllegalArgumentException or URISyntaxException
         log.warning("Problems finding a CLASSPATH root for '" + classUrl.toString() + "': " + e.toString());
         return null;
      }
   }
  
   /**
    * Recursiv go up the given path until we find a writeable directory
    * @param path
    * @return null if not found
    */
   public static String directoryForWrite(String path) {
      if (path == null) return null;
      File file = new File(path);
      if (file.isDirectory() && file.canWrite()) {
         return path;
      }
      if (file.getParentFile() == null) return null;
      String parent = file.getParentFile().getAbsolutePath();
      if (parent == null) return null;
      return directoryForWrite(parent);
   }
}
TOP

Related Classes of org.xmlBlaster.util.classloader.StandaloneClassLoaderFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.