io.macgyver.core.cli.ClientPackager.java Source code

Java tutorial

Introduction

Here is the source code for io.macgyver.core.cli.ClientPackager.java

Source

/**
 * 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 io.macgyver.core.cli;

import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.zip.ZipException;

import org.apache.mina.filter.buffer.BufferedWriteFilter;
import org.crsh.console.jline.internal.InputStreamReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.io.ByteSink;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo;

import io.macgyver.cli.CLI;
import io.macgyver.cli.Command;
import io.macgyver.core.Bootstrap;
import io.macgyver.core.Kernel;
import io.macgyver.core.resource.Resource;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.model.ZipParameters;

public class ClientPackager {

    public static final String CLIENT_DIR_NAME = ".cli-generated";
    Logger logger = LoggerFactory.getLogger(ClientPackager.class);
    File targetDir = new File(".", CLIENT_DIR_NAME);

    public ClientPackager withOutputDir(File output) {
        targetDir = output;
        return this;
    }

    public File getMacGyvverCLIExecutable() {
        return new File(targetDir, "macgyver");
    }

    public synchronized File rebuildIfNecessary() throws IOException {
        File f = getMacGyvverCLIExecutable();
        if (f.exists()) {
            return f;
        }
        return rebuild();
    }

    public synchronized File rebuild() throws IOException {
        try {
            File f = getMacGyvverCLIExecutable();
            f.delete();
            buildCli();
            return f;
        } catch (net.lingala.zip4j.exception.ZipException e) {
            throw new IOException(e);
        }
    }

    private synchronized File buildCli() throws IOException, net.lingala.zip4j.exception.ZipException {

        File tempDir = Files.createTempDir();

        File f = new File(tempDir, "collected-cli-classes.jar");

        ZipFile z = new ZipFile(f);

        ClassLoader cl = Thread.currentThread().getContextClassLoader();

        for (String packageName : CLI.findPackageNamesToSearch()) {

            logger.info("searching pacakge for CLI commands: {}", packageName);
            ClassPath.from(cl).getTopLevelClasses(packageName).forEach(it -> {

                if (isToBePackaged(it)) {
                    logger.info("adding command class: {}", it);
                    try (InputStream input = it.url().openStream()) {
                        byte[] b = ByteStreams.toByteArray(input);

                        ZipParameters zp = new ZipParameters();
                        zp.setSourceExternalStream(true);
                        zp.setFileNameInZip(it.getName().replace(".", "/") + ".class");
                        z.addStream(new ByteArrayInputStream(b), zp);
                    } catch (IOException | net.lingala.zip4j.exception.ZipException e) {
                        throw new RuntimeException(e);
                    }
                }

            });
        }

        File generatedJar = new File(tempDir, "macgyver-capsule-with-extensions.jar");

        try (InputStream in = cl.getResourceAsStream("cli/macgyver-cli-capsule.jar")) {
            Preconditions.checkNotNull(in,
                    "resource cli/macgyver-cli-capsule.jar could not be loaded.  Did you run gradle install in macgyver-cli?");
            try (FileOutputStream out = new FileOutputStream(generatedJar)) {

                ByteStreams.copy(in, out);
            }
        }

        ZipFile mj = new ZipFile(generatedJar);
        ZipParameters zp = new ZipParameters();
        zp.setFileNameInZip("collected-cli-classes.jar");

        try {
            mj.removeFile("collected-cli-classes.jar");
        } catch (Exception e) {
        }
        mj.addFile(f, zp);

        targetDir.mkdirs();
        File executable = new File(targetDir, "macgyver");
        executable.delete();

        PrintWriter bw = new PrintWriter(new FileWriter(executable));

        org.springframework.core.io.Resource resource = Kernel.getApplicationContext()
                .getResource("classpath:cli/cli-jar-header.sh");
        try (InputStreamReader isr = new InputStreamReader(resource.getInputStream())) {
            String script = CharStreams.toString(isr);
            bw.println(script);
        }
        // bw.println("#!/bin/bash");
        // bw.println("exec java -Dcli.exe=\"$0\" -Dcli.launch=true -jar $0
        // \"$@\"");

        bw.close();

        try (FileInputStream fis = new FileInputStream(generatedJar)) {
            try (FileOutputStream fos = new FileOutputStream(executable, true)) {
                ByteStreams.copy(fis, fos);
            }
        }
        executable.setExecutable(true);

        return executable;
    }

    public boolean isToBePackaged(ClassInfo ci) {
        try {
            Class z = Thread.currentThread().getContextClassLoader().loadClass(ci.getName());
            if (Command.class.isAssignableFrom(z)) {
                return true;
            }
        } catch (Exception e) {
            logger.debug("problem scanning class: " + ci);
        }
        return false;
    }
}