ffx.FFXClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for ffx.FFXClassLoader.java

Source

/**
 * Title: Force Field X.
 *
 * Description: Force Field X - Software for Molecular Biophysics.
 *
 * Copyright: Copyright (c) Michael J. Schnieders 2001-2017.
 *
 * This file is part of Force Field X.
 *
 * Force Field X is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 3 as published by
 * the Free Software Foundation.
 *
 * Force Field X is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Linking this library statically or dynamically with other modules is making a
 * combined work based on this library. Thus, the terms and conditions of the
 * GNU General Public License cover the whole combination.
 *
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent modules, and
 * to copy and distribute the resulting executable under terms of your choice,
 * provided that you also meet, for each linked independent module, the terms
 * and conditions of the license of that module. An independent module is a
 * module which is not derived from or based on this library. If you modify this
 * library, you may extend this exception to your version of the library, but
 * you are not obligated to do so. If you do not wish to do so, delete this
 * exception statement from your version.
 */
package ffx;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Class loader able to load classes and DLLs with a higher priority from a
 * given set of JARs. Its bytecode is Java 1.1 compatible to be loadable by old
 * JVMs.
 *
 * @author Michael J. Schnieders; derived from work by Emmanuel Puybaret
 *
 */
public class FFXClassLoader extends URLClassLoader {

    private final ProtectionDomain protectionDomain;
    private final Map<String, String> extensionDlls = new HashMap<>();
    private JarFile[] extensionJars = null;
    private final String[] applicationPackages = { "ffx", "javax.media.j3d", "javax.vecmath", "javax.media.opengl",
            "com.sun.j3d", "groovy", "org.codehaus.groovy", "org.apache.commons.cli",
            "org.apache.commons.configuration", "org.apache.commons.io", "org.apache.commons.lang",
            "org.apache.commons.lang3", "org.apache.commons.math3", "org.jogamp", "org.openscience.cdk",
            "edu.rit.pj", "jcuda" };
    static final List<String> FFX_FILES;
    private static String gluegen = null;
    private static String jogl = null;
    private static String jocl = null;
    private static String nativeExtension = null;

    static {
        FFX_FILES = new ArrayList<>(Arrays.asList(new String[] { "edu.uiowa.eng.ffx/algorithms.jar",
                "edu.uiowa.eng.ffx/autoparm.jar", "edu.uiowa.eng.ffx/binding.jar", "edu.uiowa.eng.ffx/crystal.jar",
                "edu.uiowa.eng.ffx/numerics.jar", "edu.uiowa.eng.ffx/potential.jar", "edu.uiowa.eng.ffx/ui.jar",
                "edu.uiowa.eng.ffx/utilities.jar", "edu.uiowa.eng.ffx/xray.jar", "edu.uiowa.eng.ffx/pj.jar",
                // Force Field X Extensions
                "edu.uiowa.eng.ffx/algorithms-ext.jar", "edu.uiowa.eng.ffx/xray-ext.jar",
                // Groovy
                "org.codehaus.groovy/groovy-all.jar",
                // CUDA
                "jcuda/jcuda-all.jar",
                // Java3D 1.6.2 (depends on JOGL v. 2.2.0)
                "java3d/j3dcore.jar", "java3d/j3dutils.jar", "java3d/j3dvrml.jar", "java3d/vecmath.jar",
                // JOGAMP GLUEGEN, JOGL and JOCL v. 2.2.0
                "org.jogamp.gluegen/gluegen-rt.jar", "org.jogamp.gluegen/gluegen-rt-main.jar",
                "org.jogamp.jogl/jogl-all.jar", "org.jogamp.jogl/jogl-all-main.jar", "org.jogamp.jocl/jocl.jar",
                "org.jogamp.jocl/jocl-main.jar",
                // Apache Commons
                "commons-beanutils/commons-beanutils.jar", "commons-cli/commons-cli.jar",
                "commons-collections/commons-collections.jar", "commons-configuration/commons-configuration.jar",
                "commons-digester/commons-digester.jar", "commons-io/commons-io.jar",
                "commons-lang/commons-lang.jar", "commons-lang/commons-lang3.jar",
                "commons-logging/commons-logging.jar", "commons-math/commons-math3.jar",
                // CDK Libraries
                "org.openscience.cdk/cdk-interfaces.jar", "org.openscience.cdk/cdk-ioformats.jar",
                "org.openscience.cdk/cdk-standard.jar", "org.openscience.cdk/cdk-qsarmolecular.jar",
                "org.openscience.cdk/cdk-charges.jar", "org.openscience.cdk/cdk-smarts.jar",
                "org.openscience.cdk/cdk-reaction.jar", "org.openscience.cdk/cdk-fragment.jar",
                "org.openscience.cdk/cdk-dict.jar", "org.openscience.cdk/cdk-qsar.jar",
                "org.openscience.cdk/cdk-formula.jar", "org.openscience.cdk/cdk-hash.jar",
                "org.openscience.cdk/cdk-atomtype.jar", "org.openscience.cdk/cdk-isomorphism.jar",
                "org.openscience.cdk/cdk-valencycheck.jar", "org.openscience.cdk/cdk-smiles.jar",
                "org.openscience.cdk/", "org.openscience.cdk/cdk-io.jar", "org.openscience.cdk/cdk-core.jar",
                "org.openscience.cdk/cdk-silent.jar", "org.openscience.cdk/cdk-inchi.jar",
                "org.openscience.cdk/cdk-builder3d.jar", "org.openscience.cdk/cdk-forcefield.jar",
                "org.openscience.cdk/cdk-sdg.jar", "org.openscience.cdk/cdk-data.jar",
                "org.openscience.cdk/cdk-extra.jar", "org.openscience.cdk/cdk-smsd.jar",
                "org.openscience.cdk/cdk-core.jar", "org.openscience.cdk/cdk-dict.jar",
                "org.openscience.cdk/cdk-formula.jar", "org.openscience.cdk/cdk-interfaces.jar",
                "org.openscience.cdk/cdk-ioformats.jar", "org.openscience.cdk/cdk-standard.jar",
                //Google Libraraies
                "com.google.guava/guava.jar",
                //ebi.beam Libraries
                "uk.ac.ebi.beam/beam-core.jar", "uk.ac.ebi.beam/beam-func.jar",
                // Mac OS X Extensions
                "macosx/AppleJavaExtensions.jar",
                // Java Help
                "javax.help/javahelp.jar",
                // BioJava
                "org.biojava/biojava3-core.jar", "org.biojava/core.jar", "org.biojava/bytecode.jar",
                "org.biojava/biojava3-structure.jar", "org.biojava/biojava3-alignment.jar",
                "org.biojava/biojava3-phylo.jar",
                // Lars Behnke's hierarchical-clustering-java
                "com.apporiented/hierarchical-clustering.jar" }));

        String osName = System.getProperty("os.name").toUpperCase();
        String osArch = System.getProperty("sun.arch.data.model");
        final boolean x8664 = "64".equals(osArch);

        if ("MAC OS X".equals(osName)) {
            nativeExtension = "-natives-macosx-universal.jar";
            // Gluegen Runtime Universal Binaries
            FFX_FILES.add("org.jogamp.gluegen/gluegen-rt-natives-macosx-universal.jar");
            // JOGL Universal Binaries
            FFX_FILES.add("org.jogamp.jogl/jogl-all-natives-macosx-universal.jar");
            // JOCL Universal Binaries
            FFX_FILES.add("org.jogamp.jocl/jocl-natives-macosx-universal.jar");
            if (x8664) {
                // JCUDA
                FFX_FILES.add("64-bit/libJCudaDriver-apple-x86_64.jnilib");
                FFX_FILES.add("64-bit/libJCudaRuntime-apple-x86_64.jnilib");
                FFX_FILES.add("64-bit/libJCufft-apple-x86_64.jnilib");
            }
        } else if ("LINUX".equals(osName)) {
            if (x8664) {
                nativeExtension = "-natives-linux-amd64.jar";
                FFX_FILES.add("64-bit/libJCufft-linux-x86_64.so");
                // Gluegen Runtime
                FFX_FILES.add("org.jogamp.gluegen/gluegen-rt-natives-linux-amd64.jar");
                // JOGL
                FFX_FILES.add("org.jogamp.jogl/jogl-all-natives-linux-amd64.jar");
                // JOCL
                FFX_FILES.add("org.jogamp.jocl/jocl-natives-linux-amd64.jar");
                // JCUDA
                FFX_FILES.add("64-bit/libJCudaDriver-linux-x86_64.so");
                FFX_FILES.add("64-bit/libJCudaRuntime-linux-x86_64.so");
            } else {
                nativeExtension = "-natives-linux-i586.jar";
                // Gluegen Runtime
                FFX_FILES.add("org.jogamp.gluegen/gluegen-rt-natives-linux-i586.jar");
                // JOGL
                FFX_FILES.add("org.jogamp.jogl/jogl-all-natives-linux-i586.jar");
                // JOCL
                FFX_FILES.add("org.jogamp.jocl/jocl-natives-linux-i586.jar");
            }
        } else if (osName.startsWith("WINDOWS")) {
            if (x8664) {
                nativeExtension = "-natives-windows-amd64.jar";
                // Gluegen Runtime
                FFX_FILES.add("org.jogamp.glugen/gluegen-rt-natives-windows-amd64.jar");
                // JOGL
                FFX_FILES.add("org.jogamp.jogl/jogl-all-natives-windows-amd64.jar");
                // JOCL
                FFX_FILES.add("org.jogamp.jocl/jocl-natives-windows-amd64.jar");
                // JCUDA
                FFX_FILES.add("64-bit/JCudaDriver-linux-x86_64.dll");
                FFX_FILES.add("64-bit/JCudaRuntime-linux-x86_64.dll");
                FFX_FILES.add("64-bit/JCufft-linux-x86_64.dll");
            } else {
                nativeExtension = "-natives-windows-i586.jar";
                // Gluegen Runtime
                FFX_FILES.add("org.jogamp.gluegen/gluegen-rt-natives-windows-i586.jar");
                // JOGL
                FFX_FILES.add("org.jogamp.jogl/jogl-all-natives-windows-i586.jar");
                // JOCL
                FFX_FILES.add("org.jogamp.jocl/jocl-natives-windows-i586.jar");
            }
        }
    }

    /**
     * Force Field X custom class loader considers JARs and DLLs of
     * <code>extensionJarsAndDlls</code> as classpath and libclasspath elements
     * with a higher priority than the ones of default classpath. It will load
     * itself all the classes belonging to packages of
     * <code>applicationPackages</code>.
     *
     * @param parent a {@link java.lang.ClassLoader} object.
     */
    public FFXClassLoader(final ClassLoader parent) {
        super(new URL[0], parent);
        protectionDomain = FFXClassLoader.class.getProtectionDomain();
    }

    /**
     * Implementation of this method is to allow use of the NetBeans JFluid
     * profiler.
     *
     * @param value
     */
    private void appendToClassPathForInstrumentation(String value) {
        String tempDir = System.getProperty("java.io.tmpdir");
        File toDir = new File(tempDir + "deployed");
        toDir.mkdir();
        toDir = new File(tempDir + "deployed/jdk16");
        toDir.mkdir();
        toDir = new File(tempDir + "deployed/jdk16/mac");
        toDir.mkdir();

        String prof = tempDir + "deployed/jdk16/mac/libprofilerinterface.jnilib";
        File toFile = new File(prof);
        prof = "/Applications/NetBeans/NetBeans 8.0.app/Contents/Resources/NetBeans/profiler/lib/deployed/jdk16/mac/libprofilerinterface.jnilib";
        File fromFile = new File(prof);

        InputStream input = null;
        OutputStream output = null;
        try {
            input = new FileInputStream(fromFile);
            output = new BufferedOutputStream(new FileOutputStream(toFile));
            byte[] buffer = new byte[8192];
            int size;
            while ((size = input.read(buffer)) != -1) {
                output.write(buffer, 0, size);
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
        } finally {
            try {
                if (input != null) {
                    input.close();
                }
                if (output != null) {
                    output.close();
                }
            } catch (Exception e) {
                System.out.println(e.toString());
            }
        }
        toFile.deleteOnExit();
    }

    /**
     * Returns the file name of a temporary copy of <code>input</code> content.
     *
     * @param input a {@link java.io.InputStream} object.
     * @param name a {@link java.lang.String} object.
     * @param suffix a {@link java.lang.String} object.
     * @return a {@link java.lang.String} object.
     * @throws java.io.IOException if any.
     */
    public static String copyInputStreamToTmpFile(final InputStream input, String name, final String suffix)
            throws IOException {
        File tmpFile = null;

        if (name.contains("gluegen-rt") && name.contains("natives")) {
            tmpFile = new File(gluegen + nativeExtension);
        } else if (name.contains("jogl-all") && name.contains("natives")) {
            tmpFile = new File(jogl + nativeExtension);
        } else if (name.contains("jocl") && name.contains("natives")) {
            tmpFile = new File(jocl + nativeExtension);
        } else {
            try {
                name = "ffx." + name + ".";
                tmpFile = File.createTempFile(name, suffix);
            } catch (Exception e) {
                System.out.println(" Could not extract a Force Field X library.");
                System.err.println(e.toString());
                System.exit(-1);
            }
        }

        tmpFile.deleteOnExit();
        OutputStream output = null;
        try {
            output = new BufferedOutputStream(new FileOutputStream(tmpFile));
            byte[] buffer = new byte[8192];
            int size;
            while ((size = input.read(buffer)) != -1) {
                output.write(buffer, 0, size);
            }
        } finally {
            if (input != null) {
                input.close();
            }
            if (output != null) {
                output.close();
            }
        }

        return tmpFile.toString();
    }

    /**
     * {@inheritDoc}
     *
     * Finds and defines the given class among the extension JARs given in
     * constructor, then among resources.
     */
    @Override
    protected Class findClass(String name) throws ClassNotFoundException {

        /*
         if (name.startsWith("com.jogamp")) {
         System.out.println(" Class requested:" + name);
         } */
        if (!extensionsLoaded) {
            loadExtensions();
        }

        // Build class file from its name
        String classFile = name.replace('.', '/') + ".class";
        InputStream classInputStream = null;
        if (extensionJars != null) {
            for (JarFile extensionJar : extensionJars) {
                JarEntry jarEntry = extensionJar.getJarEntry(classFile);
                if (jarEntry != null) {
                    try {
                        classInputStream = extensionJar.getInputStream(jarEntry);
                    } catch (IOException ex) {
                        throw new ClassNotFoundException("Couldn't read class " + name, ex);
                    }
                }
            }
        }

        // If it's not an extension class, search if its an application
        // class that can be read from resources
        if (classInputStream == null) {
            URL url = getResource(classFile);
            if (url == null) {
                throw new ClassNotFoundException("Class " + name);
            }
            try {
                classInputStream = url.openStream();
            } catch (IOException ex) {
                throw new ClassNotFoundException("Couldn't read class " + name, ex);
            }
        }

        ByteArrayOutputStream out = null;
        BufferedInputStream in = null;
        try {
            // Read class input content to a byte array
            out = new ByteArrayOutputStream();
            in = new BufferedInputStream(classInputStream);
            byte[] buffer = new byte[8192];
            int size;
            while ((size = in.read(buffer)) != -1) {
                out.write(buffer, 0, size);
            }
            // Define class
            return defineClass(name, out.toByteArray(), 0, out.size(), this.protectionDomain);
        } catch (IOException ex) {
            throw new ClassNotFoundException("Class " + name, ex);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                throw new ClassNotFoundException("Class " + name, e);
            }

        }
    }

    /**
     * {@inheritDoc}
     *
     * Returns the library path of an extension DLL.
     */
    @Override
    protected String findLibrary(String libname) {
        if (!extensionsLoaded) {
            loadExtensions();
        }

        String path = (String) this.extensionDlls.get(libname);

        if (path == null) {
            path = super.findLibrary(libname);
        }

        return path;
    }

    /**
     * {@inheritDoc}
     *
     * Returns the URL of the given resource searching first if it exists among
     * the extension JARs given in constructor.
     */
    @Override
    public URL findResource(String name) {
        if (!extensionsLoaded) {
            loadExtensions();
        }

        if (name.equals("List all scripts")) {
            listScripts();
        }

        if (extensionJars != null) {
            for (JarFile extensionJar : extensionJars) {
                JarEntry jarEntry = extensionJar.getJarEntry(name);
                if (jarEntry != null) {
                    String path = "jar:file:" + extensionJar.getName() + "!/" + jarEntry.getName();
                    try {
                        return new URL(path);
                    } catch (MalformedURLException ex) {
                        System.out.println(path + "\n" + ex.toString());
                    }
                }
            }
        }

        return super.findResource(name);
    }

    protected void listScripts() {
        if (extensionJars != null) {
            List<String> scripts = new ArrayList<>();
            for (JarFile extensionJar : extensionJars) {
                Enumeration<JarEntry> entries = extensionJar.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    String name = entry.getName();
                    // System.out.println(name);
                    if (name.startsWith("ffx") && name.endsWith(".groovy")) {
                        name = name.replace('/', '.');
                        name = name.replace("ffx.scripts.", "");
                        name = name.replace(".groovy", "");
                        scripts.add(name);
                    }
                }
            }

            String[] scriptArray = scripts.toArray(new String[scripts.size()]);
            Arrays.sort(scriptArray);
            for (String script : scriptArray) {
                System.out.println(" " + script);
            }
        }
    }

    /**
     * {@inheritDoc}
     *
     * Loads a class with this class loader if its package belongs to
     * <code>applicationPackages</code> given in constructor.
     */
    @Override
    protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (!extensionsLoaded) {
            loadExtensions();
        }

        // If no extension jars were found use the super.loadClass method.
        if (extensionJars == null) {
            return super.loadClass(name, resolve);
        }

        // Check if the class has already been loaded
        Class loadedClass = findLoadedClass(name);
        if (loadedClass == null) {
            try {
                for (String applicationPackage : applicationPackages) {
                    int applicationPackageLength = applicationPackage.length();
                    if ((applicationPackageLength == 0 && name.indexOf('.') == 0)
                            || (applicationPackageLength > 0 && name.startsWith(applicationPackage))) {
                        loadedClass = findClass(name);
                        break;
                    }
                }
            } catch (ClassNotFoundException ex) {
                // Do Nothin.
            }
            // Try to load the class via the default implementation.
            if (loadedClass == null) {
                loadedClass = super.loadClass(name, resolve);
            }
        }
        if (resolve) {
            resolveClass(loadedClass);
        }
        return loadedClass;
    }

    private boolean extensionsLoaded = false;

    private void loadExtensions() {
        if (extensionsLoaded) {
            return;
        }
        extensionsLoaded = true;

        String extensionJarsAndDlls[] = FFX_FILES.toArray(new String[FFX_FILES.size()]);

        // Compute DLLs prefix and suffix
        String dllSuffix;
        String dllSuffix2 = null;
        String dllPrefix;

        String osName = System.getProperty("os.name");
        if (osName.startsWith("Windows")) {
            dllSuffix = ".dll";
            dllPrefix = "";
        } else if (osName.startsWith("Mac OS X")) {
            dllSuffix = ".jnilib";
            dllSuffix2 = ".dylib";
            dllPrefix = "lib";
        } else {
            dllSuffix = ".so";
            dllPrefix = "lib";
        }

        // Find extension Jars and DLLs
        ArrayList<JarFile> extensionJarList = new ArrayList<>();
        for (String extensionJarOrDll : extensionJarsAndDlls) {
            try {
                URL extensionJarOrDllUrl = getResource(extensionJarOrDll);
                if (extensionJarOrDllUrl != null) {
                    int lastSlashIndex = extensionJarOrDll.lastIndexOf('/');
                    if (extensionJarOrDll.endsWith(".jar")) {
                        int start = lastSlashIndex + 1;
                        int end = extensionJarOrDll.indexOf(".jar");
                        String name = extensionJarOrDll.substring(start, end);
                        // Copy jar to a tmp file
                        String extensionJar = copyInputStreamToTmpFile(extensionJarOrDllUrl.openStream(), name,
                                ".jar");
                        // Add extracted file to the extension jars list
                        extensionJarList.add(new JarFile(extensionJar, false));
                        if (name.equals("gluegen-rt")) {
                            gluegen = extensionJar.substring(0, extensionJar.length() - 4);
                        } else if (name.equals("jogl-all")) {
                            jogl = extensionJar.substring(0, extensionJar.length() - 4);
                        } else if (name.equals("jocl")) {
                            jocl = extensionJar.substring(0, extensionJar.length() - 4);
                        }
                    } else if (extensionJarOrDll.endsWith(dllSuffix)) {
                        int start = lastSlashIndex + 1 + dllPrefix.length();
                        int end = extensionJarOrDll.indexOf(dllSuffix);
                        String name = extensionJarOrDll.substring(start, end);
                        // Copy DLL to a tmp file
                        String extensionDll = copyInputStreamToTmpFile(extensionJarOrDllUrl.openStream(), name,
                                dllSuffix);
                        // Add extracted file to extension DLLs map
                        extensionDlls.put(name, extensionDll);
                    } else if (dllSuffix2 != null && extensionJarOrDll.endsWith(dllSuffix2)) {
                        int start = lastSlashIndex + 1 + dllPrefix.length();
                        int end = extensionJarOrDll.indexOf(dllSuffix2);
                        String name = extensionJarOrDll.substring(start, end);
                        // Copy DLL to a tmp file
                        String extensionDll = copyInputStreamToTmpFile(extensionJarOrDllUrl.openStream(), name,
                                dllSuffix2);
                        // Add extracted file to extension DLLs map
                        extensionDlls.put(name, extensionDll);
                    }
                }
            } catch (IOException ex) {
                System.out.println(extensionJarOrDll);
                throw new RuntimeException(" Couldn't extract extension jar.\n", ex);
            }
        }

        // Create extensionJars array
        if (extensionJarList.size() > 0) {
            extensionJars = (JarFile[]) extensionJarList.toArray(new JarFile[extensionJarList.size()]);
        }
    }
}