org.walkmod.gradle.providers.ClassLoaderConfigurationProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.walkmod.gradle.providers.ClassLoaderConfigurationProvider.java

Source

/*
 Copyright (C) 2015 Raquel Pau and Albert Coroleu.
     
Walkmod is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
Walkmod 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 Lesser General Public License for more details.
    
You should have received a copy of the GNU Lesser General Public License
along with Walkmod.  If not, see <http://www.gnu.org/licenses/>.*/
package org.walkmod.gradle.providers;

import org.apache.commons.io.FileUtils;
import org.gradle.tooling.BuildLauncher;
import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ProjectConnection;
import org.gradle.tooling.model.ExternalDependency;
import org.gradle.tooling.model.eclipse.EclipseProject;
import org.walkmod.conf.ConfigurationException;
import org.walkmod.conf.ConfigurationProvider;
import org.walkmod.conf.entities.Configuration;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class ClassLoaderConfigurationProvider implements ConfigurationProvider {

    private File pomFile = null;

    private String buildDir = "classes";

    private Configuration configuration;

    private String installationDir;

    private String userHomeDir;

    private String workingDirectory;

    private GradleConnector connector = null;

    public ClassLoaderConfigurationProvider() {
    }

    public void setInstallationDir(String installationDir) {
        this.installationDir = installationDir;
    }

    public void setUserHomeDir(String userHomeDir) {
        this.userHomeDir = userHomeDir;
    }

    public void setWorkingDirectory(String workingDirectory) {
        this.workingDirectory = workingDirectory;
    }

    public void setBuildDir(String buildDir) {
        this.buildDir = buildDir;
    }

    public File getPomFile() {
        return pomFile;
    }

    public void setPomFile(File file) {
        this.pomFile = file;
    }

    @Override
    public void init(Configuration configuration) {
        this.configuration = configuration;
    }

    public GradleConnector getConnector() throws ConfigurationException {
        if (connector == null) {
            connector = GradleConnector.newConnector();
            if (installationDir != null) {
                connector.useInstallation(new File(installationDir));
                if (userHomeDir != null) {
                    connector.useGradleUserHomeDir(new File(userHomeDir));
                } else {
                    try {
                        userHomeDir = new File(System.getProperty("user.home"), ".gradle").getCanonicalPath();
                    } catch (IOException e) {
                        throw new ConfigurationException("Error resolving the working directory", e.getCause());
                    }
                }
            } else {
                try {
                    userHomeDir = new File(System.getProperty("user.home"), ".gradle").getCanonicalPath();
                } catch (IOException e) {
                    throw new ConfigurationException("Error resolving the working directory", e.getCause());
                }
            }
            if (workingDirectory == null) {
                try {
                    workingDirectory = new File(".").getCanonicalPath();
                } catch (IOException e) {
                    throw new ConfigurationException("Error resolving the working directory", e.getCause());
                }
            }

            connector.forProjectDirectory(new File(workingDirectory));
        }
        return connector;
    }

    public void compile() throws Exception {

        ProjectConnection connection = getConnector().connect();
        try {
            // Configure the build
            BuildLauncher launcher = connection.newBuild();
            launcher.forTasks("assemble");
            launcher.setStandardOutput(System.out);
            launcher.setStandardError(System.err);

            // Run the build
            launcher.run();
        } finally {
            // Clean up
            connection.close();
        }
    }

    private List<String> getDepsCoordinates(ProjectConnection connection) {
        BuildLauncher launcher = connection.newBuild();
        launcher.forTasks("dependencies");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        launcher.setStandardOutput(bos);
        launcher.setStandardError(System.err);
        launcher.run();
        String content = bos.toString();
        String[] lines = content.split("\\n");
        List<String> coordinates = new LinkedList<String>();
        for (int i = 0; i < lines.length; i++) {
            if (lines[i].startsWith("compile") || lines[i].startsWith("provided")) {
                if (i + 1 < lines.length && !lines[i + 1].startsWith("No dependencies")) {
                    int j = i + 1;
                    String prefix = null;
                    String nextGoal = "debugApk";
                    if (lines[i].startsWith("provided")) {
                        nextGoal = "releaseApk";
                    }
                    while (j < lines.length && !lines[j].startsWith(nextGoal)) {
                        int index = lines[j].lastIndexOf("--");
                        if (index != -1) {
                            prefix = lines[j].substring(0, index + 2);

                            if (lines[j].length() > prefix.length()) {

                                String artifact = lines[j].substring(prefix.length()).trim();
                                if (!artifact.endsWith("(*)")) {
                                    int dynamicVersionIndex = artifact.indexOf("->");
                                    if (dynamicVersionIndex != -1) {
                                        String artifactName = artifact.substring(0, artifact.lastIndexOf(":"))
                                                .trim();
                                        String version = artifact.substring(dynamicVersionIndex + 2).trim();
                                        artifact = artifactName + ":" + version;
                                    }
                                    coordinates.add(artifact);
                                }

                            }
                        }
                        j++;
                    }

                }
            }
        }
        return coordinates;
    }

    private Integer getMinAndroidSDKVersion(File projectDir) {
        File cfg = new File(projectDir, "build.gradle");
        Integer version = null;
        try {
            List<String> cfgContent = FileUtils.readLines(cfg);
            Iterator<String> it = cfgContent.iterator();
            while (it.hasNext() && version == null) {
                String line = it.next();
                int index = line.indexOf("minSdkVersion ");
                if (index != -1) {

                    version = Integer.parseInt(line.substring(index + "minSdkVersion ".length()).trim());
                }
            }

        } catch (IOException e) {
            throw new ConfigurationException("Error reading the build.gradle", e.getCause());
        }
        return version;
    }

    private File getAndroidJar(Integer version) {
        String androidHome = System.getenv("ANDROID_HOME");
        if (androidHome != null && !"".equals(androidHome)) {

            File jarDir = new File(androidHome, "platforms/");

            if (jarDir.exists()) {
                Integer versionNumber = null;
                File[] files = jarDir.listFiles();
                for (int i = 0; i < files.length; i++) {
                    String fileName = files[i].getName();
                    if (fileName.startsWith("android-")) {
                        try {
                            Integer aux = Integer.parseInt(fileName.substring("android-".length()));
                            if (aux > version) {
                                if (versionNumber == null || versionNumber > aux) {
                                    versionNumber = aux;
                                }
                            }
                        } catch (NumberFormatException e) {

                        }
                    }
                }
                if (versionNumber != null) {
                    return (new File(new File(jarDir, "android-" + versionNumber), "android.jar"));
                }
            }
        }
        return null;
    }

    public List<File> getClassPathFiles() throws ConfigurationException {
        ProjectConnection connection = getConnector().connect();
        List<File> classPathFiles = new LinkedList<File>();
        try {
            // Load the Eclipse model for the project
            EclipseProject project = connection.getModel(EclipseProject.class);
            File gradleBuildDir = project.getGradleProject().getBuildDirectory();
            File classesDir = new File(gradleBuildDir, buildDir);
            boolean isAndroid = false;

            if (classesDir.exists()) {
                File[] files = classesDir.listFiles();
                if (files != null) {
                    for (File file : files) {
                        classPathFiles.add(file);
                    }
                }
            } else {
                isAndroid = true;
                classesDir = new File(gradleBuildDir, "intermediates/classes/release/");
                classPathFiles.add(classesDir);
            }

            if (isAndroid) {

                List<String> coordinates = getDepsCoordinates(connection);

                Integer version = getMinAndroidSDKVersion(project.getGradleProject().getProjectDirectory());

                if (version != null) {
                    File jar = getAndroidJar(version);
                    if (jar != null && jar.exists()) {
                        classPathFiles.add(jar);
                    }
                }

                if (!coordinates.isEmpty()) {
                    classPathFiles.addAll(resolveArtifacts(coordinates));
                }

                File auxLibs = new File("libs");
                if (auxLibs.exists()) {
                    File[] files = auxLibs.listFiles();
                    for (File jar : files) {
                        classPathFiles.add(jar);
                    }
                }
            } else {
                for (ExternalDependency externalDependency : project.getClasspath()) {
                    classPathFiles.add(externalDependency.getFile());
                }
            }

        } finally {
            // Clean up
            connection.close();
        }
        return classPathFiles;
    }

    private void unzipAAR(String zipFilePath, String destDirectory) throws IOException {
        File destDir = new File(destDirectory);
        if (!destDir.exists()) {
            destDir.mkdir();
        }
        ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath));
        ZipEntry entry = zipIn.getNextEntry();
        // iterates over entries in the zip file
        while (entry != null) {
            String filePath = destDirectory + File.separator + entry.getName();
            if (!entry.isDirectory()) {
                if (entry.getName().endsWith(".jar")) {
                    // if the entry is a file, extracts it
                    extractFile(zipIn, filePath);
                }
            } else {
                if (entry.getName().endsWith("jars/") || entry.getName().endsWith("libs/")) {
                    File dir = new File(filePath);
                    dir.mkdir();
                }
            }
            zipIn.closeEntry();
            entry = zipIn.getNextEntry();
        }
        zipIn.close();
    }

    /**
     * Extracts a zip entry (file entry)
     *
     * @param zipIn
     * @param filePath
     * @throws IOException
     */
    private void extractFile(ZipInputStream zipIn, String filePath) throws IOException {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
        byte[] bytesIn = new byte[4096];
        int read = 0;
        while ((read = zipIn.read(bytesIn)) != -1) {
            bos.write(bytesIn, 0, read);
        }
        bos.close();
    }

    public Collection<File> resolveArtifacts(List<String> coordinates) throws ConfigurationException {
        Collection<File> result = new LinkedList<File>();
        if (coordinates != null) {
            File aux = new File(userHomeDir, "caches/modules-2/files-2.1");
            for (String coordinate : coordinates) {
                String[] parts = coordinate.split(":");
                File groupIdDir = new File(aux, parts[0]);
                File artifactIdDir = new File(groupIdDir, parts[1]);
                File versionDir = new File(artifactIdDir, parts[2]);
                File[] subdirs = versionDir.listFiles();
                boolean found = false;
                if (subdirs == null) {
                    //Trying inside the android m2 extras
                    String androidHome = System.getenv("ANDROID_HOME");
                    if (androidHome != null && !"".equals(androidHome)) {
                        File androidHomeFile = new File(androidHome);
                        File repo = null;
                        if (parts[0].contains("google")) {
                            repo = new File(androidHomeFile, "extras/google/m2repository");
                        } else {
                            repo = new File(androidHomeFile, "extras/android/m2repository");
                        }
                        groupIdDir = new File(repo, parts[0].replaceAll("\\.", File.separator));
                        artifactIdDir = new File(groupIdDir, parts[1]);
                        versionDir = new File(artifactIdDir, parts[2]);
                        File aarFile = new File(versionDir, parts[1] + "-" + parts[2] + ".aar");
                        if (aarFile.exists()) {
                            try {
                                File jarClasses = new File(versionDir, "classes.jar");
                                //if (!jarClasses.exists()) {
                                unzipAAR(aarFile.getAbsolutePath(), versionDir.getAbsolutePath());
                                //}
                                result.add(jarClasses);
                                File libFolder = new File(versionDir, "libs");
                                if (libFolder.exists()) {

                                    subdirs = libFolder.listFiles();
                                    for (File lib : subdirs) {
                                        result.add(lib);
                                    }
                                }
                            } catch (IOException e) {
                                throw new ConfigurationException(
                                        "Error extracting the aar file " + aarFile.getAbsolutePath(), e);
                            }
                        } else {
                            File jarFile = new File(versionDir, parts[1] + "-" + parts[2] + ".jar");
                            if (jarFile.exists()) {
                                result.add(jarFile);
                            } else {
                                System.out.println(coordinate);
                            }
                        }
                    }
                } else {
                    for (int i = 0; i < subdirs.length && !found; i++) {
                        File parentDir = subdirs[i];
                        if (parentDir.isDirectory()) {
                            File file = new File(parentDir, parts[1] + "-" + parts[2] + ".jar");
                            if (file.exists()) {
                                result.add(file);
                                found = true;
                            } else {
                                file = new File(parentDir, parts[1] + "-" + parts[2] + ".aar");
                                if (file.exists()) {
                                    try {
                                        File jarClasses = new File(parentDir, "classes.jar");
                                        if (!jarClasses.exists()) {
                                            unzipAAR(file.getAbsolutePath(), parentDir.getAbsolutePath());
                                        }
                                        result.add(jarClasses);
                                        File libFolder = new File(parentDir, "libs");
                                        if (libFolder.exists()) {
                                            File[] deps = libFolder.listFiles();
                                            for (File lib : deps) {
                                                result.add(lib);
                                            }
                                        }
                                        found = true;
                                    } catch (IOException e) {
                                        throw new ConfigurationException("Error extracting aar", e);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return result;
    }

    @Override
    public void load() throws ConfigurationException {
        if (configuration != null) {

            try {
                compile();
            } catch (Exception e1) {
                throw new ConfigurationException("Error compiling the project", e1.getCause());
            }
            List<File> classPathList = getClassPathFiles();
            String[] bootPath = System.getProperties().get("sun.boot.class.path").toString()
                    .split(Character.toString(File.pathSeparatorChar));
            URL[] classPath = new URL[classPathList.size() + bootPath.length];

            int i = 0;
            for (String lib : bootPath) {

                try {
                    classPath[i] = new File(lib).toURI().toURL();
                } catch (MalformedURLException e) {
                    throw new ConfigurationException("Invalid URL for the boot classpath entry " + lib,
                            e.getCause());
                }

                i++;
            }

            if (!classPathList.isEmpty()) {

                for (File entry : classPathList) {
                    try {
                        classPath[i] = entry.toURI().toURL();
                    } catch (MalformedURLException e) {
                        throw new ConfigurationException(
                                "Invalid URL for the dependency " + entry.getAbsolutePath(), e.getCause());
                    }
                    i++;
                }

            }
            URLClassLoader loader = new URLClassLoader(classPath) {
                @Override
                protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
                    Class<?> result = null;
                    try {
                        result = findClass(name);

                    } catch (Throwable e) {

                    }
                    if (result != null) {
                        return result;
                    }

                    return super.loadClass(name, resolve);
                }

                @Override
                public Class<?> loadClass(String name) throws ClassNotFoundException {
                    return loadClass(name, false);
                }
            };
            configuration.getParameters().put("classLoader", loader);

        }
    }

}