вторник, 21 января 2014 г.

А давайте перепишем DAO с использованием возможностей Java 8

В последние годы складывается следующая тенденция развития объектно-ориентированных языков программирования: к ним добавляют все больше и больше элементов, взятых из мира функционального программирования. C# обзавелся поддержкой анонимных функций несколько лет назад, настала пора и Java-разработчикам приобщиться к прекрасному. Поддержка анонимных функций и основанный на их широком использовании новый API Collection Framework'а ждет нас в Java 8. Интернет уже запестрил статьями вида "Как написать свой Hello World на Java 8". Но писать Hello World не интересно, давайте лучше рассмотрим, как можно использовать новые возможности для решения конкретных задач в нашем суровом "Ынтырпрайз" мире.

вторник, 14 января 2014 г.

DAO на голом JDBC без использования Spring Framework и AOP. Хотите немного функциональной магии?

Здравствуйте, уважаемые подписчики. Праздники кончились и очень хочется немного поработать. Сейчас Суровый разрабатывает небольшое интеграционное решение, оперирующее коллекциями разнообразных объектов. Так как есть весьма критичные требования к производительности, а ассоциации между объектами практически отсутствуют, то было принято решение отказаться от использования ORM и ограничиться чистым JDBC. А так как мы работаем с коллекциями, то всегда найдется место для функциональной магии. В данной статье я расскажу об организации "коллекционно-ориентированного" слоя доступа к данным.

Используем паттерн QueryObject


В интеграционном проекте приходится решать две основные задачи: извлекать коллекции объектов из одного хранилища, например базы данных, и записывать их в другое. Соответственно, концептуально мы имеем две операции: load и save. При этом типов объектов и алгоритмов их загрузки/извлечения может быть очень много. Для организации DAO можно описать все операции в виде методов в одном классе. Получится класс, состоящий из N*2 методов, где N - количество типов объектов. Данный класс будет очень большим и сложным, по сути у нас получится просто некоторое кладбище SQL-запросов, которое будет чрезвычайно трудоемко как сопровождать, так и тестировать.

Можно разделить передаваемые типы данных на группы по функциональному признаку и создать не один класс DAO, а несколько. Однако опыт подсказывает, что при интеграции как правило выделяется одна функциональная область, число типов данных в которой значительно превышает все остальные. Речь идет о нормативно-справочной информации (НСИ). Создание нескольких классов по два-три метода и одного с двадцатью методами не является решением проблемы. Антипаттерн "волшебный объект" все еще остается в системе.

Пойдем другим путем. Воспользуемся паттерном QueryObject. При использовании данного паттерна мы инкапсулируем логику загрузки каждого типа объекта в свой класс. Аналогично поступаем с логикой сохранения данных. Таким образом у нас получается N*2 маленьких классов, состоящих из одного бизнес-метода. Данные классы легко сопровождать и тестировать. В случае необходимости очень просто сделать заглушку (mock) - необходимо подменить всего один метод. Вероятность конфликтов при работе с системой контроля версий так же уменьшается.

четверг, 19 декабря 2013 г.

SOAP vs RESTful

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

Обычно при сравнении SOAP- и RESTful-сервисов заводят разговор о том, что мол "ну SOAP же тяжелый протокол, а REST-легкий, это всем известно". Но что тяжелого в SOAP-запросе? Пустой SOAP-запрос с заголовком выглядит следующим образом:


<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
   </soapenv:Body>
</soapenv:Envelope>

Данный запрос занимает всего 158 байт. Говорить о том, что что-то тяжелое только потому что оно на 158 байт длиннее чего-то "легкого" несерьезно. Естественно, что различия между данными подходами гораздо глубже.

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

Команда программистов в Челябинске ищет заказчика.

Коллеги, если у вас есть интересный проект, но вы испытываете затруднение с поиском команды для его реализации, то предлагаю вам обратиться к этим людям. Удаленная работа приветствуется.

Состав команды:
- два разработчика фронтэнд;
- один разработчик Windows-приложений, классификаторов, алгоритмов анализа данных;
- руководитель команды, очень опытный и грамотный специалист, за него могу поручиться лично.

Парни имеют опыт работы со следующими технологиями:

- ASP.NET MVC;
- DevExpres;
- node.js;
- noSQL СУБД mongodb;
- Amazon Web Services.

Естественно, все обучены ООП и знают, что такое SOLID, GoF-паттерны, архитектурные паттеры, CQRS. В своей работе применяют методологии гибкой разработки (Agile): Scrum, Kanban, активно используют CI (TeamCity), модульное и интеграционное тестирование, автоматическое развертывание.

Пример работы команды - портал закупок строительных материалов broxer.ru. "Фишкой" проекта является интеллектуальный анализ прайс-листов компаний-поставщиков: каждый прайс разбивается на позиции и классифицируется относительно товаров из каталога сайта (используются линейные классификаторы в два этапа, около 150 тыс. предложений в базе классификатора, эффективность в районе 95-98% на обученных категориях), для этого разработан комплекс Windows-приложений, в основном на DevExpress.

Команда сработавшаяся, слаженная. Эффективность такой команды будет выше, чем у совокупности вновь нанятых разработчиков.

По вопросам сотрудничества вы можете связаться с руководителем. Его зовут Лизунов Игорь, e-mail: igor.lizunov@gmail.com, тел: +79823120053, skype: petrovich_evm.

Понравилось сообщение - подпишитесь на блог

среда, 20 ноября 2013 г.

Новый механизм экспорта OSB проектов - OSB Offline Configuration Export или configjar

Начиная с версии 11.1.1.7 в состав Oracle Service Bus добавлена утилита для экспорта конфигурации без использования Eclipse. Данная утилита расположена в каталоге OSB_HOME/tools/configjar и позволяет экспортировать настройки шины из исходников в jar-файл для последующего развертывания.

Полная документация по утилите представлена в приложении B. Exporting an Oracle Service Bus Configuration Offline официального руководства Oracle Fusion Middleware Developer's Guide for Oracle Service Bus 11g Release 1 (11.1.1.7). Не вижу смысла пересказывать данное руководство, напишу только об опыте использования утилиты.

Экспортировать конфигурацию с использованием configjar можно из командной строки, Apache Ant и WebLogic Scripting Tool (WLST). В качестве аргумента во всех случаях необходимо передать путь к конфигурационному файлу, содержащему настройки экспорта. Подразумевается, что перед использованием утилиты пользователь выполнит скрипт OSB_HOME/tools/configjar/setenv.bat и тем самым установит нужные переменные окружения, настроит CLASSPATH и подготовит среду к работе. Я же стараюсь писать скрипты сборки таким образом, чтобы они работали без необходимости настраивать окружение. Только build.xml и конфигурационные файлы. Такой перфекционизм создает ряд проблем.

Во-первых, мне не удалось заставить работать команду для Apache Ant - com.bea.alsb.tools.configjar.ant.ConfigJarTask. Данная команда требует установленной переменной окружения weblogic.home. Разобраться как передать значение данной переменной окружения команде не удалось. Однако выход есть - запускать экспорт с помощью команды java, используя класс com.bea.alsb.tools.configjar.ConfigJar:


<java fork="true" 
      dir="${deploy.dir}"
      classname="com.bea.alsb.tools.configjar.ConfigJar"            
      failonerror="true" 
      maxmemory="${osb.export.maxmem}">
    <jvmarg line="-XX:MaxPermSize=256m"/>
    <arg line="-settingsfile ${export.config.file}"/>
    <sysproperty key="weblogic.home" value="${wl.home}"/>
    <sysproperty key="osb.home" value="${osb.home}"/>   
    <sysproperty key="user.language" value="en"/>
    <sysproperty key="user.country" value="US"/>
    <classpath refid="osb_classpath"/>
</java>

Во-вторых, для работы класса com.bea.alsb.tools.configjar.ConfigJar нужно подобрать CLASSPATH. Экспорт удалось настроить, используя следующие классы:


<property name="weblogic.classpath" value="${wl.home}/server/lib"/>
<property name="osb.lib.classpath" value="${osb.home}/lib"/>
<property name="osb.tools.classpath" value="${osb.home}/tools/configjar"/>
<property name="osb.modules.classpath" value="${osb.home}/modules"/>
<property name="osb.soa.modules.classpath" value="${osb.home}/soa/modules"/>
<property name="common.modules.classpath" value="${mw.home}/oracle_common/modules"/>

<path id="osb_classpath">
    <fileset dir="${weblogic.classpath}">
        <include name="weblogic.jar"/>     
    </fileset>
    <fileset dir="${osb.lib.classpath}">
 <include name="alsb.jar"/>   
 <include name="external/log4j_1.2.8.jar"/>
    </fileset>  
    <fileset dir="${osb.modules.classpath}">
 <include name="features/osb.server.modules_11.1.1.7.jar"/>      
    </fileset>  
    <fileset dir="${osb.soa.modules.classpath}">
 <include name="oracle.soa.common.adapters_11.1.1/oracle.soa.common.adapters.jar"/>
    </fileset>  
    <fileset dir="${common.modules.classpath}">
 <include name="oracle.jrf_11.1.1\jrf-api.jar"/>
        <include name="oracle.http_client_11.1.1.jar"/>
 <include name="oracle.xdk_11.1.0\xmlparserv2.jar"/>
 <include name="oracle.webservices_11.1.1\orawsdl.jar"/>
 <include name="oracle.wsm.common_11.1.1\wsm-dependencies.jar"/>   
    </fileset>
    <fileset dir="${osb.tools.classpath}">
 <include name="configjar.jar"/>
 <include name="L10N"/>
    </fileset>
</path>

В-третьих, утилита экспорта может работать в двух режимах: экспорт на уровне проектов и экспорт на уровне отдельных ресурсов. В любом из режимов соответствующие сущности - проекты и ресурсы - требуется явно перечислить. В моей разработке необходимо организовать взаимодействие со многими (более сорока) экземплярами одной и той же информационной системы, при этом используется подход "один проект для взаимодействия с одним экземпляром системы". Получается, что нужно каким-то образом динамически сформировать список проектов для экспорта. Так как файл с настройками экспорта представляет собой XML, то для его формирования идеально подходит технология XSLT, тем более, что Apache Ant ее поддерживает из коробки. Нужно лишь сформировать XML-файл с перечислением проектов и прогнать его через преобразование. За одно можно так же в зависимости от среды (продуктив, разработка, тестирование), для которой осуществляется сборка, указать наименование jar-файла, который будет содержать результаты экспорта.

Исходными данными для формирования списка проектов является свойство projects из файла настроек сборки (например, build.properties). Значением данного свойства являются собираемые проекты, перечисленные через запятую. Наша задача обойти данное перечисление и сформировать XML со списком проектов. Для формирования XML можно использовать команду echo:


<echo message="&lt;projects&gt;" file="${deploy.dir}/projects.xml"/>
<for list="${osb.projects}"
     delimiter=","
     param="project.name">
    <sequential>
        <echo message="&lt;project&gt;@{project.name}&lt;/project&gt;"
       file="${deploy.dir}/projects.xml"
       append="true"/>
    </sequential>
</for>
<echo message="&lt;/projects&gt;" file="${deploy.dir}/projects.xml"
      append="true"/>

Теперь полученный файл со списком проектов можно отправить на XSLT-преобразование. Так же в качестве параметра передадим требуемое имя выходного файла с кодом для шины:


<xslt in="${deploy.dir}/projects.xml"
      out="${export.config.file}"
      style="${deploy.dir}/export.settings.xsl">          
    <param name="exportfile" expression="${osb.export.jar}"/>
</xslt>

Само XSLT-преобразование выглядит следующим образом:


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    version="2.0">
    <xsl:param name="exportfile" required="yes" as="xs:string"/>
    <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
    
    <xsl:template match="/">
        <configjarSettings xmlns="http://www.bea.com/alsb/tools/configjar/config">
            <source>
                <xsl:for-each select="projects/project">
                    <project>
                        <xsl:attribute name="dir">build/<xsl:value-of select="."/></xsl:attribute>
                    </project>
                </xsl:for-each>    
                <system dir="build/_OSB Integration"/>
            </source>
            <configjar>
                <xsl:attribute name="jar">
                    <xsl:value-of select="$exportfile"/>
                </xsl:attribute>
                <projectLevel includeSystem="true"/>
            </configjar>
        </configjarSettings>
    </xsl:template>
</xsl:stylesheet>

Других проблем при использовании configjar не было.

Субъективные впечатления от новой утилиты исключительно положительные. Описанные выше проблемы тем или иным образом решаются, а кода теперь требует значительно меньше, чем для вызова Eclipse из Apache Ant. Памяти новый процесс сборки потребляет гораздо меньше, а работает быстрее. Экспорт шины, состоящей из 48 проектов и содержащей 954 прокси-, 28 бизнес-сервисов и 2300 XQuery-преобразований осуществляется за две минуты.

Концептуально конечно же завязывать сборку проектов на среду исполнения неверно. Представим себе, что компиляция Java EE-приложения не работала бы без установленного сервера приложений. Гораздо логичнее, когда сборка выполняется с помощью только средства разработки, без привлечения среды исполнения, как в том же IBM WebSphere Message Broker'е. Однако в Oracle Service Bus нельзя установить Eclipse и соответствующие плагины без самой OSB. Тяжелое наследие BEA. В итоге, если раньше человеку, ответственному за сборку, нужно было устанавливать и Eclipse, и OSB, то сейчас достаточно только установить OSB, что дает некоторый выигрыш во времени и числе подготовительных действий. В конце концов можно вообще ничего не устанавливать, а сборку производить на сервере разработки, сгружая туда исходники из SVN.

Понравилось сообщение - подпишитесь на блог

четверг, 14 ноября 2013 г.

Сравнение производительности механизмов ввода-вывода в Java: классического (блокирующего), неблокирующего и асинхронного

Не всегда при обеспечении взаимодействия нескольких программ можно полагаться на существующие транспортные механизмы. Да, у нас есть вся мощь веб-сервисов, при использовании серверов приложений нам доступна технология Java EE Connector Architecture (JCA), при взаимодействии с СУБД можно использовать JDBC, а в случае асинхронного взаимодействия можно использовать Java Message Service (JMS). Однако, не смотря на такое обилие технологий, бывает нужно обеспечить нетривиальное взаимодействие с системами, используя сокеты и самостоятельно реализуя протокол прикладного уровня. В данной заметке приведены результаты сравнения производительности трех существующих на данный момент в Java механизмов обеспечения сетевого взаимодействия: классического блокирующего, неблокирующего и асинхронного.

Краткая характеристика существующих подходов к организации ввода-вывода


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

Блокирующий ввод-вывод (IO). Данный механизм взаимодействия появился в Java самым первым и долгое время оставался единственным возможным подходом к обеспечению сетевого взаимодействия. Суть его заключается в том, что со стороны сервера и клиента создается сокет (со стороны клиента явно, со стороны сервера - в ответ на запрос соединения со стороны клиента). С каждым сокетом связаны два потока (InputStream и OutputStream): один служит для отправки сообщений, другой - для их приема. При этом все операции с данными потоками являются блокирующими, т.е. исполнение команд прерывается на время выполнения данных операций. Предположим, нам необходимо разработать индексатор сайтов (Web Crowler), являющийся частью поискового робота. Обращение к одному сайту занимает 300 мс. Если нам нужно проиндексировать 1000 сайтов, то в блокирующем режиме это займет 300 x 1000 мс, т.е. 300 секунд.

Путем ускорения работы приложений при использовании IO является многопоточность - задача делится на части, каждая из которых выполняется своим потоком управления. Например, чтобы обойти 1000 сайтов можно разбить их на четыре группы по 250 сайтов и обойти сайты из каждой группы параллельно. Самой популярной моделью взаимодействия является создание отдельного потока на каждое подключение. Таким образом все соединения обслуживаются параллельно и не мешают друг-другу. Недостатком такого подхода является линейный рост числа потоков и объема требуемой памяти при увеличении числа обслуживаемых соединений. Если потоков будет создано очень много (десятки тысяч), то операционная система сервера будет занята переключенем контекстов потоков, а на полезную работу времени может не остаться.

Неблокирующий ввод-вывод (NIO). Для решения проблем с блокирующим вводом-выводом был придуман механизм, основанный на мультиплексировании каналов. Данный механизм появился в Java 1.4.2 и был назван New IO (NIO). Суть механизма в следующем: существует мультиплексор (в терминах Java называемый селектором, java.nio.channels.Selector), который в одном потоке, последовательно, производит опрос каналов (в случае сетевого взаимодействия реализуемых классами java.nio.channels.SocketChannel и java.nio.channels.ServerSocketChannel). В результате каждого опроса селектор возвращает идентификаторы каналов, готовых к выполнению операций ввода-вывода (т.е. канал соединился с удаленной системой и в него теперь можно отправлять запрос или, наоборот, удаленная система что-то записала в канал и из него теперь эти данные можно читать). Такие идентификаторы называются "ключами" (java.nio.channels.SelectionKey). Каждый ключ содержит информацию о том, к выполнению какой операции готов канал. Задача приложения - в цикле обойти все ключи и выполнить соответствующие операции.

При использовании данного подхода решение проблемы линейного роста числа потоков при увеличении числа соединений заключается в том, что все подключенные каналы обслуживаются в одном потоке. Однако, за такое решение приходится платить тем, что выполняемые над полученными в результате сетевого взаимодействия данными операции должны быть очень короткими. Любое блокирование обслуживания одного канала сказывается на всех остальных. Если блокировки длительны, то пока поток управления дойдет до последних готовых каналов, установленные ими соединения могут быть уже разорваны по причине бездействия. Решением данной проблемы может быть так называемая отложенная обработка - на основании принятых в одном NIO-потоке данных формируются команды, которые помещаются в неблокируемую очередь, а исполняются отдельным потоком или несколькими потоками, которые в свою очередь не отвлекаются на операции ввода-вывода.

Асинхронный ввод-вывод (AIO, NIO.2). NIO является хорошим средством масштабирования приложений, активно использующих сетевые соединения, но данный подход имеет серьезный недостаток. Поток NIO вынужден явно опрашивать каналы (по-другому это называется "полинг"). При этом, если готовых к осуществлению взаимодействия каналов нет, то данный поток блокируется. Не будет ли лучше, если инициатором взаимодействия с каналами будет не приложение, а сама операционная система? Именно операционная система знает какие каналы готовы к осуществлению взаимодействия, ведь именно она осуществляет обслуживание сетевых карт, портов и прочих механизмов ввода-вывода. Как говорится, ей и карты в руки.

В Java 7 появились механизмы для обеспечения асинхронного сетевого взаимодействия. Это каналы java.nio.channels.AsynchronousSocketChannel и java.nio.channels.AsynchronousServerSocketChannel. Данные каналы содержат методы для неблокирующего установления соединения, приема соединения, записи и чтения. Каждый метод имеет два варианта: один реализован с использованием java.util.concurrent.Future - метод запускает требуемую операцию в отдельном потоке и сразу же возвращает в качестве результата объект класса java.util.concurrent.Future. Для получения результата операции необходимо вызвать Future#get(). Т.к. операция уже запущена в отдельном потоке, то она к моменту вызова Future#get() может быть уже выполнена, тогда метод get() сразу же вернет результат. В противном случае данный метод блокирует поток, в котором он вызван, до завершения операции. Другой вариант реализации асинхронных методов сетевого взаимодействия основан на использовании обработчиков обратного вызова - callback. Данный вариант реализует паттерн Proactor. Суть в следующем - каждый метод, например write, принимает в качестве параметра обработчик завершения - реализацию интерфейса java.nio.channels.CompletionHandler. Метод запускает требуемую операцию в отдельном потоке и передает управление дальше. Когда требуемая операция полностью выполняется, срабатывает один из методов переданного при старте операции обработчика завершения. Основное отличие между асинхронным и неблокирующим вводом-выводом заключается в том, что асинхронный ввод-вывод работает в многопоточной среде: операции выполняются не в тех потоках, из которых были запущены. Операции неблокирующего ввода-вывода выполняются в одном потоке путем мультиплексирования каналов.

среда, 6 ноября 2013 г.

Event-Driven Architecture на Oracle SOA Suite 11g - компонент Event Delivery Network (EDN)

Термин "управляемая событиями архитектура" (Event Driven Architecture, EDA) предложил Roy W. Schulte, аналитик Gartner в работе The Growing Role of Events in Enterprise Applications, опубликованной в 2003-м году. Одно время подход с архитектурой, управляемой событиями, рассматривали чуть ли не как замену сервисно-ориентированной архитектуре (Service-Oriented Architecture, SOA), существовал даже термин SOA 2.0. В последнее время интерес к EDA поутих, но на мой взгляд у данной концепции есть будущее. В своей заметке четырехлетней давности - Событийная модель построения приложения - Суровый пытался рассказать что такое EDA и какие компоненты необходимы для построения архитектуры, управляемой событиями, в данной же заметке рассмотрим средства, предоставляемые для этого программным продуктом Oracle SOA Suite 11g.

Сначала давайте рассмотрим зачем вообще могут быть нужны события в вашей реализации сервисно-ориентированной архитектуры. Что дает внедрение EDA простому разработчику? Прежде всего это полное разделение между системой-источником событий и системами-приемниками, что по-английски называется Super Decoupling. Что это значит? В классической SOA клиент сервиса должен знать конечную точку, предоставляемую или провайдером сервиса, или сервисной шиной предприятия. Таким образом и при смене конечной точки, и при подключении новой системы-приемника сообщений клиента необходимо изменить, как минимум перенастроить. В случае же управляемой событиями архитектуры, единственное, что должен сделать клиент - опубликовать событие. Он не должен даже знать, есть ли вообще сервисы-обработчики данного события и сколько их.

Можно провести следующую аналогию. В современных компаниях часто существует такая должность, как специалист по внутренним коммуникациям. Задача данного специалиста знать все новости компании и оповещать о них сотрудников. При этом одному сотруднику интересны новости о зарплате, другому - о каких-то кадровых изменениях, третьему - дни рождения коллег. При этом одни сотрудники работают, например, в Москве, а другие - в Челябинске. Специалист по внутренним коммуникациям заводит списки рассылки для каждого типа оповещения, а так же рассылки в разрезе городов и добавляет в них желающих получать ту или иную информацию. Затем заносит новых желающих. Затем убирает уволенных и нежелающих более получать информацию. В итоге специалист начинает что-то подозревать. В частности то, что он занимается какой-то непонятной работой. После этого он заводит корпоративный Twitter, в который начинает добавлять все новости компании, снабжая их различными хэштегами. Желающие получать определенные новости подписываются на этот Twitter, возможно с фильтром по тегам. Специалист по внутренним коммуникациям в данной метафоре является публикатором событий, желающие получать новости - подписчиками или приемниками событий, а Twitter - диспетчером событий.

При использовании шаблона проектирования "многоуровневая архитектура" необходимо обеспечить реализацию следующего правила: вызовы между слоями передаются только сверху вниз. Т.е. только верхний уровень вызывает нижний, ни в коем случае не наоборот. Нижние уровни ничего не знают о том, кто к ним обращаются. В теории все выглядит замечательно, на практике же зачастую необходимо передавать какую-то информацию от нижних уровней к верхним. Например, информацию о каких-то сервисных действиях, к примеру о выключении оборудования или о разрыве сети. Зачем передавать? К примеру, для информирования пользователя. Здесь как нельзя кстати на помощь приходит механизм событий. В стройной многоуровневой архитектуре нижний слой не знает, кто его вызывает и кого именно информировать о проблемах. Это и не нужно. Достаточно только опубликовать событие. А уже компоненты верхнего слоя подписываются на нужные им события. В итоге и канал связи между нижними и верхними слоями установлен, и стройность многоуровневой архитектуры не нарушена.