All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.oneandone.stool.setup.Install Maven / Gradle / Ivy

Go to download

Runs multiple web applications in multiple tomcats on a single machine. Stool is a command line tool that provides a lifecycle for stages: create, configure, build, run and remove. A stage contains web applications, that may come in different flavors: Maven war projects or artifacts (GAV coordinates).

The newest version!
/**
 * Copyright 1&1 Internet AG, https://github.com/1and1/
 *
 * 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 net.oneandone.stool.setup;

import net.oneandone.stool.Create;
import net.oneandone.stool.configuration.Property;
import net.oneandone.stool.configuration.StageConfiguration;
import net.oneandone.stool.configuration.StoolConfiguration;
import net.oneandone.stool.extensions.ExtensionsFactory;
import net.oneandone.stool.stage.Stage;
import net.oneandone.stool.util.Environment;
import net.oneandone.stool.util.Files;
import net.oneandone.stool.util.Logging;
import net.oneandone.stool.util.RmRfThread;
import net.oneandone.stool.util.Session;
import net.oneandone.sushi.cli.ArgumentException;
import net.oneandone.sushi.cli.Console;
import net.oneandone.sushi.fs.Settings;
import net.oneandone.sushi.fs.file.FileNode;
import net.oneandone.sushi.io.OS;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;


public class Install {
    public static final String STOOL_UPDATE_CHECKED = ".stool.update.checked";

    private final Console console;

    // to create bin directory with a stool jar. False in tests, when stool.jar is not in classpath
    private final boolean withJar;

    // configuration when installed on target system
    private final FileNode bin;
    private final FileNode man;

    private final Map globalProperties;

    public Install(Console console, boolean withJar, FileNode bin, FileNode man, Map globalProperties) {
        this.console = console;
        this.withJar = withJar;
        this.bin = bin;
        this.man = man;
        this.globalProperties = globalProperties;
    }

    public Session standalone(String user, Environment environment, FileNode home) throws Exception {
        RmRfThread cleanup;
        Session session;

        home.checkNotExists();

        cleanup = new RmRfThread(console);
        cleanup.add(home);
        Runtime.getRuntime().addShutdownHook(cleanup);

        doCreateHomeWithoutOverview(home, false);
        doCreateBinWithoutHomeLink(variables(), bin);
        bin.join("home").mklink(home.getAbsolute());
        doCreateMan(man);
        session = doCreateHomeOverview(user, environment, home);
        // ok, no exceptions - we have a proper install directory: no cleanup
        Runtime.getRuntime().removeShutdownHook(cleanup);
        return session;
    }

    public void debianFiles(FileNode dest) throws Exception {
        dest.mkdir();
        doCreateBinWithoutHomeLink(variables(), dest.join(bin.getName()));
        doCreateMan(dest.join(man.getName()));
    }

    public void debianHome(String user, Environment environment, FileNode home) throws Exception {
        if (home.exists()) {
            home.join("overview").deleteTree();
        } else {
            doCreateHomeWithoutOverview(home, true);
        }
        doCreateHomeOverview(user, environment, home);
    }

    private FileNode downloadCache(FileNode home) {
        FileNode directory;

        if (OS.CURRENT == OS.MAC) {
            directory = (FileNode) console.world.getHome().join("Downloads");
            if (directory.isDirectory()) {
                return directory;
            }
        }
        return home.join("downloads");
    }

    private void doCreateHomeWithoutOverview(FileNode home, boolean shared) throws IOException {
        StoolConfiguration conf;

        home.getParent().mkdirsOpt();
        Files.createStoolDirectory(console.verbose, home);
        conf = new StoolConfiguration(downloadCache(home));
        conf.shared = shared;
        tuneHostname(conf);
        tuneExplicit(conf);
        Files.createStoolDirectoryOpt(console.verbose, conf.downloadCache).join(STOOL_UPDATE_CHECKED).deleteFileOpt().mkfile();
        conf.save(Session.gson(home.getWorld(), ExtensionsFactory.create(home.getWorld())), home);

        for (String dir : new String[]{"extensions", "backstages", "inbox", "logs", "service-wrapper", "run", "run/users", "tomcat"}) {
            Files.createStoolDirectory(console.verbose, home.join(dir));
        }
    }

    private Session doCreateHomeOverview(String user, Environment environment, FileNode home) throws IOException {
        Session session;

        session = Session.load(Logging.forStool(home, user), user, "setup-stool", environment, console, null, null, null);
        createOverview(session);
        return session;
    }

    private void doCreateBinWithoutHomeLink(Map variables, FileNode destBin) throws IOException {
        final byte[] marker = "exit $?\n".getBytes(Settings.UTF_8);
        byte[] bytes;
        int ofs;

        Files.createStoolDirectory(console.verbose, destBin);
        Files.template(console.verbose, console.world.resource("templates/bin"), destBin, variables);
        if (withJar) {
            // strip launcher from application file
            bytes = console.world.locateClasspathItem(getClass()).readBytes();
            ofs = indexOf(bytes, marker) + marker.length;
            try (OutputStream out = destBin.join("stool.jar").createOutputStream()) {
                out.write(bytes, ofs, bytes.length - ofs);
            }
        }
    }

    public static int indexOf(byte[] array, byte[] sub) {
        int j;

        for (int i = 0; i < array.length - sub.length; i++) {
            for (j = 0; j < sub.length; j++) {
                if (sub[j] != array[i + j]) {
                    break;
                }
            }
            if (j == sub.length) {
                return i;
            }
        }
        throw new IllegalStateException();
    }

    private void doCreateMan(FileNode destMan) throws IOException {
        Files.createStoolDirectory(console.verbose, destMan);
        console.world.resource("templates/man").copyDirectory(destMan);
        Files.stoolTree(console.verbose, destMan);
    }

    private Map variables() {
        Map result;

        result = new HashMap<>();
        result.put("stool.bin", bin.getAbsolute());
        result.put("man.path", "/usr/share/man".equals(man.getAbsolute()) ? "" :
                "# note that the empty entry instructs man to search locations.\n" +
                "export MANPATH=" + man.getAbsolute() + ":$MANPATH\n");
        return result;
    }

    private void tuneHostname(StoolConfiguration conf) {
        try {
            conf.hostname = InetAddress.getLocalHost().getCanonicalHostName();
        } catch (UnknownHostException e) {
            console.info.println("WARNING: cannot configure hostname: " + e.getMessage() + ". Using " + conf.hostname);
        }
    }

    private void tuneExplicit(StoolConfiguration conf) {
        boolean error;
        Map properties;
        Property property;

        properties = StoolConfiguration.properties();
        error = false;
        for (Map.Entry entry : globalProperties.entrySet()) {
            property = properties.get(entry.getKey());
            if (property == null) {
                console.info.println("property not found: " + entry.getKey());
                error = true;
            } else {
                try {
                    property.set(conf, entry.getValue());
                } catch (Exception e) {
                    console.info.println("invalid value for property " + entry.getKey() + " : " + e.getMessage());
                    e.printStackTrace(console.verbose);
                    error = true;
                }
            }
        }
        if (error) {
            throw new ArgumentException("invalid configuration");
        }
    }

    //--

    public static void createOverview(Session session) throws IOException {
        Create create;
        String url;
        String tomcatOpts;
        StageConfiguration stageConfiguration;

        stageConfiguration = session.createStageConfiguration("");
        url = "gav:overview:overview:@overview";
        create = new Create(session, true, Stage.OVERVIEW_NAME, url, overviewDirectory(session), stageConfiguration);
        tomcatOpts = session.createStageConfiguration(url).tomcatOpts;
        if (!tomcatOpts.isEmpty()) {
            tomcatOpts += " ";
        }
        tomcatOpts += "-Doverview.stool.bin=" + session.bin.getAbsolute();
        create.remaining("tomcat.opts=" + tomcatOpts);
        create.remaining("until=reserved");
        try {
            create.doInvoke();
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    private static FileNode overviewDirectory(Session session) {
        return session.home.join(Stage.OVERVIEW_NAME);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy