ee.ioc.cs.vsle.ccl.PackageClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for ee.ioc.cs.vsle.ccl.PackageClassLoader.java

Source

package ee.ioc.cs.vsle.ccl;

/*-
 * #%L
 * CoCoViLa
 * %%
 * Copyright (C) 2003 - 2017 Institute of Cybernetics at Tallinn University of Technology
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.jar.*;

import org.apache.commons.io.FileUtils;
import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
import org.eclipse.jdt.internal.compiler.batch.FileSystem;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;

import ee.ioc.cs.vsle.editor.RuntimeProperties;
import ee.ioc.cs.vsle.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Package classloader.  Loads classes from the package directory and
 * zip and jar archives in the top level package directory.  Source files
 * are compiled on demand when needed.
 */
public class PackageClassLoader extends CCL implements INameEnvironment {

    private static final Logger logger = LoggerFactory.getLogger(PackageClassLoader.class);

    public PackageClassLoader(File pkgDir) {
        super(createPackageClassPath(pkgDir), PackageClassLoader.class.getClassLoader());

        // initialize the environment field inherited from CCL
        initNameEnvironment();
    }

    private void initNameEnvironment() {
        ArrayList<String> fileNames = new ArrayList<String>();
        for (URL u : getURLs()) {
            try {
                fileNames.add(new File(u.toURI()).getAbsolutePath());
            } catch (URISyntaxException e) {
                logger.error(null, e);
            }
        }

        for (String s : getCompilerClassPath()) {
            if (!fileNames.contains(s)) {
                fileNames.add(s);
            }
        }
        environment = new FileSystem(fileNames.toArray(new String[fileNames.size()]), new String[] {}, null);
    }

    /**
     * Creates a URL array with paths required for package class loading.
     * The returned array contains the URLs of the package directory,
     * all the jar and zip archives found in the package top level directory
     * (the directory is NOT searched recursively) and the compilation
     * classpath set by the user.
     * @return the package classpath
     */
    private static URL[] createPackageClassPath(File packagePath) {
        ArrayList<URL> urls = new ArrayList<URL>();

        // (1) CoCoVila standard libraries
        // CoCoViLa standard libraries should be accessible using the parent
        // classloader, therefore we omit them here.

        // (2) The package directory
        try {
            urls.add(packagePath.toURI().toURL());
        } catch (MalformedURLException e) {
            logger.error(null, e);
        }

        // (3) jar and zip archives from the package top level directory
        File[] pkgLibs = getLibraryFiles(packagePath);
        if (pkgLibs != null) {
            for (File f : pkgLibs) {
                try {
                    urls.add(f.toURI().toURL());
                } catch (MalformedURLException e) {
                    logger.error(null, e);
                }
            }
        }

        // (4) user set classpath
        String[] paths = RuntimeProperties.getCompilationClasspaths();

        for (String path : paths) {
            File file = new File(path);
            if (file.exists()) {
                try {
                    urls.add(file.toURI().toURL());
                } catch (MalformedURLException e) {
                    logger.error(null, e);
                }
            }
        }

        if (RuntimeProperties.isFromWebstart()) {
            urls.addAll(getWebStartClasspath());
        }

        return urls.toArray(new URL[urls.size()]);
    }

    private static boolean isJarsLoadedLocal = false;
    private static List<URL> localJarsClassPath = null;

    /**
     * Prepares classpath if the application has been started 
     * from Java Web Start
     * @return
     */
    private static synchronized List<URL> getWebStartClasspath() {
        if (isJarsLoadedLocal) {
            return localJarsClassPath;
        }

        List<URL> classpathJars = new ArrayList<URL>();

        try {

            Set<String> systemLibs = new HashSet<String>();
            for (File file : getSystemLibs()) {
                systemLibs.add(file.getAbsolutePath());
            }

            for (Enumeration<?> e = PackageClassLoader.class.getClassLoader()
                    .getResources("META-INF/MANIFEST.MF"); e.hasMoreElements();) {
                URL url = (URL) e.nextElement();

                URL localJarURL = getLocalJarURL(url, systemLibs);

                if (localJarURL != null) {
                    classpathJars.add(localJarURL);
                }
            }
        } catch (IOException exc) {
            exc.printStackTrace();
        }

        isJarsLoadedLocal = true;
        localJarsClassPath = classpathJars;

        System.err.println("localJarsClassPath: " + localJarsClassPath);

        return localJarsClassPath;
    }

    /**
     * Helper method for getWebStartClasspath() method.
     * Returns a local URL of a jar for the classpath.
     * If jar is a system lib, it is ignored.
     * @param url
     * @param systemLibs
     * @return
     */
    private static URL getLocalJarURL(URL url, Collection<String> systemLibs) {

        String urlStrJar = getJarPath(url);

        //ignore if it is a system lib
        if (systemLibs.contains(urlStrJar))
            return null;

        InputStream inputStreamJar = null;
        File tempJar;
        try {
            //check if the file is already local
            if (url.getPath().startsWith("file:"))
                return new File(urlStrJar).toURI().toURL();

            JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();
            inputStreamJar = new FileInputStream(jar.getName());

            String strippedName = urlStrJar;
            int dotIndex = strippedName.lastIndexOf('.');
            if (dotIndex >= 0) {
                strippedName = strippedName.substring(0, dotIndex);
                strippedName = strippedName.replace("/", File.separator);
                strippedName = strippedName.replace("\\", File.separator);
                int slashIndex = strippedName.lastIndexOf(File.separator);
                if (slashIndex >= 0) {
                    strippedName = strippedName.substring(slashIndex + 1);
                }
            }
            tempJar = File.createTempFile(strippedName, ".jar");
            tempJar.deleteOnExit();
            FileUtils.copyInputStreamToFile(inputStreamJar, tempJar);
            return tempJar.toURI().toURL();
        } catch (Exception ioe) {
            ioe.printStackTrace();
        } finally {
            try {
                if (inputStreamJar != null) {
                    inputStreamJar.close();
                }
            } catch (IOException ioe) {
            }
        }
        return null;
    }

    /**
     * Returns a path for a jar from a given url.
     */
    private static String getJarPath(URL url) {
        String urlString = url.getFile();

        int idx;
        if ((idx = urlString.indexOf("!/")) >= 0) {
            urlString = urlString.substring(0, idx);
        }

        if (urlString.startsWith("file:")) {
            return urlString.substring(5);
        }

        return urlString;
    }

    @Override
    protected INameEnvironment getNameEnvironment() {
        return this;
    }

    public void cleanup() {
        if (environment != null) {
            environment.cleanup();
            environment = null;
        }
    }

    public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
        NameEnvironmentAnswer rv = environment.findType(compoundTypeName);
        if (rv == null) {
            rv = findSourceAnswer(toClassName(compoundTypeName));
        }
        return rv;
    }

    private NameEnvironmentAnswer findSourceAnswer(String className) {
        String fileName = classToSourceResource(className);
        InputStream is = getResourceAsStream(fileName);
        if (is != null) {
            char[] source = FileFuncs.getCharStreamContents(is);
            if (source != null) {
                return new NameEnvironmentAnswer(new CompilationUnit(source, fileName, null), null);
            }
        }
        return null;
    }

    public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
        NameEnvironmentAnswer rv = environment.findType(typeName, packageName);
        if (rv == null) {
            rv = findSourceAnswer(toClassName(packageName, typeName));
        }
        return rv;
    }

    public boolean isPackage(char[][] parentPackageName, char[] packageName) {
        return environment.isPackage(parentPackageName, packageName);
    }
}