barbitoff programmer`s blog

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

суббота, 29 июня 2013 г.

Oracle: смена схемы по-умолчанию в рамках сессии

ALTER SESSION SET CURRENT_SCHEMA = ABC

Oracle: '' LIKE '%' = false

Столкнулся с достаточно неожиданным поведением Oracle: пустые строки у него эквивалентны NULL, поэтому выражение:
'' LIKE '%'
дает false, хотя вроде бы '%' предполагает 0+ любых символов. Т.о., если по LIKE '%' фильтруется какая-то колонка, то записи с пустой строкой в этой колонке в выборку не попадут. Однако, есть и workaround, позволяющий добавить в выборку как записи с пустой строкой, так и с NULL:
COALESCE('',' ') LIKE '%' = true
PS конечно,  LIKE '%' - достаточно странное условие, реально у меня используется:
LIKE ?||'%'
но при пустом значении параметра оно превращается именно в  LIKE '%', которое по идее должно выдавать все записи.

вторник, 25 июня 2013 г.

Развертывание Birt Runtime 4.2.2 на Weblogic 10.3.5

Если просто задеплоить birt.war на Weblogic , получаем при старте:
weblogic.application.ModuleException:
at weblogic.servlet.internal.WebAppModule.startContexts(WebAppModule.java:1510)
at weblogic.servlet.internal.WebAppModule.start(WebAppModule.java:482)
at weblogic.application.internal.flow.ModuleStateDriver$3.next(ModuleStateDriver.java:425)
at weblogic.application.utils.StateMachineDriver.nextState(StateMachineDriver.java:52)
at weblogic.application.internal.flow.ModuleStateDriver.start(ModuleStateDriver.java:119)
Truncated. see log file for complete stacktrace
Caused By: java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method "org.apache.xerces.dom.ElementImpl.getSchemaTypeInfo()Lorg/w3c/dom/TypeInfo;" the class loader (instance of weblogic/utils/classloaders/ChangeAwareClassLoader) of the current class, org/apache/xerces/dom/ElementImpl, and the class loader (instance of <bootloader>) for interface org/w3c/dom/Element have different Class objects for the type org/w3c/dom/TypeInfo used in the signature
at org.apache.xerces.dom.DeferredDocumentImpl.getNodeObject(Unknown Source)
at org.apache.xerces.dom.DeferredDocumentImpl.synchronizeChildren(Unknown Source)
at org.apache.xerces.dom.CoreDocumentImpl.getDocumentElement(Unknown Source)
at org.eclipse.birt.core.framework.jar.BundleLoader.loadExtensions(BundleLoader.java:151)
at org.eclipse.birt.core.framework.jar.BundleLoader.load(BundleLoader.java:63)
Решение данной проблемы - закомментировать в weblogic.xml тег <prefer-web-inf-classes>:
<?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE weblogic-web-app
    PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN"
    "http://www.bea.com/servers/wls810/dtd/weblogic810-web-jar.dtd" >
<weblogic-web-app>
<container-descriptor>
     <!--prefer-web-inf-classes>true</prefer-web-inf-classes-->
</container-descriptor>
</weblogic-web-app>
После этого приложение стартует, однако при попытке сгенерировать отчет валится:
org.eclipse.birt.report.engine.api.EngineException: Error happened while running the report.
   at org.eclipse.birt.report.engine.api.impl.EngineTask.handleFatalExceptions(EngineTask.java:2346)
...
Caused by: java.lang.NoSuchMethodError: org.mozilla.javascript.Context.initStandardObjects()Lorg/mozilla/javascript/ScriptableObject;
   at org.eclipse.birt.report.engine.javascript.JavascriptEngineFactory.createRootScope(JavascriptEngineFactory.java:74)
...
Проблема исправляется также через weblogic.xml, который в итоге принимает вид:
<?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE weblogic-web-app
    PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN"
    "http://www.bea.com/servers/wls810/dtd/weblogic810-web-jar.dtd" >
<weblogic-web-app>
<container-descriptor>
<!--prefer-web-inf-classes>true</prefer-web-inf-classes-->
<prefer-application-packages>        
            <package-name>org.mozilla.*</package-name>
        </prefer-application-packages> </container-descriptor>
</weblogic-web-app>



среда, 19 июня 2013 г.

Развертывание war-ника из Wavemaker на Weblogic 10.3.5

Развертывание WAR-ников, созданных Wavemaker'ом, на Weblogic имеет некоторые особенности, которые, впрочем, связаны не с самим Wavemaker'ом, а с используемыми в создаваемых приложениях библиотеками.
Первая ошибка, возникающая при работе Grid-виджета, связана с библиотекой Apache Commons Lang:
java.lang.NoSuchMethodError: org.apache.commons.lang.StringUtils.join(Ljava/util/Collection;C)Ljava/lang/String;
Проблема знакомая (Commons Lang цепляется не из приложения, а из weblogic'а), лечится добавлением в war-ник в папку WEB-INF дескриптора weblogic.xml со следующим содержимым:
<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN" "http://www.bea.com/servers/wls810/dtd/weblogic810-web-jar.dtd">
<weblogic-web-app>
  <description>Weblogic Webapp</description>
      <container-descriptor>
        <prefer-application-packages>      
            <package-name>org.apache.commons.*</package-name>
        </prefer-application-packages>
    </container-descriptor>
</weblogic-web-app>
Однако после этого вылезает другая проблема, на этот раз уже из-за Hibernate:
ClassNotFoundException: org.hibernate.hql.ast.HqlToken
Тут уже пришлось немного погуглить, и, спасибо http://blog.inflinx.com/2007/09/05/classnotfoundexception-orghibernatehqlasthqltoken-in-weblogic/, решение отыскалось, на этот раз не совсем очевидное. Заключается оно также в модификации weblogic.xml, который в итоге принял вид:
<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN" "http://www.bea.com/servers/wls810/dtd/weblogic810-web-jar.dtd">
<weblogic-web-app>
  <description>Weblogic Webapp</description>
      <container-descriptor>
        <prefer-application-packages>      
            <package-name>org.apache.commons.*</package-name>
            <package-name>antlr.*</package-name>
        </prefer-application-packages>
    </container-descriptor>
</weblogic-web-app>
Чтобы каждый раз не добавлять weblogic.xml в war-ник руками перед развертыванием, можно его вшить в проект Wavemaker с помощью вкладки "Source" -> "Resources", добавив его в папку webapproot/WEB-INF:


Java: генерация UUID

 java.util.UUID.randomUUID().toString()

Wavemaker: отображение CLOB-колонок из Oracle в Grid-виджете

Если просто импортировать таблицу в Wavemaker и создать из неё Grid, то в CLOB колонках будут показываться пустые значения (как и в соотв. полях при редактировании записей). Чтобы CLOB начал отображаться, нужно зайти в настройки БД, кликнув дважды по имени БД на вкладке "Services", и сменить для CLOB-колонки тип с "clob" на "text":


воскресенье, 16 июня 2013 г.

Развертывание war-ников на Weblogic 10.3.5 из Netbeans 7.3

Проблема

Netbeans 7.3 при развертывании war-ника на Weblogic 10.3.5 висит по пять минут на "Waiting for server to start the module...". При этом, приложение уже реально стартануло (видно через console, отвечает на запросы). 

Причина

Соотв. баг заведен в багтрекере NB (https://netbeans.org/bugzilla/show_bug.cgi?id=228419) и будет исправлен в 7.4. Проявляется он только при отсутствии в war-нике index.jsp в корне. Отсюда и workaround для проблемы - добавить index.jsp с любым содержимым в корень приложения.

Oracle: аналог AUTO_INCREMENT в MySQL

В Oracle автогенерация целочисленных идентификаторов, реализуемая в MySQL посредством модификатора AUTO_INCREMENT, выполняется по схеме sequence + trigger, т.е.:
  1. Создаем таблицу с целочисленным первичным ключом, например:
    CREATE TABLE my_table(
         my_id NUMBER(16),
         CONSTRAINT my_id_pk PRIMARY KEY (my_id)
    )
  2. Создаем последовательность:
    CREATE SEQUENCE my_id_seq;
  3. Создаем триггер:
    DELIMITER /
    CREATE OR REPLACE TRIGGER my_id_trg
         BEFORE INSERT ON my_table FOR EACH ROW
    BEGIN
         IF :NEW.my_id IS NULL THEN
              SELECT my_id_seq.NEXTVAL INTO :NEW.my_id FROM DUAL;
         END IF;
    END;
    /

JNDI-браузер

Если нужно посмотреть, что лежит в JNDI, можно воспользоваться утилитой "JNDI Warrior" (распространяется по Apache License 2.0): http://sourceforge.net/projects/jndi-warrior/?source=dlp.


вторник, 11 июня 2013 г.

Кодирование не-ASCII символов в теме email

Кодирование не-ASCII символов в теме email-сообщения выполняется в соответствии с RFC 2047. Заголовок представляется в виде одной или нескольких последовательностей следующего формата:
=?<charset>?<encoding>?<data>?<possibly repeated>?=
Здесь charset - кодировка, а encoding - способ представления закодированного текста (последовательности байт): B означает base64, Q - quoted printable.
Например:
=?UTF8?B?0J3QsNC/0YDQsNCy0LvQtdC9INC00L7QutGD0LzQtdC90YI6IA==?=
- это результат кодирования строки "Направлен документ: " с помощью кодировки utf8 и последующего представления последовательности байт в виде base64.

четверг, 6 июня 2013 г.

WSO2 ESB: задание тела запроса из свойства

Задача

В некотором property хранится сериализованное тело запроса. Нужно установить его в запрос, заменив существующее тело.

Решение

Использовать enrich-медиатор не получится: он скопирует содержимое свойства как строку, заменив недопустимые в xml символы соотв. сущностями. Для того, чтобы распарсить содержимое свойства как xml и установить его единственным сыном soap:body, можно воспользоваться таким коротким скриптлетом:
<script language="groovy"><![CDATA[
mc.setPayloadXML(mc.getProperty("myprop"));
]]></script>
, где  myprop - имя свойства, хранящего содержимое запроса.

jaxws-maven-plugin 2.2: ошибка Error executing: wsimport ... UndeclaredThrowableException: javax.xml.bind.annotation.XmlElementRef.required()

Проблема

При сборке проекта используется jaxws-maven-plugin версии 2.2 для генерации классов по wsdl. Собирается проект на JDK 6. При сборке падает:
Error executing: wsimport ... UndeclaredThrowableException: javax.xml.bind.annotation.XmlElementRef.required()
Под JDK 7 сборка проходит без проблем (ровно как и в связке jaxws-maven-plugin 2.1 + JDK 6). 

Решение

Не понял, чем именно вызвана проблема, но очевидно она связана с тем, что версия плагина - 2.2, тогда как JDK 6 идет с jax-ws / jaxb 2.1. Добавление jax-ws api 2.2 в endorsed при компиляции (как - я уже писал когда-то: http://barbitoff.blogspot.ru/2013/01/maven-wsimport-jdk-16.html) не помогает, что и логично - сборка валится при запуске wsimport, не доходя до компиляции. Проблема решается обновлением плагина jaxws-maven-plugin до версии 2.2.1.

JAX-WS: откуда берутся хост/порт в URL endpoint`ов в публикуемых WSDL

В публикуемых jaxws'ом wsdl хост и порт формируются динамически для каждого запроса исходя из заголовка "Host" http-запроса. 

суббота, 1 июня 2013 г.

WSO2 ESB: реализация итерационного синхронного вызова вспомогательного веб-сервиса при медиации сообщения

Задача

Проксируется некий запрос. В нем есть некоторое количество тегов, содержащих значения, которые нужно заменить другими значением, вызвав вспомогательный веб-сервис. После этого запрос с измененными значениями нужно прокинуть на конечный сервис.

Решение

Казалось бы, задача тривиальная, однако найти её простого и элегантного решения мне так и не удалось. В арсенале wso2 вроде бы есть все, что надо:
  • iterate-медиатор, позволяющий итерироваться по результатам некоторого xpath. Т.е. можно выбрать все теги со значениями, подлежащими замене, и обработать их
  • calluot-медиатор, выполняющий синхронный вызов веб-сервиса
Однако воспользоваться их комбинацией для решения поставленной задачи не получается. Разместить callout-медиатор внутри sequence'а медиатора iterate - не проблема. Но нам еще как-то нужно полученные от вспомогательного веб-сервиса значения сохранить в исходном проксируемом запросе. И здесь встречаемся с проблемой, о которой я уже частично писал: изнутри iterate-медиатора нет никакой возможности повлиять на родительский контекст (т.е. контекст исходного проксируемого сообщения). Следовательно, модифицировать проксируемое сообщение, подставив в него преобразованные значения, мы не сможем.
В итоге я нашел менее красивое, но все же работающее решение, заключающееся в следующем:
  1. Создается sequence, принимающий на вход envelope с одним единственным тегом, содержащим значение, подлежащее преобразованию. Он это значение извлекает, прогоняет через веб-сервис с помощью callout-медиатора и записывает обратно в тот же тег.
  2. В основном inSequence размещается script-медиатор (в качестве языка я использовал groovy). В нем мы с помощью xpath извлекаем преобразуемые значения, и в цикле формируем сообщение для созданного ранее sequence и вызываем его. Преобразованные значения записываем в исходное проксируемое сообщение.
Ссылка на sequence внутри скрипта получается следующим образом:
def mySeq = mc.getSequence("MySeq");
Для выполнения sequence предназначен метод mediate, принимающий единственный параметр - контекст org.apache.synapse.MessageContext. Вроде бы, можно передать ему контекст, используемый самим скриптом (т.е. переменную mc), предварительно установив нужное тело сообщения (а старое забэкапив куда-нибудь). Но такой вариант не проходит - оказалось, что callout-медиатор внутри нашей sequence предполагает, что ему в качестве контекста придет org.apache.synapse.core.axis2.Axis2MessageContext, в то время как mc в скрипте является объектом org.apache.synapse.mediators.bsf.ScriptMessageContext. Естественно, падает ClassCastException. Самое обидное, что ScriptMessageContext внутри себя содержит ссылку на Axis2MessageContext (это становится ясно, если посмотреть сюда и сюда), но getter`а для него нет, поэтому достать его не представляется возможным. Поэтому приходится создавать новый Axis2MessageContext:
def mc2 = new Axis2MessageContext(new org.apache.axis2.context.MessageContext(),mc.getConfiguration(), mc.getEnvironment());
mc2.setEnvelope(mc.getEnvelope());

mySeq.mediate(mc2);
Получилось несколько запутанно, но работает. 

WSO2 ESB: видимость свойств внутри и снаружи iterate-медиатора

К сожалению, свойства во всех областях видимости (default, axis2, transport) не сохраняются при выходе из последовательного iterate-медиатора (<iterate continueParent="true" expression="/some/xpath/" sequential="true">). Т.е. при установке / изменении свойств в sequence`ах внутри <iterate/> (с помощью property-медиатора или скриптом), изменения не видны ни в основной последовательности после выхода из iterate, ни на последующих итерациях самого iterate.

Отладка конфигурации log4j

Включается системным свойством log4j.debug, например, при старте jvm:
-Dlog4j.debug
Бывает крайне полезна, чтобы, скажем, понять, откуда из classpath log4j цепляет свой log4j.properties.

Netbeans: автоопределение версии Java EE

Проблема:

Создал новый maven-веб-проект, создал в нем web.xml, в котором поправил версию с 3.0 на 2.5, т.к. развертывать проект планируется на Oracle Weblogic 10.3.5. Однако, Netbeans не позволяет выбрать Weblogic как сервер для запуска приложения.
Оно и логично, ведь версия Java EE почему-то определилась как 6, не поддерживаемая Weblogic`ом версии 10.3.5. Поменять же версию Java EE на 5ую вручную Netbeans не дает.

Решение:

Откопал где-то на багтрэкере Netbeans информацию о том, что версия Java EE определяется исходя из версии web.xml. Но в моем то случае я исправил версию web.xml на 2.5, что как раз и соответствует 5ой версии Java EE. Оказалось, чтобы Netbeans заново определил версию Java EE, его необходимо его рестартануть. После рестарта все встало на свои места: