Como defino o fuso horário no Tomcat para um único aplicativo da Web?

Qual é a melhor maneira de definir o fuso horário no Tomcat para um único aplicativo da web? Vi opções para alterar os parâmetros da linha de comandos ou as variables ​​de ambiente para o Tomcat, mas existe uma maneira de defini-lo que seja autocontido no arquivo WAR e não dependente de qualquer configuração do Tomcat?

Editar: para enfatizar novamente, estou procurando uma solução que possa estar contida em um arquivo WAR, não dependente da configuração do Tomcat. Em outras palavras, um aplicativo da Web pode ser configurado para ter um fuso horário diferente do que outros aplicativos em execução na mesma instância do Tomcat?

A única maneira que encontrei é configurar um filtro e alterar o fuso horário no filtro,

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

O setDefault() altera a zona para o segmento. Assim, tudo que está sendo executado no segmento dentro do filtro terá um fuso horário padrão diferente. Nós temos que mudá-lo de volta porque o segmento é compartilhado por outros aplicativos. Você também precisa fazer o mesmo com seus methods init() , destroy() e qualquer outro tópico que você possa iniciar em seu aplicativo.

Eu tive que fazer isso porque uma biblioteca de terceiros assume o fuso horário padrão e não temos código-fonte. Foi uma bagunça porque isso muda o fuso horário do log, mas nós não queremos logar em tempos diferentes. A maneira correta de lidar com isso é usar um fuso horário específico em qualquer valor de tempo exposto aos usuários finais.

Defina a variável do sistema para CATALINA_OPTS=-Duser.timezone=America/Denver

Você também pode especificar o CATALINA_OPTS no arquivo $ TOMCAT_HOME / bin / catalina.sh ou% TOMCAT_HOME% \ bin \ catalina.bat.

Aqui está uma lista de fusos horários aceitáveis.

Fonte

EDIT: Eu estava errado sobre isso. Esta edição corrige isso.

A resposta é que você não pode portably definir o fuso horário (padrão) para um único webapp. Mas se você estiver usando o Java 6 (pelo menos), a class java.util.TimeZone implementará os methods de fuso horário padrão getDefault() , setDefault() e setDefault(TimeZone) usando um local de encadeamento herdável. Em outras palavras, chamar setDefault() afeta apenas o segmento atual e futuros segmentos filho.

O comportamento não está documentado nos Javadocs da Sun. Funciona para Java 6 e 5 (veja acima), mas não há garantias de que funcionará em Sun JREs mais antigos ou mais recentes. No entanto, eu ficaria muito surpreso se a Sun decidisse alterar / reverter para um modelo ‘global’ para o fuso horário padrão. Isso quebraria muitos aplicativos existentes e, além disso, os globais são ruins.

No JDK 6, a Sun / Oracle alterou a implementação do fuso horário. No JDK 5.0 setDefault define o fuso horário em uma variável local de encadeamento sempre e não na JVM e isso causou alguns problemas. A Sun reconheceu isso como um bug e corrigiu o JDK 1.6.

No JDK 1.6 em diante (verifiquei o código-fonte para JDK 1.6 e JDK 1.7) se a JVM não foi iniciada com um gerenciador de segurança (ou não está configurada com System.SetsecurityManager() ), o método setDefault define globalmente na JVM e não de uma maneira específica de thread. Se você quiser configurá-lo apenas para um determinado encadeamento, será necessário iniciar o JVM com um gerenciador de segurança.

Quando você inicia a JVM do Tomcat com um gerenciador de segurança, é necessário fornecer permissions individuais que não são suficientes para nós, pois estávamos atrasados ​​no ciclo de lançamento. Assim, no arquivo de política de segurança, fornecemos todas as permissions e sobrescrevemos o gerenciador de segurança JAVA padrão para negar seletivamente o access de gravação do fuso horário. Devido ao problema de boot lenta com a class Timezone, eu precisava chamar Timezone.getDefault() no bloco estático que faz com que a class Timezone seja inicializada antes que o securityManager entre em jogo.

Aqui está o meu programa de teste.

– teste de arquivo de política.

 grant { permission java.security.AllPermission; }; 

– Gerenciador de segurança personalizado

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

}

– parameters de boot da JVM

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

Confira o SimpleTimeZone . Você pode criar uma instância com base em um ID de fuso horário e usá-la para exibir datas / horas usando esse fuso horário. Se você quisesse, você poderia ler esse ID de um arquivo de configuração específico do projeto.

A melhor maneira é modificar o aplicativo da Web para que ele aceite configuração explícita de fuso horário por meio do web.xml, em vez de usar o fuso horário padrão da JVM.

Com o Java 7 / Tomcat 7, há uma alternativa / hack que permite ao desenvolvedor definir um fuso horário exclusivo por webapp. Tivemos que implementar isso em nosso aplicativo, pois precisávamos dar suporte a vários aplicativos da Web que são executados com fusos horários padrão diferentes na mesma JVM.

Outras soluções que vi no estouro de pilha não resolvem completamente o problema.

  • O uso de Timezone.setDefault () não funciona, porque altera o fuso horário na JVM.
  • A utilização de filtros de servlet é completamente insegura e não considera threads filhas
  • O uso das abordagens do SecurityManager também não leva em conta problemas de fuso horário relacionados ao encadeamento filho.

Eu também investiguei o código-fonte Java para o TimeZone, encontrei uma maneira de ter um fuso horário dynamic retornado como o fuso horário padrão para todos os chamadores, injetando uma implementação customizada da interface JavaAWTAccess. Isso pode ser feito observando o carregador de classs Thread e determinando o contexto real do webapp a partir dele, em seguida, manipulando-o apropriadamente com base em algum nome de aplicativo da Web para o mapeamento de fuso horário.

Mais uma vez, isso é específico do servidor de aplicativos e deve ser feito de forma diferente para Tomcat, Jetty, Jboss, etc. Essa abordagem também é específica da implementação da JVM (só funciona no Oracle / Sun), mas acredito que possa ser estendida para o OpenJDK e outros.

Temos uma solução de trabalho verificada para o Oracle JDK 7 SE + Tomcat 7, implantada no Windows e no Linux, hospedando vários aplicativos da Web em diferentes fusos horários.

Você também pode usar um argumento de VM para defini-lo

 -Djdk.util.TimeZone.allowSetDefault=true