2012-02-01 76 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