/******************************************************************************* * 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); } }