2012-02-01 7 views
3

Я работаю над проектом, который имеет некоторые конфигурационные файлы .properties для datasource, MQ и некоторых других вещей. У нас также есть сценарии запуска оболочки и сценарии пользовательского профиля. Проблема, с которой я столкнулся, заключается в том, что мы действительно развертываем это программное обеспечение в 5 различных средах, и, конечно, для каждого из них конфигурация различна. Это немного сложно поддерживать около 30 текстовых файлов с конфигурацией. Большинство из них в значительной степени равны, например, shell-скрипты, на которые есть только разные ссылки на пути.Java Создать конфигурацию программного обеспечения

Вы, ребята, знаете какой-либо инструмент, который я мог бы интегрировать в наш скрипт сборки, который мог бы захватить эти свойства из одного файла или встроенной базы данных, а затем создать правильную конфигурацию среды? Если бы он мог генерировать скрипты, это было бы еще более интересно.

Благодаря

ответ

0

Я являюсь хранителем 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 * сложно использовать, а только потому, что вы будете изменять существующий код который использует, скажем, API свойств Java или XML API для использования другого API, и такие модификации, как правило, утомительны).

Второй вариант лучше соответствует специфике вашего вопроса. Вы пишете шаблонные версии своих сценариев оболочки и файлов свойств. Эти файлы шаблонов будут использовать определенный синтаксис, например '${variable.name}', чтобы указать, где должны использоваться значения из файла конфигурации. Затем вы пишете небольшое служебное приложение, которое читает файл шаблона и файл конфигурации, выполняет необходимые замены и затем записывает преобразованный файл на диск. Вы можете запустить это приложение-утилиту из своей системы сборки.

0

Вы могли бы взглянуть на недавно объявленный tools4j-config, который позволяет обрабатывать конфигурацию во время выполнения, а не время сборки.

0

В предыдущем ответе я изложил, как Config4* может удовлетворить ваши потребности. Я решил съесть свою собачью пищу, поэтому я сбил готовое к компиляции и запускаемое приложение на основе Config4 *, которое будет делать то, что вы хотите. Я предоставляю код встроенный в этот ответ.Вместо того, чтобы читать код через веб-страницу StackOverview, вам может быть проще скопировать и вставить код в файлы, чтобы вы могли просматривать его с помощью текстового редактора.

Во-первых, нам нужен конфигурационный файл, который определяет три переменные:

  • deploymentType (указан в качестве аргумента командной строки, чтобы иметь значение dev, staging или prod);

  • 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 
Смежные вопросы