次のように、各サーバー操作をコーディングしてもよろしいですか?
void op1(String username, ...)
{
String userScope = getConfigurationScopeForUser(username);
String language = cfg.lookupString(userScope, "language");
int fontSize = cfg.lookupInt(userScope, "font_size");
... // business logic expressed in terms of language and fontSize
}
(上記の疑似コードは、ユーザーの名前がパラメーターとして渡されることを前提としていますが、別のメカニズム (スレッドローカルストレージなど) を介して渡すこともできます。)
上記が受け入れられる場合、Config4*は要件を満たすことができます。Config4* をgetConfigurationScopeForUser()
使用すると、上記の疑似コードで使用されているメソッドを次のように実装できます (これはcfg
、構成ファイルを解析することによって以前に初期化された構成オブジェクトであると想定しています)。
String getConfigurationScopeForUser(String username)
{
if (cfg.type("user", username) == Configuration.CFG_SCOPE) {
return Configuration.mergeNames("user", username);
} else {
return "user.default";
}
}
上記で動作するサンプル構成ファイルを次に示します。ほとんどのユーザーは「user.default」スコープから構成を取得しますが、Mary と John はこれらのデフォルト値の一部を独自にオーバーライドしています。
user.default {
language = "English";
font_size = "12";
# ... many other configuration settings
}
user.John {
@copyFrom "user.default";
language = "Klingon"; # override a default value
}
user.Mary {
@copyFrom "user.default";
font_size = "18"; # override a default value
}
上記がニーズに合っているように思われる場合は、「Getting Started Guide」の第 2 章と第 3 章を読んで、Config4* 構文と API を十分に理解し、適合性を確認/反論できるようにすることをお勧めします。必要に応じて Config4* の そのドキュメントは Config4* Web サイトにあります。
免責事項: 私は Config4* のメンテナーです。
編集: bacar のコメントに応じて、詳細を提供しています。
Config4* を Maven リポジトリーに入れていません。ただし、Config4* はサードパーティのライブラリに依存していないため、バンドルされている Ant ビルド ファイルを使用して Config4* をビルドするのは簡単です。
サーバー アプリケーションで Config4* を使用する別の方法 (bacar によるコメントによって促される) は、次のとおりです。
次の疑似コードのように、各サーバー操作を実装します。
void op1(String username, ...)
{
Configuration cfg = getConfigurationForUser(username);
String language = cfg.lookupString("settings", "language");
int fontSize = cfg.lookupInt("settings", "font_size");
... // business logic expressed in terms of language and fontSize
}
上記getConfigurationForUser()
で使用したメソッドは、次の擬似コードに示すように実装できます。
HashMap<String,Configuration> map = new HashMap<String,Configuration>();
synchronized String getConfigurationForUser(String username)
{
Configuration cfg = map.get(username);
if (cfg == null) {
// Create a config object tailored for the user & add to the map
cfg = Configuration.create();
cfg.insertString("", "user", username); // in global scope
cfg.parse("/path/to/file.cfg");
map.put(username, cfg);
}
return cfg;
}
上記で動作するサンプル構成ファイルを次に示します。
user ?= ""; // will be set via insertString()
settings {
@if (user @in ["John", "Sam", "Jane"]) {
language = "Klingon";
} @else {
language = "English";
}
@if (user == "Mary") {
font_size = "12";
} @else {
font_size = "10";
}
... # many other configuration settings
}
2 つのアプローチに関する主なコメントは次のとおりです。
最初のアプローチ (Configuration
多数の変数とスコープを含む 1 つのオブジェクト) は、2 番目のアプローチ (Configuration
それぞれ少数の変数を持つ多数のオブジェクト) よりもわずかに少ないメモリを使用する可能性があります。しかし、私の推測では、どちらのアプローチのメモリ使用量も KB または数十 KB 単位で測定され、サーバー アプリケーションの全体的なメモリ フットプリントと比較すると重要ではありません。
1 つのオブジェクトが 1 回だけ初期化され、読み取り専用スタイルの操作Configuration
でアクセスされるため、私は最初のアプローチを好みます。これは、サーバー アプリケーションがマルチスレッドであっても、オブジェクトlookup()
へのアクセスの同期について心配する必要がないことを意味します。対照的に、2 番目の方法では、サーバー アプリケーションがマルチスレッドConfiguration
の場合、 へのアクセスを同期する必要があります。HashMap
形式の操作のオーバーヘッドはlookup()
、たとえばナノ秒またはマイクロ秒のオーダーですが、構成ファイルの解析のオーバーヘッドは、たとえばミリ秒または数十ミリ秒のオーダーです (ファイルのサイズによって異なります)。最初のアプローチでは、構成ファイルの比較的コストのかかる解析が 1 回だけ実行されます。これは、アプリケーションの初期化で実行されます。対照的に、2 番目のアプローチでは、比較的コストのかかる構成ファイルの解析が "N" 回 ("N" ユーザーごとに 1 回) 実行され、サーバーがクライアントからの要求を処理している間に繰り返しコストが発生します。このパフォーマンス ヒットは、アプリケーションにとって問題になる場合とそうでない場合があります。
実装の容易さよりも使いやすさが重要だと思います。したがって、2 番目の方法で構成ファイルの保守が容易になると思われる場合は、その方法を使用することをお勧めします。
settings
2 番目のアプローチでは、「注入された」変数と一緒にグローバル スコープではなく、ほとんどの変数を名前付きスコープ ( ) に配置する理由を不思議に思うかもしれませんuser
。質問の範囲外の理由でそれを行いました.「注入された」変数をアプリケーションに表示される変数から分離すると、アプリケーションに表示される変数でスキーマ検証を実行しやすくなります。