barbitoff programmer`s blog

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

понедельник, 29 августа 2011 г.

Ошибка XPath: "Required item type of first operand of '|' is node(); supplied value has item type ..."

Проблема: при попытке объединения последовательностей в XPath с помощью оператора "|" или "union", возникает ошибка "Required item type of first operand of '|' is node(); supplied value has item type xs:string" (вместо "xs:string" может быть другой тип если последовательности состоят из элементов другого типа).
Причина: Оператор объединения в XPath действительно позволяет логически объединять (т.е. создавать из нескольких последовательностей одну, исключая повторяющиеся элементы) только последовательности из узлов (node). Любые другие последовательности (из строк, чисел и пр.) этим оператором не поддерживаются.

пятница, 26 августа 2011 г.

org.orbeon.saxon.trans.XPathException: An empty sequence is not allowed as the value of variable $control-resources

Проблема: при попытке получить pdf-ку из заполненной в orbeon формы вместо pdf-ки вываливается исключение "org.orbeon.saxon.trans.XPathException: An empty sequence is not allowed as the value of variable $control-resources".

Причина:
  • причина аналогичная таковой для исключения "org.orbeon.saxon.trans.XPathException: An empty sequence is not allowed as the value of variable $section-holder" (http://barbitoff.blogspot.com/2011/08/orgorbeonsaxontransxpathexception-empty.html) - не соотвтетствие id bind`а какого-то из элементов управления имени узла модели, к которому этот bind осуществлен.
  • если ошибка возникает из-за элемента управления select1, то, возможно, необходимо чтобы ресурсы, на которые он ссылается (label, hint, help, alert при организации их по ссылке через ref) находились завернутыми в тег, совпадающий по имени с тегом в модели (т.е. ссылка на них имела вид <xforms:label ref="$form-resources/{tagName}/label"/>, где {tagName} - имя тега модели, к которому привязан данный control). Совсем непонятный для меня факт, однако переименование ресурса заставляет форму работать..

Решение: bind`ы должны выглядеть следующим образом:

<xforms:bind id="{tagName}-bind" nodeset="{tagName}" name="..."/>

org.orbeon.saxon.trans.XPathException: An empty sequence is not allowed as the value of variable $section-holder

Проблема: при попытке получить pdf-ку из заполненной в orbeon формы вместо pdf-ки вываливается исключение "org.orbeon.saxon.trans.XPathException: An empty sequence is not allowed as the value of variable $section-holder".

Причина:
  • где-то в форме есть секция (fr:section), bind которой не имеет id вида "{sectionRootTagInModel}-bind", где {sectionRootTagInModel} - имя тэга в модели, к которому привязана секция с помощью конструкции bind. Т.е. секции должны быть привязаны к элементам модели следующим образом: <xforms:bind id="{sectionRootTagInModel}-bind " nodeset="{sectionRootTagInModel}"> для секции, объявленой как <fr:section id="{sectionRootTagInModel}-section" bind="{sectionRootTagInModel}-bind"> (использование другого id самой секции вроде как проблем не вызывает). 
  • в форме используются вложенные друг в друга bind`ы. Секции вкладывать друг в друга можно, но вот bind`ы - нет (за исключением вложения всех биндов в общий бинд к корневому тегу модели).

Решение: всегда присваивать bind`ам секций id вида "{sectionRootTagInModel}-bind". Не использовать вложенные секции

четверг, 18 августа 2011 г.

Шаблон проектирования Приспособленец (Flyweight)

Вместо:
- создание нескольких копий идентичных неизменяемых объектов
предлагается:
- использование модифицированной версии шаблона Фабрика (Factory), в которой фабрика при запросе объекта с внутренней структурой, идентичной ранее созданному объекту, возвращает ссылку на этот объект вместо создания нового объекта. В противном случае создается новый экзмепляр объекта.
Шаблон предполагает использование 2 классов - самого приспособленца (объекта, свойства которого задаются единожды при создании и не могут быть изменены в дальнейшем), и фабрики приспособленцев.

среда, 17 августа 2011 г.

Кэширование данных в компонентах 1С-Битрикс

Иногда бывает необходимо закэшировать какие-нибудь данные (а не html), например, результат работы логики компонента, т.е. массив $arResult. Ниже приведен пример использования средств кэширования 1С-Битрикс, имеющий следующие особенности:
  • используется 1С-Битрикс-класс CPHPCache
  • управление кэшированием ведется через стандартные параметры компонента CACHE_TYPE и CACHE_TIME (кэширование включается, когда тип кэширования установлен в "Авто+Управляемое", что соответствует значению 'A' в коде включения компонента)
  • кэш зависит от всех параметров массива $_GET 
  • кэш хранится в папке /bitrix/cache/my_component_name/

$useCache = ($arParams["CACHE_TYPE"] == 'A');
$cacheValid = false;

if($useCache)
	{
	$cachedResult = new CPHPCache();

	$cacheId = "";
	foreach($_GET as $key => $val)
		$cacheId.=$key.$val;

	$cacheValid = $cachedResult->InitCache(intval($arParams["CACHE_TIME"]), $cacheId, "/my_component_name");

	if($cacheValid)
		$arResult = $cachedResult->GetVars();
	}

if(!$useCache || !$cacheValid) // либо кэш отключен, либо нет действительного кэша
	{
	/*
	* Тут заполняется массив $arResult и формируется флаг $success, равный true,
	* если результат сформирован успешно или false, если возникла ошибка и кэшировать
	* результат не имеет смысла
	*/

	if($success && $useCache)
		{
		// сохранить в кэше массив $arResult
		$cachedResult->StartDataCache();
		$cachedResult->EndDataCache($arResult);
		}
	}



вторник, 16 августа 2011 г.

Netbeans: отображение файлов и папок, начинающихся с точки, в окне "Projects" и "Files"

Tools -> Options -> Miscellaneous -> "Files" tab, изменить "Ignored Files Pattern" на что-нибудь вроде:
^(CVS|SCCS|vssver.?\.scc|#.*#|%.*%|_svn|\.svn)$|~$
вместо
^(CVS|SCCS|vssver.?\.scc|#.*#|%.*%|_svn)$|~$|^\.(?!htaccess$).*$
Такой паттерн покажет все срытые файлы (а паттерн, который был по-умолчанию, показывал только .htaccess), кроме диреторий svn (.svn).

пятница, 12 августа 2011 г.

org.h2.jdbc.JdbcSQLException: Connection is broken: [90067-140]

Если верить документации h2 (http://www.h2database.com/javadoc/org/h2/constant/ErrorCode.html#c90067), сообщение такого вида при попытке подключения к БД h2 должно говорить о том, что сервер БД недоступен, не является сервером h2 или сетевое соединение потеряно.

Однако, в моём случае, с сервером все было в порядке - он принимал соединения и через веб-интерфейс, и по tcp, причем даже к выбранной БД я мог через веб интерфейс подключиться и даже выполнять запросы. Но вот WSO2GREG и WSO2ESB упорно заваливали логи исключениями такого вида (а также другими, типа Read Timeout и т.п.) при попытке соединиться с этой же базой.

Проблема оказалось в следующем: БД была повреждена (как оказалось, БД h2 состоит не только из файла dbname.h2.db, но и ещё из директории dbname.lobs.db/ в той же папке, что и .h2.db-файл, а я эту самую директорию при перемещении базы не перевез =) ).

среда, 10 августа 2011 г.

Тестирование hl7-проксирования WSO2ESB 4.0.0

Как создать hl7-прокси-сервис, я уже писал тут: http://barbitoff.blogspot.com/2011/08/wso2esb-hl7.html. Для тестирования hl7 понадобится пробный hl7-сервер и клиент, скачать его можно здесь: http://narod.ru/disk/21364371001/hl7test.7z.html, а также Apache Ant.

Честно говоря, с протоколом hl7 я не знаком, но описанный мной пример позволяет даже не знающему протокола человеку убедиться в работоспособности hl7-транспорта WSO2ESB.

Для запуска hl7-сервера:
  • Перейти в каталог hl7test (тот, который содержит build.xml) и выполнить ant hl7acceptor -Dhl7-port=9988 - запустит сервер, слушающий порт 9988
Для запуска клиента, работающего по протоколу hl7, обращающегося на сервер напрямую:
  • Перейти в каталог hl7test (тот, который содержит build.xml) и выполнить ant hl7client -Dhl7-host=127.0.0.1 -Dhl7-port=9988 (предполагается, что hl7-сервер развернут на localhost`e) - после запуска в консоль запущенного сервера должно вывалиться принятое сообщение, сервер на это сообщение ответит, ответ придет в консоль клиента, после чего последний завершится (тоже самое должно происходить и в описанных ниже случаях).
Для запуска клиента, работающего по протоколу hl7, обращающегося на сервер через прокси-сервис ESB:
  • Перейти в каталог hl7test (тот, который содержит build.xml) и выполнить ant hl7client -Dhl7-host=127.0.0.1 -Dhl7-port=9292 (предполагается, что "transport.hl7.Port" сервиса на ESB должен быть установлен в 9292, и сама шина расположена на localhost`e)
Для запуска клиента, работающего по протоколу http и оперирующего XML-сообщениями, обращающегося на сервер через прокси-сервис ESB:
  • Перейти в каталог hl7test (тот, который содержит build.xml) и выполнить ant hl7client -Dmode=soap -Dhl7-host=127.0.0.1 -Dhl7-port=8280 -Dhl7-proxy=hl7testproxy -DmetaFile="/path/to/sample_req.xml" (предполагается, что ESB слушает http-запросы на 8280 и прокси-сервис называется hl7testproxy, /path/to/sample_req.xml - путь к файлу запроса sample_req.xml, который включен в архив).

вторник, 9 августа 2011 г.

Создание прокси-сервиса WSO2ESB для работы по hl7

Во-первых, необходимо добавить в WSO2ESB поддержку hl7-транспорта (http://barbitoff.blogspot.com/2011/08/hl7-wso2esb-400.html). После этого можно создать прокси-сервис, который не только сможет принимать hl7-подключения от клиентов и перенаправлять их дальше hl7-серверу, но и принимать xml-сообщения по http/https, и, преобразуя их в hl7, отправлять дальше, преобразую ответ на обратном пути снова в xml.
Процесс создания hl7-сервиса аналогичен созданию обычного сервиса но:
  • Необходимо указать параметр "transport.hl7.Port", значение которого будет указывать порт, который будет слушаться для приема запросов по протоколу hl7 прокси-сервисом (в противном случае ESB пометит сервис как "faulty" и отключит для него hl7-транспорт).
  •   В качестве конечной точки (endpoint) указывается Address Endpoint с адресом вида "hl7://localhost:9988", указывающим хост и порт hl7-сервера.
Вот пример hl7-прокси-сервиса, принимающего запросы по http, https и hl7, логирующего запросы и ответы и перенаправляющего запрос на hl7-сервер "localhost:9988" :

<proxy xmlns="http://ws.apache.org/ns/synapse" name="hl7testproxy" transports="https,http,hl7" statistics="disable" trace="disable" startOnLoad="true">
   <target>
      <inSequence>
         <log level="full" />
      </inSequence>
      <outSequence>
         <log level="full" />
         <send />
      </outSequence>
      <endpoint name="endpoint_urn_uuid_9CB8D06C91A1E996796270828144799-1418795938">
         <address uri="hl7://localhost:9988" />
      </endpoint>
   </target>
   <parameter name="transport.hl7.Port">9292</parameter>
</proxy>

Как протестировать проски-сервис, описано здесь: http://barbitoff.blogspot.com/2011/08/hl7-wso2esb-400_10.html.

Добавление поддержки hl7-транспорта в WSO2ESB 4.0.0

  1. Configure -> Features -> Repository Management -> Add New Repository. Вводим URL http://dist.wso2.org/p2/carbon/releases/3.2.0/ и любое имя
  2. Configure -> Features -> Repository Management -> Available Features. Выбираем "Axis2 Transport HL7 Feature", жмем Install, принимаем соглашение, ждем.  
  3. Когда всё скачается / установится, перезагружаем сервер 
  4. Идем в Configure -> Transports, жмем "Enable" для Transport Listener и Transport Sender напротив HL7. 
  5. После этого Транспорт hl7 появится среди доступных транспортов наряду с http, https и jms.   

понедельник, 8 августа 2011 г.

php: рекурсивное удаление из директории файлов, имена которых не соответствуют шаблону

/**
 * Deletes from $dir all files, names of which not matching regexp
 * @param  $dir directory path (without tailing slash)
 * @param  $regexp regular expression to check filenames with preg_match.
 * Non mathing files will be deleted.
 * @param int $silent if 0, all messages (info / error) will be echoed, if 1 -
 * only error messages, if 2 - all output will be suppressed (defaults to 1)
 * @return int number of files, matching pattern and thus not deleted, -1 on failure
 */
function cleanDir($dir, $regexp, $silent=1)
{
    if(($dh=opendir($dir))!==false)
        {
        $noError = true;
        $filesStillInDir = 0;
        if(!$silent)
            echo "Cleaning dir $dir";
        while ($file = readdir($dh))
            {
            if($file != ".." && $file!=".")
                {
                if(is_dir($dir."/".$file)) // If directory - recursive call
                    {
                    $filesStillInSubDir = cleanDir($dir."/".$file,$regexp,$silent);
                    if(-1 != $filesStillInSubDir)
                        {
                        $filesStillInDir+=$filesStillInSubDir; // increase count of files, still remaining in current directory
                        
                        if(0 == $filesStillInSubDir) // no files, matching pattern, found in dir - delete it
                            {
                            if(!$silent)
                                echo "Dir $dir/$file empty - deleting";
                            if(!@rmdir($dir."/".$file))
                                {
                                $noError = false;
                                if($silent < 2)
                                    echo "
Error deleting dir after cleaning: $dir/$file
"; } } } else { if($silent < 2) echo "
Error cleaning dir $dir/$file
"; $noError = false; } } else // is file => check name and, if not matches - delete it { if(preg_match($regexp, $file) == 0) { if(!@unlink($dir."/".$file)) { $noError = false; if($silent < 2) echo "
Error deleting file $dir/$file
"; } } else $filesStillInDir++; } } } closedir($dh); if($noError) return $filesStillInDir; else return -1; } if($silent < 2) echo "
Error opening dir $dir for cleaning!
"; return -1; }

Дирактории, в которых не осталось файлов, также удаляются (кроме родительской директории, для которой функция была вызвана изначально).
Пример вызова (удаление всех файлов, кроме файлов интернационализации в Java):
$propFilesFound = cleanDir($pathToUnpack,'/Resources.*\.properties$/');

пятница, 5 августа 2011 г.

Добавление в WSO2ESB 4.0.0 возможности публиковать события на WSO2BAM

Внимание! По моему опыту установка данных модулей приводит к нестабильности ESB, увеличению потребления памяти и падению пиковой нагрузки, которую она может выдержать, практически на 2 порядка (http://barbitoff.blogspot.com/2011/10/wso2esb-400-wso2bam-130-esb.html)!

1) Configure -> Features -> Repository Management -> Add New Repository. Вводим URL http://dist.wso2.org/p2/carbon/releases/3.2.0/ и любое имя
2) Configure -> Features -> Repository Management -> Available Features. Выбираем необхожимые фичи (или по максимуму - все доступные, начинающиеся с "BAM": BAM Service Statistics Data Publisher Feature, BAM Activity Mediation Data Publisher Feature, BAM Activity Service Data Publisher Feature, BAM Mediation Statistics Data Publisher Feature), жмем Install, принимаем соглашение, ждем.
3) Когда всё скачается / установится, перезагружаем сервер (если вылезет что-нибудь типа "Error occured while reviewing provisioning action", не волнуйтесь, все в норме). После этого в Configure должны появиться пункты "Service Data Publishing", "Activity Data Publishing" и "Mediation Data Publishing", содержащие соответствующие настройки. Там можно настроить интервал, в количестве вызовов сервиса, через который будут генерироваться события. По-умолчанию он равен 20, так что не удивляйтесь, что поначалу, возможно, никаких событий BAM видеть не будет.
4) Всё, теперь можно настраивать BAM на получение данных с шины с помощью Eventing.

Подробная статья про публикацию событий на BAM тут: http://wso2.org/project/bam/1.3.0/docs/data_publishers.html

четверг, 4 августа 2011 г.

Что указывать в "WSDL Resources" при создании прокси-сервиса в WSO2ESB 4.0.0

При создании прокси-сервиса WSO2ESB на основании WSDL из реестра (Publishing WSDL - "Pick from Registry") необходимо в блоке WSDL Resources описать все ресурсы, на которые ссылается WSDL файл. Для этого нужно открыть WSDL в реестре, и найти все схемы, на которые есть ссылки. Для каждой схемы в поле "Location" вводится путь к схеме в том виде, в котором он указан в атрибуте schemaLocation тега <xs:import/>, а поле "Key" заполняется путем выбора этой схемы в реестре (локальном или Governance). ВАЖНО! Если эти схемы, в свою очередь, импортируют другие схемы (или друг друга), то для этих импортов нужно также добавлять ресурс (даже если получится, что в ресурсах одна и также схема указана дважды, с разными "Location").
Например, для UDDI-сервиса WSO2GREG ресурсы будут выглядеть следующим образом:

Location Key
../../../schemas/api_v3/uddi_org/
UDDIInquiryService2.xsd
gov:/trunk/schemas/api_v3/uddi_org/
UDDIInquiryService2.xsd
../../../schemas/org/w3/www/xml/
_1998/namespace/UDDIInquiryService.xsd
gov:/trunk/schemas/org/w3/www/xml/
_1998/namespace/UDDIInquiryService.xsd
../../../schemas/org/w3/www/
_2000/_09/xmldsig/UDDIInquiryService1.xsd
gov:/trunk/schemas/org/w3/www/
_2000/_09/xmldsig/UDDIInquiryService1.xsd
../../org/w3/www/xml/_1998/
namespace/UDDIInquiryService.xsd
gov:/trunk/schemas/org/w3/www/
xml/_1998/namespace/UDDIInquiryService.xsd
../../org/w3/www/_2000/_09/
xmldsig/UDDIInquiryService1.xsd
gov:/trunk/schemas/org/w3/www/
_2000/_09/xmldsig/UDDIInquiryService1.xsd

Последние 2 записи повторяют ключи из 2 и 3 записи, однако имеют другой "Location", т.к. он взят не из самой WSDL, а из схемы UDDIInquiryService2.xsd.



среда, 3 августа 2011 г.

Изменение оформления web-панелей продуктов WSO2

CSS-стили и изображения, используемые в административных веб-панелях продуктов WSO2, хранятся запакованными в jar-файлы вида:

wso2*/repository/components/plugins/org.wso2.*.styles-*.jar

Стили Dashboard можно найти тут:

wso2*/repository/components/plugins/org.wso2.carbon.dashboard.ui-*.jar

UDDI в WSO2GREG 4.0.0

Для включения UDDI необходимо запустить реестр с ключем "-Duddi=enable":

wso2server.bat -Duddi=enable

Если GREG запускается как служба, то в repository/conf/wrapper.conf необходимо добавить строчку (посмотрев предварительно, что 3ий параметр приложения ещё не занят, иначе поставить первую незанятую цифру):

wrapper.app.parameter.3=-Duddi=enable

У меня правда при запуске wso2greg как службы с таким параметром веб-сервис UDDI деплоится, вот только при попытке получить wsdl вываливается с исключением java.lang.ClassNotFoundException: com.sun.tools.ws.spi.WSToolsObjectFactory, решения пока не нашел. При запуске же напрямую, можно получить wsdl-ку для выполнения поисковых запросов к реестру по адресу https://localhost:9443/services/UDDIInquiryService?wsdl, или для публикации: https://localhost:9443/services/UDDIPublicationService?wsdl.
Потестировать фичу можно, открыв эту wsdl-ку, например, в soapUI (открывать правда нужно не скачанный файл, а прямо URL, т.к. содержит относительные ссылки на xsd).
Т.к. в реестре используется jUDDI, то подойтет туториал по последнему тут (с видео примерами): http://apachejuddi.blogspot.com/2011/05/getting-started-with-uddi-v3.html, я правда пока не нашел, где найти wsdl для security. 

java.lang.AbstractMethodError: org.slf4j.impl.Log4jMDCAdapter.getCopyOfContextMap()Ljava/util/Map при попытке связать WSO2ESB 4.0.0 и ActiveMQ 5.5.0

Проблема: При попытке настроить JMS-транспорт WSO2ESB 4.0.0 на работу с ActiveMQ 5.5.0, запуск ESB приводит к бесконечным исключениям "java.lang.AbstractMethodError: org.slf4j.impl.Log4jMDCAdapter.getCopyOfContextMap()Ljava/util/Map".
Причина: WSO2ESB 4.0.0 не умеет работать с клиентскими библиотеками ActiveMQ версии больше 5.4.1 (проверено экспериментально).
Решение: положить в  /repository/components/lib WSO2ESB клиентскую библиотеку ActiveMQ 5.4.1 (activemq-core-5.4.1.jar, скачать можно с официального сайта amq: http://activemq.apache.org/activemq-541-release.html). Клиент 5.4 работает без проблем с брокером ActiveMQ 5.5.0, так что брокер откатывать на более старую версию не нужно.

Запуск скриптов /etc/init.d/ от пользователя, отличного от root

Скрипты в /etc/init.d/ выполняются от root-а, что иногда совсем не нужно. Сменить пользователя через su, если у пользователя установлен пароль - тоже не вариант. Решается задача с помощью вынесения всей логики из init-скрипта в отдельный файл, и запуска этого скрипта в init-скрипте с помощью программы /sbin/start-stop-daemon, позволяющей сменить пользователя, из-под которого будет выполняться вызываемая программа / скрипт. start-stop-daemon, кроме смены пользователя, умеет также демонизировать (отправлять в фон) запускаемую программу. Вот пример вызова скрипта с демонизацией (ключ -b), сменой пользователя на "user1", выводом отладочных сообщений (-v) и передачей скрипту цели вызова init-скрипта (start/stop/status и т.п.):

/sbin/start-stop-daemon -S -v -b -x "$PATH_TO_MY_SCRIPT" --user user1 --chuid user1 -- "$1"

Вообще, параметры вызываемой программе передаются указанием их после двойного дефиса ("--"). Непосредственно за смену пользователя отвечает опция --chuid, что делает --user я до конца не понял (в контексте именно запуска, а не остановки чего-либо), но на всякий случай и там указал "user1".
Подробнее о  start-stop-daemon  написано тут: http://help.ubuntu.ru/wiki/start-stop-daemon

Отключение вывода команд при выполнении bat-файла


@echo off

Поиск файлов, принадлежащих пользователю в Linux

Например, для пользователя root:

find -user root

понедельник, 1 августа 2011 г.

Перехват вывода программы в bash-скрипте

Для того, чтобы перехватить вывод программы в переменную, используются обратные кавычки:

h2pid=`ps -A | grep h2.sh`

ВАЖНО: справа от "=" не должно быть пробелов, иначе интерпретатор посчитает, что вывод команды сам является командой и попробует её выполнить. Слева от "=" также не должно быть пробелов, т.к. в этом случае уже имя переменной будет воспринято как имя команды.

position: relative относительно родительского контейнера

Для установки абсолютного позиционирования элемента относительно родительского контейнера (а не относительно страницы вцелом), необходимо (помимо того, естественно, чтобы у самого элемента был поставлен position: absolute и заданы координаты css-свойствами top и left), чтобы у родителя был установлен position: relative или position: absolute. Вообще, при установке position: absolute у элемента его позиционирование осуществляется относительно его ближайшего предка с position: relative или position: absolute, или, если такого нет - относительно страницы.