barbitoff programmer`s blog

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

четверг, 29 ноября 2012 г.

WSO2ESB 4.5.1: настройка связки с Apache ActiveMQ 5.7.0

1) Скопировать из дистрибутива Apache ActiveMQ  5.7.0 следующие jar-ники в /repository/components/extensions шины:
  • activemq-core-5.7.0.jar
  • geronimo-j2ee-management_1.1_spec-1.0.1.jar
  • slf4j-api-1.6.6.jar
2) Раскомментировать в axis2.xml настройки receiver`а и sender`а (предполагается, что ActiveMQ стоит на локальной машине и слушает порт по-умолчанию, 61616):
<transportReceiver name="jms" class="org.apache.axis2.transport.jms.JMSListener">
<parameter name="default" locked="false">
<parameter name="java.naming.factory.initial" locked="false">org.apache.activemq.jndi.ActiveMQInitialContextFactory</parameter>
<parameter name="java.naming.provider.url" locked="false">tcp://localhost:61616</parameter>
<parameter name="transport.jms.ConnectionFactoryJNDIName" locked="false">QueueConnectionFactory</parameter>
<parameter name="transport.jms.ConnectionFactoryType" locked="false">queue</parameter>
</parameter>
</transportReceiver>
...
<transportSender name="jms" class="org.apache.axis2.transport.jms.JMSSender"/>
3) Всё, после перезапуска шины можно использовать JMS. Например, для конечных точек:
<endpoint>
<address encoding="UTF-8" statistics="disable"
trace="disable" uri="jms:/myQueue?transport.jms.ConnectionFactoryJNDIName=QueueConnectionFactory&amp;java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory&amp;java.naming.provider.url=tcp://localhost:61616&amp;transport.jms.DestinationType=queue"/>
</endpoint>


пятница, 23 ноября 2012 г.

Как сделать Pretty-print для JavaScript

Встала тут задача привести JavaScript к более ли менее читаемому виду (сам его пару лет назад минифицировал, а исходный, неужатый вариант, благополучно посеял). Есть множество онлайн сервисов, рабочих и не очень, но самый, по-моему, удобный вариант - воспользоваться встроенными возможностями Google Chrome. Здесь все просто: выбираем нужный скрипт, и вуаля - он pretty-printed (если нет - нужно нажать кнопку "Pretty print" внизу):


Жаль имена переменных говорящими уже не сделать =)

Commons VFS: получения доступа к оригинальному объекту File для LocalFile

Задача:

С помощью Commons VFS в приложении получается объект FileObject. Точно известно, что он представляет собой локальный файл (т.е. представлен имплементацией org.apache.commons.vfs2.provider.local.LocalFile). Нужно получить обычный джавовский объект File для этого файла.

Решение:

Если посмотреть в документацию по VFS, то можно увидеть, что класс LocalFile имеет метод getLocalFile(), возвращающий нужный нам File. Незадача заключается в том, что этот метод - защищенный (вообще я заметил, что в большинстве Open Source проектов самые нужные методы постоянно оказываются protected или вообще private). Наследоваться от LocalFile и делать метод публичным смысла не имеет - ведь файл приходит к нам именно в виде LocalFile. Однако, в Java защищенные члены видны не только наследникам, но и всем классам того же пакета, т.е. нам ничего не мешает создать некий Helper-класс, разместить его в пакете org.apache.commons.vfs2.provider.local.LocalFile, и этим классом сделать нужный нам метод доступным:

package org.apache.commons.vfs2.provider.local;
import java.io.File;
public class LocalFileHelper
{
    public static File getFile(LocalFile file)
    {
      return file.getLocalFile();
    }
}
UP ну а вообще можно сделать гораздо проще =)  :
public class LocalFileHelper
{
    public static File getFile(LocalFile file)
    {
    new File(file.getName().getURI().replaceFirst("^file:///", ""));
    }
}
и тут уже все равно, в каком пакете наш хелпер.

WSO2 ESB: java.io.IOException: line too long при развертывании Carbon Application

Проблема:

При старте WSO2 ESB 4.5.1 после развертывания Carbon Application валится исключение:
[2012-11-23 18:31:16,255] ERROR {org.wso2.carbon.server.extensions.DropinsBundle
Deployer} -  Error occured while deploying bundles in the dropins directory
java.io.IOException: line too long
        at java.util.jar.Attributes.read(Attributes.java:379)
        at java.util.jar.Manifest.read(Manifest.java:199)
        at java.util.jar.Manifest.<init>(Manifest.java:69)
        at java.util.jar.JarFile.getManifestFromReference(JarFile.java:179)
        at java.util.jar.JarFile.getManifest(JarFile.java:165)
        at org.wso2.carbon.server.extensions.DropinsBundleDeployer.getNewBundleI
nfoLines(DropinsBundleDeployer.java:95)
        at org.wso2.carbon.server.extensions.DropinsBundleDeployer.perform(Dropi
nsBundleDeployer.java:61)
        at org.wso2.carbon.server.Main.invokeExtensions(Main.java:128)
        at org.wso2.carbon.server.Main.main(Main.java:74)
Причина:

В одном из jar-ников директории  repository/components/dropins/ слишком длинная строка в файле манифеста META-INF/MANIFEST.MF, скорее всего, это строка Export-Package в случае, если в jar-нике много экспортируемых пакетов. Нужно разбить эту строку на несколько с помощью перевода строки и пробела (если в начале строки манифеста стоит пробел, она считается продолжением предыдущей строки), например:
Export-Package: my.package1, my.package2,my.package3
 my.package4, my.package5, my.package6, my.package7
 my.package8

WSO2ESB: доступ к параметру прокси-сервиса из пользовательского медиатора

Чтобы прочитать какой-либо параметр прокси-сервиса из кода пользовательского медиатора, нужно:
public boolean mediate(MessageContext context) {
org.apache.axis2.context.MessageContext axis2MsgContext;
axis2MsgContext = ((Axis2MessageContext)context).getAxis2MessageContext();
axis2MsgContext.getAxisService().getParameter("paramName");
...
}

JAXB: trim текстового содержимого тегов при unmarshal

Для выполнения неких операций над текстовым содержимым тегов в XML-документе при его анмаршаллинге, предназначены XML-адаптеры, т.е. классы, наследуемые от javax.xml.bind.annotation.adapters.XmlAdapter. Для выполнения trim такой адаптер выглядит так (он выполняет trim как при маршаллинге, так и при анмаршаллинге):
public class StringTrimAdapter extends XmlAdapter<String, String> {
    @Override
    public String unmarshal(String v) throws Exception {
        if (v == null)
            return null;
        return v.trim();
    }
    @Override
    public String marshal(String v) throws Exception {
        if (v == null)
            return null;
        return v.trim();
    }
}
 Подключать адаптер можно как на уровне элемента:
@XmlElement(required=true)
@XmlJavaTypeAdapter(StringTrimAdapter.class)
String name;
, так и на уровне пакета, если требуется навесить адаптер на все классы пакета. Последнее выполняется в package-info.java этого пакета:
@XmlJavaTypeAdapter(value=StringTrimAdapter.class,type=String.class)
package my.package;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import my.package.adapters.StringTrimAdapter;
(здесь адаптер вышается на классы пакета my.package, а сам адаптер расположен в пакете my.package.adapters)


WSO2 ESB: логирование из медиатора

org.apache.synapse.mediators.AbstractMediator, от которого наследуется любой пользовательский медиатор, имеет 2 элемента для логирования: log и trace. С последним все не так прозрачно, а вот настроить первый просто: нужно добавить в /repository/conf/log4j.properties:
log4j.logger.package.MediatorClass=DEBUG
, где package - пакет, в котором расположен медиатор, MediatorClass - собственно имя класса медиатора. Вместо DEBUG можно установить другой уровень. Ошибки будут валиться в консоль и в wso2carbon.log, а те, уровень которых выше или равен WARN - ещё и в wso2-esb-errors.log.

junit-тексты в maven-проекте NetBeans: как получать доступ к необходимым для теста файлам

С ant-проектами все было просто, файлы можно было класть, к примеру, в /test/files, и читать в текст-кейсах так:
File f1 = new File("test/files/text.xml");
C maven-ом все сложнее, и делается так: во-первых, в /src/test/ проекта нужно создать папку "resources", и сложить в неё все необходимые тестам файлы. Теперь получить доступ к ним из тест-кейса можно так:
String fileName = URLDecoder.decode(this.getClass().getClassLoader().getResource("test.xml").replace("+", "%2B").replace("%20","+"),"UTF-8");
File f1 = new File(fileName);
Здесь  replace`ы требуются для приведения RFC3986-кодированного URL к формату HTML 4 (http://barbitoff.blogspot.ru/2012/04/javaneturlencoderencode.htmlhttp://barbitoff.blogspot.ru/2011/11/javascript-url-encode-decode.html). Длинновато, но работает. Можно вынести в какой-нибудь статический метод и использовать во всех тест-кейсах.

четверг, 22 ноября 2012 г.

WSO2 ESB: доступ к транспортному заголовку из медиатора

Доступ к транспортным заголовкам из медиатора осуществляется немного нетривиально:
public boolean mediate(MessageContext context)
{
org.apache.axis2.context.MessageContext axis2MsgContext;
axis2MsgContext = ((Axis2MessageContext)context).getAxis2MessageContext();
Map transportHeaders = (Map)axis2MsgContext.getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
System.out.println("FILE_NAME header is:\n\n"+(String)transportHeaders.get("FILE_NAME")+"\n\n");
}
В итоге выведенное в консоли значение будет совпадать с получаемым с помощью лог-медиатора:
<log level="custom">
<property name="FILE_NAME" expression="synapse:get-property('transport','FILE_NAME')"/>
</log>

среда, 21 ноября 2012 г.

WSO2 ESB: Получение имени файла, полученного по траспорту VFS, в цепочке медиации

Получается имя файла XPath-выражением:
synapse:get-property('transport','FILE_NAME')
Например, логируется это свойство в цепочке медиации так:
<log level="custom">
<property name="vfsfilename" expression="synapse:get-property('transport','FILE_NAME')"/>
</log>
Как получить доступ к транспортному заголовку ('FILE_NAME' или любому другому) из пользовательского медиатора, я писал тут: http://barbitoff.blogspot.ru/2012/11/wso2-esb.html

Немного offtop: отсутствие в SWIFT-сообщении поля 50K

Если в SWIFT-сообщении поля 50K нет, значит плательщик - физическое лицо, информация о нем размещается в поле 50F.

Maven-проект в Netbeans: как загрузить все зависимости в одну папку

Задача:

Есть Maven-проект Netbeans 7.2.1, необходимо, чтобы при сборке jar-ники всех зависимостей загружались в какую-либо папку. Действие "Build with Dependencies" почему-то не производит ожидаемого эффекта.

Решение:
  1. Свойства проекта -> Actions -> Выбираем "Build with Dependencies"
  2. В "Execute Goals" добавляем "dependency:copy-dependencies" через пробел после "install":

  3. Теперь при выполнении "Build with Dependencies" зависимости будут копироваться в /target/dependency проекта. Вообще, очень похоже, что именно так и должно было происходить при нажатии "Build with Dependencies", но разработчики Netbeans куда-то потеряли нужный гол из конфигурации действия.

суббота, 17 ноября 2012 г.

Java: p6spy или JDBC-logging anywhere

Иногда возникает необходимость трэйсить SQL-запросы к БД в Java-приложении. Если приложение свое (или чужое, но есть доступ к исходникам и возможность их пересобрать) - проблема решается просто:
DriverManager.setLogWriter(writer);
Connection con = DriverManager.getConnection(...);
...
или
DataSource.setLogWriter(writer);
Если доступа к исходникам нет - тут уже все становится сложнее. Предположим, что у нас есть хотя бы доступ к некоторому properties-файлу, в котором задаются параметры JDBC-соединения изучаемого приложения: драйвер, URL и т.п., например:
# The database driver
database.driver = transbase.jdbc.Driver
# The database server or connection string
database.connectionString = jdbc:transbase://localhost:2024/mydb
# Database Login and Password
database.user = iamadmin
...
В таком случае возникает естественная идея - нельзя ли вместо настоящего драйвера подложить какой-нибудь свой, который будет трэйсить запросы и затем отправлять их уже настоящему драйверу БД. Сделать это действительно можно, да и прокси-драйвер такой уже написан. Более того, он является бесплатным (распространяется по Apache Software License). Называется этот драйвер p6spy, доступен для скачки здесь: http://sourceforge.net/projects/p6spy/.
Вот пример подключения p6spy к веб-приложению, развернутому на Tomcat 6:
  1. Качаем с sourceforge файл p6spy-install.jar, распаковываем 
  2. Кладем p6spy.jar в /webapps/<appname>/WEB-INF/lib 
  3. Кладем spy.properties в /webapps/<appname>/WEB-INF/classes
  4. Меняем название драйвера в properties-файле приложения на "com.p6spy.engine.spy.P6SpyDriver" (все остальные параметры подключения не трогаем)
  5. Редактируем spy.properties:
    logfile = D:\spy.log
    excludecategories=
    realdriver=transbase.jdbc.Driver
    Здесь мы задали файл, куда будут писаться логи (я на всякий случай его заранее создал), убрали все исключения из логирования, чтобы в логи писалось абсолютно все, что можно, а также задали имя настоящего драйвера.
  6. Готово, перезагружаем веб-приложение (или томкат целиком), читаем логи =)

пятница, 16 ноября 2012 г.

WSO2 BAM 2.0.x: бинд порта для Thrift SSL на внешний сетевой интерфейс

WSO2 BAM 4.0.x биндит порт 7611 (по-умолчанию для Thrift) как на 127.0.0.1, так и на внешний сетевой интерфейс, а вот 7711 (по-умолчанию для Thrift SSL) - почему-то только на внешний. Поэтому при настройке отсылки событий на BAM использовать localhost нельзя, только внешний IP.

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

Как узнать пароль уже имеющегося в системе ODBC Data Source

Задача

В системе есть некий ODBC DataSource, но при его редактировании пароль закрыт звездочками (или точками, не суть). Нужно узнать его.

Решение

В зависимости от того, системный DSN или пользовательский, лезем в :
HKEY_LOCAL_MACHINE\Software\Odbc\Odbc.ini\
или
HKEY_CURRENT_USER\Software\Odbc\Odbc.ini\
, ищем там нужный датасорс по имени и смотрим пароль там ;)

Ruby: bundle за прокси с авторизацией

Для работы gem за прокси (с авторизацией или без неё) предусмотрен специальный параметр --http-proxy:
gem ... --http-proxy http://user:password@host:port
Для bundle такого параметра нет, но управлять использованием прокси можно через переменную окружения "http_proxy":
export http_proxy=http://user:password@host:port
bundle install ... 

среда, 7 ноября 2012 г.

away3d 4: 3D-модели, экспортируемые из Prefab3D, и утечка памяти

Полчаса размышлений, и источник утечки памяти, возникающей при пересоздании 3D-объектов, экспортированных из Prefab3D (версия - 2.124), найден. В классе ASBase, являющимся базовым для всех экспортируемых 3D-моделей, отсутствует корректное переопределение метода dispose(), очищающее занимаемые ресурсы. Переопределив метод так:

public override function dispose():void
{
super.dispose();
var i:uint;
for(i=0;i<_meshes.length;i++)
_meshes[i].dispose();
_meshes=null;
for(i=0;i<_containers.length;i++)
_containers[i].dispose();
_containers=null;  
for(i=0;i<_materials.length;i++)
_materials[i].dispose();
_materials=null;
}  
мы избавляемся от утечки памяти.

пятница, 2 ноября 2012 г.

PostgreSQL: прочитать строку из hex-представления

Задача:

В колонке таблицы хранится строка, представленная в виде шестнадцатеричных кодов своих символов в UTF8-кодировке (тип колонки - text). Нужно извлечь из неё строковые значения.

Решение:
SELECT convert_from(decode("a",'hex'),'UTF8') FROM ...
где "а" - имя колонки с hex-кодированными строками.

away3d 4.0: view.camera.project() и инициализация сцены

Столкнулся с непонятной для меня особенностью away3d 4.0: если использовать метод  view.camera.project() для проецирования 3D-точки в 2D-плоскость экрана в конструкторе основного спрайта флешки, или же в обработчике его события Event.ADDED_TO_STAGE, проецирование выполняется неверно (по крайней мере, оно не совпадает с последующими попытками спроецировать ту же точку). Тоже поведение наблюдается и при использовании метода в обработчике Event.ENTER_FRAME при его первых 3-4 срабатываниях. 
Описанное выше поведение я замечаю при использовании линзы OrthographicLens, а ошибка проецирования заключается в сжатии проекции по оси Ox.
Как оказалось, на линзе возникает событие LensEvent.MATRIX_CHANGED, до которого проецирование неверное, а после которого становится верным. Поэтому, если после инициализации флешки есть необходимость спроецировать какую-либо 3d-точку с использованием камеры, нужно дождаться сначала срабатывания события LensEvent.MATRIX_CHANGED.