単一のWebアプリのTomcatでタイムゾーンを設定するための最良の方法は何ですか? Tomcatのコマンドラインパラメーターまたは環境変数を変更するためのオプションを見てきましたが、WARファイルに自己完結型で、Tomcatの構成に依存しないように設定する方法はありますか?
編集:強調するために、Tomcatの構成に依存せずにWARファイルに含めることができるソリューションを探しています。言い換えると、1つのWebアプリを、同じTomcatインスタンスで実行されている他のアプリとは異なるタイムゾーンを持つように構成できますか?
私が見つけた唯一の方法は、フィルターを設定し、フィルターのタイムゾーンを変更することです。
_public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
TimeZone savedZone = TimeZone.getDefault();
TimeZone.setDefault(webappZone);
chain.doFilter(request, response);
TimeZone.setDefault(savedZone);
}
_
setDefault()
は、スレッドのゾーンを変更します。したがって、フィルター内のスレッドで実行されているものはすべて、異なるデフォルトのタイムゾーンになります。スレッドは他のアプリで共有されているため、元に戻す必要があります。 init()
、destroy()
メソッド、およびアプリケーションで開始する可能性のあるその他のスレッドについても同じことを行う必要があります。
サードパーティのライブラリはデフォルトのタイムゾーンを想定しており、ソースコードがないため、これを行う必要がありました。これによりログのタイムゾーンが変更されるため、混乱しましたが、別の時間にログインする必要はありません。これを処理する正しい方法は、エンドユーザーに公開される任意の時間値で特定のタイムゾーンを使用することです。
編集:私はこれについて間違っていました。この編集はそれを修正します。
答えは、単一のWebアプリに(デフォルトの)タイムゾーンを設定することはできないということですポータブル。ただし、Java 6(少なくとも))を使用している場合、_Java.util.TimeZone
_クラスはデフォルトのタイムゾーンメソッドgetDefault()
、setDefault()
、およびsetDefault(TimeZone)
は、ローカルの継承可能なスレッドを使用します。つまり、setDefault()
を呼び出すと、現在のスレッドと将来の子スレッドにのみ影響します。
Sun Javadocでは、動作は文書化されていませんです。 Java 6および5(上記を参照)で機能しますが、古いまたは新しいSun JREで機能する保証はありません。ただし、Sunが変更/元に戻すことを決定した場合は非常に驚きます。デフォルトのTimeZoneの「グローバル」モデルに変更します。既存のアプリケーションが多すぎて壊れてしまい、さらにグローバルは悪いです。
システム変数をCATALINA_OPTS=-Duser.timezone=America/Denver
に設定します
$ Tomcat_HOME/bin/catalina.shまたは%Tomcat_HOME%\ bin\catalina.batファイルでCATALINA_OPTSを指定することもできます。
JDK 6では、Sun/Oracleがタイムゾーンの実装を変更しました。 JDK 5.0では、setDefaultは、JVM全体ではなく、常にスレッドローカル変数にタイムゾーンを設定するため、いくつかの問題が発生しました。 Sunはこれをバグとして認識し、JDK1.6で修正しました。
JDK 1.6以降(JDK1.6とJDK1.7の両方のソースコードを確認しました)、JVMがセキュリティマネージャで起動されていない場合(またはSystem.SetsecurityManager()
で設定されていない場合)、setDefault
メソッドスレッド固有の方法ではなく、JVM全体でグローバルに設定します。特定のスレッドに対してのみ設定する場合は、セキュリティマネージャを使用してJVMを起動する必要があります。
セキュリティマネージャを使用してTomcatJVMを起動する場合は、個別のアクセス許可を提供する必要がありますが、リリースサイクルが遅れたため、これは私たちにとって初心者ではありませんでした。したがって、セキュリティポリシーファイルですべての権限を指定し、デフォルトのJavaセキュリティマネージャを上書きして、タイムゾーンの書き込みアクセスを選択的に拒否しました。タイムゾーンクラスの初期化の問題が原因で、Timezone.getDefault()
securityManagerが機能する前にTimezoneクラスを初期化する静的ブロック内。
これが私のテストプログラムです。
-ポリシーファイルtest.policy
grant {
permission Java.security.AllPermission;
};
-カスタムセキュリティマネージャー
import Java.security.Permission;
import Java.util.TimeZone;
public class CustomSecurityManager extends SecurityManager {
static {
TimeZone.getDefault().getDisplayName();
}
public CustomSecurityManager() {
super();
}
public void checkPermission(Permission perm) throws SecurityException,NullPointerException
{
String propertyName = perm.getName();
String actionName = perm.getActions();
if(propertyName != null && actionName != null)
{
if(propertyName.equalsIgnoreCase("user.timezone")
&& actionName.equalsIgnoreCase("write"))
{
throw new SecurityException("Timezone write is not permitted.");
}
}
}
}
--JVM起動パラメータ
-Djava.security.manager = CustomSecurityManager -Djava.security.policy = C:/workspace/test/src/test.policy
Java 7/Tomcat 7では、開発者がWebアプリケーションごとに一意のタイムゾーンを設定できる回避策/ハックがあります。複数のWebアプリをサポートする必要があるため、これをアプリケーションに実装する必要がありました。同じJVMで異なるデフォルトのタイムゾーンで実行します。
私がスタックオーバーフローで見た他の解決策は、問題に完全に対処していません。
また、TimeZoneのJavaソースコードを調査しました。JavaAWTAccessインターフェイスのカスタム実装を挿入することで、すべての呼び出し元のデフォルトのタイムゾーンとして動的タイムゾーンを返す方法を見つけました。これは実行できます。 Threadクラスローダーを調べ、そこから実際のWebアプリケーションコンテキストを決定し、Webアプリ名からタイムゾーンへのマッピングに基づいて適切に処理します。
繰り返しになりますが、これはアプリケーションサーバー固有であり、Tomcat、Jetty、Jbossなどでは異なる方法で実行する必要があります。このアプローチもJVM実装固有です(Oracle/Sunでのみ機能します)が、OpenJDKなどにも拡張できると思います。
Oracle JDK 7 SE + Tomcat 7の検証済みの動作ソリューションがあり、WindowsとLinuxの両方にデプロイされ、異なるタイムゾーンで複数のWebアプリケーションをホストしています。
VM引数を使用して定義することもできます
-Djdk.util.TimeZone.allowSetDefault=true
最良の方法は、デフォルトのJVMタイムゾーンを使用する代わりに、web.xmlを介して明示的なタイムゾーン構成を受け入れるようにWebアプリケーションを変更することです。
SimpleTimeZone をチェックしてください。タイムゾーンIDに基づいてインスタンスを作成し、それを使用して、そのタイムゾーンを使用して日付/時刻を表示できます。必要に応じて、プロジェクト固有の構成ファイルからそのIDを読み取ることができます。