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()