Java 如何在Tomcat中为单个web应用设置时区?
在Tomcat中为单个web应用程序设置时区的最佳方法是什么?我已经看到了为Tomcat更改命令行参数或环境变量的选项,但是有没有一种方法可以在WAR文件中自我包含,并且不依赖于任何Tomcat配置Java 如何在Tomcat中为单个web应用设置时区?,java,configuration,tomcat,timezone,Java,Configuration,Tomcat,Timezone,在Tomcat中为单个web应用程序设置时区的最佳方法是什么?我已经看到了为Tomcat更改命令行参数或环境变量的选项,但是有没有一种方法可以在WAR文件中自我包含,并且不依赖于任何Tomcat配置 编辑:为了再次强调,我正在寻找一个可以包含在WAR文件中的解决方案,而不依赖于Tomcat配置。换句话说,一个web应用是否可以配置为与在同一Tomcat实例中运行的其他应用具有不同的时区?将系统变量设置为CATALINA\u OPTS=-Duser.timezone=America/Denver
编辑:为了再次强调,我正在寻找一个可以包含在WAR文件中的解决方案,而不依赖于Tomcat配置。换句话说,一个web应用是否可以配置为与在同一Tomcat实例中运行的其他应用具有不同的时区?将系统变量设置为
CATALINA\u OPTS=-Duser.timezone=America/Denver
您还可以在$TOMCAT_HOME/bin/CATALINA.sh或%TOMCAT_HOME%\bin\CATALINA.bat文件中指定CATALINA_选项
编辑:我错了。此编辑更正了它 答案是你不能为一个webapp设置(默认)时区。但如果您使用的是Java 6(至少),则
Java.util.TimeZone
类使用可继承的本地线程实现默认时区方法getDefault()
、setDefault()
和setDefault(TimeZone)
。换句话说,调用setDefault()
只会影响当前线程和将来的子线程
Sun Javadocs中没有记录这种行为。它适用于Java6和Java5(见上文),但不能保证它适用于较旧或较新的Sun JRE。然而,如果Sun决定更改/恢复默认时区的“全局”模型,我会非常惊讶。它会破坏太多的现有应用程序,而且全局应用程序也不好。请查看。您可以基于时区ID创建实例,并使用该ID显示使用该时区的日期/时间。如果需要,可以从特定于项目的配置文件中读取该ID。我找到的唯一方法是设置筛选器并更改筛选器中的时区
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()
方法和应用程序中可能启动的任何其他线程执行相同的操作
我不得不这样做,因为第三方库假定默认时区,而我们没有源代码。这是一个混乱,因为这改变了日志时区,但我们不希望在不同的时间登录。正确的处理方法是在向最终用户公开的任何时间值中使用特定时区。从'21更新了答案:现在,每个JVM只能运行一个应用程序,并在系统属性中设置TZ
09年过时的答案:最好的方法是修改web应用程序,使其通过web.xml接受显式时区配置,而不是使用默认的JVM时区。在JDK 6中,Sun/Oracle更改了时区的实现。在JDK 5.0中,setDefault总是在线程局部变量中设置时区,而不是跨JVM设置时区,这导致了一些问题。Sun承认这是一个bug,并在JDK1.6中修复了它 在JDK1.6以后的版本中(我检查了JDK1.6和JDK1.7的源代码),如果JVM没有使用安全管理器启动(或者没有使用
System.SetsecurityManager()
),则setDefault
方法在JVM中全局设置它,而不是以线程特定的方式。如果只想为给定的线程设置它,那么必须使用安全管理器启动JVM
当您使用安全管理器启动Tomcat JVM时,您需要提供单独的权限,这对我们来说是不可能的,因为我们已经进入了发布周期的后期。因此,在安全策略文件中,我们提供了所有权限,并重写了默认的JAVA安全管理器,以选择性地拒绝时区写访问。由于时区类的延迟初始化问题,我需要在静态块中调用Timezone.getDefault()
,使时区类在securityManager发挥作用之前初始化
这是我的测试程序
--策略文件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
grant {
permission java.security.AllPermission;
};
在Java7/Tomcat7中,有一种变通/破解方法,允许开发人员为每个webapp设置唯一的时区。我们必须在应用程序中实现这一点,因为我们必须支持在同一JVM中以不同默认时区运行的多个Web应用程序 我在堆栈溢出上看到的其他解决方案并没有完全解决这个问题
- 使用Timezone.setDefault()不起作用,因为它会在JVM中更改时区
- 使用servlet过滤器是完全线程不安全的,并且不考虑子线程
- 使用SecurityManager方法也不会考虑与子线程相关的时区问题