com.ebay.jetstream.dynamicconfig.ConfigUploaderToMongo.java Source code

Java tutorial

Introduction

Here is the source code for com.ebay.jetstream.dynamicconfig.ConfigUploaderToMongo.java

Source

/*******************************************************************************
 *  Copyright  2012-2015 eBay Software Foundation
 *  This program is dual licensed under the MIT and Apache 2.0 licenses.
 *  Please see LICENSE for more information.
 *******************************************************************************/
package com.ebay.jetstream.dynamicconfig;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ebay.jetstream.config.ApplicationInformation;
import com.ebay.jetstream.config.ConfigException;
import com.ebay.jetstream.config.MongoLocator;
import com.ebay.jetstream.config.RootConfiguration;
import com.ebay.jetstream.config.mongo.JetStreamBeanConfigurationDo;
import com.ebay.jetstream.config.mongo.MongoConfigMgr;
import com.ebay.jetstream.config.mongo.MongoConfiguration;

/**
 * Tool to upload spring bean config to mongo database.
 * 
 * Run it as Java Application.
 * 
 * It requires the following to execute successfully
 * 
 * 1. MONGO_HOME environment variable Sample MONGO_HOME =
 * slookupmdbd2.vip.qa.ebay
 * .com:27017/jetstream;slookupmdbd1.vip.qa.ebay.com:27017
 * /jetstream;slookupmdbd3.vip.qa.ebay.com:27017/jetstream
 * 
 * 2. Options you can pass as arguments to application
 * 
 * -app=JetstreamSamplesApp -beandefxml=C:\TestBeanFile.xml
 * -beanid=SampleTestBean -scope=local -user=naga -version=1.2
 * 
 * OR
 * 
 * -app=JetstreamSamplesApp -beandefxml=C:\TestBeanFile.xml,C:\TestBeanFile2.xml
 * -beanid=SampleTestBean,SampleTestBean -scope=local -user=naga -version=1.2
 * 
 * OR
 * 
 * -app=JetstreamSamplesApp,TestApp
 * -beandefxml=C:\TestBeanFile.xml,C:\TestBeanFile2.xml
 * -beanid=SampleTestBean,SampleTestBean -scope=local,global -user=naga
 * -version=1.2,1.3
 * 
 * 
 * app is Application Name beandefxml = xml file containing spring bean
 * definition. Required to Use different file for different beans so that you
 * can also pass the bean name. beanid = name of the bean. Keep it in sync with
 * the name in beandefxml. scope = local or global or dc specific version =
 * Application Version
 */

public class ConfigUploaderToMongo {
    @SuppressWarnings("restriction")
    private MongoConfiguration mongoConfiguration = null;
    @SuppressWarnings("restriction")
    private MongoConfigMgr mongoConfigMgr = null;

    private final Options options = new Options();
    private final CommandLineParser parser = new GnuParser();

    private boolean invalidOption = true;
    private boolean getBeanName = false;

    private String application;
    private String scope;
    private String version;
    private String user;
    private String beanId;
    private String beanDefXml;
    private boolean publish = true;
    private boolean display = false;
    private boolean delete = false;
    private boolean reupload = true;

    private String[] beanDefXmls;
    private String[] beanIds;

    private String beanVersion;

    private boolean multipleBeanDefXmls = false;
    private boolean multipleBeanIds = false;

    PublishConfigMessage publisher = null;

    String beans_beginning_tag = "\n" + "<beans xmlns=\"http://www.springframework.org/schema/beans\"" + "\n"
            + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + "\n"
            + "xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\""
            + "\n" + "default-lazy-init=\"false\">" + "\n\n";

    String beans_end_tag = "\n\n" + "</beans>";

    private static final Logger LOGGER = LoggerFactory
            .getLogger(ConfigUploaderToMongo.class.getPackage().getName());

    @SuppressWarnings("restriction")
    public ConfigUploaderToMongo(String[] args) throws ConfigException {

        initCommandLineParameters();
        if (parseOptions(args)) {

            ApplicationInformation ai = new ApplicationInformation("ConfigChange", "0.0.0.0");

            List<String> configs = RootConfiguration.getContexts(RootConfiguration.getConfigurationRoot());

            String[] configArray = new String[configs.size()];
            new RootConfiguration(ai, configs.toArray(configArray));

            getMongoConfiguration();

            publisher = new PublishConfigMessage();

        }

        if (args != null) {
            LOGGER.info("Config Upload user input is : " + Arrays.toString(args));
        }
    }

    public boolean isDisplay() {
        return display;
    }

    public boolean isDelete() {
        return delete;
    }

    public void display() throws Exception {
        if (application != null && version != null) {
            List<JetStreamBeanConfigurationDo> currentConfigs = new ArrayList();
            if (getBeanName) {
                currentConfigs = mongoConfigMgr.getJetStreamConfiguration(application, version);
            } else {
                if (multipleBeanIds) {
                    for (int i = 0; i < beanIds.length; i++) {
                        beanId = beanIds[i];
                        if (beanId != null) {
                            currentConfigs
                                    .addAll(mongoConfigMgr.getJetStreamConfiguration(application, version, beanId));
                        }
                    }
                } else {
                    if (beanId != null) {
                        currentConfigs
                                .addAll(mongoConfigMgr.getJetStreamConfiguration(application, version, beanId));
                    }
                }
            }

            if (currentConfigs == null || currentConfigs.isEmpty()) {
                LOGGER.info("\n\n There is no config for application : " + application + ", version : " + version);
            }

            LOGGER.info("\n\n Displaying config for application : " + application + ", version : " + version);
            for (JetStreamBeanConfigurationDo currentConfig : currentConfigs) {
                LOGGER.info("\n\n Config details  : " + currentConfig.toString());
            }
        }
    }

    public void delete() throws Exception {
        if (application != null && version != null && beanId != null) {
            LOGGER.info("\n\n Deleting config for application : " + application + ", version : " + version
                    + ", beanId : " + beanId);

            List<JetStreamBeanConfigurationDo> configs = mongoConfigMgr.getJetStreamConfiguration(application,
                    version, beanId);

            if (configs.size() > 1) {
                LOGGER.info(" found more than 1 config for application : " + application + ", version : " + version
                        + ", beanId : " + beanId + " -- size is  : " + configs.size());
            }

            JetStreamBeanConfigurationDo config = configs.get(0);
            LOGGER.info("Config being deleted is " + config.toString());

            boolean result = mongoConfigMgr.removeJetStreamConfiguration(application, version, beanId,
                    config.getBeanVersion(), config.getScope());

            if (result) {
                LOGGER.info("Deleted config for application");
            }

            uploadPreviousConfig();
        }
    }

    private void uploadPreviousConfig() throws Exception {
        if (reupload) {
            List<JetStreamBeanConfigurationDo> configs = mongoConfigMgr.getJetStreamConfiguration(application,
                    version, beanId);
            if (configs.size() > 1) {
                LOGGER.info(" found more than 1 config for application : " + application + ", version : " + version
                        + ", beanId : " + beanId + " -- size is  : " + configs.size());
            }

            JetStreamBeanConfigurationDo config = configs.get(0);
            LOGGER.info("Config being re-uploaded is " + config.toString());

            scope = config.getScope();
            user = "AutoUploadAfterDelete";

            uploadSingle(beanId, config.getBeanDefinition());
            publishSingle(beanId);

            LOGGER.info("Config re-uploaded is DONE ");
        }
    }

    public void upload() throws Exception {
        if (getBeanName) {
            // user did not pass bean id, so get all the beans from xml file(s) and upload it separately
            //if(beanDefXmls != null && beanDefXmls.length > 0) {
            if (multipleBeanDefXmls) {
                for (int i = 0; i < beanDefXmls.length; i++) {
                    beanDefXml = beanDefXmls[i];
                    uploadAndPublishAllBeanIdsFromFile();
                }
            } else {
                uploadAndPublishAllBeanIdsFromFile();
            }
        } else {
            // user passed bean id, so just work on those id's

            if (multipleBeanIds) {
                for (int i = 0; i < beanIds.length; i++) {
                    beanId = beanIds[i];
                    uploadAndPublishBeanId();
                }
            } else {
                uploadAndPublishBeanId();
            }
        }

    }

    private void uploadAndPublishBeanId() throws Exception {
        // single bean id - have to search in multiple files possibly
        if (multipleBeanDefXmls) {

            for (int i = 0; i < beanDefXmls.length; i++) {
                beanDefXml = beanDefXmls[i];
                if (uploadAndPublishBeanIdFromFile(beanId)) {
                    break; // found the bean definition, do not need to look in other xml files
                }
                // else continue looking for beanid in other xml files
            }

        } else {
            uploadAndPublishBeanIdFromFile(beanId);
        }
    }

    private void uploadAndPublishAllBeanIdsFromFile() throws Exception {
        File beanFile = new File(beanDefXml);
        List<String> beanIdsList = XmlUtil.getBeanIds(beanFile);
        if (beanIdsList != null && !beanIdsList.isEmpty()) {
            LOGGER.info("Number of Bean Ids are [" + beanIdsList.size() + "]" + " from bean definition file : "
                    + beanDefXml);
            LOGGER.info("Bean Ids are : " + beanIdsList.toString());

            //beanIds = (String[])beanIdsList.toArray();
            for (String beanId : beanIdsList) {
                if (beanId != null && beanId.trim().length() > 0) {
                    String beanDefinition = XmlUtil.getBeanDefinition(beanId, beanFile);
                    beanDefinition = appendBeanTags(beanDefinition);

                    if (beanDefinition != null && beanDefinition.trim().length() > 0) {
                        LOGGER.info("Uploading following spring bean definition for " + beanId
                                + " from bean definition file : " + beanDefXml);
                        LOGGER.info("\n" + beanDefinition);
                        //CalEventHelper.

                        uploadSingle(beanId, beanDefinition);
                        publishSingle(beanId);
                    } else {
                        LOGGER.info("Did NOT find bean definition for " + beanId + " from bean definition file : "
                                + beanDefXml);
                    }
                } else {
                    LOGGER.info("Did NOT do upload because of Bean Id being [" + beanId + "]");
                }
            }
        }
    }

    private boolean uploadAndPublishBeanIdFromFile(String beanId) throws Exception {
        File beanFile = new File(beanDefXml);
        if (beanId != null && beanId.trim().length() > 0) {
            String beanDefinition = XmlUtil.getBeanDefinition(beanId, beanFile);
            if (beanDefinition == null) {
                LOGGER.info("Did NOT find bean definition for " + beanId + " from bean definition file : "
                        + beanDefXml);
                return false;
            }
            beanDefinition = appendBeanTags(beanDefinition);

            if (beanDefinition != null && beanDefinition.trim().length() > 0) {
                LOGGER.info(
                        "Uploading bean definition for " + beanId + " from bean definition file : " + beanDefXml);
                //            LOGGER.info( "Uploading following spring bean definition for " +beanId + " from bean definition file : " +beanDefXml);
                //            LOGGER.info( "\n" + beanDefinition);

                uploadSingle(beanId, beanDefinition);
                publishSingle(beanId);

                return true; // indicates success
            }
        } else {
            LOGGER.info("Did NOT do upload because of Bean Id being [" + beanId + "]");
        }

        return false;
    }

    // can optimize it later to notify on multiple bean updates instead of one.
    @SuppressWarnings("restriction")
    public void uploadSingle(String beanId, String beanDefinition) throws Exception {

        String beanVer = "0";
        JetStreamBeanConfigurationDo currentConfig = mongoConfigMgr.getJetStreamConfiguration(application, version,
                beanId, scope);
        if (currentConfig != null) {
            int currentVersion = Integer.valueOf(currentConfig.getBeanVersion());
            beanVer = String.valueOf(currentVersion + 1);
        }

        beanVersion = beanVer; // to publish the event

        JetStreamBeanConfigurationDo configDo = new JetStreamBeanConfigurationDo();

        configDo.setAppName(application);
        configDo.setVersion(version);
        configDo.setScope(scope);

        configDo.setBeanName(beanId);
        configDo.setBeanDefinition(beanDefinition);
        configDo.setBeanVersion(beanVersion);

        configDo.setCreatedBy(currentConfig != null ? currentConfig.getCreatedBy() : user);
        Date currentDate = new Date();
        configDo.setCreationDate(currentConfig != null ? currentConfig.getCreationDate() : currentDate.getTime());
        configDo.setModifiedBy(user);
        configDo.setModifiedDate(currentDate.getTime());

        uploadConfig(configDo);
    }

    private String appendBeanTags(String beanDefinition) {
        int index = beanDefinition.indexOf("<bean");
        StringBuffer beanDef = new StringBuffer(beanDefinition);
        beanDef.insert(index, beans_beginning_tag);
        beanDef.append(beans_end_tag);

        return beanDef.toString();
    }

    private void publish() {
        if (publish) {
            publisher.publish(application, scope, version, beanId, beanVersion);
            LOGGER.info("Publish SUCCESSFUL for bean : " + beanId);
        }
    }

    private void publishSingle(String beanId) {
        if (publish) {
            LOGGER.info("Attempting to Publish for bean : " + beanId);
            publisher.publish(application, scope, version, beanId, beanVersion);
        }
    }

    private void uploadConfig(JetStreamBeanConfigurationDo configDo) {

        mongoConfigMgr.uploadJetStreamConfiguration(configDo);

        LOGGER.info("UPLOAD to Mongo SUCCESSFUL for bean : " + beanId + " and bean details are : "
                + configDo.toString());
    }

    private void getMongoConfiguration(String mongo_url) {
        // it should not be null
        LOGGER.info(" Mongo URL is : " + mongo_url);
        if (mongo_url == null || mongo_url.length() < 1) {
            logErrorAndExit(
                    "Fatal Error : MONGO_HOME is not set and it is required to run the application. \n Fatal Error : MONGO_HOME sample example value is slookupmdbd2.vip.qa.ebay.com:27017/jetstream",
                    null);
        }

        if (mongoConfiguration == null) {
            String db = null;
            String port = null;
            List<String> hosts = new ArrayList<String>();

            try {

                String[] urls = mongo_url.split(";");
                for (String url : urls) {
                    int index = url.indexOf("mongo://");
                    url = url.substring(index + "mongo://".length());

                    String[] hostAndDb = url.split("/");
                    String hostAndPort = hostAndDb[0];
                    db = hostAndDb[1];
                    String[] hostPort = hostAndPort.split(":");
                    String host = hostPort[0];
                    port = hostPort[1];

                    hosts.add(host);
                }
            } catch (Exception e) {
                logErrorAndExit("Fatal Error : MONGO_HOME value parsing errors for url : " + mongo_url
                        + " and exception is : " + e.getMessage(), e);
            }

            if (db == null || port == null || hosts.isEmpty()) {
                logErrorAndExit("Fatal Error : check the value of MONGO_HOME and parsed values of db [" + db
                        + "] , port [" + port + "] , hosts [" + hosts + "]", null);
            }

            mongoConfiguration = new MongoConfiguration();
            mongoConfiguration.setDb(db);
            mongoConfiguration.setHosts(hosts);
            mongoConfiguration.setPort(port);
            // mongoConfiguration.setUser(user);
            // mongoConfiguration.setPw(pw);
        }

        try {
            mongoConfigMgr = new MongoConfigMgr(mongoConfiguration);
        } catch (Exception e) {
            logErrorAndExit("Fatal Error : Could not establish connection to mongo host " + e.getMessage(), e);
        }
    }

    private void logErrorAndExit(String msg, Exception e) {
        if (e != null) {
            LOGGER.info(msg, e);
        } else {
            LOGGER.info(msg);
        }
        System.exit(1);
    }

    private MongoConfiguration getMongoConfiguration() {
        String mongoUrl = null;
        if (mongoConfiguration == null) {
            mongoUrl = getPropOrEnv("MONGO_HOME");
            LOGGER.info(" Mongo URL from ENV variable is : " + mongoUrl);

            if (mongoUrl == null || mongoUrl.length() < 1) {
                mongoUrl = MongoLocator.getMongoLocation();
                LOGGER.info(" Mongo URL from DNS is : " + mongoUrl);
            }

            getMongoConfiguration(mongoUrl);
        }

        return mongoConfiguration;
    }

    private String getPropOrEnv(String key) {
        String it = System.getProperty(key);
        if (it == null)
            it = System.getenv(key);
        return it;
    }

    // cmd line arguments
    private void initCommandLineParameters() {
        /* Specify options */
        options.addOption("h", false, "help");
        options.addOption("app", true, "Application Name");
        options.addOption("version", true, "Version");
        options.addOption("scope", true, "Scope");
        options.addOption("user", true, "User Name");
        options.addOption("beanid", true, "Bean Name");
        options.addOption("beandefxml", true, "Bean Def Xml");
        options.addOption("publish", true, "Publish to Apps");
        options.addOption("display", true, "Display Config of App");
        options.addOption("delete", true, "Delete Config of App");
        options.addOption("beanversion", true, "Bean Version");
        options.addOption("reupload", true, "Auto upload after Delete");
    }

    private boolean parseOptions(String[] args) {
        CommandLine line = null;

        try {
            line = parser.parse(options, args);
        } catch (ParseException e) {
            LOGGER.info("Exception happened parsning cmd line options");
            throw new RuntimeException(e);
        }

        if (line != null) {
            /* Check for HELP */
            if (line.hasOption("-h")) {
                printUsage();
                return !invalidOption;
            }

            if (line.hasOption("-display")) {
                if (getOtherOptionValuesForDisplay(line)) {
                    invalidOption = false;
                }
            } else if (line.hasOption("-delete")) {
                if (getOtherOptionValuesForDelete(line)) {
                    invalidOption = false;
                }
            } else {
                if (getOtherOptionValues(line)) {
                    invalidOption = false;
                }
            }

        }

        if (invalidOption) {
            printUsage();
        }

        return !invalidOption;
    }

    private void printUsage() {
        /* Help Information */
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("ConfigChangeOptions", options);
    }

    private boolean getOtherOptionValues(CommandLine line) {
        boolean returnValue = false;

        /* Check for app */
        if (line.hasOption("app")) {
            application = line.getOptionValue("app");

            if (application == null) {
                return returnValue;
            }
        } else {
            return returnValue;
        }

        /* Check for version */
        if (line.hasOption("version")) {
            version = line.getOptionValue("version");

            if (version == null) {
                return returnValue;
            }
        } else {
            return returnValue;
        }

        /* Chack for Beanid */
        if (line.hasOption("beanid")) {
            beanId = line.getOptionValue("beanid");

            // if (beanId == null) {
            // return returnValue;
            // }
            if (beanId == null || beanId.trim().length() < 1) {
                getBeanName = true;
            } else {
                if (beanId.indexOf(",") != -1) {
                    beanIds = beanId.split(",");
                    multipleBeanIds = true;
                }
            }
        } else {
            getBeanName = true;
        }

        /* Check for publish */
        if (line.hasOption("display")) {
            String displayOption = line.getOptionValue("display");

            if (displayOption != null) {
                if (Boolean.valueOf(displayOption)) {
                    display = Boolean.valueOf(displayOption);
                }
            }
        }

        if (display) {
            return true;
        }

        /* Check for scope */
        if (line.hasOption("scope")) {
            scope = line.getOptionValue("scope");

            if (scope == null) {
                return returnValue;
            }
        } else {
            return returnValue;
        }

        /* Check for Bean Definition File */
        if (line.hasOption("beandefxml")) {
            beanDefXml = line.getOptionValue("beandefxml");

            if (beanDefXml == null) {
                return returnValue;
            } else {
                if (beanDefXml.indexOf(",") != -1) {
                    beanDefXmls = beanDefXml.split(",");
                    multipleBeanDefXmls = true;
                }
            }
        } else {
            return returnValue;
        }

        /* Chack for User */
        if (line.hasOption("user")) {
            user = line.getOptionValue("user");

            if (user == null) {
                return returnValue;
            }
        } else {
            return returnValue;
        }

        /* Check for publish */
        if (line.hasOption("publish")) {
            String publishOption = line.getOptionValue("publish");

            if (publishOption != null) {
                if (!Boolean.valueOf(publishOption)) {
                    publish = Boolean.valueOf(publishOption);
                }
            }
        }

        return !returnValue;
    }

    private boolean getOtherOptionValuesForDisplay(CommandLine line) {
        boolean returnValue = false;

        /* Check for app */
        if (line.hasOption("app")) {
            application = line.getOptionValue("app");

            if (application == null) {
                return returnValue;
            }
        } else {
            return returnValue;
        }

        /* Check for version */
        if (line.hasOption("version")) {
            version = line.getOptionValue("version");

            if (version == null) {
                return returnValue;
            }
        } else {
            return returnValue;
        }

        /* Chack for Beanid */
        if (line.hasOption("beanid")) {
            beanId = line.getOptionValue("beanid");

            // if (beanId == null) {
            // return returnValue;
            // }
            if (beanId == null || beanId.trim().length() < 1) {
                getBeanName = true;
            } else {
                if (beanId.indexOf(",") != -1) {
                    beanIds = beanId.split(",");
                    multipleBeanIds = true;
                }
            }
        } else {
            getBeanName = true;
        }

        /* Check for publish */
        if (line.hasOption("display")) {
            String displayOption = line.getOptionValue("display");

            if (displayOption != null) {
                if (Boolean.valueOf(displayOption)) {
                    display = Boolean.valueOf(displayOption);
                }
            }
        }

        return !returnValue;
    }

    private boolean getOtherOptionValuesForDelete(CommandLine line) {
        boolean returnValue = false;

        /* Check for app */
        if (line.hasOption("app")) {
            application = line.getOptionValue("app");

            if (application == null) {
                return returnValue;
            }
        } else {
            return returnValue;
        }

        /* Check for version */
        if (line.hasOption("version")) {
            version = line.getOptionValue("version");

            if (version == null) {
                return returnValue;
            }
        } else {
            return returnValue;
        }

        /* Chack for Beanid */
        if (line.hasOption("beanid")) {
            beanId = line.getOptionValue("beanid");
            if (beanId == null) {
                return returnValue;
            }
        } else {
            return returnValue;
        }

        /* Check for publish */
        if (line.hasOption("delete")) {
            String deleteOption = line.getOptionValue("delete");

            if (deleteOption != null) {
                if (Boolean.valueOf(deleteOption)) {
                    delete = Boolean.valueOf(deleteOption);
                }
            }
        }

        /* Check for reupload */
        if (line.hasOption("reupload")) {
            String reuploadOption = line.getOptionValue("reupload");

            if (reuploadOption != null) {
                if (!Boolean.valueOf(reuploadOption)) {
                    reupload = Boolean.valueOf(reuploadOption);
                }
            }
        }

        return !returnValue;
    }

    public static void main(String[] args) {
        ConfigUploaderToMongo uploader = null;
        try {
            uploader = new ConfigUploaderToMongo(args);

        } catch (Exception e) {
            LOGGER.info("Fatal Error : Config Exception" + e.getMessage(), e);
            System.exit(1);
        }

        try {
            if (!uploader.invalidOption) {
                if (uploader.isDisplay()) {
                    uploader.display();
                } else if (uploader.isDelete()) {
                    uploader.delete();
                } else {
                    uploader.upload();
                }

            } else {
                LOGGER.info("Fatal Error : Options/Arguments passed to Application are invalid.");
            }
        } catch (Exception e) {
            LOGGER.info("Fatal Error : Config Publish Exception" + e.getMessage(), e);
            System.exit(1);
        }

        System.exit(0);

    }

}