bear.core.BearProject.java Source code

Java tutorial

Introduction

Here is the source code for bear.core.BearProject.java

Source

/*
 * Copyright (C) 2013 Andrey Chaschev.
 *
 * 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 bear.core;

import bear.annotations.Configuration;
import bear.annotations.Method;
import bear.annotations.Project;
import bear.annotations.Variable;
import bear.plugins.DeploymentPlugin;
import bear.plugins.Plugin;
import bear.plugins.PluginShellMode;
import bear.plugins.ServerToolPlugin;
import bear.plugins.misc.ReleasesPlugin;
import bear.plugins.sh.GenericUnixRemoteEnvironmentPlugin;
import bear.session.Address;
import bear.session.DynamicVariable;
import bear.session.Result;
import bear.task.*;
import chaschev.lang.OpenBean;
import chaschev.lang.reflect.Annotations;
import chaschev.lang.reflect.MethodDesc;
import chaschev.util.Exceptions;
import com.google.common.base.*;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.logging.log4j.core.helpers.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URI;
import java.util.*;

import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.base.Optional.fromNullable;
import static java.util.Collections.singletonList;

/**
 * @author Andrey Chaschev chaschev@gmail.com
 */
public abstract class BearProject<SELF extends BearProject> {
    private static final Logger logger = LoggerFactory.getLogger(BearProject.class);

    /**
     * Typically you would want to turn it off when you do several calls one of which redefines configuration.
     */
    protected boolean useAnnotations = true;
    protected GlobalContextFactory factory;
    protected GlobalContext global;
    private boolean configured;
    private Map<Object, Object> variables;
    private BearMain bearMain;
    protected Bear bear;

    protected boolean shutdownAfterRun = true;

    protected DeploymentPlugin.Builder defaultDeployment;

    protected ReleasesPlugin releases;
    private boolean unblockUninstall;
    private Object input;
    private boolean async;

    protected BearProject() {
        this(GlobalContextFactory.INSTANCE);
        global = factory.getGlobal();
        bear = global.bear;
    }

    protected BearProject(GlobalContextFactory factory) {
        this.factory = factory;
        global = factory.getGlobal();
        bear = global.bear;
    }

    public BearProject(GlobalContextFactory factory, @Nullable String propsResourceString) {
        this(factory);

        if (propsResourceString != null) {
            try {
                loadProperties(getClass().getResourceAsStream(propsResourceString));
            } catch (Exception e) {
                throw Exceptions.runtime(e);
            }
        }
    }

    public BearProject withMap(Map<Object, Object> variables) {
        this.variables = Collections.unmodifiableMap(variables);
        return this;
    }

    public BearProject(GlobalContextFactory factory, @Nullable File file) {
        this(factory);

        if (file != null) {
            loadProperties(file);
        }
    }

    public final SELF configure() {
        try {
            return configure(factory);
        } catch (Exception e) {
            throw Exceptions.runtime(e);
        }
    }

    public final SELF configure(GlobalContextFactory factory) throws Exception {
        main().configure();

        Preconditions.checkArgument(!configured, "already configured");

        for (Field field : OpenBean.fieldsOfType(this.getClass(), Plugin.class)) {
            Class<? extends Plugin<TaskDef>> aClass = (Class<? extends Plugin<TaskDef>>) field.getType();

            factory.requirePlugins(aClass);
        }

        factory.initPluginsAndWire(this);

        Iterable<Field> fields = OpenBean.fieldsOfType(this, GridBuilder.class);

        for (Field field : fields) {
            ((GridBuilder) field.get(this)).init(this);
        }

        //        List<MethodDesc<? extends GridBuilder>> list = OpenBean.methodsReturning(this.getClass(), GridBuilder.class);

        //        iter

        configureMe(factory);

        configured = true;

        return self();
    }

    protected abstract GlobalContext configureMe(GlobalContextFactory factory) throws Exception;

    public BearProject loadProperties(InputStream is) throws Exception {
        global.loadProperties(is);

        return this;
    }

    public BearProject loadProperties(Properties props) {
        global.loadProperties(props);
        return this;
    }

    public BearProject loadProperties(File file) {
        try {
            final FileInputStream fis = new FileInputStream(file);
            loadProperties(fis);
            return this;
        } catch (Exception e) {
            throw Exceptions.runtime(e);
        }

    }

    public GlobalContextFactory getFactory() {
        return factory;
    }

    public GlobalContext getGlobal() {
        return global;
    }

    public boolean isConfigured() {
        return configured;
    }

    //    public void run() {
    //        BearMain.run(this, variables, true);
    //    }

    public <T> SELF set(DynamicVariable<? extends T> var, T value) {
        if (!global.isSet(var)) {
            global.putConst(var, value);
        }

        return self();
    }

    @SuppressWarnings("unchecked")
    protected SELF self() {
        return (SELF) this;
    }

    public SELF injectMain(BearMain bearMain) {
        this.bearMain = bearMain;
        return self();
    }

    public synchronized BearMain main() {
        if (bearMain == null) {
            bearMain = new BearMain(global, BearMain.getCompilerManager());
        }

        return bearMain;
    }

    public GridBuilder newGrid() {
        GridBuilder gb = new GridBuilder();

        gb.project = this;
        gb.bearMain = main();

        return gb;
    }

    public TaskDef<Object, TaskResult<?>> newDeployTask() {
        checkDeployment();
        return defaultDeployment.build();
    }

    protected List<TaskDef<Object, TaskResult<?>>> startServiceTaskDefs() {
        checkDeployment();

        return defaultDeployment.getStartService()
                .createTasksToList(new ArrayList<TaskDef<Object, TaskResult<?>>>());
    }

    protected List<TaskDef<Object, TaskResult<?>>> stopServiceTaskDefs() {
        checkDeployment();

        return defaultDeployment.getStopService()
                .createTasksToList(new ArrayList<TaskDef<Object, TaskResult<?>>>());
    }

    @Method
    public GlobalTaskRunner start() {
        return runTasksWithAnnotations(new Supplier<List<TaskDef<Object, TaskResult<?>>>>() {
            @Override
            public List<TaskDef<Object, TaskResult<?>>> get() {
                return startServiceTaskDefs();
            }
        }, useAnnotations);
    }

    @Method
    public GlobalTaskRunner stop() {
        return runTasksWithAnnotations(new Supplier<List<TaskDef<Object, TaskResult<?>>>>() {
            @Override
            public List<TaskDef<Object, TaskResult<?>>> get() {
                return stopServiceTaskDefs();
            }
        }, useAnnotations);
    }

    @Method
    public GlobalTaskRunner deploy() {
        return runTasksWithAnnotations(new Supplier<List<TaskDef<Object, TaskResult<?>>>>() {
            @Override
            public List<TaskDef<Object, TaskResult<?>>> get() {
                return singletonList(defaultDeployment.build());
            }
        }, useAnnotations);
    }

    @Method
    public GlobalTaskRunner setup() {
        return setup(true);
    }

    public GlobalTaskRunner setup(boolean autoInstall) {
        if (autoInstall) {
            set(bear.verifyPlugins, true);
            set(bear.autoInstallPlugins, true);
            set(bear.checkDependencies, true);
        }

        input = this;

        return runTasksWithAnnotations(new Supplier<List<InstallationTaskDef<InstallationTask>>>() {
            @Override
            public List<InstallationTaskDef<InstallationTask>> get() {
                return singletonList(global.tasks.setup);
            }
        });
    }

    public void rollbackTo(final String ref) {
        runTasksWithAnnotations(new Supplier<List<TaskDef<Object, TaskResult<?>>>>() {
            @Override
            public List<TaskDef<Object, TaskResult<?>>> get() {
                return singletonList(rollbackToTask(ref));
            }
        });
    }

    public void invoke() {
        String method = getClass().getAnnotation(Project.class).method();

        Preconditions.checkArgument(!method.isEmpty(),
                "method() must not be empty for @Project when not providing a invoking a project");

        invoke(method);
    }

    public void invoke(String method, Object... params) {
        Preconditions.checkNotNull(method, "method must not be null");

        setProjectVars();

        MethodDesc methodDesc = OpenBean.getClassDesc(getClass()).getMethodDesc(method, false, params);

        Configuration projectAnnotation = projectConf();
        Configuration methodAnnotation = methodDesc.getMethod().getAnnotation(Configuration.class);

        configureWithAnnotations(methodAnnotation, projectAnnotation);

        global.put(bear.useUI, useUI(firstNonNull(methodAnnotation, projectAnnotation)));

        Object result = methodDesc.invoke(this, params);

        System.out.println("returned result: " + result);
    }

    public GlobalTaskRunner run(final List<? extends NamedCallable> callables) {
        return run(callables, useAnnotations);
    }

    public GlobalTaskRunner run(final List<? extends NamedCallable> callables, boolean useAnnotations) {
        return runTasksWithAnnotations(new Supplier<List<? extends TaskDef>>() {
            @Override
            public List<? extends TaskDef> get() {
                return Lists.newArrayList(Lists.transform(callables, new Function<TaskCallable, TaskDef>() {
                    @Override
                    public TaskDef apply(TaskCallable input) {
                        return new TaskDef(input);
                    }
                }));
            }
        }, useAnnotations);
    }

    protected Function<GridBuilder, Void> preRunHook;

    public GlobalTaskRunner runTasksWithAnnotations(Supplier<? extends List<? extends TaskDef>> taskList) {
        return runTasksWithAnnotations(taskList, useAnnotations);
    }

    public GlobalTaskRunner runTasksWithAnnotations(Supplier<? extends List<? extends TaskDef>> taskList,
            boolean useAnnotations) {
        global.putConst(bear.activeProject, this);

        Configuration projectConf = configureWithAnnotations(useAnnotations);

        GridBuilder grid = newGrid().setShutdownAfterRun(shutdownAfterRun);

        if (input != null) {
            grid.setInput(input);
            input = null;
        }

        if (preRunHook != null) {
            preRunHook.apply(grid);
        }

        grid.addAll(taskList.get());

        grid.setAsync(async);

        if (useUI(useAnnotations ? projectConf : null)) {
            return grid.runUi();
        } else {
            return grid.runCli();
        }
    }

    public Configuration configureWithAnnotations(boolean useAnnotations) {
        setProjectVars();

        Configuration projectConf = projectConf();

        if (useAnnotations) {
            configureWithAnnotations(projectConf, null);
        }

        if (!configured) {
            configure();
        }

        return projectConf;
    }

    private Configuration projectConf() {
        return getClass().getAnnotation(Configuration.class);
    }

    private boolean useUI(Configuration annotation) {
        if (global.isSet(bear.useUI))
            return global.var(bear.useUI);

        if (annotation == null) {
            return Annotations.defaultBoolean(annotation, "useUI");
        }

        boolean r = annotation.useUI();

        global.put(bear.useUI, r);

        return r;
    }

    private Configuration configureWithAnnotations(@Nullable Configuration annotation,
            @Nullable Configuration fallbackTo) {

        if (annotation == null && fallbackTo == null)
            return null;

        String properties = (String) chooseValue("properties", annotation, fallbackTo);

        if (properties != null) {
            File file = new File(properties);

            if (!file.exists() && !file.getName().endsWith(".properties")) {
                file = new File(properties + ".properties");
            }

            Preconditions.checkArgument(file.exists(), "properties file does not exist: %s",
                    file.getAbsolutePath());

            set(main().propertiesFile, file);
        }

        setAnnotation(bear.repositoryURI, chooseValue("vcs", annotation, fallbackTo));
        setAnnotation(bear.vcsBranchName, chooseValue("branch", annotation, fallbackTo));
        setAnnotation(bear.vcsTag, chooseValue("tag", annotation, fallbackTo));
        setAnnotation(bear.stage, chooseValue("stage", annotation, fallbackTo));
        setAnnotation(bear.sshUsername, chooseValue("user", annotation, fallbackTo));
        setAnnotation(bear.sshPassword, chooseValue("password", annotation, fallbackTo));

        Variable[] vars = (Variable[]) chooseValue("variables", annotation, fallbackTo);

        if (vars != null) {
            for (Variable variable : vars) {
                String name = variable.name();
                Preconditions.checkArgument(global.variableRegistry.contains(name),
                        "variable %s was not found in the registry", name);
                global.putConst(name, variable.value());
            }
        }

        return annotation;
    }

    private Object chooseValue(String property, Configuration annotation, Configuration fallbackTo) {
        Object value = null;

        if (annotation != null) {
            Object defaultValue = Annotations.defaultValue(annotation, property);
            value = OpenBean.invoke(annotation, property);
            if (defaultValue.equals(value))
                value = null;
        }

        if (value == null && fallbackTo != null) {
            Object defaultValue = Annotations.defaultValue(fallbackTo, property);
            value = OpenBean.invoke(fallbackTo, property);
            if (defaultValue.equals(value))
                value = null;
        }

        return value;
    }

    private void setAnnotation(DynamicVariable<String> var, Object value) {
        if (value != null) {
            set(var, (String) value);
        }
    }

    protected TaskDef<Object, TaskResult<?>> rollbackToTask(final String labelOrPath) {
        Preconditions.checkArgument(Strings.isNotEmpty(labelOrPath), "release reference string is empty");

        return defaultDeployment.build().getRollback().addBeforeTask(releases.findReleaseToRollbackTo(labelOrPath));
    }

    protected SELF checkDeployment() {
        Preconditions.checkNotNull(defaultDeployment,
                "deployment is not present, you need to set BearProject.defaultDeployment field");
        return self();
    }

    private void setProjectVars() {
        Project projectAnn = getClass().getAnnotation(Project.class);

        setAnnotation(bear.fullName, projectAnn.name());
        setAnnotation(bear.name, projectAnn.shortName());
    }

    public SELF setShutdownAfterRun(boolean p) {
        this.shutdownAfterRun = p;
        return self();
    }

    public DeploymentPlugin.Builder getDefaultDeployment() {
        return defaultDeployment;
    }

    public List<Plugin<TaskDef>> getAllOrderedPlugins() {
        try {
            Set<Plugin<TaskDef>> plugins = new HashSet<Plugin<TaskDef>>();

            for (Field field : OpenBean.fieldsOfType(this, Plugin.class)) {
                Plugin plugin = (Plugin) field.get(this);
                Set<Plugin<TaskDef>> set = plugin.getAllPluginDependencies();

                plugins.add(plugin);
                plugins.addAll(set);
            }

            return global.plugins.orderPlugins(plugins);
        } catch (IllegalAccessException e) {
            throw Exceptions.runtime(e);
        }
    }

    public Optional<? extends Plugin<TaskDef>> findShell(String shell) {
        Plugin<TaskDef> shellPlugin = null;

        for (Plugin<TaskDef> plugin : getAllShellPlugins(global, this.getClass())) {
            if (shell.equals(plugin.cmdAnnotation())) {
                shellPlugin = plugin;
                break;
            }
        }

        return fromNullable(shellPlugin);
    }

    public List<Plugin<TaskDef>> getAllShellPlugins(GlobalContext global, Class<? extends BearProject> aClass) {
        Set<Plugin<TaskDef>> plugins = new LinkedHashSet<Plugin<TaskDef>>();

        plugins.add((Plugin) global.plugin(GenericUnixRemoteEnvironmentPlugin.class));

        for (Field field : OpenBean.fieldsOfType(aClass, Plugin.class)) {
            Plugin plugin = global.getPlugin((Class<? extends Plugin>) field.getType()).get();

            addIfHasShell(plugins, plugin);

            for (Plugin<TaskDef> pl : (Set<Plugin<TaskDef>>) plugin.getAllPluginDependencies()) {
                addIfHasShell(plugins, pl);
            }
        }

        return global.plugins.orderPlugins(plugins);
    }

    private static void addIfHasShell(Set<Plugin<TaskDef>> plugins, Plugin plugin) {
        PluginShellMode shell = plugin.getShell();

        if (shell != null) {
            plugins.add(plugin);
        }
    }

    public SELF setInteractiveMode() {
        async = true;
        shutdownAfterRun = false;
        return self();
    }

    public void setAsync(boolean async) {
        this.async = async;
    }

    public boolean isAsync() {
        return async;
    }

    public static class PulseResult extends TaskResult<PulseResult> {
        public PulseResult(Result result) {
            super(result);
        }

        public PulseResult(Throwable e) {
            super(e);
        }
    }

    public DependencyResult pulse() {
        DependencyResult result = new DependencyResult(Result.OK);

        for (Field field : OpenBean.fieldsOfType(this, ServerToolPlugin.class)) {
            try {
                ServerToolPlugin plugin = (ServerToolPlugin) field.get(this);

                result.join(pulse(plugin, Predicates.<String>alwaysTrue()));
            } catch (IllegalAccessException e) {
                throw Exceptions.runtime(e);
            }
        }

        return result;
    }

    protected DependencyResult pulse(ServerToolPlugin serverTool, Predicate<String> bodyPredicate) {
        DependencyResult result = new DependencyResult("pulse from " + serverTool.getClass().getSimpleName());

        HttpClient httpClient = new DefaultHttpClient();

        for (Address address : global.var(global.bear.getStage).addresses) {
            List<String> ports = global.var(serverTool.portsSplit);

            for (String port : ports) {
                URI uri = null;

                try {
                    uri = new URIBuilder().setScheme("http").setHost(address.getAddress())
                            .setPort(Integer.parseInt(port)).build();

                    logger.info("sending pulse to {}", uri);

                    HttpGet httpget = new HttpGet(uri);
                    HttpResponse response = httpClient.execute(httpget);

                    int code = response.getStatusLine().getStatusCode();

                    if (code != 200) {
                        result.add("code " + code + " for " + uri);
                        continue;
                    }

                    String body = IOUtils.toString(response.getEntity().getContent());

                    if (!bodyPredicate.apply(body)) {
                        result.add("predicate doesn't match for " + uri);
                        //                        throw new RuntimeException("predicate doesn't match for " + uri);
                    }
                } catch (Exception e) {
                    result.add(String.valueOf(uri) + ": " + e.toString());
                }
            }
        }

        return result;
    }

    public SELF setInput(Object input) {
        this.input = input;
        return self();
    }
}