如何让Tomcat中的webapp使用不同的时区
来源:互联网 发布:侣皓吉吉和盛一伦 知乎 编辑:程序博客网 时间:2024/05/21 10:18
引入问题
有两个webapp:一个要求默认时区是东8区,也就是北京时间;一个要求默认时区是0时区。这两个webapp之间有内在业务上的关联,希望部署在同一个Tomcat中。
在Java中,默认时区是可以通过JVM启动参数-Duser.timezone=GMT+8
来设定的。如果Java应用启动时没有设置,则采用系统的默认时区。显然,默认时区是JVM隔离级别的。而同一个Tomcat实例中的webapp都运行在同一个JVM实例上,所以是共用同一个默认时区的。
所以就有了这么个问题:如何让处于同一个Tomcat的不同webapp使用各自独立的时区?
几个有问题的方案
stackoverflow上「How do I set the timezone in Tomcat for a single web app?」探讨了这个问题,但给出的几个答案都有些问题。
使用Filter
JDK 1.5中,关于TimeZone类的setDefault方法源码如下:
public static synchronized void setDefault(TimeZone zone) { defaultZoneTL.set(zone); }
defaultZoneTL中TL的意思是ThreadLocal,所以调用这个方法只对当前线程有效。由此,我们可以使用Filter,在业务开始前调用setDefault设置正确的时区,业务完成后再调用setDefault恢复到原先的时区。代码如下:
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);}
使用Filter的方式只针对JDK1.5有效,当JDK1.6和JDK1.7实现代码不同后,这种方式也就可能得到未知的结果了。
开启默认的SecurityManager
JDK 1.6中,关于TimeZone类的setDefault方法源码如下:
public static void setDefault(TimeZone zone) { if (hasPermission()) { synchronized (TimeZone.class) { defaultTimeZone = zone; defaultZoneTL.set(null); } } else { defaultZoneTL.set(zone); }}
可以看到,设置默认时区时,需要判断是否拥有权限。如果没有权限,则行为跟JDK1.5一致,设置只对当前线程有效;如果拥有权限,则设置对全局的JVM有效。这里的权限相关代码如下:
private static boolean hasPermission() { boolean hasPermission = true; SecurityManager sm = System.getSecurityManager(); if (sm != null) { try { sm.checkPermission(new PropertyPermission("user.timezone", "write")); } catch (SecurityException e) { hasPermission = false; } } return hasPermission; }
这里提到了SecurityManager的概念。默认情况下代码中的sm为null,hasPermission()函数的返回值为true,所以setDefault()函数对全局的JVM造成影响,这不是我们希望的。
如果我们希望调用setDefault()函数后得到与JDK1.5的相同的效果,则必须配置Java的SecurityManager,并且权限检查必须抛出异常。为应用设置SecurityManager可以通过设置启动参数实现:-Djava.security.manager <class_name>
如果没有指定一个class,则Java将会使用默认的SecurityManager,这个安全策略是在$JAVA_HOME/jre /lib/security/java.policy
中定义的。经过测试,默认的安全策略不允许我们修改全局范围的默认时区,hasPermission()函数返回false。这样,我们就还可以采用Filter的方式来对不同webapp分时区了。
采用开启默认的SecurityManager,然后进行Filter的方式只对JDK1.5,JDK1.6有效,因为JDK1.7的实现又变了,所以这种方式对JDK1.7将造成不一样的结果。
使用定制的JavaAwtAccess
JDK 1.7中,关于TimeZone类的setDefault方法源码如下:
public static void setDefault(TimeZone zone) { if (hasPermission()) { synchronized (TimeZone.class) { defaultTimeZone = zone; setDefaultInAppContext(null); } } else { setDefaultInAppContext(zone); } }
可以看到,JDK1.7和之前的JDK相比,已经不再采用ThreadLocal的方式来存储时区了。它用了一个AppContext的概念。setDefaultInAppContext()函数的源码如下:
private static void setDefaultInAppContext(TimeZone tz) { JavaAWTAccess javaAWTAccess = SharedSecrets.getJavaAWTAccess(); if (javaAWTAccess == null) { mainAppContextDefault = tz; } else { if (!javaAWTAccess.isDisposed()) { javaAWTAccess.put(TimeZone.class, tz); if (javaAWTAccess.isMainAppContext()) { mainAppContextDefault = null; } } } }
这里用到了javaAWTAccess接口。如果运行时javaAWTAccess为null,那么设置时区就是改变mainAppContextDefault的值;如果运行时javaAWTAccess不为null,那么设置时区就是讲时区方位javaAWTAccess对象中去。进一步观察mainAppContextDefault,发现它是TimeZone类的静态私有变量,也是被全局的JVM共享的,跟设置默认JVM时区没有区别。
那么,就只有一种办法,就是实现一个javaAWTAccess的定制类,并在运行时,让其加载它。但是这种方式只适用于JDK 1.7。
总结
以上三种方案,分别针对JDK1.5,1.6,1.7,并不是通用的解决方案。而Java官方并没有在文档上对setDefault()这个函数做出具体实现上的说明。在这种情况下,针对一种具体实现而开发Java应用,就违背了Java标榜的write once, run anywhere的初衷。所以都是不推荐的。
最稳妥的方案还是:将对时区有不同要求的webapp放在不同的Tomcat实例下运行。
- 如何让Tomcat中的webapp使用不同的时区
- 如何让tomcat bundle liferay使用不同的数据库
- 如何使用PHP和PEAR进行不同时区的转换
- 在tomcat中Spring如何管理不同webapp目录下的应用程序
- 使用JavaScript获取不同时区的时间
- 如何解决不同的webApp的session 共享问题
- 让同一个tomcat中不同的工程的使用不同的rul编码方式
- webapp如何从tomcat的conf目录中加载配置?
- Eclipse下如何建立基于Tomcat的WebApp
- tomcat如何访问非webapp下的资源文件
- tomcat如何访问非webapp下的资源文件
- 使用maven的tomcat插件实现webapp的自动部署
- NSDate不同时区的问题
- 管理不同时区的数据
- eclipse中启动tomcat,网页中打不开tomcat的主页,且项目不在tomcat中的webapp中
- 如何让label.text 中的某些字变成不同的颜色
- 如何让不同的二级域名访问不同的目录
- 如何让不同的HTML代码适应不同的浏览器
- TCP/IP详解笔记(2)
- jdbcTemplate 对数据库的一些操作
- 仿一个美团的效果
- asp.net 使用HttpModule对全站输出的动态页面的HTML内容进行修改,不会错乱
- unity中C#委托的应用
- 如何让Tomcat中的webapp使用不同的时区
- excel2007不显示文件名
- 算法训练 最小乘积(基本型)
- lnmp添加nginx-sticky-module-1.1模块解决负载均衡会话保持问题。
- 跳出NSDate | KeithMorning
- 基本的优化规则
- C编译器剖析_4.2 语义检查_表达式的语义检查(1)
- SQL Server附加数据库提示“版本为661,无法打开,支持655版本……”
- log4j配置