Как установить часовой пояс в Tomcat для одного веб-приложения?

Каков наилучший способ установить часовой пояс в Tomcat для одного веб-приложения? Я видел варианты изменения параметров командной строки или переменных окружения для Tomcat, но есть ли способ установить его автономно в файле WAR и не зависит от какой-либо конфигурации Tomcat?

Изменить: переопределить, я ищу решение, которое может содержаться в WAR файле, не зависящее от конфигурации Tomcat. Другими словами, можно ли настроить одно веб-приложение на другой часовой пояс, чем другие приложения, работающие в одном экземпляре Tomcat?

Ответы

Ответ 1

Единственный способ, которым я нашел, - установить фильтр и изменить часовой пояс в фильтре,

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() и любого другого потока, который вы можете запустить в своем приложении.

Мне пришлось это делать, потому что сторонняя библиотека принимает по умолчанию часовой пояс, и у нас нет исходного кода. Это было беспорядок, потому что это изменяет часовой пояс журнала, но мы не хотим, чтобы он регистрировался в разное время. Правильный способ справиться с этим - использовать определенный часовой пояс в любом временном значении, доступном для конечных пользователей.

Ответ 3

EDIT: Я ошибся. Это редактирование исправляет его.

Ответ заключается в том, что вы не можете переносить часовой пояс (по умолчанию) для одного веб-приложения. Но если вы используете Java 6 (по крайней мере), класс java.util.TimeZone реализует методы часового пояса по умолчанию getDefault(), setDefault() и setDefault(TimeZone), используя наследуемый поток local. Другими словами, вызов setDefault() влияет только на текущий поток и будущие дочерние потоки.

Поведение не описано в Sun Javadocs. Он работает на Java 6 и 5 (см. Выше), но нет никаких гарантий, что он будет работать в более старых или более новых Sun JRE. Тем не менее, я был бы очень удивлен, если бы Sun решила изменить/вернуться к "глобальной" модели для TimeZone по умолчанию. Это сломает слишком много существующих приложений, и, кроме того, глобальные переменные BAD.

Ответ 4

В 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 до запуска 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

Просмотрите SimpleTimeZone. Вы можете создать экземпляр на основе идентификатора часового пояса и использовать его для отображения дат/времени с использованием этого часового пояса. Если вы хотите, вы можете прочитать этот идентификатор из конфигурационного файла проекта.

Ответ 6

Лучший способ - изменить веб-приложение, чтобы он принимал явную настройку часового пояса через web.xml вместо того, чтобы использовать часовой пояс JVM по умолчанию.

Ответ 8

С Java 7/Tomcat 7 существует обходное/хакерское решение, которое позволяет разработчику устанавливать уникальный часовой пояс для каждого веб-клиента. Мы должны были реализовать это в нашем приложении, поскольку нам приходилось поддерживать несколько веб-приложений, которые запускаются с разными часовыми поясами по умолчанию в одной JVM.

Другие решения, которые я видел при переполнении стека, не полностью устраняют проблему.

  • Использование Timezone.setDefault() не работает, поскольку оно изменяет часовой пояс на JVM.
  • Использование сервлет-фильтров полностью небезопасно и не учитывает дочерние потоки.
  • Использование подходов SecurityManager также не учитывает проблемы с часовым поясом, связанные с дочерними потоками.

Я также изучил исходный код Java для TimeZone, я нашел способ вернуть динамический часовой пояс в качестве часового пояса по умолчанию для всех вызывающих абонентов, введя пользовательскую реализацию интерфейса JavaAWTAccess. Это можно сделать, посмотрев загрузчик класса Thread и определив фактический контекст webapp из него, а затем обработав его соответствующим образом на основе некоторого имени webapp для отображения часового пояса.

Еще раз, это специфический сервер приложений, и он должен быть выполнен по-разному для Tomcat, Jetty, Jboss и т.д. Этот подход также специфичен для JVM-реализации (работает только на Oracle/Sun), но я считаю, что он может быть расширен до OpenJDK и другие.

У нас есть проверенное рабочее решение для Oracle JDK 7 SE + Tomcat 7, развернутое как на Windows, так и на Linux, размещающее несколько веб-приложений в разных часовых поясах.

Ответ 9

Вы также можете использовать аргумент VM для его определения

-Djdk.util.TimeZone.allowSetDefault=true