neembuu.uploader.zip.generator.NUZipFileGenerator.java Source code

Java tutorial

Introduction

Here is the source code for neembuu.uploader.zip.generator.NUZipFileGenerator.java

Source

/* 
 * Copyright 2015 Shashank Tulsyan <shashaank at neembuu.com>.
 *
 * 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.
 */
package neembuu.uploader.zip.generator;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import neembuu.uploader.utils.HashUtil;
import org.apache.commons.io.FilenameUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import shashaank.smallmodule.SmallModule;

/**
 * Make operations to create the zip files.
 * @author davidepastore
 */
public class NUZipFileGenerator {

    private final Path[] uploadersDirectories;
    private final Path outputDirectory;
    private final Index index;
    private final URLClassLoader classLoader;
    private final Environment env;

    /*public NUZipFileGenerator(File gitDirectory, File outputDirectory,String[]modules,
    String[]uploaderModuleName) {*/
    public NUZipFileGenerator(Environment env) {
        this.env = env;
        uploadersDirectories = new Path[env.modulesToCheckForExportibles().length];
        Path gitDirectory = Paths.get(env.gitDirectory());
        for (int i = 0; i < env.modulesToCheckForExportibles().length; i++) {
            String uploadermod = env.modulesToCheckForExportibles()[i];
            uploadersDirectories[i] = gitDirectory.resolve("modules/" + uploadermod + "/build/");
        }

        this.outputDirectory = Paths.get(env.outputDirectory());
        classLoader = l(env.sortedListOfModulesToCompile(), gitDirectory);
        index = new Index(this.outputDirectory.resolve("index.json"), env);
    }

    private static URLClassLoader l(String[] modules, Path pth) {
        try {
            URL[] u = new URL[modules.length];
            for (int i = 0; i < u.length; i++) {
                u[i] = pth.resolve("modules/" + modules[i] + "/build/").toUri().toURL();
            }
            return new URLClassLoader(u);
        } catch (Exception a) {
            throw new IllegalStateException(a);
        }
    }

    public void createZipFiles() {
        Logger.getLogger(NUZipFileGenerator.class.getName()).log(Level.INFO, "Create the zip files");

        try {
            index.intialize();
            walkOverAllFiles();
            index.complete();
        } catch (Exception ex) {
            Logger.getLogger(NUZipFileGenerator.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void walkOverAllFiles() throws IOException {
        for (final Path uploadersDirectory : uploadersDirectories) {
            Files.walkFileTree(uploadersDirectory, new FileVisitor<Path>() {
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    exc.printStackTrace();
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    if (file.getFileName().toString().endsWith(".class")) {
                        visitClassFile(file, attrs, uploadersDirectory);
                    }
                    return FileVisitResult.CONTINUE;
                }
            });
        }

    }

    private Class load(Path f, Path uploadersDirectory) throws Exception {
        String p = uploadersDirectory.normalize().relativize(f.normalize()).toString();
        p = p.replace(File.separatorChar, '.');
        p = p.substring(0, p.lastIndexOf('.'));//to remove .class
        return classLoader.loadClass(p);
    }

    private void visitClassFile(Path file, BasicFileAttributes attrs, Path uploadersDirectory) throws IOException {
        Class c;
        try {
            c = load(file, uploadersDirectory);
        } catch (Throwable a) {
            System.err.println("skipping " + file);
            a.printStackTrace();
            return;
        }

        SmallModule moduleDescription = (SmallModule) c.getAnnotation(SmallModule.class);
        if (moduleDescription == null) {
            System.out.println("not a small module" + file);
            return;
        }
        if (moduleDescription.ignore()) {
            Logger.getLogger(NUZipFileGenerator.class.getName()).log(Level.INFO, "Ignoring : {0}",
                    FilenameUtils.removeExtension(c.getName()));
            return;
        }
        handleSmallModule(moduleDescription, c, uploadersDirectory);
    }

    private void handleSmallModule(SmallModule moduleDescription, Class clzz, Path uploadersDirectory)
            throws IOException {
        Logger.getLogger(NUZipFileGenerator.class.getName()).log(Level.INFO, "Create zip for: {0}", clzz.getName());
        Path outputModulePath = outputDirectory.resolve("sm").resolve(moduleDescription.name() + ".zip");
        Files.createDirectories(outputModulePath.getParent());
        while (Files.exists(outputModulePath)) {
            try {
                Files.deleteIfExists(outputModulePath);
            } catch (Exception a) {
                a.printStackTrace();
            }
        }

        Map<String, String> env = new HashMap<>();
        env.put("create", "true");
        boolean destroyZipIsCorrupt = false;
        URI uri = URI.create("jar:" + outputModulePath.toUri());
        try (FileSystem fs = FileSystems.newFileSystem(uri, env)) {
            smallModuleCreateZip(fs, moduleDescription, clzz, uploadersDirectory);
        } catch (Exception e) {
            e.printStackTrace();
            destroyZipIsCorrupt = true;
        }
        if (destroyZipIsCorrupt) {
            Files.delete(outputModulePath);
        } else {
            String hash = HashUtil.hashFile(outputModulePath.toFile(), index.getHashalgorithm());
            try {
                index.addSmallModule(moduleDescription, hash);
            } catch (Exception a) {
                a.printStackTrace();//ignore
            }
        }
    }

    private void smallModuleCreateZip(FileSystem fs, SmallModule moduleDescription, Class clzz,
            Path uploadersDirectory) throws IOException, JSONException {
        JSONObject metaData = makeMetaData(moduleDescription);
        Files.write(fs.getPath("SmallModule.json"), metaData.toString(3).getBytes());

        //zip(fs, moduleDescription.interfaces()); these are already
        //in the classpath of NU, so we need not repackage them in zip
        zip(fs, moduleDescription.exports(), uploadersDirectory);
        zip(fs, moduleDescription.dependsOn(), uploadersDirectory);

        if (moduleDescription.jarsRequired() != null && moduleDescription.jarsRequired().length > 0) {
            throw new IllegalStateException("jars not supported as of now");
            /*Path extraJarRequiredPath = outputDirectory.resolve("jarsRequired");
            Files.createDirectories(extraJarRequiredPath);*/
        }
    }

    private void zip(FileSystem fs, Class[] z, Path uploadersDirectory) throws IOException {
        for (Class c : z) {
            String relClassPath = c.getName().replace('.', File.separatorChar);
            Path pathInZip = fs.getPath(relClassPath).getParent();
            Files.createDirectories(pathInZip);//creates all directory entires if required.

            handleClassEntry(pathInZip, c, fs, uploadersDirectory);
        }
    }

    private void handleClassEntry(Path pathInZip, final Class c, FileSystem fs, Path uploadersDirectory)
            throws IOException {
        Path classLocationOnDisk = uploadersDirectory.resolve(pathInZip.toString());
        DirectoryStream<Path> ds = Files.newDirectoryStream(classLocationOnDisk,
                new DirectoryStream.Filter<Path>() {
                    @Override
                    public boolean accept(Path entry) throws IOException {
                        String fn = entry.getFileName().toString();
                        String cn = c.getSimpleName();
                        return fn.equals(cn + ".class") || fn.startsWith(cn + "$");
                    }
                });
        for (Path p : ds) {
            byte[] b = Files.readAllBytes(p);
            Files.write(pathInZip.resolve(p.getFileName().toString()), b);
        }

        // say we want to zie SomeClass.class
        // then we also need to zip SomeClass$1.class
        // That is, we also need to zip inner classes and inner annoymous classes 
        // into the zip as well
    }

    private JSONObject makeMetaData(SmallModule moduleDescription) throws JSONException {
        JSONObject metaData = new JSONObject();
        metaData.put("name", moduleDescription.name());
        JSONArray exports = new JSONArray();

        assert moduleDescription.exports().length != moduleDescription.interfaces().length;

        for (int j = 0; j < moduleDescription.exports().length; j++) {
            JSONObject exportableItem = new JSONObject();
            exportableItem.put("implementation", moduleDescription.exports()[j].getName());
            exportableItem.put("interface", moduleDescription.interfaces()[j].getName());

            exports.put(exportableItem);
        }

        metaData.put("exports", exports);
        return metaData;
    }

}