如何在Tomcat中为单个Web应用程序设置时区?

时间:2009-08-24 22:14:37

标签: java configuration tomcat timezone

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

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

9 个答案:

答案 0 :(得分:8)

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

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

Sun Javadocs中的行为未记录。它适用于Java 6和5(见上文),但不保证它可以在较旧或较新的Sun JRE中使用。但是,如果Sun决定更改/恢复为默认TimeZone的“全局”模型,我会非常惊讶。它会破坏太多现有的应用程序,除了全局变量之外还有BAD。

答案 1 :(得分:8)

我找到的唯一方法是设置过滤器并更改过滤器中的时区

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

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

答案 2 :(得分:7)

将系统变量设置为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

答案 3 :(得分:3)

在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类。

这是我的测试程序。

- 政策文件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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

最好的方法是修改Web应用程序,使其通过web.xml接受显式时区配置,而不是使用默认的JVM时区。

答案 6 :(得分:0)

答案 7 :(得分:0)

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

我在堆栈溢出时看到的其他解决方案并没有完全解决这个问题。

  • 使用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应用程序。

答案 8 :(得分:0)

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

-Djdk.util.TimeZone.allowSetDefault=true