Java 如何在Tomcat中为单个web应用设置时区?

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

在Tomcat中为单个web应用程序设置时区的最佳方法是什么?我已经看到了为Tomcat更改命令行参数或环境变量的选项,但是有没有一种方法可以在WAR文件中自我包含,并且不依赖于任何Tomcat配置


编辑:为了再次强调,我正在寻找一个可以包含在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方法也不会考虑与子线程相关的时区问题
我还研究了时区的Java源代码,通过注入JavaAWTAccess接口的自定义实现,找到了一种将动态时区作为所有调用方的默认时区返回的方法。这可以通过查看线程类加载器来完成,并从中确定实际的webapp上下文,然后根据一些webapp名称到时区的映射来适当地处理它

同样,这是特定于应用程序服务器的,必须对Tomcat、Jetty、Jboss等进行不同的操作。这种方法也是特定于JVM实现的(仅适用于Oracle/Sun),但我相信可以扩展到OpenJDK和其他应用程序

我们为Oracle JDK 7 SE+Tomcat 7提供了一个经过验证的工作解决方案,部署在Windows和Linux上,在不同的时区托管多个Web应用程序。

您可以