barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)

суббота, 28 апреля 2012 г.

Java: генерация MD5-хэша

Есть не одна библиотека, обладающая таким функционалом, но если проект использует Spring Security, можно воспользоваться md5-кодировщиком Md5PasswordEncoder оттуда:
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.authentication.encoding.PasswordEncoder;

PasswordEncoder encoder = new Md5PasswordEncoder();
String hash= encoder.encodePassword(str, null); 
Вторым параметром encodePassword можно задать salt.

Java: Генерация случайных строк (например, паролей)

Функционал по генерированию случайных строк есть в Apache Commons Lang (http://commons.apache.org/lang/) в классе RandomStringUtils. Он умеет генерировать алфавитные строки заданной длины, буквенно-цифровые и ASCII-строки. Также можно самостоятельно задать набор символов для генерации строки. Полученные строки можно использовать в качестве, например, паролей (не знаю правда, насколько это безопасно).

PostgreSQL: поля serial и "ERROR: duplicate key value" при вставке

Проблема:

При вставке строки в таблицу (пусть она называется "mytable"), использующую поле типа "serial" (пусть оно называется "id", а соответствующий ему ключ - "id_pkey") в качестве первичного ключа, появляется ошибка:
ERROR:  duplicate key value violates unique constraint "id_pkey"
, хотя в запросе явно это поле не задается и вроде бы должно быть автоматически взято из соответствующей последовательности (пусть она называется "id_sequence").

Причина:

Последовательность, используемая для генерации поля "id_pkey", почему-то выдала значение, которое уже есть в таблице. Такое может случиться, например, если в какой-то строке таблице значение "id_pkey" задавалось / редактировалось вручную, в обход последовательности. 

Решение:

Можно просто попробовать повторить вставку на случай, если следующее значение последовательности не приведет к ошибке. Или же восстановить последовательность:
select setval(' id_sequence', (select max( id ) + 1 from  mytable));
Другой вариант - выполнить:
REINDEX INDEX  id_pkey; 
Спасибо http://vasilec.blogspot.com/2010/04/duplicate-key-value-violates-unique.html за подсказку. 

пятница, 27 апреля 2012 г.

Spring Security за AJP proxy

Проблема:

Приложение, использующее Spring Security, находится за AJP-прокси (Apache2 mod_proxy_ajp), которое меняет контекст приложения:
ProxyPass / ajp://localhost:8009/Context/
ProxyPassReverse / ajp://localhost:8009/Context/
Редиректы, генерируемые Spring Security, привязаны к контексту веб-приложения, что вроде бы должно исправляться директивой ProxyPassReverse, однако не работает для  AJP-проксирования (если использовать http, то Apache2 корректно модифицирует контекст при перенаправлениях).

Решение:

Вариантов, в принципе, 3:
  1. Отказаться от AJP-проксирования в пользу HTTP
  2. Разворачивать веб-приложение в корне backend-сервера
  3. Использовать mod_rewrite для корректировки контекста на стороне прокси-сервера:
RewriteEngine on
RewriteRule ^/Context/(.*)$ /$1 [R=301]
Спасибо http://stackoverflow.com/questions/6063339/spring-security-and-ajp-proxy

java.net.URLEncoder.encode и пробелы

java.net.URLEncoder.encode кодирует строку в соответствии со стандартом HTML 4.01, который предписывает заменять символ пробела на "+", а не на "%20", как предписывает RFC3986

среда, 25 апреля 2012 г.

PostgreSQL: просмотр значений пользовательского ENUM-типа

Выполняется запросом:
select enum_range(null::my_enum_type)
, где my_enum_type - имя типа, созданного ранее запросом вроде:
CREATE TYPE  my_enum_type  AS ENUM ('sad', 'ok', 'happy');

Использование карт в конфигурации Spring Security

Задача:

Одному из бинов, используемых в xml-файле конфигурации Spring Security, необходимо установить свойство, передав качестве значения карту (Map).

Решение:

Инициализация карты и её установка в качестве свойства бина делается так:

<beans:bean id="myBeanId" class="my.bean.Class" >
<beans:property name="myMapProperty">
 <beans:map>
<beans:entry key="key1" value="val1"/>
<beans:entry key="key2" value="val2"/>
 </beans:map>
</beans:property>
</beans:bean>
(здесь xmlns:beans="http://www.springframework.org/schema/beans)

Псевдоэлемент :after и теги input / select

Как это ни печально, большинство браузеров не поддерживают псевдоэлемент :after для заменяемых элементов страницы (изображений и полей ввода). Из всех опробованных браузеров  (Firefox 11, Opera 11.60, IE 8, Chrome 20) только Chrome отобразил контент после тега <input>. После <select> не отобразил контент ни один браузер.
Такое поведение браузеров вроде бы не противоречит спецификации W3C, т.к. :after добавляет содержимое во внутреннее дерево элемента, которое у заменяемых элементов страницы (и вообще любых элементов не-контейнеров) отсутствует.

JSTL: итерация по Map с помощью c:forEach

Итерация по содержимому некоторой карты с помощью c:forEach осуществляется следующим образом:
<c:forEach items="${myBean.someMap}" var="elem">
  <option value="<c:out value="${elem.key}"/>"><c:out value="${elem.value}"/></option>
</c:forEach>    
Этот пример создаст опции select`а на основании карты, возвращаемой вызовом myBean.getSomeMap() таким образом, чтобы ключи карты стали значениями опций, а соответствующие этим ключам значения карты - текстом опций.

пятница, 20 апреля 2012 г.

Обработка атрибута title тега

Атрибут title тега <option> иногда удобно использовать, когда какой-то пункт слишком длинный, и чтобы не растягивать ширину select`а, можно обрезать текст в option`е, а в атрибут title поместить текст целиком, чтобы он появлялся при наведении на пункт.
Вот только, к сожалению, далеко не все браузеры на этот атрибут обращают внимание. Провел небольшое исследование, и результаты следующие:
  1. Firefox 11.0 - нет
  2. Firefox 3.6.24 - да / нет. Тут возникла странность, заключающаяся в том, что на Portable-версии под Win7 не рабоает, в то время как на обычной под WinXP все ок.
  3. Internet Explorer 8 - да
  4. Opera 11.60 - нет
  5. Chrome 20 - да

Валидация полей ввода, использующих jQuery.UI-виджет autocomplete

Задача:

При валидации формы с использованием плагина jquery.validation проверить, что в полях, использующих виджет автодополнения jQuery UI (autocomplete) введено значение, входящее в их список автодополнения (в качестве источника автодополнения используется статичный массив).

Решение:

Добавляем свой метод валидации и привязываем его к классу "ui-autocomplete-input", который устанавливается autocomplete-виджетом на полях ввода, к которым он привязан:
jQuery.validator.addMethod("jqueryUiAutocomplete",function(value, element){
if(this.optional(element))
 return true;
return $(element).autocomplete("option","source").indexOf(value)!=-1;
},"Пожалуйста, выберите значение из списка");
jQuery.validator.addClassRules("ui-autocomplete-input",{jqueryUiAutocomplete:true}); 
Необходимо учесть деталь, что метод Array.indexOf есть не во всех браузерах (как это поправить, я уже когда-то давно писал тут). Если используется другой вариант источника данных для автозаполнения (функция / AJAX), такой вариант не подходит.

четверг, 19 апреля 2012 г.

Валидация ip-v4 адресов на jquery-input-ip-address-control и jquery.validation

1) Подключаем нужные скрипты (качаются тут и тут):
<script type="text/javascript" src="js/jquery.validate.min.js"></script>
<script type="text/javascript" src="js/jquery.input-ip-address-control-1.0.min.js"></script>   
2) Добавляем скрипт:
$(document).ready(function(){
    jQuery.validator.addMethod("ipv4",function(value, element)
{
return this.optional(element) || value!="___.___.___.___";
});
  jQuery.validator.addClassRules("ip-address-input",{ipv4:true});                                  
    $("#myFormId").validate(
{
errorPlacement: function(error, element){} // no error messages        
});
    $('input.ip-address-input').ipAddress({v:4});  
  }); 
Этот скрипт включит валидацию для формы #myFormId (про неё я уже писал тут), а также обеспечивает все поля ввода с классом "ip-address-input" маской для ввода ip-v4 адреса. В случае, если у такого поля ввода установлен также класс "required", форма не отправится, если в поле не введен корректный ip-v4 адрес (в функции-валидаторе значение проверяется только на неравенство "___.___.___.___", т.к.  jquery-input-ip-address-control не даст ввести в поле некорректный ip-v4) . Пример такого поля ввода:
<input type="text" class="ip-address-input required" name="ip_1" />
Также, чтобы незаполненные поля подсвечивались красной рамкой, нужно добавить css:
.error
{
  border-color: red;
}

Проблема с бьющейся кодировкой имен загружаемых на сервер файлов при использовании org.apache.commons.fileupload

Проблема:

При обработке загружаемых на сервер файлов с помощью org.apache.commons.fileupload бьется кодировка имен файлов, получаемых вызовом FileItem.getName(). Заголовки запроса кодируются UTF8.

Причина:

Кодировка, используемая org.apache.commons.fileupload при чтении заголовков запроса - это не UTF8, а, по-видимому, системная кодировка (в случае Windows - cp1251). Т.к. имена файлов передаются именно через заголовки (а именно, "Content-Disposition"), то неверное определение кодировки заголовка приводит к ломающейся кодировке имен файлов.

Решение:

Задать явно кодировку ServletFileUpload объекта вызовом метода setHeaderEncoding() перед парсингом запроса методом parseRequest():
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF8");

среда, 18 апреля 2012 г.

Потокобезопасная реализация org.apache.http.client.AuthCache

Базовая реализация AuthCache, входящая в библиотеку HttpClient - org.apache.http.impl.client.BasicAuthCache, не является потокобезопасной, о чем свидетельствует соответствующая аннотация @NotThreadSafe. ThreadSafe реализации я пока не нашел (если она вообще существует), что достаточно печально т.к. потоконебезопасность не позволяет разделять кэш в многопоточной среде (например, среде веб-приложения).

Простейшая валидация обязательных полей формы с jQuery-плагином Validation

Задача:

Не дать пользователю отправить формы с незаполненными некоторыми полями, подсветить незаполненные поля красной рамкой при попытке отправки.

Решение:

1) Подключаем плагин (скачать можно тут):
<script type="text/javascript" src="js/jquery.validate.min.js"></script> 
2) Устанавливаем класс "required" для тех полей формы, которые мы хотим сделать обязательными:
<input type="text" name="ip" size="30" class="required"/>
3) Добавляем JS-код:


$(document).ready(function(){
    $("#myFormId").validate(
{
errorPlacement: function(error, element){} // no error messages                                  

});
  });
Здесь предполагается, что валидируемая форма имеет идентификатор "myFormId". Пустая функция  errorPlacement позволит не показывать не нужные текстовые сообщения о незаполненности поля (т.к. нам достаточно только подсветки).
4) Делаем собственно подсветку незаполненных полей, добавляя CSS:
.error
{
  border-color: red;
}

Bash: подсчет числа коннектов на определенный ip:port

netstat -n | grep 192.168.1.1:777 | wc -l
выведет число коннектов на ip 192.168 .1.1, порт 777

вторник, 17 апреля 2012 г.

JSON POST запросы к Redmine REST API

Проблема:

При выполнении POST-запроса в JSON-формате к Redmine REST API сервер ведет себя так, как будто не воспринимает тело запроса. Например, при попытке добавить Issue возвращается ошибка 404, а в логах Redmine видно:
Processing IssuesController#create to json (for 192.168.10.156 at 2012-04-17 13:59:05) [POST]
  Parameters: {"format"=>"json", "action"=>"create", "controller"=>"issues"}
Filter chain halted as [:find_project] rendered_or_redirected.
Completed in 47ms (View: 0, DB: 10) | 404 Not Found [http://redmine-tp/issues.json]
, хотя передаваемый идентификатор проекта 100% верен (существует в таблице "projects" в БД Redmine). При этом в заголовке Content-Type запроса указывается вроде бы корректное значение "text/json"; при попытке указать неверное "text/xml" возвращается вообще 500 ошибка, а в логи валится:

/!\ FAILSAFE /!\  Tue Apr 17 14:13:38 +0400 2012
  Status: 500 Internal Server Error
  undefined method `name' for nil:NilClass
    /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/xml_mini/rexml.rb:29:in `merge_element!'
    /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/xml_mini/rexml.rb:18:in `parse'
    /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/xml_mini.rb:12:in `__send__'
...
    /usr/lib/ruby/1.8/phusion_passenger/abstract_server.rb:196:in `start_synchronously'
    /usr/lib/phusion_passenger/passenger-spawn-server:61

Причина:

Неверно установлен заголовок Content-Type запроса. Вместо "text/json" необходимо использовать "application/json".

понедельник, 16 апреля 2012 г.

Orbeon: установка значения переменной в сессии по загрузке формы

Задача:

Установить некоторое значение в сессии по окончании загрузки Orbeon-формы.

Решение:

Внутри <xforms:model id="fr-form-model"> добавить следующий тег:

<xforms:action ev:event="xforms-ready">
<xforms:insert context="." origin="xxforms:set-session-attribute('sessionAttrName', 'sessionAttrValue')"/>
</xforms:action>

Установка rmagick-гема в Debian Squeeze

Если при попытке выполнить:
sudo gem install rmagick
вываливаются ошибки, что невозможно найти какой-то конфигурационный файл, необходимо убедиться, что установлены необходимые пакеты:
apt-get install imagemagick libmagickwand-dev

Установка pg-гема в Debian Squeeze

Если при попытке выполнить:
sudo gem install pg
вываливаются ошибки, что, например, невозможно найти pg_config или что-то вроде этого, необходимо убедиться, что установлены необходимые пакеты:
sudo apt-get install postgresql-client libpq5 libpq-dev

Postgres: узнать версию СУБД SQL-запросом

SELECT version();

суббота, 14 апреля 2012 г.

Яркость экрана в Kubuntu 11.10 на Lenovo B460e

Открываем /etc/default/grub, добавляем в GRUB_CMD_LINUX_DEFAULT "acpi_osi=Linux acpi_backlight=vendor" получится нечто вроде:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash acpi_osi=Linux acpi_backlight=vendor"
Обновляем GRUB:
sudo update-grub
Перезагружаемся, регулировка яркости комбинациями Fn+Стрелки вверх/вниз должна заработать.

Linux и WiFi на Lenovo B460e

Пробовал ставить на Lenovo B460e и Ubuntu 11.10, и последний OpenSUSE, нигде из коробки WiFi не работал. Точнее так: карточка вроде бы видится и в iwconfig, и в NetworkManager`е, вот только подключиться к сети не получается. В NetworkManager`е беспроводная сеть неактивна, при попытке включить её она на 1-2 сек вроде включается, а потом снова отключается. В опенсусе я попробовал снести NetworkManager, и сеть вроде бы поднялась, но после некоторых манипуляций я случайно убил сусь, и решил таки поставить Kubuntu 11.10 и заставить вафлю работать в ней.
В итоге последовательность действий следующая:
1) Удаляю NetworkManager (через apt или muon)
2) Перезагружаюсь. Сеть при этом пропадет, и проводная, и беспроводная. Подключаю кабель с интернетом к Ethernet, поднимаю сеть:
ifconfig etho0 uo
dhclient eth0
3) Устанавливаю пакет wicd-kde
4) Запускаю wicd через Приложения -> Интернет -> Network Manager
5) Вуаля, сети видятся, можно подключаться =)

пятница, 13 апреля 2012 г.

Скробблинг на last.fm с MTP-девайсов в Linux

Скробблить всё, что слушаешь, на ласт - достаточно навязчивая привычка. Т.к. львиную долю музыки я слушал на плеере (сейчас правда перешел на андроидофон), возникло желание как-то вытягивать с него проигранные треки и скробблить на ласт. Встал  вопрос - каким же образом?
Плеер мой - старичок Creative Zen V, никакой операционки в нём и в помине нет, поэтому написанием приложения под неё не отделаться. Писать свою прошивку тоже желания не было =) Надежду во мне посеял тот факт, что плеер сохраняет для каждого трека число его проигрываний, которое можно посмотреть, зайдя в свойства трека в интерфейсе плеера. Осталось теперь научиться получать это число с ПК, и желательно, уметь ещё и обнулять его после скробблинга. Потеря времени проигрывания терка для меня лично была некритична - я готов был скробблить все загруженные с девайса треки "пачкой", начиная с некоторого заданного момента времени.
Т.к. ПК видит мой плеер как MTP-устройство, я сразу решил посмотреть, можно ли по MTP видеть число воспроизведений. Оказалось можно - это умеет даже проигрыватель Amarok. Раз умеет амарок - значит научусь и я, даже если придется покопаться в сорцах амарока. Копаться правда не пришлось - то ли в зависимостях, то ли где-то ещё, я увидел, что для работы с MTP-устаройствами он использует библиотеку libmtp, обладающую, как оказалось, отличной документацией с достаточным количеством примеров.  Так что встала задача разработки приложения, извлекающего проигрывания в некоторую БД, из которой потом уже можно сробблить. 
В итоге получилась консольная программка, извлекающая все треки с девайса и сохраняющая их в БД sqlite, после чего сбрасывающая счетчики проигрываний в плеере. По каким-то причинам, правда, я позже решил отказаться от sqlite, и стал вместо вставки данных в БД формировать SQL-дамп, который можно потом загрузить куда угодно (точнее в ту СУБД, которая съест используемый синтаксис).
Теперь дело встало за малым - извлечь инфу из БД, и заскробблить на ласт. Тут я решил поискать опенсорсный скробблер, т.к. копать REST-API ласта не хотелось. Вариант нашелся, имя ему - lastfmsubmitd, его можно поставить из репов Debian / Ubuntu. В его комплект, помимо демона-скробблера, входит вспомогательный скрипт-скробблер, принимающий параметры скроблимого трека через аргументы командной строки. Задача формирования последовательности вызовов этого скробблера (в виде sh-скрипта) на основании инфы из базы легла на php-скрипт, который написался достаточно быстро. 
В итоге последовательность действий для сробблинга получилась такая:
1) Подключаю девайс, запускаю свою C++-программку, формирую SQL дамп
2) Гружу дамп в MySQL
3) Правлю в php-скрипте время начала скробблинга, запускаю его, получаю на выходе sh-скрипт.
4) Запускаю sh, после того, как он отработает, запускаю демон скробблера lastfmsubmitd. И вуаля - треки улетают на ласт.
Сложновато конечно вышло =) Но работает, и с учетом того, что повторял я эти действия не очень часто - пару раз в неделю, меня это не особо напрягало. Ну а потом я разжился андридофоном (вернее, купил уже 4ый по счету андроидофон, на котором качество проигрывания музыки меня наконец-то устроило), и необходимость скробблить с плеера отпала - сробблеров под андроид полным полно.
Сорцы своих "творений" я выложил на github - вдруг кому будет интересно: https://github.com/barbitoff/mtpScrobblingUtils.

Скробблинг на lastfm из Qmmp

Достаточно нетривиальная реализация получилась у разработчиков qmmp. Включается сроббленг на ласт следующий образом: Настройки -> Модули -> Ставим галочку "Модуль scrobbler", после чего нажимаем кнопку "Настройки" под списком модулей при выделенном модуле скробблинга, ставим галочку "Last.fm", в поле "Сессия" вводим, как ни странно, свой логин на ласте, ставим галочку "Зарегистрировать новую сессию", жмём ок. Должен открыться браузер с запросом на доступ приложению qmmp к ласту. Разрешаем, перезапускаем qmmp. Должно заработать (в настройках модуля должен появиться идентификатор сессии вместо ранее введенного логина). Если не появился - повторяем вышеуказанные действия, пока не появится (у меня как-то не с первого раза заработало).

Сканирование изображений в KDE

В Gnome для сканирования есть минималистичный simple-scan, для кед есть нечто аналогичное - scanlite. Вполне юзабельно: можно устанавливать dpi, цветность, даже сразу инвертировать цвета при сканировании. Есть выбор сканируемой обасти, предпросмотр, разные форматы для сохранения.


Настройка прокси с авторизацией в build-файлах ant

Настройка прокси с авторизацией в build-файлах ant осуществляется с помощью задания setproxy:
<setproxy proxyhost="${proxy.host}" proxyport="${proxy.port}"
      proxyuser="${proxy.user}" proxypassword="${proxy.pass}"/>

Tomcat 6 на Debian Squeeze / Mint 13

Особенности томката 6, поставленного из репозиториев Debian Squeeze / Mint 13:
  1. CATALINA_HOME: /usr/share/tomcat6 - директории /lib и /bin
  2. CATALINA_BASE: /var/lib/tomcat6 - директории common, conf, logs, server, shared, webapps, work. При этом часть из них - симлинки:



  3. В /etc/tomcat6/server.xml нет UTF-8 для коннектора AJP, поэтому необходимо его добавить, если используется проксирование по AJP и русские символы в параметрах запросов (по-умолчанию используется ISO-8859-1):
  4.     <!-- Define an AJP 1.3 Connector on port 8009 -->
        <Connector port="7781" protocol="AJP/1.3" redirectPort="8443"
                   URIEncoding="UTF-8" />
  5. Может показаться, что параметры JVM можно задать в /etc/init.d/tomcat6 (в строке JAVA_OPTS = ...), однако это не так, т.к. заданные в этой строке опции затираются ниже по скрипту значениями из файла умолчаний:


    Файл умолчаний расположен в /etc/default/tomcat6 и задает опции:
    JAVA_OPTS="-Djava.awt.headless=true -Xmx128m -XX:+UseConcMarkSweepGC"
  6. Поэтому опции JVM нужно менять либо в нём, либо в /etc/init.d/tomcat6, уже после загрузки опций из файла умолчаний.

Debian: Быстрая настройка pure-ftpd

pure-ftpd - один из многих ftp-серверов под Linux, но как-то так сложилось, что я использую именно его. Приведу небольшой пример его послеустановочной настройки в Debian.
Во-первых, идем в /etc/pure-ftpd/conf/. В некоторых системах все конфигурационные директивы собраны в один файл (типа /usr/local/etc/pure-ftpd.conf), в Debian же каждой конфигурационной директиве соответствует один файл в /etc/pure-ftpd/conf/, где имя файла совпадает с именем директивы (имя регистро-зависимо!), а значение директивы определяется содержимым этого файла:


По-моему не самое лучшее решение, но придется с этим мириться. 
По-умолчанию установлены настройки MinUID=1000 (что уже не даст зайти под системными пользователями, в т.ч. под root), NoAnonymous=yes (отключает анонимных клиентов), и включена PAM-аутентификация. Меня устраивает и UNIX-аутентификация, поэтому я задал в файле PAMAuthentication значение 'no', а в UnixAuthentication - 'yes'. Кстати, порядок использования средств аутентификации задается в /etc/pure-ftpd/auth символическими ссылками на соответствующие конфигурационные файлы в /etc/pure-ftpd/conf/:


Единственная опция, которую я решил добавить, это ChrootEveryone, чтобы пользователи блокировались в своей домашней директории. Для этого создаем файл ChrootEveryone, открываем его и записываем в него 'yes' (без кавычек).

среда, 11 апреля 2012 г.

c3p0: com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector -- APPARENT DEADLOCK!!!

Проблема:

При нагрузочном тестировании веб-приложения, использующего пул соединений c3p0, при высокой нагрузке (порядка 170 запросов в секунду) иногда в логи валится исключение следующего вида:
com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector -- APPARENT DEADLOCK!!!
приводящие, в конечном итоге, к SQLExceptio`у в приложении.
Конфигурация c3p0 следующая:
<Resource name="jdbc/myDb" auth="Container"
  type="com.mchange.v2.c3p0.ComboPooledDataSource"
  factory="org.apache.naming.factory.BeanFactory"
  user="xxx" password="yyy" driverClass="oracle.jdbc.driver.OracleDriver"
  jdbcUrl="jdbc:oracle:thin:@//myoraserver:1521/zzz"
  maxPoolSize="50"
  maxIdleTime="7200"
  minPoolSize="5"
  testConnectionOnCheckout="true"
  idleConnectionTestPeriod="300"
  maxIdleTimeExcessConnections="120"/>  

Решение:

Здесь в одном из постов предполагается, что такая проблема вызвана одновременным закрытием закэшированного PreparedStatement и используемого им соединения (как такое может случится, я не понял, но пост писал человек, видимо, видевший исходники c3p0, так что я поверил ему на слово), и рекомендуется два выхода:
  1. Запретить кэширование  PreparedStatement`ов вообще установкой maxStatements=0
  2. Установить maxStatements в 0, а maxStatementsPerConnection равным (или чуть большим) чем число различных  PreparedStatement`ов, используемых приложением, чтобы уменьшить вероятность возникновения данной ситуации. 
Почему 2 вариант поможет, я не совсем понял, но попробовал его у себя, т.к. помимо борьбы с вышеуказанной проблемой, такая настройка позволяет повысить эффективность кэширования (негативно, правда, сказываясь на использовании памяти) за счет того, что все используемые приложением  PreparedStatement `ы будут браться из кэша. Не помогло - исключения всё равно валятся, так что пришлось прибегнуть к первому варианту. Но и он оказался безуспешным - валились те же исключения.
После долгих поисков был найден следующий вариант решения этой проблемы: если установить numHelperThreads равным maxPoolSize, APPARENT DEADLOCK`и проходят. Не самый красивый вариант в плане требовательности к ресурсам, но работает.
Второй вариант, на котором я, пожалуй, и остановлюсь - это установка maxAdministrativeTaskTime в некоторое достаточно большое значение, за которое предполагается, что любой запрос к БД выполнится (например, 600 сек). Тогда пока это время не истечет, c3p0 не будет расценивать ситуацию как deadlock. Документация утверждает, что установка этого значения в 0 приводит к тому, что "зависший" поток никогда не будет прерван, однако реально это нет так: c3p0 достаточно быстро ругается на deadlock, а через 60 секунд вызывает interrupt() потока.

c3p0: "WARNING: A C3P0Registry mbean is already registered" при переразвертывании веб-приложения на Tomcat

Проблема:
При повторном развертывании веб-приложения, использующего пул JDBC-соединений c3p0 как JNDI DataSource в консоль вываливается сообщение:
WARNING: A C3P0Registry mbean is already registered. This probably means that an application using c3p0 was undeployed, but not all PooledDataSources were closed prior to undeployment. This may lead to resource leaks over time. Please take care to close all PooledDataSources.
Решение:
PooledDataSource необходимо закрывать перед отменой развертывания приложения. Для этого в ServletContextListener`е в методе contextDestroyed() нужно выполнить следующий код:
import com.mchange.v2.c3p0.DataSources;
import com.mchange.v2.c3p0.PooledDataSource;

try
  {
  Context initContext = new InitialContext();
  DataSource ds = (DataSource)initContext.lookup("java:/comp/env/jdbc/myDataSource");
  if(ds instanceof PooledDataSource)
{
((PooledDataSource)ds).close();
DataSources.destroy(ds);
}
  }
catch(NamingException ex)
  {
  log.log(Level.SEVERE, "Got NamingException while cleaning up DataSource", ex);
  }
catch(SQLException ex)
  {
  log.log(Level.SEVERE, "Got NamingException while cleaning up DataSource with com.mchange.v2.c3p0.DataSources.destroy()", ex);
  } 

Конфигурация пула JDBC-соединений c3p0 как JDNI DataSource в Tomcat

Ниже приведен пример минимальной конфигурации JNDI DataSource пула соединений c3p0, размещаемой внутри тега <Context> в context.xml веб-приложения Tomcat (на примере соединения с БД Oracle):
<Resource name="jdbc/myDb" auth="Container"
  type="com.mchange.v2.c3p0.ComboPooledDataSource"
  factory="org.apache.naming.factory.BeanFactory"
  user="xxx" password="yyy" driverClass="oracle.jdbc.driver.OracleDriver"
  jdbcUrl="jdbc:oracle:thin:@//myoraserv:1521/zzz"/>    

Пример арифметики дат / времени в Postgres

Следующий запрос вернет момент времени, который был месяц назад:
SELECT CAST('now' AS timestamp) - CAST('1 month' as interval)

вторник, 10 апреля 2012 г.

Java: Валидация цепочки сертификатов с использованием расширения CRL Distribution Point с возможностью кэширования CRL

Java PKI API, а точнее, его реализация под названием PKIX, не имеет функционала по работе с расширением CRL Distribution Point (далее - CRL DP). Возможность проверки сертификатов по CRL есть, включается она следующим кодом:
builderParams.setRevocationEnabled(true);
, где builderParams - объект параметров построителя цепочки сертификатов PKIXBuilderParameters. Однако CertPathBuilder (или CertPathValidator) извлекают при этом эти самые CRL не по URL`у, указанному в CRL DP сертификата, а из т.н. CertStore.
Работа с CRL DP осуществляется уже на уровне реализации PKIX. Например, для Sun-овской реализации использование CRL DP при получении CRL включается следующим образом:
System.setProperty("com.sun.security.enableCRLDP", "true");    
Для реализации IBM устанавливаемое свойство имеет другое имя. 
Не знаю как в реализации от IBM, но в таковой от Sun я не нашел возможности как-либо управлять кэшированием CRL. Без кэширования использование проверки цепочки сертификатов в высоконагруженном веб-приложении практически невозможно: каждый запрос к приложению приводит к запросу CRL с Distribution Point (а если в цепочке сертификатов между проверяемым сертификатом и доверенным несколько сертификатов - то к нескольким CRL), что крайне негативно сказывается на производительности. Более того, каждый раз загружать CRL попросту не нужно (уж по крайней мере по несколько раз в минуту - точно).
Для решения этой проблемы можно воспользоваться открытой библиотекой jTrust: http://code.google.com/p/jtrust/. У неё тоже есть несколько минусов: во-первых, она жестко завязана на криптопровайдер Bouncy Castle, что особенно неудобно, когда в приложении уже используется другой криптопровайдер. Во-вторых, спроектирована библиотека таким образом, что предполагается её использование в качестве полной замены стандартному Java PKI API, а не как дополнение к нему, поэтому просто взять оттуда функционал по кэшированию CRL не получится. К тому же, реализация кэширования в ней имхо не вполне адекватная: для хранения CRL в кэше в памяти используются SoftReference, что приводит к очистке кэша сборщиком мусора, что, как минимум, не является ожидаемым поведением для кэша. В дополнение к этому, время жизни кэша отсчитывается почему-то не от момента загрузки CRL, а от момента времени, указанного в thisUpdate в самом CRL. Да ещё и устанавливать время жизни кэша можно только в часах. 
Подумав надо всем этим, я написал свою реализацию кэширующего репозитория для jTrust, код чуть позже выложу на github. А интегрировал в свое приложение библиотеку jTrust я следующим образом: строится и валидируется цепочка сертификатов средствами PKIX, а, точнее, его sun`овской реализации (неявно, т.к. эта реализация, как я понял, используется томкатом). При этом флаг revocationEnabled установлен в false, чтобы выполнялись все проверки, за исключением CRL. После успешного построения и валидации цепочки она дополняется trusted-сертификатом в конце (т.к. PKIX формирует цепочку, завершающуюся сертификатом, выданным доверенным субъектом, а jTrust оперирует цепочками, оканчивающимися непосредственно сертификатом самого доверенного субъекта), и используется CrlTrustLinker для проверки валидности каждой связи в цепочке (а в качестве репозитория CRL этот линкер использует мою самописную имплементацию интерфейса CrlRepository).

Пул соединений с БД от Orcale

Oracle вместе со своим JDBC-драйвером поставляет также и пул соединений, поэтому я решил попробовать использовать его вместо стандартного томкатовского commons-dbcp. Его подключение несколько отличается от подключения dbcp и выглядит примерно следующим образом (показан конфиг context.xml веб-приложения):
<Context antiJARLocking="true" path="/myapp">
  <Resource name="jdbc/myOraDb" auth="Container" type="oracle.jdbc.pool.OracleConnectionPoolDataSource"
               driverClassName="oracle.jdbc.driver.OracleDriver"
               factory="oracle.jdbc.pool.OracleDataSourceFactory"
               maxActive="100" maxIdle="30" maxWait="10000"
               user="xxx" password="yyy"
               url="jdbc:oracle:thin:@//myoraserver:1521/xxx"/>              
</Context>
Вот только нагрузочное тестирование показывает, что он почему-то примерно на 40% медленнее DBCP. Возможно из-за неоптимальной конфигурации, пока не было времени с этим разобраться.

Относительная производительность проверки ЭЦП ГОСТ 3410 в КриптоПРО JCP и Bouncy Castle

Особенно глубоко эту тему не исследовал, но данные профилировщика Netbeans говорят о примерно одинаковой производительности криптопровайдеров КриптоПРО JCP и Bouncy Castle при проверке ЭЦП, использующей алгоритм ГОСТ 3410.

понедельник, 9 апреля 2012 г.

Использование криптопровайдеров в веб-приложениях на Tomcat и размещение библиотек криптопровайдера

Столкнувшись с необходимостью использовать в веб-приложении криптопровайдеры (КриптоПРО JCP и Bouncy Castle), я пришел к следующему выводу относительно размещения библиотек криптопровайдера: их не стоит размещать в war-файле веб-приложения, т.к. при переразвертывании приложения без перезапуска веб-сервера криптопровайдер работать не будет. В случае с JCP библиотеки вообще нужно размещать в lib/ext JRE, которое используется Томкатом (JCP туда собственно сам устаналивается скриптом установки), а для Bouncy Castle подойдет размещение библиотеки в /lib томката.

Tomcat: настройка логирования в масштабе веб-приложения

Можно, конечно, настраивать логирование и программно, устанавливая обработчики, задавая их уровни и прочую конфигурационную информацию внутри программного кода, но Tomcat позволяет сделать это проще, в одном конфигурационном файле, настройки которого отразятся на всём веб-приложении в целом. Для этого необходимо создать файл logging.properties в WEB-INF/classes приложения (или, при сборке приложения NetBeans`ом, в "Исходный файлах"), где и настроить логирование. Подробнее о настройке можно почитать в доках по томкату (http://tomcat.apache.org/tomcat-6.0-doc/logging), приведу лишь пример настройки:
handlers = org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
org.apache.juli.FileHandler.level = WARNING
org.apache.juli.FileHandler.directory = ${catalina.base}/logs
org.apache.juli.FileHandler.prefix = mywebappname.
org.apache.catalina.level=WARNING
java.util.logging.ConsoleHandler.level = WARNING
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

Замена пулу commons-dbcp в Tomcat 7

В Tomcat 7 на смену пулу JDBC-соединений commons-dbcp пришел tomcat-jdbc-pool, который вроде бы избавлен от недостатков commons-dbcp. Скачать новый пул можно отсюда: http://people.apache.org/~fhanik/jdbc-pool/. Использовать новый пул можно и на Tomcat 6, положив скачанный jar-ник в /lib томката и использовав следующую конфигурацию JNDI DataSource:

<Resource name="jdbc/myDb" auth="Container" type="javax.sql.DataSource"
  factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
  maxActive="200" maxIdle="30" maxWait="10000"
  username="xxx" password="yyy" driverClassName="oracle.jdbc.driver.OracleDriver"
  url="jdbc:oracle:thin:@//myora:1521/zzz"/>  

ЗЫ По некоторым данным, по производительности tomcat-jdbc-pool выигрывает не только у старичка DBCP, но и у c3po. По результатам моего же тестирования с конфигурацией, указанной выше, результаты примерно следующие (при нагрузке 5 запросов / сек; абсолютные значения не имеют значения т.к. приложение много чего ещё делает помимо взаимодействия с БД, важна разница между значениями):
  • commons-dbcp : 85-90 мс
  • tomcat-jdbc-pool: 110-115 мс
  • c3p0: 105-110 мс
  • oracle.jdbc.pool.OracleConnectionPoolDataSource: 115-120 мс
ЗЫЗЫ после небольшого докручивания c3p0 показал результат, аналогичный commons-dbcp.

PostgreSQL: просмотр активных процессов

Для того, чтобы просмотреть, какие процессы СУБД на данный момент активны (включая информацию о выполняемом запросе, используемой БД, имени пользователя и пр.), нужно выполнить:
SELECT * FROM pg_stat_activity;

воскресенье, 8 апреля 2012 г.

GUI для GIT под Linux

Под Win я пользуюсь Tortoise (во много, наверное, из-за предшествующего опыта работы с Tortoise SVN), под Linux же из всех вариантов мне больше всего приглянулся SmartGit (он написан на Java, так что под Win им тоже можно пользоваться).

Добавление переменных окружения в Linux

1) Создаем исполняемый файл в /etc/profile.d:
sudo touch /etc/profile.d/myenv.sh
sudo chmod +x /etc/profile.d/myenv.sh
2) Добавляем в него команды установки необходимых переменных окружения:
MYENV1=myvar1
MYENV2=myvar2
export MYENV1 MYENV2
3) Сохраняем, перезаходим в систему. Всё, переменные окружения доступны:
echo $MYENV1
myvar1
Существует несколько вариантов добавления переменной окружения в Linux, но этот вариант хорош тем, что:
  • Устанавливает переменную окружения для всех пользователей
  • Установка переменной окружения не слетает после обновления /etc/profile при обновлении системы
ЗЫ Не знаю, насколько он подходит для дистрибутивов, отличных от Ubuntu. В некоторых дистрибутивах, вместо создания файлов в /etc/profile.d/ нужно использовать файл /etc/profile.local.

Настройка темы GTK3 приложений в KDE (Kubuntu 11.10)

По-умолчанию GTK-3 приложения отображаются с использованием совсем минималистичной квадратной темы, а kcmshell4 kcmgtk (из пакета kde-config-gtk) позволяет задать тему только для GTK2-приложений, тогда как в репозитории ubuntu большинство gtk-приложений переехали на 3 версию.
Для настройка темы GTK3-приложений есть утилита kde-gtk-config, не входящая правда в репозиторий убунты. Найти её можно тут: http://packages.netrunner-os.com/pool/main/k/kde-gtk-config/. Устанавливаем, заходим в "Настройка" -> "Параметры системы" -> "Внешний вид приложений", а там - на вкладку "Gtk Configuration":

Новые темы можно скачивать в меню "Get New Themes". У меня правда почему-то большинство скачанных тем не видны в селекте темы =( Из установившихся тем меня вполне устроила, пожалуй, только "Clearwaita".

пятница, 6 апреля 2012 г.

Настройка grub для загрузки Android-x86

При установке андроида на хард я не стал ставить Grub, так как он у меня уже есть и удобнее просто добавить в него новую ось. Благодаря статье на myubuntu удалось сделать это достаточно быстро, всего лишь добавив в конец файла /etc/grub.d/40_custom следующие строки:
menuentry "Android-x86 4.0 RC1 (on /dev/sda3)" {
        set root='(hd0,3)'
        linux /android-4.0-RC1/kernel quiet root=/dev/ram0 androidboot.hardware=eeepc acpi_sleep=s3_bios,s3_mode SRC=/android-4.0-RC1 SDCARD=/data/sdcard.img
        initrd /android-4.0-RC1/initrd.img
}
 , сделав этот файл исполняемым и выполнив:
sudo update-grub
В принципе, можно было бы сразу отредактировать /boot/grub/grub.cfg, вот только, например, при обновлении ядра Linux`а, он перезатерся бы после выполнения update-grub, что нехорошо.

Создание Live USB с Android-x86 4.0 RC1 для Asus EEEPC T101MT

Мой T101MT очень привередлив в плане загрузки с флешки, поэтому Android-x86 также удалось запустить с флешки не с первого раза. 
Из-под винды сделать загрузочную флешку с андроидом вообще не получилось: образ, зарезанный UltraISO образ вис сразу после загрузки, независимо от используемой ФС (NTFS, FAT, exFAT, FAT32) и метода записи. Universal-USB-Installer-1.8.0.8 выругался на образ андроида как поврежденный.
Зато из-под Kubuntu все отлично записалось: отформатировал флешку в ext3, зарезал образ Unetbootin`ом. И вот он, Андроид:


ЗЫ Под Win Unetbootin тоже без проблем зарезал образ на флешку, отформатированную в FAT32 со стандартным размером кластера.

Ошибка NetBeans: "Duplicate class: ..." после выполнения git merge над проектом NetBeans

Проблема:

После выполнения git merge в папке проекта NetBeans (веб-приложения с частично автоматически генерируемыми исходниками) последний сошел с ума и начал ругаться на один из классов: "Duplicate class: ...". Причем удаление класса ничуть не помешало NB построить проект. Переоткрытие проекта, самого нетбинса и перезагрузка системы не помогли.

Причина:

NetBeans имеет какую-то хитрую систему кэширования, куда он что-то закэшировал (что именно, трудно сказать, видимо исходники класса, на который он выругался как на дубликат).

Решение:

Очистить индекс кэша, лежащий по адресу (для версии 7.0):
.netbeans\7.0\var\cache\index
или (для 7.2.1)
AppData\Local\NetBeans\Cache\7.2.1\index
относительно домашней директории пользователя.

org.xml.sax.SAXException: FWK005 parse may not be called while parsing.

Проблема:

При использовании javax.xml.parsers.DocumentBuilder для парсинга XML в веб-приложении на Tomcat периодически вываливается исключение:
org.xml.sax.SAXException: FWK005 parse may not be called while parsing.
Причина:

Исключение говорит о том, что метод parse() объекта парсера вызывается каким-то потоком во время того, как другой поток уже вызвал этот метод и последний ещё не завершил свою работу.

Решение:

Быть осторожнее с разделением одного парсера несколькими потоками.

ЗЫ спасибо mail-листу апача: http://mail-archives.apache.org/mod_mbox/xerces-j-dev/200011.mbox/%3COF4E2B6E7F.2F420D05-ON85256998.005A659F@torolab.ibm.com%3E.

Вывод отладочной информации Java PKI API

Включается опцией явы:
 -Djava.security.debug=certpath

четверг, 5 апреля 2012 г.

Корректное развертывание веб-приложения, использующего КриптоПРО JCP

Методом проб и ошибок была определена следующая конфигурация, позволяющая корректно развернуть на Tomcat 6 веб-приложение, использующее КриптоПРО JCP 1.0.49 (как для разработки, так и для продуктивной среды):
1) JCP устанавливается с указанием в качестве jre папки /jre в директории JDK, используемого томкатом
2) В /lib/ext этого же JRE кладутся следующие jar-ники:
  • xmlsec-1.5.1.jar (или 1.2.1, но 1.5.1 предпочтительнее, т.к. в нём в лог не валятся сообщения при каждой проверке подписи, т.к. уровень логирования таких сообщений исправлен с INFO на DEBUG / WARNING)
  • commons-logging-1.1.1.jar (или commons-logging-1.1.jar)
  • xalan-2.6.0.jar
  • serializer-2.7.1.jar
  • xercesImpl-2.9.1.jar
  • xml-apis-1.3.04.jar
  • xsltc.jar
Последний брался из дистрибутива Xalan 2.7.1 (однако с xalan.jar из этого же дистрибутива JCP работать не будет по этой причине). Serializer, xercesImpl и xml-apis взяты из дистрибутива xmlsec-1.5.1.
3) В сам war-файл веб-приложения вышеуказанные библиотеки (как и библиотеки JCP) включать не нужно. 

java.security.NoSuchProviderException No such provider: BC при использовании библиотеки BouncyCastle

Проблема:

При выполнии проекта, использующего крипто-библиотеку BouncyCastle вываливается исключение:
java.security.NoSuchProviderException No such provider: BC
хотя все необходимые jar-ники подключены.

Решение:

Перед использованием криптопровайдера его необходимо зарегистрировать в окружении безопасности программно:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
 или с помощью policy-файла:
security.provider.<n>=org.bouncycastle.jce.provider.BouncyCastleProvider 

Перегрузка статических методов в Java

Статические методы в Java не перегружаются. При объявлении в производном классе метода, который должен бы перегрузить метод базового класса, происходит лишь "скрытие" базового метода, т.е. то, какой метод из статических методов будет вызван, зависит от типа переменной, а не от того, на объект какого класса она указывает (как с невиртуальными методами в C++). Например:
class Foo {
     public static void classMethod() {
         System.out.println("classMethod() in Foo");
     }
     public void instanceMethod() {
         System.out.println("instanceMethod() in Foo");
     }
 }
class Bar extends Foo {
     public static void classMethod() {
         System.out.println("classMethod() in Bar");
     }
     public void instanceMethod() {
         System.out.println("instanceMethod() in Bar");
     }
 }

class Test {
     public static void main(String[] args) {
         Foo f = new Bar();
         f.instanceMethod();
         f.classMethod();
     }
 }
выведет:
instanceMethod() in Bar
classMethod() in Foo
Спасибо http://www.coderanch.com/how-to/java/OverridingVsHiding.

org.apache.commons.httpclient и библиотека Apache HttpClient 4.x

В версии Apache HttpClient 4.0 и выше используется пространство имен org.apache.http вместо org.apache.commons.httpclient. Поэтому если какие-то библиотеки ругаются на отсутствие классов в org.apache.commons.httpclient, то качать нужно не последнюю, 4ую версию Apache HttpClient, а старую третью. Например, отсюда: http://archive.apache.org/dist/httpcomponents/commons-httpclient/.

java.lang.ClassNotFoundException: org.apache.xpath.compiler.FuncLoader при запуске приложения, использующего КриптоПРО JCP, при том, что xalan.jar в Classpath есть

Проблема:

При запуске приложения, использующего КриптоПРО JCP, валится исключение:
java.lang.ClassNotFoundException: org.apache.xpath.compiler.FuncLoader
, при том, что в Classpath xalan.jar и все необходимые ему jar-ники есть.

Причина:

В новых версиях Xalan (по крайней мере, в 2.7.1) действительно нет класса FuncLoader. 

Решение:

Использовать Xalan версии 2.6.

Настройка пути к JDK, который используется Netbeans`ом

В Windows путь к JDK задается в файле etc/netbeans.conf относительно директории установки NetBeans в переменной netbeans_jdkhome:
# Default location of JDK, can be overridden by using --jdkhome <dir>:
netbeans_jdkhome="C:\jdk"