【问题标题】:How do I set the timezone in Tomcat for a single web app?如何在 Tomcat 中为单个 Web 应用程序设置时区?
【发布时间】:2010-11-22 10:38:10
【问题描述】:

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

编辑:再次强调,我正在寻找一个可以包含在 WAR 文件中的解决方案,而不依赖于 Tomcat 配置。换句话说,一个 Web 应用程序是否可以配置为与在同一 Tomcat 实例中运行的其他应用程序具有不同的时区?

【问题讨论】:

  • 好问题;我对 Tomcat 安装摸不着头脑,我的 Tomcat 日志中的时间戳比系统时间晚一个小时。

标签: java configuration tomcat timezone


【解决方案1】:

我发现的唯一方法是设置过滤器并更改过滤器中的时区,

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() 方法以及您可能在应用程序中启动的任何其他线程执行相同的操作。

我必须这样做,因为第三方库采用默认时区,而我们没有源代码。这是一团糟,因为这会更改日志时区,但我们不希望在不同时间登录。处理此问题的正确方法是在暴露给最终用户的任何时间值中使用特定时区。

【讨论】:

  • 你应该使用 try-finally 。请注意,在处理期间创建的任何线程都将继承当时设置的时区。未来的 JRE 可能会有不同的怪异行为。
  • 好收获。我同意这可能会与未来的 JRE 中断,因为我在文档中没有看到任何地方说 setDefault() 只会影响调用线程。毕竟这是一个hack。除非必须,否则不要这样做。
  • 我希望你意识到,如果你有多个 web 应用程序,这将会失败。调用 setDefault() 会更改 JVM 中每个线程的默认时区。
  • 我们在 Java 5 上试过这个,它只影响了调用线程。所有其他 web 应用程序不受影响。我同意这是一个未记录的功能,它可能会在未来的 JRE 中中断。
  • 警告:Java 5 与 1.4 和 Java 6+ 的工作方式不同!不要依赖这个答案来解决你的问题。有关详细信息,请参阅stackoverflow.com/questions/2176784/…
【解决方案2】:

编辑:我错了。此编辑更正了它。

答案是您不能可移植地为单个 web 应用设置(默认)时区。但是,如果您使用 Java 6(至少),java.util.TimeZone 类使用可继承的本地线程实现默认时区方法 getDefault()setDefault()setDefault(TimeZone)。也就是说,调用setDefault()只会影响当前线程和未来的子线程。

Sun Javadocs 中没有记录该行为。它适用于 Java 6 和 5(见上文),但不能保证它适用于较旧或较新的 Sun JRE。但是,如果 Sun 决定更改/恢复默认 TimeZone 的“全局”模型,我会感到非常惊讶。它会破坏太多现有的应用程序,而且全局变量是不好的。

【讨论】:

  • 应用程序范围的全局变量只比线程绑定的全局变量略差... :)
【解决方案3】:

将系统变量设置为CATALINA_OPTS=-Duser.timezone=America/Denver

您也可以在 $TOMCAT_HOME/bin/catalina.sh 或 %TOMCAT_HOME%\bin\catalina.bat 文件中指定 CATALINA_OPTS。

Here's a list of acceptable timezones.

Source

【讨论】:

  • 此解决方案将更改 Tomcat 服务器的整体时区;问题作者想为每个 webapp 设置不同的时区。
【解决方案4】:

在 JDK 6 中,Sun/Oracle 更改了 Timezone 的实现。在 JDK 5.0 中,setDefault 始终在线程局部变量中设置时区,而不是跨 JVM,这会导致一些问题。 Sun 承认这是一个错误并在 JDK 1.6 中修复。

从 JDK 1.6 开始(我检查了 JDK 1.6 和 JDK 1.7 的源代码),如果 JVM 没有使用安全管理器启动(或者它没有使用 System.SetsecurityManager() 设置),setDefault 方法将其全局设置为JVM,而不是线程特定的方式。如果您只想为给定线程设置它,那么您必须使用安全管理器启动 JVM。

当您使用安全管理器启动 Tomcat JVM 时,您需要提供单独的权限,因为我们处于发布周期的后期,所以这对我们来说不是一个初学者。因此,在安全策略文件中,我们提供了所有权限,并且我们覆盖了默认的 JAVA 安全管理器,以选择性地拒绝时区写入访问。由于 Timezone 类的延迟初始化问题,我需要在静态块中调用 Timezone.getDefault(),这使得 Timezone 类在 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

【讨论】:

    【解决方案5】:

    查看SimpleTimeZone。您可以基于时区 ID 创建一个实例,并使用它来显示使用该时区的日期/时间。如果需要,您可以从项目特定的配置文件中读取该 ID。

    【讨论】:

      【解决方案6】:

      在 Java 7/Tomcat 7 中,有一种解决方法/hack 允许开发人员为每个 webapp 设置一个唯一的时区。我们必须在我们的应用程序中实现这一点,因为我们必须支持在同一个 JVM 中以不同默认时区运行的多个 web 应用程序。

      我在堆栈溢出方面看到的其他解决方案并未完全解决该问题。

      • 使用 Timezone.setDefault() 不起作用,因为它会更改 JVM 中的时区。
      • 使用 servlet 过滤器完全是线程不安全的,并且不考虑子线程
      • 使用 SecurityManager 方法也不能解决子线程相关的时区问题。

      我还研究了 TimeZone 的 Java 源代码,我找到了一种方法,通过注入 JavaAWTAccess 接口的自定义实现,将动态时区作为所有调用者的默认时区返回。这可以通过查看 Thread 类加载器,并从中确定实际的 webapp 上下文,然后根据一些 webapp 名称到时区映射适当地处理它来完成。

      再一次,这是特定于应用程序服务器的,并且必须针对 Tomcat、Jetty、Jboss 等进行不同的处理。这种方法也是特定于 JVM 实现的(仅适用于 Oracle/Sun),但我相信可以扩展到 OpenJDK和别的。

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

      【讨论】:

      • 你用JDK7 + Tomcat 7的解决方案是什么?
      【解决方案7】:

      您也可以使用 VM 参数来定义它

      -Djdk.util.TimeZone.allowSetDefault=true
      

      【讨论】:

        【解决方案8】:

        21 年的更新答案:如今,您应该只为每个 JVM 运行一个应用程序,并在系统属性中设置 TZ。

        '09 的过时答案: 最好的方法是修改 Web 应用程序,使其通过 web.xml 接受明确的时区配置,而不是使用默认的 JVM 时区。

        【讨论】:

        • 如果您告诉如何在 web.xml 中设置它会有所帮助
        • @BillLeeper 我写这个答案已经 10 多年了,它已经过时了。如今,很少有人在每个 servlet 运行器上运行多个应用程序,而当他们这样做时,这是一个架构问题,应该在进行此类修复之前予以纠正。
        猜你喜欢
        • 2015-06-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-14
        相关资源
        最近更新 更多