io.coala.eve3.Eve3Config.java Source code

Java tutorial

Introduction

Here is the source code for io.coala.eve3.Eve3Config.java

Source

/* $Id: fb69241d05ed885974ecab5db634c312f0a25453 $
 * 
 * @license
 * 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.coala.eve3;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

import javax.servlet.Filter;
import javax.servlet.Servlet;

import org.aeonbits.owner.ConfigFactory;
import org.aeonbits.owner.Mutable;
import org.apache.logging.log4j.Logger;

import com.almende.eve.agent.Agent;
import com.almende.eve.agent.AgentBuilder;
import com.almende.eve.agent.AgentConfig;
import com.almende.eve.capabilities.AbstractCapabilityBuilder;
import com.almende.eve.protocol.Protocol;
import com.almende.eve.scheduling.Scheduler;
import com.almende.eve.state.State;
import com.almende.eve.transport.Transport;
import com.almende.eve.transport.http.HttpTransportBuilder;
import com.almende.eve.transport.http.ServletLauncher;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import io.coala.bind.LocalConfig;
import io.coala.bind.LocalId;
import io.coala.config.ConfigUtil;
import io.coala.config.GlobalConfig;
import io.coala.config.YamlUtil;
import io.coala.log.LogUtil;
import io.coala.util.FileUtil;

/**
 * {@link Eve3Config}
 * 
 * @version $Id: fb69241d05ed885974ecab5db634c312f0a25453 $
 * @author Rick van Krevelen
 */
public interface Eve3Config extends GlobalConfig, Mutable {

    String CONFIG_PATH_KEY = "eve.config-path";

    String CONFIG_PATH_DEFAULT = "eve.yaml";

    String AGENTS_KEY = "agents";

    String TEMPLATES_KEY = "templates";

    @Key(CONFIG_PATH_KEY)
    @DefaultValue(CONFIG_PATH_DEFAULT)
    String yamlConfigPath();

    default Map<String, AgentBuilderConfig> agentConfigs(final Map<?, ?>... imports) {
        return subConfigs(AGENTS_KEY, AgentBuilderConfig.class, imports);
    }

    default Map<String, AgentBuilderConfig> templateConfigs(final Map<?, ?>... imports) {
        return subConfigs(TEMPLATES_KEY, AgentBuilderConfig.class, imports);
    }

    default AgentBuilderConfig forAgent(final LocalId localId, final Map<?, ?>... imports) {
        // default: servlet context is root ancestor of (recursive) local id
        String context = "/agents";
        String id = "";
        for (LocalId i = localId; i.parentRef() != null; i = i.parentRef()) {
            if (i.parentRef() != null && i.parentRef().parentRef() == null)
                context += "/" + i.parentRef();
            id = id.isEmpty() ? i.unwrap().toString() : i.unwrap().toString() + LocalId.ID_SEP_REGEX + id;
        }
        final Map<String, String> map = new HashMap<>();
        map.put(LocalConfig.ID_KEY, id);
        map.put(DefaultAgentBuilderConfig.HTTP_CONTEXT_KEY, context);

        // FIXME add default servlet context with links to each agent's GUI

        // start agent transports
        final DefaultAgentBuilderConfig defaults = ConfigFactory.create(DefaultAgentBuilderConfig.class,
                ConfigUtil.join(map, imports));
        // try agent with given id
        for (Map.Entry<String, AgentBuilderConfig> entry : agentConfigs(imports).entrySet()) {
            final ObjectNode agentConfig = (ObjectNode) entry.getValue().toJSON();
            // match (local) agent id
            if (!id.equals(agentConfig.get(AgentBuilderConfig.ID_KEY).asText()))
                continue;
            // expand 'extends' from templates
            if (agentConfig.has(AgentBuilderConfig.EXTENDS_KEY)) {
                final String tplName = agentConfig.get(AgentBuilderConfig.EXTENDS_KEY).asText();
                final AgentBuilderConfig tpl = templateConfigs(imports).get(tplName);
                if (tpl == null)
                    getLogger().warn("Missing template '{}' for agent '{}'", tplName, id);
                copyMissing(tpl.toJSON(), agentConfig);
            }
            // add missing defaults
            copyMissing(defaults.toJSON(), agentConfig);
            return ConfigFactory.create(AgentBuilderConfig.class, ConfigUtil.flatten(agentConfig));
        }
        // no agent config, try template with given id
        final AgentBuilderConfig tpl = templateConfigs(imports).get(id);
        if (tpl != null) {
            getLogger().trace("Using template for agent '{}'", id, id);
            final ObjectNode agentConfig = (ObjectNode) tpl.toJSON();
            copyMissing(defaults.toJSON(), agentConfig);
            return ConfigFactory.create(AgentBuilderConfig.class, ConfigUtil.flatten(agentConfig));
        }
        // no agent or template of given id, return defaults
        getLogger().trace("Using defaults for agent '{}'", id);
        return defaults;
    }

    /**
     * @param imports
     * @return this {@link Eve3Config} initialized from the (default) path
     *         specified with {@link #CONFIG_PATH_KEY} (if accessible)
     */
    default Eve3Config load(final Map<?, ?>... imports) {
        return load(yamlConfigPath(), imports);
    }

    /**
     * @param path
     * @param imports
     * @return this {@link Eve3Config} initialized from the specified path (if
     *         accessible)
     */
    default Eve3Config load(final String path, final Map<?, ?>... imports) {
        try (final InputStream is = FileUtil.toInputStream(path)) {
            if (is != null)
                YamlUtil.flattenYaml(is).forEach((key, value) -> {
                    setProperty(key.toString(), value.toString());
                });
        } catch (final IOException e) {
            getLogger().info(e.getMessage());
        }
        return this;
    }

    static Logger getLogger() {
        return LogUtil.getLogger(Eve3Config.class);
    }

    static void copyMissing(final JsonNode source, final ObjectNode target) {
        final Iterator<Entry<String, JsonNode>> fields = source.fields();
        while (fields.hasNext()) {
            final Entry<String, JsonNode> field = fields.next();
            if (!target.has(field.getKey()))
                target.set(field.getKey(), field.getValue());
        }
    }

    /**
     * {@link AgentBuilderConfig}
     * 
     * @version $Id: fb69241d05ed885974ecab5db634c312f0a25453 $
     * @author Rick van Krevelen
     */
    interface AgentBuilderConfig extends LocalConfig {
        String EXTENDS_KEY = "extends";

        @Key(ID_KEY)
        // removes @DefaultValue
        String rawId();

        default AgentConfig builderConfig() {
            return new AgentConfig((ObjectNode) toJSON());
        }

        default Eve3Container buildAgent() {
            final AgentConfig config = builderConfig();
            // FIXME apply localId.parent() as servlet context
            final Agent result = new AgentBuilder().with(config).onBoot().build();
            Objects.requireNonNull(result);
            getLogger().trace("Booted agent: {}, urls: {}", result.getId(), result.getUrls());
            return (Eve3Container) result;
        }

    }

    /**
     * {@link DefaultAgentBuilderConfig}
     * 
     * @version $Id: fb69241d05ed885974ecab5db634c312f0a25453 $
     * @author Rick van Krevelen
     */
    interface DefaultAgentBuilderConfig extends AgentBuilderConfig {
        String CLASS_KEY = "class";
        String STATE_BUILDER_CLASS_KEY = "state.class";
        String STATE_PATH_KEY = "state.path";
        String SCHEDULER_BUILDER_CLASS_KEY = "scheduler.class";
        String HTTP_BUILDER_CLASS_KEY = "transport.0.class";
        String HTTP_AUTHENTICATE_KEY = "transport.0.doAuthentication";
        String HTTP_SHORTCUT_KEY = "transport.0.doShortcut";
        String HTTP_JETTY_PORT_KEY = "transport.0.jetty.port";
        String HTTP_JETTY_CORS_FILTER_TYPE_KEY = "transport.0.jetty.cors.class";
        String HTTP_JETTY_CORS_FILTER_PATH_KEY = "transport.0.jetty.cors.path";
        String HTTP_SERVLET_TYPE_KEY = "transport.0.servletClass";
        String HTTP_LAUNCHER_TYPE_KEY = "transport.0.servletLauncher";
        String HTTP_URL_KEY = "transport.0.servletUrl";
        String HTTP_SCHEME_KEY = "transport.0.servlet.scheme";
        String HTTP_HOST_KEY = "transport.0.servlet.host";
        String HTTP_PORT_KEY = "transport.0.servlet.port";
        String HTTP_CONTEXT_KEY = "transport.0.servlet.path";
        String JSONRPC_PROTOCOL_BUILDER_CLASS_KEY = "protocols.0.class";
        String JSONRPC_PROTOCOL_TIMEOUT_SECS_KEY = "protocols.0.rpcTimeout";

        @Key(EXTENDS_KEY)
        String extend();

        @Key(CLASS_KEY)
        @DefaultValue("io.coala.eve3.Eve3Container$Simple")
        Class<? extends Agent> agentClass();

        @Key(SCHEDULER_BUILDER_CLASS_KEY)
        @DefaultValue("com.almende.eve.scheduling.SimpleSchedulerBuilder")
        Class<? extends AbstractCapabilityBuilder<Scheduler>> schedulerBuilderType();

        @Key(STATE_BUILDER_CLASS_KEY)
        @DefaultValue("com.almende.eve.state.memory.MemoryStateBuilder")
        Class<? extends AbstractCapabilityBuilder<State>> stateBuilderType();

        @Key(STATE_PATH_KEY)
        //      @DefaultValue( ".eve_agents" )
        Class<? extends State> statePath();

        @Key(HTTP_BUILDER_CLASS_KEY)
        @DefaultValue("com.almende.eve.transport.http.HttpTransportBuilder")
        Class<? extends AbstractCapabilityBuilder<Transport>> httpBuilderType();

        @Key(HTTP_SERVLET_TYPE_KEY)
        @DefaultValue("com.almende.eve.transport.http.DebugServlet")
        Class<? extends Servlet> httpServletType();

        /**
         * @return {@link Filter} type for Cross-Origin Resource Sharing (CORS)
         */
        @Key(HTTP_JETTY_CORS_FILTER_TYPE_KEY)
        @DefaultValue("com.thetransactioncompany.cors.CORSFilter")
        Class<? extends Filter> httpCORSFilterType();

        /**
         * @return the Cross-Origin Resource Sharing (CORS) filter path
         */
        @Key(HTTP_JETTY_CORS_FILTER_PATH_KEY)
        @DefaultValue("/*")
        String httpCORSFilterPath();

        @Key(HTTP_SCHEME_KEY)
        @DefaultValue("http")
        String httpScheme();

        @Key(HTTP_HOST_KEY)
        @DefaultValue("localhost")
        String httpHost();

        @Key(HTTP_PORT_KEY)
        @DefaultValue("" + 8081)
        int httpPort();

        @Key(HTTP_JETTY_PORT_KEY)
        @DefaultValue("${" + HTTP_PORT_KEY + "}")
        int httpJettyPort();

        /**
         * FIXME update the HttpService context in {@link HttpTransportBuilder}
         */
        @Key(HTTP_CONTEXT_KEY)
        @DefaultValue("/agents")
        String httpContext();

        @Key(HTTP_URL_KEY)
        @DefaultValue("${" + HTTP_SCHEME_KEY + "}://${" + HTTP_HOST_KEY + "}:${" + HTTP_PORT_KEY + "}${"
                + HTTP_CONTEXT_KEY + "}/")
        URI httpServletUrl();

        @Key(HTTP_LAUNCHER_TYPE_KEY)
        @DefaultValue("com.almende.eve.transport.http.embed.JettyLauncher")
        Class<? extends ServletLauncher> httpServletLauncher();

        @Key(HTTP_AUTHENTICATE_KEY)
        @DefaultValue("" + false)
        boolean httpAuthenticate();

        @Key(HTTP_SHORTCUT_KEY)
        @DefaultValue("" + true)
        boolean httpShortcut();

        @Key(JSONRPC_PROTOCOL_BUILDER_CLASS_KEY)
        @DefaultValue("com.almende.eve.protocol.jsonrpc.JSONRpcProtocolBuilder")
        Class<? extends AbstractCapabilityBuilder<Protocol>> jsonrpcBuilderType();

        @Key(JSONRPC_PROTOCOL_TIMEOUT_SECS_KEY)
        @DefaultValue("" + Integer.MAX_VALUE) // Invoker handles timeouts
        int jsonrpcTimeout();
    }
}