io.mesosphere.mesos.frameworks.cassandra.executor.ProdObjectFactory.java Source code

Java tutorial

Introduction

Here is the source code for io.mesosphere.mesos.frameworks.cassandra.executor.ProdObjectFactory.java

Source

/**
 *    Copyright (C) 2015 Mesosphere, Inc.
 *
 * 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.mesosphere.mesos.frameworks.cassandra.executor;

import com.fasterxml.jackson.dataformat.yaml.snakeyaml.Yaml;
import com.google.common.base.Joiner;
import com.google.common.io.Files;
import io.mesosphere.mesos.frameworks.cassandra.CassandraFrameworkProtos;
import io.mesosphere.mesos.frameworks.cassandra.executor.jmx.JmxConnect;
import io.mesosphere.mesos.frameworks.cassandra.executor.jmx.ProdJmxConnect;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;

import java.io.*;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import static io.mesosphere.mesos.frameworks.cassandra.CassandraFrameworkProtos.*;

// production object factory - there's another implementation, that's used for mocked unit tests
final class ProdObjectFactory implements ObjectFactory {
    private static final Logger LOGGER = LoggerFactory.getLogger(ProdObjectFactory.class);

    @NotNull
    @Override
    public JmxConnect newJmxConnect(@NotNull final CassandraFrameworkProtos.JmxConnect jmx) {
        return new ProdJmxConnect(jmx);
    }

    @Override
    @NotNull
    public WrappedProcess launchCassandraNodeTask(@NotNull final Marker taskIdMarker,
            @NotNull final CassandraServerRunTask serverRunTask) throws LaunchNodeException {
        try {
            writeCassandraServerConfig(taskIdMarker, serverRunTask.getVersion(),
                    serverRunTask.getCassandraServerConfig());
        } catch (final IOException e) {
            throw new LaunchNodeException("Failed to prepare instance files", e);
        }

        final ProcessBuilder processBuilder = new ProcessBuilder(serverRunTask.getCommandList())
                .directory(new File(System.getProperty("user.dir")))
                .redirectOutput(new File("cassandra-stdout.log")).redirectError(new File("cassandra-stderr.log"));
        for (final TaskEnv.Entry entry : serverRunTask.getCassandraServerConfig().getTaskEnv().getVariablesList()) {
            processBuilder.environment().put(entry.getName(), entry.getValue());
        }
        processBuilder.environment().put("JAVA_HOME", System.getProperty("java.home"));
        if (LOGGER.isDebugEnabled())
            LOGGER.debug("Starting Process: {}", processBuilderToString(processBuilder));
        try {
            return new ProdWrappedProcess(processBuilder.start());
        } catch (final IOException e) {
            throw new LaunchNodeException("Failed to start process", e);
        }
    }

    @Override
    public void updateCassandraServerConfig(@NotNull final Marker taskIdMarker,
            @NotNull final CassandraServerRunTask cassandraServerRunTask,
            @NotNull final UpdateConfigTask updateConfigTask) throws ConfigChangeException {
        try {
            writeCassandraServerConfig(taskIdMarker, cassandraServerRunTask.getVersion(),
                    updateConfigTask.getCassandraServerConfig());
        } catch (final IOException e) {
            throw new ConfigChangeException("Failed to update instance files", e);
        }
    }

    private static void writeCassandraServerConfig(@NotNull final Marker taskIdMarker,
            @NotNull final String version, @NotNull final CassandraServerConfig serverConfig) throws IOException {
        for (final TaskFile taskFile : serverConfig.getTaskFilesList()) {
            final File file = new File(taskFile.getOutputPath());
            if (LOGGER.isDebugEnabled())
                LOGGER.debug(taskIdMarker, "Overwriting file {}", file);
            Files.createParentDirs(file);
            Files.write(taskFile.getData().toByteArray(), file);
        }

        modifyCassandraYaml(taskIdMarker, version, serverConfig);
        modifyCassandraEnvSh(taskIdMarker, version, serverConfig);
        modifyCassandraRackdc(taskIdMarker, version, serverConfig);
    }

    @NotNull
    private static String processBuilderToString(@NotNull final ProcessBuilder builder) {
        return "ProcessBuilder{\n" + "directory() = " + builder.directory() + ",\n" + "command() = "
                + Joiner.on(" ").join(builder.command()) + ",\n" + "environment() = "
                + Joiner.on("\n").withKeyValueSeparator("->").join(builder.environment()) + "\n}";
    }

    private static void modifyCassandraRackdc(@NotNull final Marker taskIdMarker, @NotNull final String version,
            @NotNull final CassandraServerConfig serverConfig) throws IOException {
        LOGGER.info(taskIdMarker, "Building cassandra-rackdc.properties");

        final Properties props = new Properties();
        final RackDc rackDc = serverConfig.getRackDc();
        props.put("dc", rackDc.getDc());
        props.put("rack", rackDc.getRack());

        // Add a suffix to a datacenter name. Used by the Ec2Snitch and Ec2MultiRegionSnitch to append a string to the EC2 region name.
        //props.put("dc_suffix", "");
        // Uncomment the following line to make this snitch prefer the internal ip when possible, as the Ec2MultiRegionSnitch does.
        //props.put("prefer_local", "true");
        final File cassandraRackDc = new File("apache-cassandra-" + version + "/conf/cassandra-rackdc.properties");
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(cassandraRackDc))) {
            props.store(bw, "Created by Apache Mesos Cassandra framework");
        }
    }

    private static void modifyCassandraEnvSh(@NotNull final Marker taskIdMarker, @NotNull final String version,
            @NotNull final CassandraServerConfig cassandraServerConfig) throws IOException {
        int jmxPort = 0;
        String localJmx = "yes";
        boolean noJmxAuth = false;
        for (final TaskEnv.Entry entry : cassandraServerConfig.getTaskEnv().getVariablesList()) {
            if ("JMX_PORT".equals(entry.getName())) {
                jmxPort = Integer.parseInt(entry.getValue());
            } else if ("LOCAL_JMX".equals(entry.getName())) {
                localJmx = entry.getValue();
            } else if ("CASSANDRA_JMX_NO_AUTHENTICATION".equals(entry.getName())) {
                noJmxAuth = "no".equals(entry.getValue());
            }
        }

        LOGGER.info(taskIdMarker, "Building cassandra-env.sh");

        // Unfortunately it is not possible to pass JMX_PORT as an environment variable to C* startup -
        // it is explicitly set in cassandra-env.sh

        final File cassandraEnvSh = new File("apache-cassandra-" + version + "/conf/cassandra-env.sh");

        LOGGER.info(taskIdMarker, "Reading cassandra-env.sh");
        final List<String> lines = Files.readLines(cassandraEnvSh, Charset.forName("UTF-8"));
        for (int i = 0; i < lines.size(); i++) {
            final String line = lines.get(i);
            if (line.startsWith("JMX_PORT=")) {
                lines.set(i, "JMX_PORT=" + jmxPort);
            } else if (line.startsWith("LOCAL_JMX=")) {
                lines.set(i, "LOCAL_JMX=" + localJmx);
            } else if (noJmxAuth) {
                if (line.contains("JVM_OPTS=\"$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=")) {
                    lines.set(i, "JVM_OPTS=\"$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=false\"");
                }
            }
        }
        LOGGER.info(taskIdMarker, "Writing cassandra-env.sh");
        try (PrintWriter pw = new PrintWriter(new FileWriter(cassandraEnvSh))) {
            for (final String line : lines)
                pw.println(line);
        }
    }

    @SuppressWarnings("unchecked")
    private static void modifyCassandraYaml(@NotNull final Marker taskIdMarker, @NotNull final String version,
            @NotNull final CassandraServerConfig cassandraServerConfig) throws IOException {
        LOGGER.info(taskIdMarker, "Building cassandra.yaml");

        final File cassandraYaml = new File("apache-cassandra-" + version + "/conf/cassandra.yaml");

        final Yaml yaml = new Yaml();
        final Map<String, Object> yamlMap;
        LOGGER.info(taskIdMarker, "Reading cassandra.yaml");
        try (BufferedReader br = new BufferedReader(new FileReader(cassandraYaml))) {
            yamlMap = (Map<String, Object>) yaml.load(br);
        }
        LOGGER.info(taskIdMarker, "Modifying cassandra.yaml");
        for (final TaskConfig.Entry entry : cassandraServerConfig.getCassandraYamlConfig().getVariablesList()) {
            switch (entry.getName()) {
            case "seeds":
                final List<Map<String, Object>> seedProviderList = (List<Map<String, Object>>) yamlMap
                        .get("seed_provider");
                final Map<String, Object> seedProviderMap = seedProviderList.get(0);
                final List<Map<String, Object>> parameters = (List<Map<String, Object>>) seedProviderMap
                        .get("parameters");
                final Map<String, Object> parametersMap = parameters.get(0);
                parametersMap.put("seeds", entry.getStringValue());
                break;
            default:
                if (entry.hasStringValue()) {
                    yamlMap.put(entry.getName(), entry.getStringValue());
                } else if (entry.hasLongValue()) {
                    yamlMap.put(entry.getName(), entry.getLongValue());
                } else if (!entry.getStringValuesList().isEmpty()) {
                    yamlMap.put(entry.getName(), entry.getStringValuesList());
                }
            }
        }
        if (LOGGER.isDebugEnabled()) {
            final StringWriter sw = new StringWriter();
            yaml.dump(yamlMap, sw);
            LOGGER.debug("cassandra.yaml result: {}", sw);
        }
        LOGGER.info(taskIdMarker, "Writing cassandra.yaml");
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(cassandraYaml))) {
            yaml.dump(yamlMap, bw);
        }
    }

}