代码之家  ›  专栏  ›  技术社区  ›  sk.

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

  •  15
  • sk.  · 技术社区  · 15 年前

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

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

    9 回复  |  直到 8 年前
        1
  •  8
  •   ZZ Coder    15 年前

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

    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
  •  8
  •   Stephen C    15 年前

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

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

    行为是 未记录的 在阳光下,爪哇。它适用于Java 6和5(见上文),但不能保证它将在较旧的或更新的SunJies中工作。但是,如果Sun决定为默认时区更改/恢复“全局”模型,我会非常惊讶。它会破坏太多的现有应用程序,而且全局应用程序也很糟糕。

        3
  •  7
  •   Cuga    15 年前

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

    您还可以在$tomcat_home/bin/catalina.sh或%tomcat_home%\bin\catalina.bat文件中指定catalina选项。

    Here's a list of acceptable timezones.

    Source

        4
  •  3
  •   jonsca    12 年前

    在JDK6中,Sun/Oracle改变了时区的实现。在JDK5.0setDefault中,总是在线程局部变量中设置时区,而不是在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

        5
  •  0
  •   Tai Squared    15 年前

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

        6
  •  0
  •   Mikael Gueck    15 年前

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

        8
  •  0
  •   zzhu8192    11 年前

    使用Java 7/Tomcat 7,有一个处理/黑客允许开发者在每个WebApp上设置一个唯一的时区。我们必须在应用程序中实现这一点,因为我们必须支持在同一个JVM中使用不同默认时区运行的多个webapps。

    我在堆栈溢出上看到的其他解决方案并不能完全解决这个问题。

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

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

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

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

        9
  •  0
  •   Icegras    8 年前

    也可以使用vm参数来定义它

    -Djdk.util.TimeZone.allowSetDefault=true