2012-02-01 70 views
3

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

你们是否知道有什么样的工具,我可以对我们的构建脚本集成可能从单个文件或嵌入式数据库抓住这些属性,然后产生适当的环境配置?如果它也可以生成脚本,它会更有趣。

感谢

回答

0

我的Config4*维护者,这是在C的配置文件解析库++和Java的味道。在CONFIG4 *配置文件大部分内容是名称=值语句,但你可以参考的环境变量和执行类似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}'指定应使用配置文件中的值。然后编写一个小型实用程序应用程序,该应用程序可以读取模板文件和配置文件,执行所需的替换,然后将转换后的文件写入磁盘。您可以从构建系统运行该实用程序应用程序。

0

你可以看看新公布的tools4j-config,它可以让你在运行时处理配置而不是编译时间。

0

在前面的回答,我概述了如何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 deploymentType\n"); 
      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 

然后将生成的文件hello.sh的样子:

#!/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