How do I set the timezone in Tomcat for a single w

2019-03-15 01:51发布

问题:

What is the best way to set the time zone in Tomcat for a single web app? I've seen options for changing the command-line parameters or environment variables for Tomcat, but is there a way to set it that is self-contained in the WAR file and not dependent on any Tomcat configuration?

Edit: to reemphasize, I'm looking for a solution that can be contained within a WAR file, not dependent on Tomcat configuration. To put it another way, can one web app be configured to have a different time zone than other apps running in the same Tomcat instance?

回答1:

The only way I found is to setup a filter and change the timezone in the filter,

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);
}

The setDefault() changes the zone for the thread. So everything running in the thread inside the filter will have a different default timezone. We have to change it back because the thread is shared by other apps. You also need to do the same for your init(), destroy() methods and any other thread you might start in your application.

I had to do this because a third-party library assumes default timezone and we don't have source code. It was a mess because this changes log timezone but we don't want log in different times. The correct way to handle this is to use a specific timezone in any time value exposed to end users.



回答2:

EDIT: I was wrong about this. This edit corrects it.

The answer is that you cannot portably set the (default) timezone for a single webapp. But if you are using Java 6 (at least), the java.util.TimeZone class implements the default timezone methods getDefault(), setDefault() and setDefault(TimeZone) using an inheritable thread local. In other words, calling setDefault() only affects the current thread and future child threads.

The behaviour is not documented in the Sun Javadocs. It works for Java 6 and 5 (see above), but there are no guarantees it will work in older or newer Sun JREs. However, I would be very surprised if Sun decided to change/revert to a 'global' model for the default TimeZone. It would break too many existing applications, and besides globals are BAD.



回答3:

Set the system variable to CATALINA_OPTS=-Duser.timezone=America/Denver

You can also specify the CATALINA_OPTS in the $TOMCAT_HOME/bin/catalina.sh or %TOMCAT_HOME%\bin\catalina.bat file as well.

Here's a list of acceptable timezones.

Source



回答4:

In JDK 6, Sun/Oracle has changed the implementation of Timezone. In JDK 5.0 setDefault sets the timezone in a thread local variable always and not across the JVM and that caused a few issues. Sun acknowledged this as a bug and fixed in JDK 1.6.

In JDK 1.6 onwards ( I checked the source code for both JDK 1.6 and JDK 1.7) if the JVM is not started with a security manager ( or it's not set with System.SetsecurityManager()), setDefault method sets it globally across the JVM and not in a thread specific way. If you want to set it only for a given thread then you have to start JVM with a security manager.

When you start Tomcat JVM with a security manager you need to provide individual permissions which was a no starter for us as we were late in the release cycle. Hence in the security policy file we provided all permissions and we overrode the default JAVA security manager to selectively deny the timezone write access. Due to lazy initialization issue with Timezone class I needed to call Timezone.getDefault() in the static block that makes the Timezone class initialized before the securityManager comes into play.

Here is my test program.

--Policy file test.policy

grant {
        permission java.security.AllPermission;

};

-- Custom Security Manager

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 startup Parameters

-Djava.security.manager=CustomSecurityManager -Djava.security.policy=C:/workspace/test/src/test.policy



回答5:

Check out the SimpleTimeZone. You can create an instance based on a time zone ID and use that to display dates/times using that time zone. If you wanted, you could read that ID from a project specific configuration file.



回答6:

The best way is to modify the web application so it accepts explicit time zone configuration through web.xml instead of using the default JVM timezone.



回答7:

http://seamframework.org/Documentation/JSFEnhancementDefaultApplicationTimeZone



回答8:

With Java 7/Tomcat 7, there's a workaround/hack that allows the developer to set a unique timezone per webapp. We had to implement this in our application, as we had to support multiple webapps that run with different default timezones in the same JVM.

Other solutions that I've seen on stack overflow do not completely address the issue.

  • Using Timezone.setDefault() doesn't work, because it changes the timezone across the JVM.
  • Using servlet filters is completely thread unsafe, and does not account for child threads
  • Using SecurityManager approaches also does not account for child thread related timezone issues.

I also investigated the Java source code for TimeZone, I found a way to have a dynamic timezone returned as the default timezone for all callers by injecting a custom implementation of the JavaAWTAccess interface. This can be done by looking at the Thread class loader, and determining the actual webapp context from it, then handling that appropriately based on some webapp name to timezone mapping.

Once again, this is application server specific, and must be done differently for Tomcat, Jetty, Jboss, etc. This approach is also JVM implementation specific (only works on Oracle/Sun), but I believe can be extended to OpenJDK and others.

We have a verified working solution for Oracle JDK 7 SE + Tomcat 7, deployed on both Windows and Linux, hosting multiple webapps in different timezones.



回答9:

You can also use a VM argument to define it

-Djdk.util.TimeZone.allowSetDefault=true