barbitoff programmer`s blog

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

пятница, 30 декабря 2011 г.

Транзакции и ограничения

Столкнулся тут с интересным поведением PostgreSQL, связанным с UNIQUE-ограничениями и параллельными транзакциями: пусть имеется табличка "h" с колонкой "u" типа int, на которую установлено ограничения "UNIQUE", и параллельно выполняются две транзакции (назовем их t1 и t2), делающие следующую последовательность операций:
1) t1: BEGIN
2) t2: BEGIN
3) t1: INSERT INTO h("u") VALUES (1)
4) t2: INSERT INTO h("u") VALUES (1)
5) t1: COMMIT
6) t2: COMMIT
Уровень изоляции транзакций - "read committed", т.е. транзакции видят только откомиченные изменения друг друга.
Мне казалось что реакция СУБД должна быть следующая: оба INSERT`а проходят нормально, т.к. на момент их выполнения UNIQUE-ограничение не нарушается, COMMIT у транзакции t1 завершается успешно, а у t2 вызывает ошибку и откат транзакции, т.к. на момент этого коммита ограничение уже нарушено.
На самом же деле СУБД ведет себя по-другому: при попытке выполнить INSERT транзакции t2, который может нарушить ограничение в случае, если t1 будет откомиченна, СУБД приостанавливает выполнение команды и заканчивает его только после COMMIT`а или ROLLBACK`а t1 (соответственно, ругаясь на нарушение ограничения или успешно вставляя строку). Такое поведение наблюдается только если нарушение ограничения действительно возможно. Т.е. если вторая транзакция вставляла бы в таблицу значение "2", INSERT выполнялся бы по-обычному, не ожидая первой транзакции.
Теоретически, предполагаемое мной поведение могло бы реально наблюдаться, если бы ограничение было бы задано как DEFERRABLE INITIALLY DEFERRED (тогда ограничение проверялось бы только при COMMIT`е, а не сразу при выполнении операции), но у меня сейчас под рукой только Postgres 8.3, тогда как до 9ой версии можно было объявлять откладываемыми только FOREIGN KEY ограничения.
Насчет поведения других СУБД ничего сказать не могу, надо будет попробовать.

среда, 28 декабря 2011 г.

Отключение кэширования данных браузером или прокси-сервером с помощью заголовков HTTP

Кэширование данных браузером и прокси-сервером отключается следующими 4 заголовками:
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Last-Modified: <now>
, где <now> - текущая дата и время. В JSP (и вообще в любом HTTP-сервлете) это можно установить следующим образом:
DateFormat fullDateTimeFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", new Locale("EN"));
response.setHeader("Expires", "Mon, 26 Jul 1997 05:00:00 GMT");
response.setHeader("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("Last-Modified", fullDateTimeFormat.format(new java.util.Date())); 

вторник, 27 декабря 2011 г.

JSP-аналог $_SERVER['DOCUMENT_ROOT'] в php

Иногда бывает необходимо из веб-странице определить реальный путь к какому-либо файлу (т.е. не путь относительно контекста сервлета / корня виртуального хоста, а именно путь в файловой системе). В php такой путь можно определить исходя из $_SERVER['DOCUMENT_ROOT'] и пути относительно корня виртуального хоста, в JSP же это делается так:
this.getServletContext().getRealPath("/some/path/relative/to/context");
Путь, аналогичный $_SERVER['DOCUMENT_ROOT'], получается вызовом:
this.getServletContext().getRealPath("/");

JavaScript: сериализация объекта в строку в формате JSON

Преобразование объекта в JSON-строку осуществляется теми же библиотеками, что и парсинг строки в объект. например, http://www.json.org/js.html или, при работе с YUI, - YAHOO.lang.JSON. Метод, сериализирующий объекты называется и там, и там одинаково - stringify:
var serialisedData = YAHOO.lang.JSON.stringify(dataObj);
Обе библиотеки позволяют явно задать список полей, подлежащих сериализации, или функцию для предобработки значений перед сериализацией. 

понедельник, 26 декабря 2011 г.

Работа с JSON в Java

Если в Java требуется работать с форматом JSON (например, на JSP странице для отправки данных JavaScript-скрипту), то можно воспользоваться очень удобной библиотекой - org.json (http://www.json.org/java/). Библиотека позволяет быстро создавать строки  с JSON-данными, парсить строки с JSON-данными в Java-объекты, и даже преобразовывать XML в JSON. Например:

JSONObject myObj = new JSONObject();
myObj.put("key1", "val1");
myObj.put("key2", "val2");
myObj.put("key3", "val3");
JSONArray myArr = new JSONArray();
myArr.put("a");
myArr.put("b");
myArr.put("c");
myObj.put("key4",myArr);
System.out.println(myObj.toString());
выведет:

{"key4":["a","b","c"],"key3":"val3","key2":"val2","key1":"val1"}
И наоборот:
String jsonStr = "{\"key4\":[\"a\",\"b\",\"c\"],\"key3\":\"val3\",\"key2\":\"val2\",\"key1\":\"val1\"}";
JSONObject myParsedObj = new JSONObject(jsonStr);
JSONArray myParsedArr = (JSONArray)myParsedObj.get("key4");
System.out.println(myParsedArr.get(2));
выведет символ "c".
Метод XML.toJSONObject(String) разбирает переданный XML и преобразует его естественным образом в JSON (вложенные теги становятся вложенными объектами, несколько соседних тегов с одним именем превращаются в массив), так, например, вызов:
System.out.println(XML.toJSONObject("<a><b>1</b><b>2</b><b>3</b><c><d>45</d></c></a>").toString());
выдаст:
{"a":{"b":[1,2,3],"c":{"d":45}}}
Также возможно и обратное преобразование.
Для представления NULL в методах put используется JSONObject.NULL.

Объекты JSONObject можно конструировать из любых объектов, поддерживающих интерфейс Map<K,V>, а JSONArray - List<T>.

Java: чтение и запись строк в файл в кодировке, отличной от кодировки по-умолчанию, установленной в системе

Приведенный ниже код создает BufferedReader, читающий строки из файла tmpFile в кодировке 'UTF-8' в ОС Windows, где кодировка по-умолчанию - cp1251:
BufferedReader bufReader = new BufferedReader(new InputStreamReader(new FileInputStream(tmpFile), Charset.forName("UTF-8")));
А так выполняется запись в файл в кодировке UTF-8:

BufferedWriter bufWriter = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(tmpFile),
Charset.forName("UTF-8")
)
);  

Параметризация трансформаций Pentaho Kettle

Параметризуются трансформации Kettle достаточно просто: зайдя в графическом редакторе трансформаций в "Transformation settings" на вкладке "Parameters" можно указать любое количество параметров трансформации, а также их значения по-умолчанию. Например, если объявить там параметр с именем "srcPath", то затем в настройках различных этапов трансформации можно будет пользоваться переменной "${srcPath}", значение которой при выполнении трансформации будет подставлено исходя из фактически переданного параметра (или его значения по-умолчанию, если он не был задан). При выполнении трансформаций из командной строки параметры можно устанавливать с помощью ключа -param, и при запуске трансформации из Java - так:
TransMeta transMeta = new TransMeta("/path/to/ktr-file");
transMeta.setParameterValue("srcPath", mySrcPath);
Подробнее и со скриншотами почитать про параметры можно тут: http://wiki.pentaho.com/display/EAI/Named+Parameters, а про класс TransMeta  - в JavaDoc`е: http://javadoc.pentaho.com/kettle/org/pentaho/di/trans/TransMeta.html#setParameterValue(java.lang.String, java.lang.String).

четверг, 22 декабря 2011 г.

JSP и "multipart/form-data"-формы

"Из коробки" спецификация Servlet API 2.x не поддерживает автоматическое распознавание запросов "multipart/form-data", а, значит, например, Apache Tomcat версии, раньше 7ой, работать с ними также не умеет. В итоге, при передаче файла на JSP страницу среди параметров мы его не найдем. Выход - использовать контейнер сервлетов с поддержкой Servlet API 3.0 или воспользоваться сторонними разработками для работы с такими запросами. Например, Apache Commons FileUpload (Apache Commons FileUpload). Его использование состоит из следующих этапов:
1) В библиотеки добавить jar-ники самого commons-fileupload и commons-io (http://commons.apache.org/io/download_io.cgi).
2) На странице добавить импорты:
<%@ page import ="org.apache.commons.fileupload.*,org.apache.commons.fileupload.disk.*,org.apache.commons.fileupload.servlet.*" %>
3) Теперь в коде странице проверяем, является ли запрос мультипартовым, если да - обрабатываем его с помощью  Apache Commons FileUpload (подробнее про варианты обработки можно почитать тут - http://commons.apache.org/fileupload/using.html):

boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if(isMultipart)
{
// Создаем фабрику (при необходимости можно задать временную директорию и макс. размер файла)
FileItemFactory factory = new DiskFileItemFactory();
// Создаем обработчик
ServletFileUpload upload = new ServletFileUpload(factory);
// Парсим запрос
List /* FileItem */ items = upload.parseRequest(request);
// Обрабатываем результат
Iterator iter = items.iterator();
while (iter.hasNext())
{
FileItem item = (FileItem) iter.next();
if (item.isFormField()) // пропускаем не интересующие нас обычные поля формы
continue;
String fieldName = item.getFieldName();
String fileName = item.getName();
String contentType = item.getContentType();
boolean isInMemory = item.isInMemory();
long sizeInBytes = item.getSize();
System.out.println("fieldName="+fieldName+",fileName="+fileName+",contentType="+contentType+",isInMemory="+isInMemory+",size="+sizeInBytes+"b");
/*
* Здесь делаем, например, item.getInputStream() и работаем с ним.
*/
}
}
else
{
// ...
}

YUI: изменение положения editor`а поля на экране

Иногда editor поля таблицы DataTable автоматически располагается на экране не лучшим образом (например, при использовании YAHOO.widget.DropdownCellEditor с очень длинными вариантами выбора может запросто вылезти за пределы экрана, вызвав появление горизонтальной прокрутки). Можно динамически корректировать его положение в момент его появления на экране (ведь статически задать его положение стилями не всегда возможно) следующим образом:
1) Подписаться на событие 'showEvent' editor`а:
myDataTable.getColumn('myColumn').editor.subscribe('showEvent',myHandler)
2) В myHandler editor будет доступен как this, что позволит манипулировать его стилями (например, сделать так, чтобы он расположился в 10 пикселях от правого края экрана):

this.getContainerEl().style.left = YAHOO.util.Dom.getViewportWidth() -     YAHOO.util.Dom.getRegion(this.getContainerEl()).width-10 + "px";
Надо иметь ввиду, что на момент генерации события 'showEvent' элемент на странице уже появился (это верно для FF, неверно для IE), что может повлиять на некоторые параметры (например, на ширину документа, если элемент вылезает за правый край).   

среда, 21 декабря 2011 г.

Особенности работы YAHOO.widget.Paginator.setRowsPerPage()

Метод setRowsPerPage() класса YAHOO.widget.Paginator позволяет динамически устанавливать число элементов на странице. Имеет он два параметра:

  • rpp - новое число элементов на страницу
  • silent - двоичный флаг, значение true которого принудительно предотвращает обновление данных (т.е. генерацию события changeRequest).
Не вполне (имхо) ожидаемое поведение этого метода заключается в следующем (по крайней мере, при использовании пагинатора с виджетом DataTable): пусть, например, текущее значение числа элементов на странице равно 20 и выполняются следующие вызовы:
paginator.setRowsPerPage(50,true);
// ...
paginator.setRowsPerPage(50,false);
Казалось бы, после второго вызове setRowsPerPage с silent=false данные, отображаемые в таблице, должны обновиться в соответствии с новым rpp. Однако нет -  данные остаются прежними. Если на момент выполнения приведенного выше кода отображалась первая страница таблицы, то спасает вызов datatable.render(), однако в случае, если страница была последней, и этот вариант не проходит, а данные, оказавшиеся за пределами отображаемой страницы, становятся недоступными. Можно попереключать страницы (вручную через интерфейс пагинатора или вызовом paginator.setPage), однако если после увеличения числа элементов на странице они все уместились на одной (должны были уместиться), это тоже не вариант (вызов paginator.setPage(1,false) ничего не меняет).

вторник, 20 декабря 2011 г.

Русификация YAHOO.widget.Calendar

Для русификации виджета календаря YUI (чтобы названия дней недели и месяцев были по-русски,  неделя начиналась с понедельника), необходимо передать ему следующую конфигурацию:

var calendarConfig =
{
MONTHS_LONG: [
 "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь",
 "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"
   ],
WEEKDAYS_SHORT : ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"] ,
START_WEEKDAY: 1
}

Если в календаре нужно использовать навигатор (позволяющий быстро перейти к заданному месяцу и году), то конфигурация дополнится следующим образом:

var calendarConfig =
{
MONTHS_LONG: [
  "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь",
  "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"
   ],
WEEKDAYS_SHORT : ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"] ,
START_WEEKDAY: 1,
navigator:
{
strings:
{
month: "Месяц",
year: "Год",
submit: "Выбрать",
cancel: "Отмена",
invalidYear: "Неверный год"
}
}
}
Имея такую конфигурацию, можно создавать виджет:
var cal = new YAHOO.widget.Calendar("cal_div_id",config);
Или же эту конфигурацию можно использовать для настройки YAHOO.widget.DateCellEditor, использующегося для inline-редактирования YAHOO.widget.DataTable:

var editor = new YAHOO.widget.DateCellEditor(
{
calendarOptions: calendarConfig,
LABEL_CANCEL: 'Отмена',
LABEL_SAVE: 'Ок'
});


JavaScript: получение индекса элемента среди его соседей с тем же именем тега

Задача:
Получить индекс (начиная от 0) элемента среди его соседей (непосредственных детей его родителя) с тем же именем тега, что и у него самого (на чистом JS, без использования библиотек).

Решение:
Не нашел другого решения, кроме как сделать это перебором потомков:
function getElementIndexWithinSameTagged(elem)
{
    var children = elem.parentNode.getElementsByTagName(elem.tagName);
    for(var i=0;i<children.length;i++)
        if(elem == children.item(i))
            return i;
    return -1; // never will get here
}

понедельник, 19 декабря 2011 г.

dump объекта на JavaScript

Выводит dump объекта alert`ом:

function dump(obj) {
    var out = "";
    if(obj && typeof(obj) == "object"){
        for (var i in obj) {
            out += i + ": " + obj[i] + "\n";
        }
    } else {
        out = obj;
    }
    alert(out);
}
Спасибо http://snippets.org.ua/page/var_dump-v-javascript.

Пример валидатора поля YUI DataTable - required field валидатор

Ниже приведен пример валидатора, требующего, чтобы поле было непусто:

requiredFieldValidator (oData)
{
    if(oData == "")
        {
        alert("Значение не может быть пустым!");
        return;
        }
    else
        return oData;
}
Несмотря на то, что в документации по YUI написано, что валидатор YAHOO.widget.DataTable.validateNumber возвращает null при не успешной валидации, на самом деле он возвращает undefined. Поэтому и приведенный выше валидатор возвращает неопределенное значение, чтобы поле редактирования значения не закрывалось и невалидное значение полю не присваивалось.
Использование валидатора:
var myCols = [
{
key: "myNotEmptyField",
label: "Непустое поле",
editor: new YAHOO.widget.TextboxCellEditor({validator:requiredValueValidator}),
formatter: YAHOO.widget.DataTable.formatText
}
];

Formatter и parser даты в российском формате для YUI

YAHOO.widget.DataTable.formatDate использует американский формат отображения даты (ММ/ДД/ГГГГ), поэтому пришлось написать свой formatter для отображения дат по-русски  (ДД.ММ.ГГГГ):
var rusDateFormatter =  function(elCell, oRecord, oColumn, oData)
        {
            function addLeadingZero(num)
            {
                if(num<10)
                    return "0"+num;
                else
                    return num;
            }
         
            var date = new Date(oData);
            elCell.innerHTML = addLeadingZero(date.getDate())+"."+addLeadingZero(date.getMonth()+1)+"."+date.getFullYear();
        }
Этот formatter можно использовать при объявлении колонок DataTable:
var myCols = [
{
key: "someDate",
label: "Какая-то дата",
editor: new YAHOO.widget.DateCellEditor(),
formatter: rusDateFormatter
}
];
Тоже самое с парсером: YAHOO.util.DataSource.parseDate (или просто "date") умеет парсить только американские даты, т.к. используется Date.parse() JavaScript`а. Поэтому пришлось использоваться свой парсер:

var rusDateParser = function(oData)
{
    var parts = oData.toString().split('.');
    return new Date(parts[1]+"/"+parts[0]+"/"+parts[2]);
}
Теперь его можно использовать при задании схемы данных:
myDataSource.responseSchema =
    {
    fields: [
            {key:"someDate",parser:rusDateParser}
            ]
    }
UP: по крайней мере написание собственного formatter`а можно заменить передачей в качестве доп. опций конструктора DataTable:
dateOptions:{format:'%d.%m.%Y'} 

VBA: диалог выбора файла

Ниже представлена функция, открывающая диалог выбора файла (таблицы MS Excel) и возвращающая через имя путь к этому файлу или пустую строку, если файл не выбран:
Public Function GetFile()
Dim FName As String
Dim result As Integer
With Application.FileDialog(1)
    .Title = "Выберите xls / xlsx файл"
    .InitialFileName = "C:\" 'default path
    .AllowMultiSelect = False
    .Filters.Clear
    .Filters.Add "Таблицы Microsoft Excel", "*.xls; *.xlsx", 1
    End If
 
result = .Show
If result = 0 Then Exit Function
FName = Trim(.SelectedItems.Item(1))
End With
GetFile = FName
End Function

воскресенье, 18 декабря 2011 г.

Виджет календаря / агенды, аналогичный таковым в Sense и Launcher Pro

После перехода с Launcher Pro на Sony Ericsson Home столкнулся с проблемой отсутствия какого-либо вменяемого виджета для календаря / агенды. К моему сожалению, стандартный виджет "Календарь" Андроида не претерпел никаких изменений от версии 2.1 к версии 2.3 и остался таким же бесполезным. В результате часа поиска и проб разных виджетов наткнулся на пак виджетов, практически неотличимых от тех, что есть в сенсе и launcher pro. Называется он "Android Pro Widgets" и совершенно бесплатен. Правда, для разблокировки дополнительных опций необходим лицензионный ключ, стоящий около 80р, однако и без него виджет вполне заменяет по функционалу и внешнему виду сенсовский. Пришлось правда отключить опцию "Использовать быстрое событие", т.к. из-за неё вылетает календарь при попытке нажатия кнопки "Редактировать" в этом самом быстром событии (впрочем, мне лично это "быстрое событие" и не нужно, в Launcher Pro жил без него без особых неудобств).

суббота, 17 декабря 2011 г.

Создание кнопок запуска для запуска приложений от root`a в Linux

Задача:


Создать "Кнопку запуска" (на рабочем столе или какой-либо панели), запускающую какое-либо приложение от root`а (например, gedit).

Решение:

В поле "Команда" при добавлении кнопки ввести не просто имя приложения, а gksu имя_приложения, например, для запуска gedit из-под root`а, нужно ввести туда:
gksu gedit
В результате этого при нажатии на кнопку будет появляться графическое окно запроса sudo-пароля, такое же, как, скажем, появляется при запуске какой-либо задачи, требующей административных прав, например, менеджера пакетов Synaptic. 

пятница, 16 декабря 2011 г.

java.lang.IllegalArgumentException: Invalid Proxy при использовании Proxy.Type.HTTP для создания сокета

Проблема: 
При попытке использовать прокси типа Proxy.Type.HTTP для создания сокета следующим кодом:

Proxy prx = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("webprox", 8888));
Socket socket = new Socket(prx);
 вылетает исключение:

java.lang.IllegalArgumentException: Invalid Proxy
Решение:
Действительно, при создании ява-сокета использовать HTTP-прокси нельзя, что, в общем-то, и логично =), ведь HTTP-прокси умеет работать только с протоколом HTTP, а сокет является более низкоуровневым объектом, не привязанным ни к какому протоколу прикладного уровня. А вот в URL.openConnection HTTP-прокси использовать уже можно, если, конечно, используемый урлом протокол http или https.
Для создания сокета, работающего через прокси, можно использовать только прокси типа Proxy.Type.SOCKS (и, соответственно, Ваш прокси должен поддерживать Socks).

четверг, 15 декабря 2011 г.

Получение заголовков запроса из php


Если сервер - apache2 и используется mod_php, заголовки получаются вызовом apache_request_headers(). Чтобы код был переносимым и не зависел от сервера, под которым работает скрипт, можно сделать так:
if(!function_exists('apache_request_headers'))
{
function apache_request_headers()
{
$headers = array();
foreach($_SERVER as $key => $value) {
if (substr($key, 0, 5) <> 'HTTP_') {
continue;
}
$header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
$headers[$header] = $value;
}
return $headers;
}
}


Смена shell`а пользователя в Linux

chsh -s /path/to/shell username

вторник, 13 декабря 2011 г.

Формирование Орбеоном "эффективных" id элементов XBL-компонента при отрисовке страницы

При отрисовке страницы Орбеоном окончательные id ("эффективные" в терминологии Орбеона - http://wiki.orbeon.com/forms/doc/developer-guide/xforms-javascript-integration), присваиваемые элементам управления компонента (и по которым, можно, скажем, получить их значения  из JS вызовом ORBEON.xforms.Document.getValue()), отличаются от тех, которые были заданы в xbl-файле компонента.
Во-первых, в начало id-шника добавляется нечто вроде 'xf-396$', где вместо 396 будут другие цифры, выполняющие роль идентификатора компонента для разделения областей видимости id-шников разных компонентов, т.е. для разных экземпляров одного и того же компонента это число одинаково (не проверял, постоянно ли оно для последовательных загрузок формы или для различных запусков JVM, т.к. считаю, что в любом случае полагаться на это постоянство опасно). Если компонент вкладывается в другой компонент - соответственно, в начале id-шника оказывается уже несколько таких последовательностей.
Во вторых, если компонент вложен в repeat, то в конце к его id добавляется '·1', '·2' и т.п. в зависимости от текущей итерации repeat`а (где '·' - это символ U+00B7). Если repeat`ы - вложенные, то окончания эффективного id буду выглядеть как '·1-1', '·1-2', '·2-1' и т.п. (где первая цифра - итерация самого внешнего repeat`а, а после неё через дефис идут итерации вложенных в порядке углубления). Правило относительно repeat`ов относится не только к компонентам, но и к любым элементам управления.

Orbeon XPath: получение свойств из properties-local.xml

XPath-расширения Орбеона позволяют получать значения свойств, заданные в настроечном файле properties-local.xml (расположенном в 'WEB-INF\resources\config'). Так, например, XPath-выражение:
xxforms:property('some.property.name')
вернет содержимое свойства:
<property as="xs:anyURI"  name="some.property.name" value="http://ya.ru"/>

XPath trim(), а также выбор узлов, содержащих только пробелы

Функции trim(), удаляющей из строки концевые и начальные пробелы, в XPath нет, однако есть normalize-space(), делающая даже немного больше: она к тому же преобразует последовательности из нескольких пробелов внутри строки в один пробел.
Таким образом, условие для выбора узлов, пустых или содержащих только пробелы, выглядит следующим образом:
[normalize-space(text())='']

понедельник, 12 декабря 2011 г.

Установка CryptoPro CSP на Debian Lenny

Во-первых, для установки дистрибутива CryptoPro CSP на Debian понадобится установщик rpm-пакетов alien:
sudo apt-get install alien
Качаем с сайта CryptoPro дистрибутив под свою архитектуру, распаковываем, переходим в директорию с распакованными файлами, выполняем:
sudo ./install.sh
В консоль валится куча ошибок вида:
/var/lib/dpkg/info/lsb-cprocsp-kc1-64.postinst: line 8: /opt/cprocsp/sbin/amd64/cpconfig: Нет такого файла или каталога
Хотя файл cpconfig очень даже есть. После установки при попытке запуска этого файла видим абсурдное:
bash: ./cpconfig: Нет такого файла или каталога
Думал дело в отсутствующих библиотеках, попробовал посмотреть зависимости с помощью ldd:
ldd ./cpconfig
Однако и ldd не видит cpconfig О_о
Оказалось дело вот в чем (спасибо форуму криптопро: http://www.cryptopro.ru/forum2/default.aspx?g=posts&t=712): дистрибутив рассчитан на линухи, удовлетворяющие требованиям LSB. Debian требованиям этим не удовлетворяет (на самом деле, не особо в курсе LSB, надо будет на досуге почитать), поэтому необходимо установить пакет cprocsp-compat-altlinux-64-1.0.0-1.noarch.rpm (cprocsp-compat-altlinux-1.0.0-1.noarch.rpm для 32 бит) перед запуском install.sh:
sudo alien -kci cprocsp-compat-altlinux-64-1.0.0-1.noarch.rpm 
 Повторяем установку:
sudo ./uninstall.sh
sudo ./install.sh
На этот раз завалилась установка cprocsp-rdr-gui-64, но для меня это не важно, все равно иксов на сервере нет:

Настраивается пакет cprocsp-rdr-gui-64 (3.6.1-4) ...
dpkg: не удалось обработать параметр cprocsp-rdr-gui-64 (--install):
 подпроцесс post-installation script возвратил код ошибки 1
При обработке следующих пакетов произошли ошибки:
 cprocsp-rdr-gui-64
Unable to install at /usr/share/perl5/Alien/Package/Deb.pm line 92.
find cprocsp-rdr-gui-64-3.6.1 -type d -exec chmod 755 {} ;
rm -rf cprocsp-rdr-gui-64-3.6.1
 В остальном CSP заработал, можно, скажем, посмотреть лицензию:
/opt/cprocsp/sbin/amd64# ./cpconfig -license -view
The type of the license: DRV
Expires: 3 month(s) 1 day(s) 

Java: получение пути ко временной директории и создание временных файлов

Путь ко временной директории Явы получается вызовом: 
String tmpDir = System.getProperty("java.io.tmpdir");
В Win получаемая директория завершается слешем в конце, в Linux - нет.
Если путь ко временной директории нужен для создания временного файла, удобнее воспользоваться статическим методом File.createTempFile(), который сразу генерирует имя для временного файла с указанным префиксом и суффиксом. Например:
File tmpFile = File.createTempFile("upload_", ".jpg"); 
Таким образом, вы получаете новый файл, без риска попасть в уже существующий.

пятница, 9 декабря 2011 г.

Orbeon: отображение индикатора загрузки

Для того, чтобы вручную показать индикатор загрузки (появляющийся, например, когда orbeon отправляет запросы на сервер), нужно выполнить:
xformsDisplayIndicator('loading', 'Ваш текст для отображения на индикаторе');
Скрыть его можно следующим образом:

var form = ORBEON.xforms.Globals.requestForm;
if(form!=null && ORBEON.xforms.Globals.formLoadingLoadingOverlay[form.id]!=null)
     ORBEON.xforms.Globals.formLoadingLoadingOverlay[form.id].hide();

Orbeon: отображение и скрытие стандартной модальной панели ожидания

В Орбеоне есть своя модальная панель ожидания, которая затемняет всё окно и отображает крутящиеся стрелочки посередине (например, во время отправки формы). Показывается она вызовом:
ORBEON.util.Utils.displayModalProgressPanel();
, а скрывается:
ORBEON.util.Utils.hideModalProgressPanel()

Копирование информации из SAFEARRAY

SAFEARRAY используется в C++  для передачи массивов в COM-вызовах (причем массивов, состоящих из любых элементов и любой размерности). Ниже показан пример как извлечь из такого "массива" (в данном случае - одномерного) элементы в массив типа byte:
SAFEARRAY *retSafeArray = NULL;
// здесь где-то вызывается COM-метод, возвращающий результат в retSafeArray
long resultByteArraySize = (retSafeArray->rgsabound)[0].cElements; // получаем число элементов, т.е. размер единственного измерения нашего одномерного массива
byte* resultByteArray = new byte[scanResultArraySize];
HRESULT safeArrayExtractionRes;
byte arrElem;
for(long i=0;i<resultByteArraySize;i++)
{
safeArrayExtractionRes = SafeArrayGetElement(retSafeArray, &i, &arrElem);
resultByteArray[i] = arrElem;
if(safeArrayExtractionRes != S_OK)
{
SafeArrayDestroy(retSafeArray);
retSafeArray = NULL; throw new exception("Error extracting data from SAFEARRAY %u\n",safeArrayExtractionRes);
}
}
SafeArrayDestroy(retSafeArray); // удалить больше ненужный SAFEARRAY
retSafeArray = NULL;


JavaScript: Как установить текст внутри узла DOM, не являющегося текстовым

Задача:
Задать текст внутри узла типа ELEMENT_NODE (т.е. обычного узла DOM-дерева). Пусть узел содержится в переменной element, а текст - в переменной txt (и element.nodeType равен 1 или, в Firefox, константе element.ELEMENT_NODE).
Решение:
Создать дочерний узел типа 3 (TEXT_NODE) с заданным текстом:
var txtNode = document.createTextNode(txt);
element.appendChild(txt);
В принципе, можно было воспользоваться и не стандартизированным свойством element.innerHtml , но приведенный выше способ корректнее с точки зрения стандарта.

Javascript: убрать из строки символы перевода строки

mystr.replace(/[\r\n]/g,'')

четверг, 8 декабря 2011 г.

js-ctypes: получение значений по указателям, передача в функцию адреса переменной и возврат из функции указателя на массив.

Если переменная ptr была создана как указатель, скажем, на int:
var ptr = new ctypes.int.ptr;
то получить её значение можно (естественно, если она была передана в какую-то функцию по адресу, т.к. иначе этот указатель будет NULL и пытаться получить что-либо по нему бессмысленно) следующим образом:
ptr.contents
Это свойство вернет объект CData, представляющий значение по этому указателю, а собственно само целочисленное значение можно получить через свойство value этого объекта:
ptr.contents.value
Для передачи переменной ptr в функцию по адресу необходимо вызвать метод address() для неё:
someCFunction(ptr.address())
Эта функция сможет установить указатель ptr куда-либо, откуда потом данные можно считать.
Единственное, чего я не понял - если возвращаемый указатель указывает на массив, как получить доступ к элементам массива, кроме первого. ctypes.cast() к типу массива выполнить не получается (даже задавая размер массива при создании массивного типа равным реальному размера массива по возвращенному указателю), передавать в функцию вместо двойного указателя указатель на массив тоже не выходит - ругается ctypes. При возврате из функции указателя (не через аргумент, а обычным return) объявить в JS эту функцию как возвращающую не указатель, а массив, тоже не удается - пишет, что "Return type cannot be an array or function" (ну собственно как и в C, возвращать массив нельзя).
Пришлось в C делать отдельную функцию, которая вытягивает элементы из массива по индексу, не оптимально, но это единственный вариант, который я вижу.

среда, 7 декабря 2011 г.

Base64 кодирование и декодирование в C# (и вообще в .NET)

Чтобы не изобретать велосипед, можно воспользоваться стандартной функцией:
String System.Convert.ToBase64String(byte[])
И наоборот:
byte[] System.Convert.FromBase64String(String)

Использование WIA Automation в C#

Во-первых, COM-объект wiaaut.dll нужно зарегистрировать в системе, делается это следующим вызовом (из-под администратора):
C:\Windows\system32\regsvr32.exe C:\Windows\system32\wiaaut.dll
Теперь в проекте C# добавляем ссылку на COM-объект. В русскоязычной MS Visual C# Express 2010 это "Обозреватель решений" -> Выбираем текущее решение и проект -> "Ссылки" -> Клик правой кнопкой -> "Добавить ссылку", где переходим на вкладку "COM" и ищем "Microsoft Windows Image Acquisition Library 2.0", после чего нажимаем "ОК".
Всё, теперь в проекте доступно пространство имен WIA, в котором расположены все необходимые интерфейсы. Посмотреть их можно, щелкнув правой кнопкой по "WIA" в Ссылках, и выбрав "Просмотр в обозревателе решений". Например, для получения цветного изображения со сканера  в формате BMP с минимизацией размера получаемого файла используется следующий код:

WIA.CommonDialog dlg = new WIA.CommonDialog();
WIA.ImageFile img = dlg.ShowAcquireImage(WIA.WiaDeviceType.ScannerDeviceType, WIA.WiaImageIntent.ColorIntent,
WIA.WiaImageBias.MinimizeSize, "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}", false, true, false);
Вообще с WIA Automation как-то грустно всё в плане толковой документации и рабочих примеров, особенно на C#, или, не дай Бог, на C++ (есть кое-что на мсдн, но этого явно недостаточно). Как её использовать на чистом (не CLR) C++ я вообще не понял: ни заголовочного файла, ни tlb или idl я так и не нашел, а без них работать становится совсем неудобно. 

Регистрация COM-компонентов

Для регистрации .NET-сборок в .NET имеется инструмент regasm. Например, для регистрации сборки ext.dll необходимо выполнить следующее:
regasm.exe ext.dll /tlb /codebase
Такой вызов регистрирует сборку ext.dll используя файловый путь к ней, т.к. она не расположена в глобальном кэше сборок (опция /codebase) с генерацией библиотеки типов (tlb).
Для регистрации не-.NET компонентов используется regsvr32:
c:\windows\system\regsvr32 ext.dll
Зарегистрированные компоненты и их CLSID можно смотреть в HKEY_CLASSES_ROOT реестра. 

вторник, 6 декабря 2011 г.

Определение из JavaScript, является ли браузер Firefox 3.6.x


var mozIs36Regexp = /.*Firefox\/3\.6(.*)$/
var mozIs36 = mozIs36Regexp.test(window.navigator.userAgent);

Расширение Firefox: Получение пути к файлу xpi-пакета расширения из JavaScript

Задача:
Из JavaScript-кода расширения Firefox получить путь (полноценный абсолютный путь файловой системы) к какому-либо файлу из xpi-пакета приложения, который при установке расширения распаковывается в AppData пользователя. Например, пусть нужен путь к файлу ext.dll, размещенного в папке "components/" xpi-архива, для его последующей загрузки функцией ctypes.open() библиотеки ctypes.

Решение:
Для версий Firefox 4.0+ (Gecko 2.0 и выше) имеется компонент AddonManager, предназначенный для работы с расширениями, и, в частности, позволяющий получать абсолютные пути к любым ресурсам расширения следующим образом:
addonId = "myaddon@somesite.org";// id, указанный в теге <em:id> install.rdf расширения
Components.utils.import("resource://gre/modules/AddonManager.jsm");
AddonManager.getAddonByID(addonId, function(addon)
{
var uri = addon.getResourceURI("components/ext.dll");
if (uri instanceof Components.interfaces.nsIFileURL && uri.file.path)
{
// работаем с путем uri.file.path, например, сохраняем его в какой-нибудь переменной, видимой снаружи
}
else
console.debug(addonId+": getAddonByID callback: uri is not Components.interfaces.nsIFileURL or uri.file.path is null or empty");
});
Однако, если Вам необходимо, чтобы Ваше расширение работало и в Firefox 3.6, придется изменить подход (вернее, можно использовать код, приведенный выше, для новых FF, и отдельный код для 3.6). Вызвана такая необходимость отсутствием в 3.6 AddonManager`а. Выход следующий: разместить файл, к которому нужно получить доступ, в chrome-реестре браузера, после чего воспользоваться преобразованием Chrome URI -> путь файловой системы. Размещение файла в chrome-реестре описывается в chrome.manifest файле расширения. В моем случае оно выглядело так (я продублировал нужную мне dll-ку, поместив её также в chrome/content/ xpi-пакета):
content     myaddon     chrome/content/
Теперь моя dll-ка доступна по chrome-пути "chrome://myaddon/content/ext.dll" (можно проверить, набрав такой URL в браузере). Для того, чтобы получить к ней абсолютный путь файловой системы, понадобится чуть больше кода, чем для 2ого Gecko (для работы данного кода нужен также енкодер/декодер URL, код которого я приводил тут):
/**
* Создает объект URI (реализующий интерфейс nsIURI) из переданной строки-URI, и, опционально, кодировки и базового URI
*/
function makeURI(aURL, aOriginCharset, aBaseURI)
{
var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
return ioService.newURI(aURL, aOriginCharset, aBaseURI);
}
/**
* Получает путь в фаловой системе к объекту, хранимому в chrome-реестре FF.
*/
function getFilepathFromChromeURL(chromeURL)
{
var chromeRegistry = Components.classes["@mozilla.org/chrome/chrome-registry;1"].getService(Components.interfaces.nsIChromeRegistry);
var convertedURI;
try
{
convertedURI = chromeRegistry.convertChromeURL(makeURI(chromeURL,null,null));
}
catch(ex)
{
return false; }
if(convertedURI.scheme!="file")
return false;
return Url.decode(convertedURI.path.substring(1));
}
myPath = getFilepathFromChromeURL("chrome://myaddon/content/ext.dll");
В итоге в переменной myPath мы получим абсолютный путь к искомому файлу или false при ошибке.

понедельник, 5 декабря 2011 г.

Создание Document из строки в Orbeon

Подходы для парсинга строки в дерево Document на JavaScript различаются в разных браузерах (в IE используется ActiveX-компонент Microsoft.XMLDOM, в Firefox - XMLSerializer), но при написании кода для Orbeon можно использовать его кроссбраузерную функцию ORBEON.util.Dom.stringToDom().

пятница, 2 декабря 2011 г.

Создание подписи XML Signature, независимой от пространств имен, объявленных выше по иерархии по отношению к подписываемому узлу, но реально не использующихся внутри подписываемого поддерева

При использовании метода канонизации xml-документа "Canonical XML" (http://www.w3.org/TR/2001/REC-xml-c14n-20010315) получаемая подпись (здесь речь идет о случае, когда подписывается один узел xml-дерева, а не весь документ целиком) будет зависеть от пространств имен, объявленных в элементах выше по иерархии в дереве по отношению к подписываемому узлу, даже если эти пространства не используются внутри подписываемого поддерева. Таким образом, если контекст подписанного узла будет изменен (он будет перемещен в пределах этого же дерева или перемещен в другое xml-дерево), подпись может стать недействительна, если пространства имен, объявленные выше по иерархии, изменятся. В таких случаях нужно использовать метод канонизации "Exclusive Canonical XML" (http://www.w3.org/2001/10/xml-exc-c14n#), который не учитывает объявленные выше по иерархии пространства имен (кроме пространств имен, которые действительно используются внутри подписываемого поддерева, но объявлены выше по иерархии). В C# изменение метода канонизации делается указанием:

signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NWithCommentsTransform;
XmlDsigExcC14NWithCommentsTransform transform = new XmlDsigExcC14NWithCommentsTransform();
reference.AddTransform(transform );

В этом примере signedXml - объект SignedXml, представляющий подписываемую XML, reference - объект Reference, указывающий на подписываемый узел (для которого выполняется signedXml.AddReference(reference); перед вычислением подписи). В примере использован стандарт   "Exclusive Canonical XML with comments" (http://www.w3.org/2001/10/xml-exc-c14n#WithComments), корректно работающий с комментариями в XML.

Orbeon: создание XML-элемента с пространством имен с помощью XPath

Задача: 

Создать элемент с пространством имен, описанным в этом же элементе (установить xmlns как атрибут, передав его вторым параметром функции xxforms:element, нельзя - Orbeon просто проигнорирует атрибуты с именем xmlns или из пространства имен xmlns). Например:
<foo:mytag xmlns:foo='some-name-space'/>

Решение:

xxforms:element(QName('some-name-space'),'foo:mytag'))

или, если, к примеру, нужно взять namespace другого узла:

xxforms:element(QName(namespace-uri(/path/to/other/node),'foo:mytag')

Правда, такое создание элемента связано со следующей проблемой (вернее, скорее всего, багом орбеона): при использовании пространства имен по-умолчанию (т.е. передавая в QName имя элемента без пространства имен), элемент не создается. Если пытаться выполнить эту операцию в XForms Inspector, вылетает "java.lang.StringIndexOutOfBoundsException: String index out of range: -1", при использовании этого XPath в других местах он также приводит к некорректному поведению орбеона. Другого способа создавать элементы с пространством имен по-умолчанию я не нашел, разве что описывать элемент строкой, после чего парсить её с помощью saxon:parse:

saxon:parse('<mytag xmlns=''some-name-space''/>')
В таком случае, однако, нельзя одновременно с созданием элемента добавить ему потомков без их сериализации в строку, как это позволяет сделать xxforms:element.

JavaScript: преобразование Document в String

Реализации различаются для IE и Firefox, насчет работоспособности в других браузерах - не пробовал:

function XMLtoString(elem)
{ var serialized; try {
// XMLSerializer exists in current Mozilla browsers
serializer = new XMLSerializer();
serialized = serializer.serializeToString(elem);
}
catch (e) {
// Internet Explorer has a different approach to serializing XML
serialized = elem.xml;
} return serialized;
}

Источник: http://www.sencha.com/forum/showthread.php?34553-Convert-DOM-XML-Document-to-string.

четверг, 1 декабря 2011 г.

Сборка C# из командной строки

Для сборки cs-файлов без использования Visual Studio имеется утилита csc.exe, расположенная в директории вроде C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc.exe. Пример вызова:

C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc.exe /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Security.dll /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll  /target:library project.cs

xpath: получение текста узла

Тривиально, но всё же:
/path/to/text/node/text()

среда, 23 ноября 2011 г.

Javascript URL encode / decode

Следующий объект предоставляет методы encode() и decode() для, соответственно, кодирования и декодирования строк с использованием URL-метода кодирования.
/**
*
* URL encode / decode
* http://www.webtoolkit.info/
*
**/

var Url = {

// public method for url encoding
encode : function (string) {
return escape(this._utf8_encode(string));
},

// public method for url decoding
decode : function (string) {
return this._utf8_decode(unescape(string));
},

// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";

for (var n = 0; n < string.length; n++) {

var c = string.charCodeAt(n);

if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}

}

return utftext;
},

// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;

while ( i < utftext.length ) {

c = utftext.charCodeAt(i);

if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}

}

return string;
}

}
 Важно то, что используется стандарт RFC 3986, кодирующий пробел как "%20", а не как "+" (так делается при кодировании в соответствии с типом контента application/x-www-form-urlencoded). При необходимости получить строку, закодированную именно как application/x-www-form-urlencoded, необходимо в полученной с помощью метода encode() строке выполнить замену "+" на "%2B", а "%20" - на "+".  

Windows: управление сертификатами пользователя не через Internet Explorer

Управлять сертификатами пользователя можно через IE (Свойства обозревателя -> Содержание -> Сертификаты). Но можно это делать и без него: Выполнить -> mmc -> Файл -> Добавить или удалить оснастку -> Найти в списку "Сертификаты" и нажать "Добавить", а потом - "Ок". В дереве консоли появится пункт "Сертификаты", позволяющий управлять сертификатами. Параметры консоли необходимо сохранить в msc-файл, запуская который Вы будете попадать в только что созданную консоль управления сертификатами.

вторник, 22 ноября 2011 г.

PHP Startup: Unable to load dynamic library '.../php_pgsql.dll' - Не найден указанный модуль

Проблема:
При попытке запуска apache + php под Windows с подключенным в php.ini расширением php_pgsql.dll в логи апача вываливает ошибка:
PHP Startup: Unable to load dynamic library '.../php_pgsql.dll' - Не найден указанный модуль
Ну и модуль, естественно, не загружается.
Причина:
Php не может найти динамические библиотеки Postgres, необходимые расширению для работы.
Решение:
Добавить директории bin и lib установки Postgres в системный PATH (например, E:\Programs\Postgres8.3\lib;E:\Programs\Postgres8.3\bin).

воскресенье, 20 ноября 2011 г.

Как мне НЕ удалось подружить WSO2 WSF/PHP 2.1.0 и 1С-Битрикс Веб-Окружение 2.1.

Собственно возникла необходимость использования внутри компонента 1С-Битрикс функционала SOAP-клиента, и для этого был выбран WSO2 WSF/PHP (http://wso2.org/library/wsf/php).
Для начала был скачан бинарник WSF/PHP версии 2.1.0, распакован (в директорию "F:\Install\wso2-wsf-php-bin-2.1.0-win32\"), wsf.dll был положен в папку "apache2\zendserver\lib\phpext\" относительно директории установки окружения 1С-Битрикс. В php.ini Битрикса (apache2\zendserver\etc\php.ini) были внесены следующие изменения:
...
[Zend]
include_path=".;F:\Install\wso2-wsf-php-bin-2.1.0-win32\scripts"
...
extension=php_xsl.dll
extension=wsf.dll
...
wsf.home="F:\Install\wso2-wsf-php-bin-2.1.0-win32\wsf_c"
wsf.log_path="F:\Install\wso2-wsf-php-bin-2.1.0-win32\wsf_c\logs"
wsf.log_level=3
wsf.rm_db_dir="F:\Install\wso2-wsf-php-bin-2.1.0-win32\wsf_c"
...
Однако при попытке запустить битрикс стала появляться ошибка о том, что не найден файл "php5ts.dll". Действительно, в директории apache2\zendserver\bin можно найти только php5.dll, т.к. php в Битриксе видимо не-threadsafe. Ладно, попробуем подложить туда ещё и php5ts.dll (взят он был из php 5.3.5). Теперь сообщение об отсутствующем php5ts.dll ушло, однако расширение wsf все равно не подключается, а в логах видим:
[20-Nov-2011 17:23:36] PHP Warning:  PHP Startup: wsf: Unable to initialize module
Module compiled with build ID=API20090626,TS,VC9
PHP    compiled with build ID=API20090626,NTS,VC9
These options need to match
 in Unknown on line 0
Теперь build ID модуля конфликтует с версией битриксовского php - у битрикса php non-threadsafe, о чем говорит аббревиатура NTS, а модуль собран для threadsafe (TS). 
Придется видимо собирать WSF из сорцов, благо они есть на wso2.org.
Помимо самих сорцов, пришлось скачать и установить Win32 OpenSSL (http://www.slproweb.com/products/Win32OpenSSL.html), ну и конечно же, понадобилась Visual Studio. Лучше девятка (2008). У меня была только 2010, что в дальнейшем и послужило граблями. Также понадобились исходники и бинарники php, которые были скачаны с php.net (бинарники, правда, были взяты threadsafe, что в дальнейшем послужило проблемой). В принципе, в качестве бинарников можно было взять php из самого Битрикса, однако их там в явном виде не присутствует, т.к. в основе окружения битрикса лежит Zend Server, так что пришлось брать со стороны.
Для того, чтобы собрать не-threadsafe версию WSF/PHP, нужно в configure.in распакованных исходников закомментировать строку:
# ZTS =1
Далее в этом же файле нужно прописать путь к директории установки Open SSL, к исходниками и бинарникам php. В моём случае это выглядело так:
OPENSSL_BIN_DIR = E:\Programs\OpenSSL-Win32
PHP_SRC_DIR = F:\Install\php-5.3.8-src
PHP_BIN_DIR = C:\php\php5.3.8
Перед сборкой необходимо проверить, что директория VC\bin относительно директории установки Visual Studio включена в PATH.
Пробуем собрать, запуская build.bat в директории исходников WSF/PHP, однако получаем ошибку:
f:\install\php-5.3.8-src\zend\zend_config.w32.h(25) : fatal error C1083: Cannot open
include file: '../main/config.w32.h': No such file or directory
Оказывается, прежде чем выполнять build.bat исходников WSF/PHP, нужно запустить в директории исходников php:
buildconf.bat
cscript configure.js
После этого при запуске cscript вылезла ошибка "Отсутствует исполняющее ядро для расширения имени файла ".js"" - см. http://barbitoff.blogspot.com/2011/11/cscript-js.html, потом -  "The application has failed to start bacause mspdb100.dll was not found ..." при попытке вызова cl.exe, пришлось предварительно запустить vcvars32.bat из Visual Studio (см. http://barbitoff.blogspot.com/2011/11/ms-visual-studio-clexe-mspdb100dll-not.html).
Но и тут ждет неудача:
ERROR: bison is required
Идем на http://gnuwin32.sourceforge.net/packages/bison.htm, качаем и устанавливаем Бизон, добавляем путь к его исполняемым файлам (расположены в bin директории) в PATH.
После этого configure.js отработал удачно, создав недостающий ранее файл "main/config.w32.h".
Возвращаемся в исходники WSF/PHP, жмем build.bat, но снова ошибка:
LINK : fatal error LNK1181: cannot open input file 'php5.lib'
Библиотеку линкер ищет в бинарниках php, и, естественно, там её не находит, т.к. бинарники threadsafe, и там есть только php5ts.lib. Берем php5.lib из "apache2\zendserver\bin" Битрикса, кладем в папку dev бинарников php. Снова делаем build.bat... и ура, WSF собрался.
Кладем wsf.dll куда надо, меняем пути в php.ini к новым, только что собранным директориям. Пытаемся запустить Битрикс, и видим в логах:
[20-Nov-2011 21:21:12] PHP Warning:  PHP Startup: wsf: Unable to initialize module
Module compiled with build ID=API20090626,NTS,VC10
PHP    compiled with build ID=API20090626,NTS,VC9
These options need to match
 in Unknown on line 0
Да, теперь не совпадает версия студии. И на этом моменте надо было бы поискать 2008 студию, но я решил обмануть php. В Zend\zend_build.h исходников php поправил 
#define ZEND_BUILD_SYSTEM "," PHP_COMPILER_ID
на 
#define ZEND_BUILD_SYSTEM ",VC9"
Пересобрал WSF/PHP. Модуль наконец-таки загрузился, вот только примеры из дистрибутива не работают, php падает молча, а в логах винды видно:
Имя сбойного приложения: php-cgi.exe, версия: 5.3.2.0, отметка времени: 0x4c0b8710
Имя сбойного модуля: MSVCR90.dll, версия: 9.0.30729.4974, отметка времени 0x4b7a226f
Код исключения: 0xc0000005
Смещение ошибки: 0x0003ae7a
Идентификатор сбойного процесса: 0x20fc
Время запуска сбойного приложения: 0x01cca817c8b93692
Путь сбойного приложения: E:\Programs\Bitrix Environment\Apache2\ZendServer\bin\php-cgi.exe
Путь сбойного модуля: C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.4974_none_50940634bcb759cb\MSVCR90.dll
Код отчета: 0845dff7-140b-11e1-8fee-fcf4765882af
Мда, придется все-таки пересобрать 9ой студией, вдруг поможет.
2008 студия поставлена, теперь нужно перезапустить config.js исходников php (на всякий случай), для этого потребуется, чтобы в PATH были директории "Common7\Tools" и "VC\bin" 2008 студии, причем в таком порядке и перед путями к 2010 студии (удобнее менять в данном случае не глобальный PATH системы, а устанавливать PATH командой set).

И да, слеши в путях в php.ini пришлось поставить в другую сторону, как в Unix ("/"), т.к. глупый WSF использует "/" независимо от платформы, конкатенируя свои пути с путями из php.ini.

Однако и после всех этих манипуляций собранная 2008-ой студией библиотека WSF работать отказалась, с теми же сообщениями в логах, что и собранная 2010-ой.

Плюнув на самостоятельную сборку non-threadsafe библиотеки WSF, я развернул битрикс не в его родном веб-окружении, а на wamp 2.2a, благо там php threadsafe и собран 2008-ой студией, т.е. бинарник WSF должен подойти. Собственно, ожидания оправдались, WSF подключился, тестовые примеры заработали.

Дальше правда всё снова стало грустно: при попытке создать клиент по моей wsdl WSF стал молча виснуть, ничего не говоря в логи, после чего я забил на него и воспользовался стандартной php-шной библиотекой php_soap, которая, по крайней мере по функционалу soap-клиента, ничем не хуже WSF.

cscript: ошибка "Отсутствует исполняющее ядро для расширения имени файла ".js""

Проблема: при попытке запуска js-файла из командной строки с помощью команды cscript (например, "cscript configure.js" при компиляции php) появляется ошибка:
Отсутствует исполняющее ядро для расширения имени файла ".js"
Причина: нарушена файловая ассоциация файлов с расширением js.
Решение: запустить cmd из-под администратора, выполнить
ASSOC .JS=JSFile

MS Visual Studio 2010: cl.exe и "mspdb100.dll not found".

Проблема: при вызове компилятора MS Visual Studio 2010 cl.exe из командной строки получаем ошибку: "The application has failed to start bacause mspdb100.dll was not found ...".
Решение:  перед вызовом непосредственно компилятора cl.exe необходимо запустить в той же командной строке vcvars32.bat.

пятница, 18 ноября 2011 г.

"1С-Битрикс Веб-окружение": добавление поддержки Postgres в php

В "Веб-окружении" "1С-Битрикс" php-модуль для работы с Postgres уже есть, нужно только включить его в php.ini, расположенном в apache2\zendserver\etc относительно директории установки окружения:
extension=php_pgsql.dll

суббота, 12 ноября 2011 г.

пятница, 11 ноября 2011 г.

MySQL: как убрать NUL в конце строк

Проблема: 
при переносе БД из другой СУБД в конце всех перенесенных varchar-строк оказался символ NUL (или "00" в шестнадцатеричном представлении, что является символом конца строки, например, в С, но в БД этот лишний байт вовсе не нужен).
Решение:
Решается проблема простым UPDATE`ом, который удалит лишний байт в конце всех строк, если он там есть (строки расположены в столбце brand таблицы manufacturers):

UPDATE `manufacturers`
SET brand=SUBSTRING(brand,1,CHAR_LENGTH(brand)-1)
WHERE HEX(SUBSTRING(brand FROM CHAR_LENGTH(brand)))='00'

пятница, 28 октября 2011 г.

js-ctypes в Firefox 3.6

В Firefox 3.6 и Gecko 1.9.2 js-stypes ещё был немного недоразвит, но всё же работоспособен.
Для передачи C-функции параметров-строк (или для возврата строк), использующих Юникод (т.е. wchar на стороне C), можно использовать ctypes.ustring вместо появившегося позже типа ctypes.jschar.ptr.
Также в 3.6 нет вообще указателей, т.е. нельзя получить из типа указатель на него, обратившись к полю ptr, и мне кажется, что обойти это ограничение нельзя.
Для типов ctypes.int и ctypes.long имеются аналоги ctypes.int16_t и ctypes.int32_t.

Копирование из BSTR в wchar_t


Ниже приведен пример, демонстирующий, как можно скопировать данные из строки типа BSTR (полученной, например, после вызова метода COM-объекта, возвращающего строку) в строку типа wchar_t*:
UINT length = SysStringLen(myBstr);        // определяем длину BSTR
wchar_t *myString = new wchar_t[lenght+1]; // используем "+1" т.к. SysStringLen не учел nul в конце строки
wcscpy_s(myString ,wcslen(myString ), myBstr); // копируем, используя безопасную wcscpy_s вместо wcscpy, помеченной как "deprecated"
SysFreeString(myBstr); // скопированную уже BSTR-строку теперь можно корректно удалить

четверг, 27 октября 2011 г.

Обращение к COM-объектам из C++

Для того, чтобы в коде C++ обращаться к методам некоторого COM-объекта, нужно следующее:
1) Импортировать tlb COM-объекта, с которым будем работать (импорт этот статический, выполняется на этапе компиляции):
#import "someCom.tlb" raw_interfaces_only
2) Перед началом работы с COM-объектом инициализировать COM, после окончания - деинициализировать:
HRESULT hr = CoInitialize(NULL);
if(SUCCEEDED(hres = CoInitialize(NULL)))
     {
     ... // тут идет создание COM-объекта, вызов его методов
     CoUninitialize();
     }
3) Создать указатель на COM-объект для последующей работы. Пусть COM-объект, с которым мы хотим работать, имеет пространство имен COMObjectNamespace, имя интерфейса COMInterfaceName и имя класса, реализующего этот интерфейс - COMClassName. Тогда создается ссылка на объект так:
     COMObjectNamespace::COMInterfaceNamePtr myComObj(COMObjectNamespace::COMClassName);
Далее полученную ссылку myComObj можно использовать как указатель для вызова методов, VisualStudio даже будет подсказывать объявления методов.

Информация взята отсюда: http://support.microsoft.com/kb/828736, где рассмотрен пример взаимодействия по технологии COM между C++-программой и модулем на C#.

среда, 26 октября 2011 г.

Создание BSTR из nsAString и наоборот

BSTR - тип, используемый для строк в COM.
nsAString - тип, используемый для строк в XPCOM от мозиллы.

bstr_string = SysAllocString(nsastring_string.BeginReading());

И наоборот:

nsastring_string.Assign((wchar_t*)bstr_string ,wcslen(bstr_string ));

(предполагается, что в качестве nsAString::char_type используется PRUnichar, который, в свою очередь, является wchar_t).

вторник, 25 октября 2011 г.

Создание строк типа BSTR

BSTR  - тип данных, применяемый в С/С++ для строк при работе с COM. Создается строка типа BSTR вызовом:
BSTR bstrString = SysAllocString(new OLECHAR(L'Hello world'));
или
wchar_t *someStr; ...
BSTR bstrString = SysAllocString(someStr);

Подробнее про тип BSTR прочитать можно тут: http://blogs.msdn.com/b/ericlippert/archive/2003/09/12/52976.aspx

Создание расширения Firefox на C++ c использованием XPCOM

Внимание: использование XPCOM уже не рекомендуется разработчиками Mozilla (как минимум из-за того, что придется перекомпилировать бинарные компоненты с выходом новой версии FF, которые теперь будут выходить часто). В версиях FF 4+ можно использовать гораздо более простой способ - js-ctypes (https://developer.mozilla.org/en/js-ctypes/Using_js-ctypes). js-ctypes, с небольшими изменениями, работает даже в FF 3.6.20 (например, там нет ctypes.char, но есть ctypes.ustring).

Уф, один рабочий день и наконец-то получилось понять, как же все-таки писать расширения для FF с использованием XPCOM. MDN оказался куда хуже чем я ожидал - туториалы все древние и не работают с новыми версиями xulrunner-sdk, а то, что удалось накопать по новому движку Gecko2 - толком не описано, поэтому пришлось искать решение практически методом тыка.

Задача для начала простая - написать расширение, к которому можно было обратиться из JavaScript`а на веб-странице  вызвать метод echo, принимающий на входе строку и возвращающий её же.

Для начала качаем Gecko SDK, он же xulrunner-sdk: http://releases.mozilla.org/pub/mozilla.org/xulrunner/releases/7.0.1/sdk/xulrunner-7.0.1.en-US.win32.sdk.zip. Затем - проект-пример: https://developer.mozilla.org/samples/xpcom/xpcom-test.zip. Проект создан для старой версии xulrunner, поэтому компилироваться не будет, но всё же удобнее использовать его чем начинать с нуля. 
Пусть xulrunner-sdk распакован в "O:\xulrunner-sdk", а проект-пример - в O:\xpcom-test.

Во-первых, нужно описать интерфейс будущего расширения на языке idl (это не майкросовтовский IDL, а мозилловский собственный). Создаем файл (пусть он будет называться echo.idl, его нужно положить в O:\xpcom-test) со следующим содержимым (uuid можно сгенерировать любым генератором UUID):
#include "nsISupports.idl"
[scriptable, uuid(27110427-209a-4f4b-b829-8ca5ebe356a6)]
interface ISpecialThing : nsISupports
{
 AString echo(in AString srcStr);
};
В проект нужно внести следующие изменения:
  • Открываем xpcom-test/xpidl-build.bat и меняем его содержимое на:
    ..\xulrunner-sdk\bin\xpidl.exe -m header -I..\xulrunner-sdk\idl echo.idl
    ..\xulrunner-sdk\bin\xpidl.exe -m typelib -I..\xulrunner-sdk\idl echo.idl
    Запускаем батник, находясь в O:\xpcom-test (создадутся файлы echo.xpt и echo.h).
  • Открываем проект xpcom-test (я использовал MS VS 2005: во-первых, другого под рукой не нашлось, да и проект подойдет для него без конвертации). 
    • Заходим в свойства проектаC/C++ > General > Additional Include Directories, меняем на "..\xulrunner-sdk\include".
    • Затем - в Linker > General > additional Library Directories, ставим "..\xulrunner-sdk\lib"
    • Linker > Input > Additional Dependencies, "nspr4.lib xpcom.lib xpcomglue_s.lib mozalloc.lib"
  • Исключаем из проекта все файлы, кроме созданного нами idl. В свойствах idl-файла отменяем его компиляцию ("Exclude from build" = Yes). Включаем в проект созданный в пункте 1 h-файл с заголовком интерфейса. В комментариях этот файл содержит заготовку для класса, реализующего этот интерфейс.
  • Создаем класс, реализующий описанный в h-файле интерфейс. Пусть он будет называться echoImpl. Ниже приведен код заголовочного файла этого класса:
#include "echo.h"
#include "nsStringAPI.h"
#define ECHO_CONTRACTID "@some.domain.com/echosample;1"
#define ECHO_CLASSNAME "echosample"
#define ECHO_CID { 0xe99152f7, 0x73f7, 0x45d3, { 0x8e, 0x94, 0x26, 0x3e, 0x21, 0x10, 0x63, 0x57 } }
static const nsCID kEchoCID = IACSIGN_CID;
class echoImpl: public ISpecialThing
{
public:
  NS_DECL_ISUPPORTS
  NS_DECL_ISPECIALTHING
  echoImpl();
  ~echoImpl();
};
Define`ами в данном файле задает 3 важных параметра будущего компонента:  
    • ECHO_CONTRACTID - contract id компонента, имеющий вид домен/модуль/компонент;версия и используемый для идентификации компонента в массиве Components.classes при обращении из JavaScript.
    • ECHO_CLASSNAME - имя класса компонента (не нашел пока, где оно используется при эксплуатации компонента)
    •  ECHO_CID - уникальный GUI класса компонент. Сгенерировать его можно любым средством генерации GUID (например, http://www.guidgenerator.com/), после чего представить в указанном виде.
Теперь - само тело класса:
#include "echoImpl.h"
#include "nsStringAPI.h"
#include "nsIClassInfoImpl.h"
#include "nsMemory.h"
NS_IMPL_CLASSINFO(echoImpl, NULL, 0, ECHO_CID)
NS_IMPL_ISUPPORTS1_CI(echoImpl, ISpecialThing) 
echoImpl::echoImpl(){}
echoImpl::~echoImpl(){} 
NS_IMETHODIMP echoImpl::echo(const nsAString & srcStr, nsAString & _retval NS_OUTPARAM)
{
return NS_StringCopy(_retval,srcStr);
}
Тут все предельно просто - пара макросов и собственно метод echo.
  • Теперь необходимо описать модуль - код, который будет сообщать FF о написанном нами модуле. Создаем файл echoModule.cpp со следующим наполнением (код взят из примера http://mxr.mozilla.org/mozilla-central/source/xpcom/sample/, который, правда, не является 100% рабочим):
#include "echoImpl.h"
#include "mozilla/ModuleUtils.h"
#include "nsIClassInfoImpl.h"
////////////////////////////////////////////////////////////////////////
// With the below sample, you can define an implementation glue
// that talks with xpcom for creation of component nsSampleImpl
// that implement the interface nsISample. This can be extended for
// any number of components.
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Define the contructor function for the object nsSampleImpl
//
// What this does is defines a function nsSampleImplConstructor which we
// will specific in the nsModuleComponentInfo table. This function will
// be used by the generic factory to create an instance of nsSampleImpl.
//
// NOTE: This creates an instance of nsSampleImpl by using the default
// constructor nsSampleImpl::nsSampleImpl()
//
NS_GENERIC_FACTORY_CONSTRUCTOR(echoImpl);
// The following line defines a kNS_SAMPLE_CID CID variable.
NS_DEFINE_NAMED_CID(ECHO_CID);
// Build a table of ClassIDs (CIDs) which are implemented by this module. CIDs
// should be completely unique UUIDs.
// each entry has the form { CID, service, factoryproc, constructorproc }
// where factoryproc is usually NULL.
nsresult myF (nsISupports* aOuter,
                 const nsIID& aIID,
                 void** aResult) {
*aResult = new echoImpl();
return NS_OK;
}
static const mozilla::Module::CIDEntry kSampleCIDs[] = {
{ &kEchoCID , false, NULL, myF },
    { NULL }
};
// Build a table which maps contract IDs to CIDs.
// A contract is a string which identifies a particular set of functionality. In some
// cases an extension component may override the contract ID of a builtin gecko component
// to modify or extend functionality.
static const mozilla::Module::ContractIDEntry kSampleContracts[] = {
    { ECHO_CONTRACTID , &kEchoCID },
    { NULL }
};
// Category entries are category/key/value triples which can be used
// to register contract ID as content handlers or to observe certain
// notifications. Most modules do not need to register any category
// entries: this is just a sample of how you'd do it.
// @see nsICategoryManager for information on retrieving category data.
static const mozilla::Module::CategoryEntry kSampleCategories[] = {
    //{ "my-category", "my-key", ECHO_CONTRACTID },
    { NULL }
};
static const mozilla::Module kSampleModule = {
    mozilla::Module::kVersion,
    kSampleCIDs,
    kSampleContracts,
    kSampleCategories
};
// The following line implements the one-and-only "NSModule" symbol exported from this
// shared library.
NSMODULE_DEFN(nsSampleModule) = &kSampleModule;
// The following line implements the one-and-only "NSGetModule" symbol
// for compatibility with mozilla 1.9.2. You should only use this
// if you need a binary which is backwards-compatible and if you use
// interfaces carefully across multiple versions.
// NS_IMPL_MOZILLA192_NSGETMODULE(&kSampleModule)

Здесь стоит обратить внимание на функцию myF - она возвращает объект созданного ранее класса echoImpl. В примере с MDN её не было (точнее имя-то функции было, а вот тела - нет).

Всё, проект можно компилировать. Если всё пройдет ок, то на выходе получим dll-файл, и представляющий собой компонент (к совокупности с полученным ранее xpt-файлом, описывающим его интерфейс).

Теперь дополнение необходимо добавить в FF. Для этого понадобиться xpi-файл со следующей структурой:
  • components/ - директория, куда нужно положить колченные xpt и dll файлы
  • chrome.manifest - файл реестра FF, описывающий компонент, со следующим содержимым:
binary-component components/echo.dll
interfaces components/echo.xpt
component {99152f77-3f74-5d38-e942-63e21106357} components/echo.dll
Здесь в фигурных скобках указан GUID, установленный в коде компонента с помощью константы ECHO_CID.

  •  install.rdf - файл, описывающий компонент и необходимый для его установки. Минимальное содержимое файла следующее:
<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
  <Description about="urn:mozilla:install-manifest">
    <em:id>echo@demo.com</em:id>
    <em:type>2</em:type>
    <em:name>Echo echo</em:name>
    <em:version>0.2.1</em:version>
    <em:creator>barbitoff</em:creator>
    <em:contributor/>
    <em:description>Blablabla echo</em:description>
    <em:aboutURL/>
<em:unpack>true</em:unpack>
    <em:targetApplication>
      <Description>
        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!--FF-->
        <em:minVersion>3.0</em:minVersion>
        <em:maxVersion>7.*</em:maxVersion>
      </Description>
    </em:targetApplication>
  </Description>
</RDF>
Такое содержимое  install.rdf позволит установить xpi на версии FF от 3.0 до 7.* (т.е. любой 7ой версии).
Всё, теперь пакуем созданную структуру в zip-файл, меняем расширение на xpi и перетаскиваем его на окно FF. Компонент должен успешно установиться, перезапускаем FF. Вызвать наш echo-метод из JS можно:
var a = Components.classes["@some.domain.com/echosample;1"].createInstance();
alert(a.echo("HELLO WORLD!"));
Примечание - на эту операцию требуется разрешение. Читал, что оно устанавливается вызовом netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect") (естественно, использовать его можно только в тестовых целях, а не на боевых веб-страницах), но у меня почему-то и с ним не работает. Приведенный выше вызов работает из js-консоли Firebug, и должен также работать при вызове из js-кода расширения.

понедельник, 24 октября 2011 г.

PRBool

PRBool - мозилловская вариация на тему обычного булева типа. По сути является enum`ом с 2 значениями: PR_FALSE = 0 и PR_TRUE = 1.

Пример использования:

NS_IMETHODIMP myModule::CanUnload(nsIComponentManager *aCompMgr, PRBool *_retval NS_OUTPARAM)
{
*_retval = PR_TRUE;
return NS_OK;
}

четверг, 20 октября 2011 г.

WSO2ESB и endpoint из реестра WSO2GREG

При добавлении WSDL в реестр WSO2GREG он создает в ветке реестра "/_system/governance/trunk/endpoints/..." ресурс, соответствующий конечной точке данной WSDL. 
При добавлении прокси-сервиса на WSO2ESB есть опция выбора конечной точки из реестра. Поэтому и появилась идея - почему бы не выбрать там конечную точку, волшебным образом созданную при импорте WSDL в реестр? Оказалось как бы не так: прокси сервис создается, однако при попытке отправить на него запроса возвращается лишь HTTP-загловок "202 Accepted", в логах - тишина, до конечного сервиса запрос также не доходит. При создании faultSequence с логированием, можно лишь увидеть, что возникающая ошибка имеет код 0 и текст "Configuration is not in proper format":
Synapse ERROR_CODE : 0 ERROR_MESSAGE : Configuration is not in proper format.
Оказывается дело в следующем: конечная точка, создаваемая в реестре при добавлении WSDL содержит лишь URL, тогда как ESB ожидает увидеть в реестре полноценную xml-конфигурацию конечной точки.
Так что единственный вариант использовать реестр для хранения конечных точек WSO2ESB - это вручную загружать туда xml-ки с их конфигурациями.
Источник: http://wso2.org/forum/thread/11705 

Error processing POST request: Transport level information does not match with SOAP Message namespace URI

WSO2ESB (а точнее, лежащий в её основе Apache Axis2) при поступлении входящего сообщения пытается определить используемую сообщением версию протокола SOAP (1.1 или 1.2). Делает он это 2 способами:
  1. На основании пространства имен SOAP envelope, которое равно "http://schemas.xmlsoap.org/soap/envelope/" для вресии 1.1 и "http://www.w3.org/2003/05/soap-envelope" для 1.2
  2. На основании HTTP-заголовка в случае, если нижележащим протоколом является HTTP. Для SOAP 1.1 должен присутствовать заголовок SOAPAction, для 1.2 может присутствовать параметр SOAPAction внутри заголовка Content-type.
В случае, если полученные в результате этих двух проверок протоколы не совпадают, генерируется ошибка "Transport level information does not match with SOAP Message namespace URI".
Похожая проверка выполняется для ответов, получаемых от внешних сервисов. Выполняется сопоставление пространства имен SOAP-конверта и HTTP-заголовка Content-Type. Для пространства имен SOAP 1.1 ожидается Content-Type "text/xml", для SOAP 1.2 - "application/soap+xml". В случае несовпадения также генерируется ошибка "Transport level information does not match with SOAP Message namespace URI".