Поговорим немного о знаменитой ошибке ORA-12705. Быстрый гуглёж показывает, насколько много людей сходили с ума, пытаясь решить эту проблему. Баг широко распространен среди новичков, устанавливающих 10g, а именно, при этом невозможно подключиться к свежеустановленной базе Oracle XE. Обычно в этом случае советуют поковырять Windows registry на клиентской машине (точно также советует действовать и сам Oracle). Звучит все это логично, однако в моем случае использовался тонкий Java-клиент, который не использует реестр вообще.
Как же Oracle решил определять NLS_LANG в тонком клиенте? Очень просто — он использует локаль JVM (Locale.getDefault), специальным образом отображая ее в настройку NLS_LANG. Я уверен, что этот подход замечательно работает, если оба — клиент и сервер — используют Latin-1. К сожалению, в Oracle немного забыли про существование других стран и других кодировок. Например, Oracle XE не знает такую страну, как RUSSIA, и соответственно не имеет понятия, какой NLS_LANG назначить клиенту. Как видно при декомпиляции драйвера Oracle, корректной NLS_TERRITORY для локали RU изготовители драйверов Oracle считают CIS, то есть СНГ (!).
Российские пользователи были настолько огорчены, что предлагали самые невероятные решения, вплоть до взлома драйвера. Самое простое решение, впрочем, поменять локаль JVM на английскую. На этом бы мои страдания и закончились. Но не все так просто — несколько тестов в проекте используют соединение с Oracle. Эти тесты запускаются плагином Surefire внутри нашего сборщика Maven.
А теперь как следует подумаем. Для начала, можно поменять настройки JVM используя Locale.setDefault в каждом таком тесте. Однако такой подход небезопасен: изменяется состояние всей JVM, что может повлиять на другие тесты. Кстати, нам очень хочется, чтобы тесты всегда имели возможность работать параллельно. Locale.setDefault также нельзя использовать при инициализации теста (например, в @BeforeClass), потому что порядок запуска тестов не определен заранее. Единственное, что можно сделать — заставить Surefire изначально запускаться с английской локалью.
Последующие 3 часа были потрачены на такие действия:
- Установка системных свойств через конфигурацию плагина Surefire (т.е. внутри pom.xml)
- Борьба с fork-режимами запуска Surefire (как выяснилось, fork-per-test работает, но тормозит просто невероятно).
- Борьба с command-line ключами Maven (если запретить режим fork в Surefire, есть надежда, что подействуют настройки Maven-а)
- Изучение исходного кода Locale.getDefault().
Как думаете, что из этого помогло? Ничего. Магия состоит в следующем:
Locale.getDefault() действительно регулируется системными свойствами JVM. Однако значение локали по-умолчанию устанавливается раз и навсегда при первом_вызове_метода. Любые попытки повлиять на системные свойства JVM ПОСЛЕ первого вызова этого метода будут бесполезны. Точка.
Можно передавать Maven-у сотни ключей -Duser.language, но все эти ключи не будут давать никакого эффекта, поскольку применяются уже после запуска JVM. Единственный способ что-то изменить — это залезть в shell-файл, которым запускается Maven, и отрегулировать параметры JVM там. После этого Maven переключится на английскую локаль, и драйвер Oracle наконец-то найдет нужную страну.