Java生成软件配置



我正在处理一个项目,该项目有一些数据源、MQ和其他东西的.properties配置文件。我们也有启动shell脚本和用户配置文件脚本。我面临的问题是,我们确实在5个不同的环境中部署了这个软件,当然,每个环境的配置都不同。使用该配置维护大约30个纯文本文件有点困难。它们中的大多数几乎是相等的,就像shell脚本上只有一些不同的路径引用一样

你们知道我可以在我们的构建脚本上集成什么工具吗?它可以从单个文件或嵌入式数据库中获取这些属性,然后生成正确的环境配置?如果它也能生成脚本,那就更有趣了。

感谢

Maven提供了开箱即用的功能:http://maven.apache.org/guides/mini/guide-building-for-different-environments.html.

我是Config4*的维护者,Config4*是C++和Java风格的配置文件解析器库。Config4*配置文件中的大多数内容都是name=value语句,但您可以引用环境变量和执行一些命令(如hostname)的标准输出。您也可以在配置文件中使用if-then-else语句。例如(关键字前缀为"@"):

@if (exec("hostname") @in ["host1", "host2", "host3"]) {
    ... # set variables to values for production environment
} @elseIf (exec("hostname") @in ["host4", "host5", "host6"]) {
    ... # set variables to values for staging environment
} @else {
    @error "Unknown host";
}

我称之为自适应配置,因为单个配置文件可以根据各种主机、用户名等调整其内容。Config4*提供了一种将命令行选项与配置文件集成的简单方法,因此可以根据命令行选项(如-env production-env staging)的存在来调整其内容的配置文件。例如:

env ?= ""; # set by a command-line option
if (env == "production") {
    ... # set variables to values for production environment
} @elseIf (env == "staging") {
    ... # set variables to values for staging environment
} @else {
    @error "You must specify '-env production' or '-env staging' as a command-line option";
}

我可以想出两种可能的方法,Config4*可能会对您有所帮助。

一种选择是在应用程序中嵌入Config4*解析器。然而,尽管我认为在开发新的应用程序时,这是一种很好的方法,但我认为将Config4*改造为现有的应用程序可能会很乏味(不是因为Config4*很难使用,而是因为您将修改使用Java属性API或XML API的现有代码,以使用不同的API,而且这种修改往往很乏味)。

第二个选项更符合您问题的具体情况。您可以编写shell脚本和属性文件的模板版本。这些模板文件将使用特定的语法,例如'${variable.name}'来指定应该在哪里使用配置文件中的值。然后编写一个小型实用程序,读取模板文件和配置文件,执行所需的替换,然后将转换后的文件写入磁盘。您可以从构建系统中运行该实用程序。

您可以看看最新发布的tools4j-config,它允许您在运行时而不是构建时处理配置。

在前面的回答中,我概述了Config4*如何满足您的需求。我决定自己吃狗粮,所以我开发了一个可以编译和运行Config4*的应用程序,它可以做你想做的事情。我在这个答案中提供了内联代码。与通过StackOverview网页阅读代码相比,您可能会发现将代码复制并粘贴到文件中更容易,这样您就可以使用文本编辑器进行查看。

首先,我们需要一个定义三个变量的配置文件:

  • deploymentType(指定为命令行参数以具有值devstagingprod);

  • files(模板文件和输出文件对);

  • searchAndReplace(要应用于模板文件以生成输出文件的搜索和替换字符串对)。所使用的字符串对取决于deploymentType的值。

以下是这样一个文件的示例(将其复制并粘贴到templates.cfg中):

deploymentType ?= ""; # specified with a command-line argument
files = [
    # template file                     output file
    # ----------------------------------------------------
     "log4j-template.properties",       "log4j.properties",
     "hello-template.sh",               "hello.sh",
];
@if (deploymentType == "dev") {
    searchAndReplace = [
        "${db.host}",                   "localhost",
        "${db.user}",                   "guest",
        "${db.log.level}",              "2",
    ];
} @elseIf (deploymentType == "staging") {
    searchAndReplace = [
        "${db.host}",                   exec("hostname"),
        "${db.user}",                   getenv("USERNAME"),
        "${db.log.level}",              "0",
    ];
} @elseIf (deploymentType == "prod") {
    searchAndReplace = [
        "${db.host}",                   "production.example.com",
        "${db.user}",                   getenv("USERNAME"),
        "${db.log.level}",              "0",
    ];
} @else {
    @error "deploymentType must be 'dev', 'staging' or 'prod'";
}

这是应用程序的主线。您应该将以下内容剪切粘贴到InstantiateTemplateFiles.java中:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import org.config4j.Configuration;
import org.config4j.SchemaValidator;
import org.config4j.ConfigurationException;
public class InstantiateTemplateFiles
{
    public static void main(String[] args)
    {
        Configuration       cfg = Configuration.create();
        SchemaValidator     sv = new SchemaValidator();
        String[]            searchAndReplace;
        String[]            files;
        String              contents;
        String              modifiedContents;
        String              templateFile;
        String              outputFile;
        int                 i;
        String[]            schema = new String[] {
            "deploymentType = string",
            "searchAndReplace=table[string,search, string,replace]",
            "files=table[string,template-file, string,output-file]",
        };
        if (args.length != 2) {
            System.err.println("nusage: java InstantiateTemplateFiles"
                    + " meta-config-file.cfg deploymentTypen");
            System.exit(1);
        }
        try {
            //--------
            // Parse the configuration file, perform schema validation
            // and retrieve the required configuration variables.
            //--------
            cfg.insertString("", "deploymentType", args[1]);
            cfg.parse(args[0]);
            sv.parseSchema(schema);
            sv.validate(cfg, "", "");
            searchAndReplace = cfg.lookupList("", "searchAndReplace");
            files = cfg.lookupList("", "files");
            //--------
            // Do the real work
            //--------
            for (i = 0; i < files.length; i += 2) {
                Util.searchAndReplaceInFile(files[i + 0], files[i + 1],
                                            searchAndReplace);
            }
        } catch(IOException ex) {
            System.err.println("n" + ex.getMessage() + "n");
            System.exit(1);
        } catch(ConfigurationException ex) {
            System.err.println("n" + ex.getMessage() + "n");
            System.exit(1);
        }
    }
}

最后,这里是对文件执行搜索和替换的代码。此代码独立于Config4*,因此即使您决定构建一个非基于Config4*的实用程序,您也可能会发现它很有用。您应该将此代码剪切粘贴到Util.java:中

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Util
{
    public static void searchAndReplaceInFile(
        String      inputFile,
        String      outputFile,
        String[]    searchAndReplacePairs) throws IOException
    {
        String      contents;
        String      modifiedContents;
        contents = Util.readTextFile(inputFile);
        modifiedContents = Util.replace(contents, searchAndReplacePairs);
        Util.writeTextFile(outputFile, modifiedContents);
    }
    public static String readTextFile(String fileName) throws IOException
    {
        BufferedReader          in;
        StringBuffer            result;
        String                  line;
        result = new StringBuffer();
        in = new BufferedReader(new FileReader(fileName));
        while ((line = in.readLine()) != null) {
            result.append(line).append("n");
        }
        in.close();
        return result.toString();
    }
    public static void writeTextFile(String fileName, String contents)
                                                        throws IOException
    {
        PrintWriter             out;
        StringBuffer            result;
        String                  line;
        out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
        out.print(contents);
        out.close();
    }
    public static String replace(
        String                  origStr,
        String                  searchStr,
        String                  replacementStr)
    {
        StringBuffer            result;
        int                     origStrLen;
        int                     searchStrLen;
        int                     currStart;
        int                     pIndex;
        result = new StringBuffer();
        origStrLen = origStr.length();
        searchStrLen = searchStr.length();
        currStart = 0;
        pIndex = origStr.indexOf(searchStr, currStart);
        while (pIndex != -1) {
            result.append(origStr.substring(currStart, pIndex));
            result.append(replacementStr);
            currStart = pIndex + searchStrLen;
            pIndex = origStr.indexOf(searchStr, currStart);
        }
        result.append(origStr.substring(currStart));
        return result.toString();
    }
    public static String replace(
        String                  origStr,
        String[]                searchAndReplacePairs)
    {
        int                     i;
        int                     currIndex;
        String                  subStr;
        String                  replaceStr;
        StringBuffer            result;
        SearchAndReplacePair[]  pairs;
        SearchAndReplacePair    nextPair;
        pairs = new SearchAndReplacePair[searchAndReplacePairs.length / 2];
        for (i = 0; i < searchAndReplacePairs.length; i += 2) {
            pairs[i/2] = new SearchAndReplacePair(origStr,
                                                  searchAndReplacePairs[i + 0],
                                                  searchAndReplacePairs[i + 1]);
        }
        result = new StringBuffer();
        currIndex = 0;
        nextPair = findNextPair(origStr, currIndex, pairs);
        while (nextPair != null) {
            subStr = origStr.substring(currIndex, nextPair.indexOf);
            result.append(subStr);
            result.append(nextPair.replace);
            currIndex = nextPair.indexOf + nextPair.length;
            for (i = 0; i < pairs.length; i++) {
                pairs[i].findNext(currIndex);
            }
            nextPair = findNextPair(origStr, currIndex, pairs);
        }
        subStr = origStr.substring(currIndex);
        result.append(subStr);
        return result.toString();
    }
    private static SearchAndReplacePair findNextPair(
        String                      origStr,
        int                         currIndex,
        SearchAndReplacePair[]      pairs)
    {
        int                         i;
        SearchAndReplacePair        bestSoFar;
        SearchAndReplacePair        item;
        bestSoFar = null;
        for (i = 0; i < pairs.length; i++) {
            item = pairs[i];
            if (item.indexOf == -1) {
                continue;
            }
            if (bestSoFar == null) {
                bestSoFar = item;
                continue;
            }
            if (bestSoFar.indexOf < item.indexOf) {
                continue;
            }
            if (bestSoFar.indexOf > item.indexOf) {
                bestSoFar = item;
                continue;
            }
            if (bestSoFar.length < item.length) {
                bestSoFar = item;
            }
        }
        return bestSoFar;
    }
}

class SearchAndReplacePair
{
    String      source;
    String      search;
    String      replace;
    int         length;
    int         indexOf;
    int         sourceLength;
    public SearchAndReplacePair(String source, String search, String replace)
    {
        this.source = source;
        this.sourceLength = source.length();
        this.search  = search;
        this.replace = replace;
        this.length  = search.length();
        this.indexOf = source.indexOf(search);
    }

    public void findNext(int fromIndex)
    {
        if (indexOf == -1 || indexOf + 1 == sourceLength) {
            indexOf = -1;
        } else {
            indexOf = source.indexOf(search, fromIndex);
        }
    }
}

假设您已经下载并安装了Config4J(从Config4*网站),您可以使用以下内容编译该实用程序:

CLASSPATH=.:/path/to/config4j.jar;
export CLASSPATH
javac -classpath .:/ag/projects/config4j/lib/config4j.jar *.java

下面是一个运行它的例子:

java InstantiateTemplateFiles templates.cfg prod

如果文件hello-template.sh看起来像:

#!/bin/sh
DB_HOST=${db.host}
DB_USER=${db.user}
DB_LOG_LEVEL=${db.log.level}
echo Hello from $DB_USER at log level $DB_LOG_LEVEL on host $DB_HOST

则生成的CCD_ 16文件将看起来像:

#!/bin/sh
DB_HOST=production.example.com
DB_USER=cjmchale
DB_LOG_LEVEL=0
echo Hello from $DB_USER at log level $DB_LOG_LEVEL on host $DB_HOST

最新更新