org.pepstock.jem.commands.CreateNode.java Source code

Java tutorial

Introduction

Here is the source code for org.pepstock.jem.commands.CreateNode.java

Source

/**
JEM, the BEE - Job Entry Manager, the Batch Execution Environment
Copyright (C) 2012-2015   Simone "Busy" Businaro
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.pepstock.jem.commands;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Properties;

import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.ParseException;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.io.FileUtils;
import org.pepstock.jem.commands.util.ArgumentsParser;
import org.pepstock.jem.commands.util.ConfsUpdater;
import org.pepstock.jem.commands.util.NodeAttributes;
import org.pepstock.jem.commands.util.NodeProperties;
import org.pepstock.jem.log.LogAppl;
import org.pepstock.jem.log.MessageException;
import org.pepstock.jem.node.NodeMessage;
import org.pepstock.jem.node.configuration.ConfigKeys;
import org.pepstock.jem.node.configuration.ConfigurationException;
import org.pepstock.jem.node.security.keystore.Factory;
import org.pepstock.jem.node.security.keystore.KeyStoreInfo;
import org.pepstock.jem.node.security.keystore.KeyStoreUtil;

/**
 * Create a node on a jem installation. Is a command (to execute by command
 * line) with 1 arguments that is mandatory <br>
 * <code>-properties [string]</code> mandatory, indicates the file of properties
 * used by the user to configure the new node and or environment. A template of
 * properties file is present under the jem_home/bin folder where are documented
 * all the properties needed <br>
 * The creation will use a standard copy of a jem node/environment that is
 * present in the JEM_HOME/src making the proper modification to the config
 * files and the shell scripts. It's possible to have help from command line by
 * <code>-help</code> argument. If a node belong to a new environment a new
 * environment will be created as well with the web distribution in the form of
 * a war file<br>
 * 
 * <br>
 * <b>CreateNode -properties ... </b><br>
 * <br>
 * Is possible to have help from command line by <code>-help</code> argument.<br>
 * 
 * @author Simone "Busy" Businaro
 * @version 1.0
 * 
 */
public class CreateNode {

    /**
     * Key for the url for global file system
     */
    private static final String PROPERTIES = "properties";

    /**
     * To avoid any instantiation
     */
    private CreateNode() {

    }

    /**
     * Create the new nodes
     * 
     * @param args
     * @throws MessageException if any exception occurs during the creation of the node
     * @throws ConfigurationException 
     * @throws IOException 
     * @throws ParseException 
     * @throws KeyStoreException 
     */
    public static void main(final String[] args)
            throws MessageException, ParseException, IOException, ConfigurationException, KeyStoreException {

        NodeAttributes nodeAttributes = checkArguments(args);
        nodeAttributes.init();
        if (nodeAttributes.getNodeDir().exists()) {
            LogAppl.getInstance().emit(NodeMessage.JEMC060E, nodeAttributes.getNodeDir().getAbsolutePath());
            throw new MessageException(NodeMessage.JEMC060E, nodeAttributes.getNodeDir().getAbsolutePath());
        }
        if (nodeAttributes.getEnvDir().exists()) {
            createNode(nodeAttributes);
            LogAppl.getInstance().emit(NodeMessage.JEMC059I, "Node", nodeAttributes.getNodeDir().getAbsolutePath());
        } else {
            createEnvironment(nodeAttributes);
            LogAppl.getInstance().emit(NodeMessage.JEMC059I, "Environment",
                    nodeAttributes.getEnvDir().getAbsolutePath());
            LogAppl.getInstance().emit(NodeMessage.JEMC059I, "Node", nodeAttributes.getNodeDir().getAbsolutePath());
        }
    }

    /**
     * Create the new environment with the new node
     * 
     * @param nodeAttributes
     * @throws MessageException if any exception occurs
     * @throws IOException 
     * @throws ConfigurationException 
     * @throws CertificateException 
     * @throws NoSuchAlgorithmException 
     * @throws KeyStoreException 
     * @throws UnrecoverableKeyException 
     */
    private static void createEnvironment(NodeAttributes nodeAttributes)
            throws IOException, ConfigurationException, KeyStoreException, MessageException {
        NodeProperties np = nodeAttributes.getNodeProperties();
        // copy al the template environment directory inside JEM_HOME
        File srcDir = nodeAttributes.getTemplateEnvDirectory();
        File destDir = nodeAttributes.getEnvDir();
        FileUtils.copyDirectory(srcDir, destDir);
        ConfsUpdater confs = new ConfsUpdater(nodeAttributes);
        // copy all the template gfs config environment directory
        if (!nodeAttributes.getGfsConfigDirectory().exists()) {
            File srcGfsConfigDir = nodeAttributes.getTemplateGfsConfigDirectory();
            File destGfsConfigDir = nodeAttributes.getGfsConfigDirectory();
            FileUtils.copyDirectory(srcGfsConfigDir, destGfsConfigDir);
            LogAppl.getInstance().emit(NodeMessage.JEMC059I, "gfs environment", destGfsConfigDir.getAbsolutePath());
            // create encription key and keystore
            // genero il keystore con la chiave e lo memorizzo su file
            // system all'interno del persistence path
            boolean mkdir = new File(
                    np.getPersistencePath() + "/" + np.getEnvironmentName() + "/" + Factory.KEYSTORES_FOLDER)
                            .mkdir();
            if (!mkdir) {
                LogAppl.getInstance().debug("Unable to create directory");
            }
            File keystoreFile = new File(np.getPersistencePath() + "/" + np.getEnvironmentName() + "/"
                    + Factory.KEYSTORES_FOLDER + "/" + np.getKeystoreName());
            KeyStoreInfo clusterkeystoreInfo = new KeyStoreInfo(KeyStoreInfo.JCEKS_KEYSTORE_TYPE);
            clusterkeystoreInfo.setFile(keystoreFile);
            clusterkeystoreInfo.setBackupFile(new File(keystoreFile.getAbsolutePath() + ".backup"));
            clusterkeystoreInfo.setPassword(np.getKeystorePwd());
            clusterkeystoreInfo.setSymmetricKeyAlias(np.getEnvironmentName());
            clusterkeystoreInfo.setSymmetricKeyPwd(np.getCryptKeyPwd());
            KeyStoreUtil.generate(clusterkeystoreInfo);
            LogAppl.getInstance().emit(NodeMessage.JEMC059I, "cluster Keystore", keystoreFile.getAbsolutePath());
            // create user keystore where we will store the x509 certificate
            File usekeystoreFile = new File(np.getPersistencePath() + "/" + np.getEnvironmentName() + "/"
                    + Factory.KEYSTORES_FOLDER + "/" + np.getUserKeystoreName());
            KeyStoreInfo userkeystoreInfo = new KeyStoreInfo(KeyStoreInfo.JKS_KEYSTORE_TYPE);
            userkeystoreInfo.setFile(usekeystoreFile);
            userkeystoreInfo.setBackupFile(new File(usekeystoreFile.getAbsolutePath() + ".backup"));
            userkeystoreInfo.setPassword(np.getKeystorePwd());
            KeyStoreUtil.generate(userkeystoreInfo);
            LogAppl.getInstance().emit(NodeMessage.JEMC059I, "users Keystore", usekeystoreFile.getAbsolutePath());
            // update configuration files
            confs.updateEnvGfsConfig();
            FileUtils.copyFileToDirectory(keystoreFile, nodeAttributes.getWarConfigDir());
            // create the war file (if does not exist) for the web distribution of the environment 
            zipDirectory(nodeAttributes.getWarDir(), nodeAttributes.getWarFile());
            LogAppl.getInstance().emit(NodeMessage.JEMC059I, "war file", nodeAttributes.getWarFile());
        }
        // raname the node foder with the one set by the user
        File newNodeDir = new File(nodeAttributes.getEnvDir() + "/" + NodeAttributes.TEMPLATE_NODE_DIRECTORY_NAME);
        boolean isRenamed = newNodeDir.renameTo(nodeAttributes.getNodeDir());
        if (!isRenamed) {
            throw new MessageException(NodeMessage.JEMC154E);
        }
        // update configuration files
        confs.updateEnvConfigs();
        // update configuration files
        confs.updateNodeConfigs();

        int count = 0;
        // this while is added to solve the issues
        // about removing the directory.
        // happens that zip is still locking the file when delete is performing
        // in this way it tries again when exception occurs
        // it tries only 10 times, and then throw an exception
        while (true) {
            try {
                FileUtils.deleteDirectory(nodeAttributes.getWarDir());
                break;
            } catch (Exception e) {
                LogAppl.getInstance().ignore(e.getMessage(), e);
                // increments number of errors
                count++;
                if (count == 10) {
                    throw new MessageException(NodeMessage.JEMC247E, e);
                }
                // waits for 500ms to try again
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e1) {
                    LogAppl.getInstance().ignore(e1.getMessage(), e1);
                }
            }
        }
    }

    /**
     * Create the new node inside the environment
     * 
     * @param nodeAttributes
     * @throws IOException 
     * @throws Exception
     */
    private static void createNode(NodeAttributes nodeAttributes) throws MessageException, IOException {
        File srcDir = nodeAttributes.getTemplateNodeDirectory();
        File destDir = nodeAttributes.getNodeDir();
        FileUtils.copyDirectory(srcDir, destDir);
        ConfsUpdater confs = new ConfsUpdater(nodeAttributes);
        confs.updateNodeConfigs();
    }

    /**
     * Verify the arguments passed to the main method
     * 
     * @param args
     * @return an object with the attributes of the node @see
     *         org.pepstock.jem.node.creation.NodeAttributes
     * @throws MessageException if any exception occurs
     * @throws ParseException 
     * @throws IOException 
     * @throws ConfigurationException 
     */
    @SuppressWarnings("static-access")
    private static NodeAttributes checkArguments(String[] args)
            throws MessageException, ParseException, IOException, ConfigurationException {
        String jemHome = System.getenv().get(ConfigKeys.JEM_HOME);
        if (jemHome == null) {
            throw new MessageException(NodeMessage.JEMC058E, ConfigKeys.JEM_HOME);
        }
        // -env mandatory
        Option propFile = OptionBuilder.withArgName(PROPERTIES).hasArg().withDescription(
                "The path of the properties file for the configuration of a new node/foder. See template create_node.properties inside JEM_HOME/config")
                .create(PROPERTIES);
        propFile.setRequired(true);
        // -node optional arg

        ArgumentsParser parser = new ArgumentsParser(CreateNode.class.getName());
        parser.getOptions().add(propFile);
        Properties properties = parser.parseArg(args);

        String propertyUrlPath = properties.getProperty(PROPERTIES);
        URL url = null;
        try {
            url = new URL(propertyUrlPath);
        } catch (MalformedURLException ex) {
            // if it's not an URL, try as a file
            File jcl = new File(propertyUrlPath);
            url = jcl.toURI().toURL();
        }
        Properties props = new Properties();
        props.load(url.openStream());
        NodeProperties np = new NodeProperties(props);
        np.checkMandatoryTag();
        return new NodeAttributes(jemHome, np);
    }

    private static void zipDirectory(File directoryPath, File zipPath) throws IOException {
        FileOutputStream fOut = new FileOutputStream(zipPath);
        BufferedOutputStream bOut = new BufferedOutputStream(fOut);
        ZipArchiveOutputStream tOut = new ZipArchiveOutputStream(bOut);
        zip(directoryPath, directoryPath, tOut);
        tOut.finish();
        tOut.close();
        bOut.close();
        fOut.close();
    }

    private static final void zip(File directory, File base, ZipArchiveOutputStream zos) throws IOException {
        File[] files = directory.listFiles();
        for (int i = 0, n = files.length; i < n; i++) {
            if (files[i].isDirectory()) {
                zip(files[i], base, zos);
            } else {
                FileInputStream in = null;
                try {
                    in = new FileInputStream(files[i]);
                    ZipArchiveEntry entry = new ZipArchiveEntry(
                            files[i].getPath().substring(base.getPath().length() + 1));
                    zos.putArchiveEntry(entry);
                    IOUtils.copy(in, zos);
                    zos.closeArchiveEntry();
                } catch (IOException e) {
                    throw e;
                } finally {
                    if (in != null) {
                        in.close();
                    }
                }
            }
        }
    }

}