пятница, 24 июля 2009 г.

Ofbiz overview

Apache Open For Business (Apache OFBiz) – компонетно-ориентированный для построение J2EE приложений.

Framework

Базовая архитектура выглядит следующим образом:



Фактический OfBiz framework предоставляет разработчику набор нескольких приложений которые организуют всю эту инфраструктуру для разработки компонент и их взаимодействию между собой. Компонента на абстрактном уровне состоит из трёх частей как на рисунке: Data Layer (Model), Logic (Service) layer (Controler) и User Interface Layer (View), т.е. спроектирована с использованием MVC паттерна.
Все базовые компоненты организующие эту инфраструктуру расположены в папке framework. Кратко пробежимся по ним:
base - главная компонента, которая загружает все остальные компоненты, содержит конфигурационные файлы отображенные на рисунке в разделе Configuration&Deployment.
appserver - базовая абстрактная компонента предоставляющие связь base компоненты с вебсервером на которые должны деплоиться компоненты. По умолчанию используеться компонента catalina, которая представляет собой сервер Jakarta Tomcat 6.
geronimo - компонента предоставляющие возможность организации транзакций в работе компонент.
entity, entityext и datafile - предоставляю собой компоненты управления Data Layer в платформе, вся работа с базами данных идёт через эти компоненты.
service - компонента организующая Logic layer. Она позволяет остальным компонентам создавать сервисы и организовывать взаимодействие между ними.
webapp, webslinger - компоненты организующие базовый User Interface для компонент. Предоставляющие способы доступа из web части к service и engine частям компоненты.
widget - реализация базовых виджетов (элементов графического интерфейса страниц) для компонент.
security - компонента обеспечивающая модель аутентификации и авторизации для всех трёх слоёв каждой компоненты.
minilang - компонента обеспечивающая поддержку интернационализации в компонентах.
webtools - вспомогательная компонента, которая предоставляет веб интерфейс для отслеживания работы платформы, к примеру для непосредственного доступа к entity находящимся в engine, просмотра запросов к компонентам, статистике работы сервисов и т.п.



Component

Теперь рассмотрим архитектуру компонент платформы на примере компонент framework/example и framework/exampleext. Все компоненты должны иметь достаточно строгую структуру каталогов, которая позволяет отделить все три уровня компоненты.

Data Layer

Ключевые папки data и entitydef. А также базовый файл ofbiz-component.xml который описывает ключевые моменты компоненты.
В entitydef мы помещаем описание новых сущностей (по сути table в базе данных) и отображение сущностей (по сути view). Описываться на xml, и эти описания потом используются entity компонентой для построение базы данных, а также кеширование этих данных. Вот простой пример описание сущности:


<entity name="Example" title="Example Entity">
<field name="exampleId" type="id-ne">
<description>primary sequenced ID</description>
</field>
....
<prim-key field="exampleId">
<relation name="EXMPL_TYP" type="one">
<key-map name="exampleTypeId">
</key-map>
<index name="EXAMPLE_NAME_IDX" unique="true">
<index-field name="name">
</index-field>
</index>
</relation></prim-key></entity>


Т.е. структура достаточно простая, entity объявляет сущность (таблицу), потом добавляем ей поля (колонки), prim-key описываю ключ к сущности, relation описывает связи с другими сущностями, index создание индексов для быстрого поиска сущностей, а также уникальных полей.


<view-entity name="ExampleView">
<member-entity alias="EXPL" name="Example">
<member-entity alias="EXST" name="ExampleStatus">
<alias-all alias="EXST">
<alias alias="EXPL" field="statusId" name="currentStatusId">
<alias name="statusDelay">
<complex-alias operator="-">
<complex-alias-field alias="EXST" field="statusDate">
<complex-alias-field alias="EXPL" field="exampleDate">
</complex-alias-field>
</complex-alias-field>
<view-link alias="EXPL">
<key-map name="exampleId">
</key-map>
</view-link>
</complex-alias></alias></alias></alias-all></member-entity></member-entity></view-entity>


View entity соответствует view в таблице, т.е. для удобство и улучшение производительности агрегирует данные из нескольких таблиц в определённом формате, вообщем получается виртуальная таблица. Синтаксис тоже достаточно очивидный, именуем сущности а потом через эти имена выбираем и именуем поля в новой виртуальной сущности.
Так же как в обычном sql доступны различные агрегирующие функции count, min, sum and etc., функции для работы со строками и т.п.
Понятно, что преимущество данного подхода в объектной модели работы с базой данных и не зависимости от специфики базы данных. Т.е. OfBiz может работать с любой RDBMS, которая имеет jdbc драйвер. Но иногда в конкретном проекте требуется сделать специфичный запрос, для этого в можно воспользоваться таким примером


<alias function="concat" name="addressFull">
</alias>


вызовем функцию concat специфичную для MySQL например.
Ещё один вид сущностей, это расширенные сущностей, вернее не вид, а способ описания. OfBiz поставляет много готовых компонент, к примеру Product, но нам может понадобиться добавить пару полей например с сущности Product для нашей задачи. Меня код OfBiz в этом случае не лучше решение, поэтому есть возможность расширить существующую сущность:


<extend-entity name="Product">
<field name="color" type="varchar"></field>
</extend-entity>


Теперь продукт имеет цвет.
Теперь необходимо помочь OfBiz'у понять что надо загрузить, для этого требуется в файл ofbiz-component.xml в корне компонента добавить следующую строчку:


<entity-resource loader="main" location="entitydef/entitymodel.xml" name="main" type="model">
</entity-resource>


где entitydef/entitymodel.xml файл с описанием определений сущностей.
Теперь у нас есть несколько новых наших сущностей, либо мы расширили имеющееся. Но иногда требуется, чтобы таблица была заполнена какими-нибудь начальными данными. OfBiz позволяет на этапе инициализации приложения создать несколько экземпляров сущностей:


<exampletype description="Real World" exampletypeid="REAL_WORLD" parenttypeid="">
</exampletype>


А также добавить информацию о файле с инициализациями в ofbiz-component.xml


<entity-resource loader="main" location="data/ExampleTypeData.xml" name="seed" type="data">
</entity-resource>


О создании DataLayer можно пока закончить.

Logic Layer

OfBiz реализует SOA подход в разработке. Поэтому каждый компонент должен содержать набор сервисов которые относятся к логическому уровню. Их задача предоставлять доступ и совершать манипуляции с сущностями расположенными на Data уровне. Так что теперь поговорим о том как создать сервисы в OfBiz
Для начало следует описать сервис, обычно такие описания располагаются в директории servicedef и имеют имя services*.xml, опять же мы должны сообщить OfBiz'у о этих описаниях, для этого изменим файл ofbiz-component.xml:


<service-resource loader="main" location="servicedef/services.xml" type="model">
</service-resource>


Теперь опишем в нём сервис:


<service auth="true" engine="entity-auto" invoke="create" name="createExample">
<description>Create a Example</description>
<permission-service action="CREATE" name="exampleGenericPermission">
<auto-attributes include="pk" mode="OUT" optional="false">
<auto-attributes include="nonpk" mode="IN" optional="true">
<override name="exampleTypeId" optional="false">
<override name="statusId" optional="false">
<override name="exampleName" optional="false">
</override>
</override></override></auto-attributes></auto-attributes></permission-service></service>


OfBiz позволяет описать сервисы несколькими способами. Представленный выше относиться к entity-auto. Т.о. можно описать простые сервисы для манипуляции с данными и для этого не потребуется никакого кода. А мы получили уже готовый сервис для создания сущностей exemple.
Наиболее мощным средством для создания сервисов является java. Опишем ещё один сервис, но код для него уже напишем сами на java:


<service engine="java" invoke="exampleFirstService" location="org.ofbiz.example.ExampleServices" name="exampleFirstService">
<description>Example First Service</description>
<attribute mode="IN" name="name" optional="true" type="String">
</attribute></service>


Ключевые моменты следующие:
1. engine указывает то как будет реализован сервис, мы его пишем на java;
2. location, в нашем случае следует указать полное имя класса реализующего сервис, стоит его расположить в папке src компонента.
3. атрибуты, это входные и выходные параметры сервиса.
Теперь напишем java код сервис:

package org.ofbiz.example;

import java.util.Map;
import org.ofbiz.service.DispatchContext;
import org.ofbiz.service.ServiceUtil;

public class ExampleServices {

public static final String module = ExampleServices.class.getName();

public static Map exampleFirstService(DispatchContext dctx, Map context) {
Map resultMap = ServiceUtil.returnSuccess("Hello world!");
return resultMap;
}
}


переданные сервису параметры можно получить из контекста context.get("name"); Так же могут быть OUT параметры в которых сервис может возвращать некоторые данные, они добавляются в resultMap, который возвращает сервис. Параметр DispatchContext позволяет получить доступ к некоторым служебным классам платформы, например, чтобы обеспечить безопасность доступа к сервису можно получить Security объект и проверить права пользователя:


Security security = dctx.getSecurity();
Map resultMap = null;
if  (context.get("userLogin") == null || !security.hasPermission("EXAMPLE_VIEW",
(GenericValue)context.get("userLogin"))) {
resultMap = ServiceUtil.returnError("You have no access here. You're not welcome!");
} else {
resultMap = ServiceUtil.returnSuccess("Welcome! You have access!");
}


А также к делегатару, для манипулирования сущностями dctx.getDelegator(). Класс Dispatcher поможет обращаться к другим сервисам dctx.getDispatcher().
Все вызовы сервисов могут быть синхронными и асинхронными, различия надеюсь понятно. Также в OfBiz есть планировщик, который позволяет вызывать сервисы по некоторому расписанию.
Есть ещё один тип сервисов: simple. Позволяет с помощью xml языка описать функционирования сервиса.
Объявим такой сервис:


<service engine="simple" invoke="exampleGenericPermission" location="component://example/script/org/ofbiz/example/ExamplePermissionServices.xml" name="exampleGenericPermission">
<implements service="permissionInterface">
</implements></service>


Он реализует так называемый интерфейсный сервис, который не имеет реализации


<service engine="interface" name="permissionInterface">
..
</service>


Ключевой момент это location, место расположения xml с реализацией сервиса. Для указание используется специальный тип адресации внутри платформы. Эта адресация очень часто используется для доступа к ресурсам компонент.
Теперь рассмотрим саму реализацию сервиса:


<simple-methods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nonamespaceschemalocation="http://ofbiz.apache.org/dtds/simple-methods.xsd">
<simple-method method-name="exampleGenericPermission" short-description="Main permission logic">
<set field="mainAction" from-field="parameters.mainAction">
<if-empty field="mainAction">
<add-error><fail-message message="In the permission-service element for the exampleGenericPermission service the main-action attribute was missing but is required"></fail-message></add-error>
<check-errors>
</check-errors></if-empty>
<if-has-permission action="_${parameters.mainAction}" permission="EXAMPLE">
<set field="hasPermission" type="Boolean" value="true">
<field-to-result field="hasPermission">
<else>
<property-to-field field="failMessage" property="ExamplePermissionError" resource="ExampleUiLabels">
<set field="hasPermission" type="Boolean" value="false">
<field-to-result field="hasPermission">
<field-to-result field="failMessage">
</field-to-result></field-to-result></set></property-to-field></else>
</field-to-result></set></if-has-permission>
</set></simple-method>
</simple-methods>


Синтаксис достаточно очевидный, конструкция ${} используется для доступа к параметрам внутри строк.
И ещё одной частью Logic layer являеться технология Event Condition Action, которая позволяет определить триггеры для сервисов и сущностей. Добавим файл (ofbiz-component.xml):


<service-resource loader="main" location="servicedef/secas.xml" type="eca">
<entity-resource loader="main" location="entitydef/eecas.xml" reader-name="main" type="eca">
</entity-resource></service-resource>


1. SECA (Service Event Condition Action): Это используется, когда
мы хотим, чтобы вызвать еще один сервис (или выполнить действие) по завершению сервиса при соблюдении определенных условий.


<eca event="commit" service="createExample">
<condition field-name="exempleId" operator="is-not-empty">
<action mode="sync" service="createExampleExample">
</action></condition></eca>


Т.о. после запуска сервиса createExample, если он вернёт не пустой exampleId, вызоветься сервис createExampleExample.
2. EECA (Entity Event Condition Action):
Аналогичное поведение мы можем получить если повесим триггер на создание сущности:


<eca entity="Example" event="return" operation="create">
<condition field-name="exampleId" operator="is-not-empty">
<action mode="sync" service="createExampleExample">
</action></condition></eca>


Теперь рассмотрим построение интерфейса для компонент

User Interface Layer

Интерфейс можно реализовать двумя способами. Первый - это разместить в wepapp директории стандартное java web приложение построенное с помощью сервлетов или jsp, а в нём обращаться к уже написанным сервисам. Более эффективным способом будет использование техлоногии виджетов OfBiz.
По традиции каждое java web приложение имеет папку WEB-INF с файлом web.xml, его нам тоже требуется добавить. В качестве заготовки стоит взять оный из example компоненты. Ключевые момент на который стоит обратить внимание это набор контекстных параметров. Большая часть из них задаёт описание нашей компоненты в платформе. Компонента example описана таким образом, чтобы вписываться в портал, котрый состоит их нескольких компонент с общим интерфейсом (Об этом попозже). Главный для нас сейчас параметр это mainDecoratorLocation описывающий точку входа пользователя на нашу компоненту.
Важным является файл controller.xml который должен находиться в этой же папке, с помощью этого файла мы описываем карту нашего компонента с точки зрения web. Для example подключаются контролеры портала.
А дальше описываются специфичные для этой компоненты параметры
первый важный раздел Request Mappings
К примеру маппинг для созданного сервиса создание Example:


<request-map uri="createExample">
<security auth="true" https="true">
<event invoke="createExample" type="service">
<response name="success" type="request-redirect" value="EditExample">
<redirect-parameter name="exampleId">
</redirect-parameter></response>
<response name="error" type="view" value="EditExample">
</response></event></security></request-map>


Настройки достаточно очевидны, но без этого маппинга мы не сможем обратиться к нашему сервису или виджету. Обращение к данному сервису возможно только для пользователей прошедших аутентификацию, работа будет вестись по протоколу https и в случае успешного выполнения сервиса мы перейдём к виджету редактирования примера.


<request-map uri="EditExample"><security auth="true" https="true"><response name="success" type="view" value="EditExample"></response></security></request-map>


Раздел View Mappings описывает так называемые screen, т.е. некоторое одно отображение / html страница. К примеру скрин редактирования примера на который мы перейдём в предыдущем примере:


<view-map name="EditExample" page="component://example/widget/example/ExampleScreens.xml#EditExample" type="screen">
</view-map>


Теперь рассмотрим файл ExampleScreens.xml в котором описаны наши screen на технологии виджетов.
Файлы виджетов обычно располагают в папке widget.
Скрин обычно состоит из секций, главной частью секции являеться раздел виджет


<screen name="HelloScreen">
<section>
<widgets>
<label text="Hello world!">
</label></widgets></section></screen>


В результате зайдя на HelloScreen мы увидим любимое всеми программистами тестовое сообщение.
Но секция может иметь несколько дополнительных разделов: condition - условие, которое должно выполниться для того чтобы отобразилась widgets часть, иначе будет выполнен раздел fail-widgets, и ещё один раздел actions - действие, которые должны быть выполнены до отображение, например скрипт или вызов сервиса, или просто установка каких-то полей в значения по умолчанию. Вот пример:


<screen name="HelloScreen">
<section>
<condition>
<if-compare field-name="parameters.hello" operator="equals" value="widgets">
</if-compare></condition>
<actions>
<set field="who" value="Pasha">
</set></actions>
<widgets>
<label text="Hello ${who}">

<fail-widgets>
<label text="Bye ${who}">
</label></fail-widgets></label></widgets></section></screen>


Все переменные создаются в специальном локально контексте виджета соответствующим контексту запроса. Так же по умолчанию в нём можно найти делегатор для доступа к сущностям, контекст безопасности, диспечер для вызова запросов на другие скрины и сервисы. Для секции виджетов есть много готовых элементов соответствующим базовым элементом интерфейса, такие как формы, поля ввода, списки и поля выбора. Но если этого не достаточно с помощью скриптового языка FreeMaker можно создать расширенный html и включить его в виджет:


<platform-specific><html><html-template location="component://example/webapp/crmsfa/includes/subheader.ftl"></html></html-template></platform-specific>


Очень важным вопросом является создание декораторов, они позволяют описать вид скрина, а остальные скрины использую этот декоратор могут добавлять только свои данные. Например для каждой страницы некоторого сайта есть footer и header, создадим такой декоратор.


<screen name="html-decorator">
<section>
<widgets>
<include-screen name="header">
<decorator-section-include name="body">
<include-screen name="footer">
</include-screen></decorator-section-include></include-screen></widgets>
</section>
</screen>


где header и footer некоторые базовые скрины. Все остальные скрины наших страниц могут описываться следующим образом:


<screen name="HelloScreen">
<section>
<widgets>
<decorator-screen name="html-decorator">
<decorator-section name="body">
<label text="Hello world!">
</label></decorator-section></decorator-screen></widgets></section></screen>


Ещё одной полезной функцией для User Interface является интернационализация. Для этого в папке config располагают xml файлы с сообщениями на разных языках:


<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<property key="Example">
<value xml:lang="en">Examples</value>
<value xml:lang="fr">Пример</value>
</property>
...
</resource>


А в виджитах используем следующим образом:


<property-map global="true" map-name="uiLabelMap" resource="ExampleUiLabels">
<set field="layoutSettings.companyName" from-field="uiLabelMap.ExampleCompanyName" global="true">
</set></property-map>


где ExampleUiLabels файл с нашими сообщениями.
О интерфейсе пока все.

OfBiz Common Portal

Есть одна интересная ещё фишечка. Это компонента common. Она создаёт портал, как бы объединяет несколько web частей разных компонент в одну с одним общими декораторами для виджетов, которые берутся из компонент themes (это обычные компоненты, просто они имеют только декоратор виджиты). Чтобы компонента была включена в портал, она должна добавить себя в базу в PortalPortlet сущность, а тк же как было показано выше включить в свой контролер контролер common компоненты. И использовать common декораторы и виджеты при построении своих виджитов.

8 комментариев:

  1. Спасибо за статью. Для начала очень познавательно!
    Но подскажите куда двигаться дальше?
    И вообще из опыта насколько реально небольшой команде разработчиков поднять и развивать эту систему для электронной коммерции?

    ОтветитьУдалить
  2. Не за что, не думал, что она будет интересна. Она создавалась для маленькой группы людей, у которых была идея, но идея не выстрелила. Поэтому как можно заметить других статей на эту тему и не было =/
    Насчёт движения, если чисто в теории, то можно почитать Rupert Howell, Jonathon Wong: Apache OFBiz Development: The Beginner's Tutorial. Хорошая книга, хоть и не шибко новая. На официальном сайте тоже много вики статей. Если с практической точки зрения, то что-нибудь сделать :)
    Тут такое дело, надо различать две вещи:
    1. Ядро - это то что я пытался описать в статье, оно предоставляет великолепную инфраструктуру для разработки приложений.
    2. Набор стандартных приложений. Их очень много, они находятся в папке applications. Они предоставляют backend для ERP, CRM и пр. систем. В specialpurpose можно найти примеры fronend'а и дополнительных приложении.
    От них по моему и надо отталкиваться. Но стандартные приложения очень монструозны, потому что пытаются покрыть максимально возможную функциональность. Если зайти на страницу создания товара в стандартном бекенде, можно сума сойти от кучи вкладок и полей.
    Если Вы хотя бы примерно представляете требования к разрабатываемой системе. То возможно стоит продолжить с адаптирования этих стандартных приложений под ваши нужды.
    Насчёт практического опыта, он у меня был небольшой и печальный :) И была небольшая команда из 3х человек. В целом поднять не проблема. Просто как я уже описал могут быть проблемы со стандартными приложениями, их избыточностью. Но как у большинства проектов апатча, комьюнити очень большое и активное. Я на мейл группе задавал пару вопросов, отвечали активно и очень интересно. Мы кстати рассматривали например вариант использовать только инфраструктуру, приложения все писать самостоятельно. Не такая уж глупая и безнадёжная идея даже для небольшой команды. Вообще открытые исходники, позволяют очень гибко закастомизировать стандартные приложения, просто с апдейтами будет не шибко весело, но можно использовать только стабильные версии, которые не так часты.
    Какой-то сумбурный ответ получился, но надеюсь что-нибудь прояснит, но я к сожалению пока заморозил свою работу с офбизом. Так что на многое не могу ответить.

    ОтветитьУдалить
  3. Павел, спасибо за такой развёрнутый ответ и подсказки по проекту!
    Я как раз нахожусь на этапе выбора платформы для создание функциональных интернет приложений, поэтому хотелось бы выбрать надёжный и удобный инструмент. Возможно вы можете порекомендовать на какие проекты, помимо OFBiz, также стоит обратить внимание?

    ОтветитьУдалить
  4. Ну как то слишком абстрактно :)
    Просто можно рассматривать как веб фреймворки типа Apache Wicket, так и более специализированные штуки как Ofbiz. Вообщем трудно что-то посоветовать. Но можно подумать над Django молодой фреймворк на python для веб разработки. Сам сейчас небольшой проект на нём пишу, может чутка попозже опишу свой опыт. Вообщем интересная штука, а для небольшой команды очень как раз придётся. Понятно что там нет такого набора стандартных приложений как у Ofbiz'а. Но в следствии его невероятной простоты и удобства, вы сами можете их написать достаточно быстро. Но при этом у него есть просто произведение программерского искусства - автоматически генерируемая админка. Вообщем обзорную статью можно почитать в одном из лучших русских блогах о джанге. Но подходы и концепции там совершенно другие, может быть Вас даже больше заинтересует.

    ОтветитьУдалить
  5. А да, насчёт статьи, она немного старовата, но основная идеология там присутствует :) но других статей тоже не проблема найти, джанга постепенно становится очень популярной.

    ОтветитьУдалить
  6. очень нужен разработчик, который знаком с ofbiz, оплатим настройку и, хотелось бы, дальнейшее сопровождение. напишите в icq 666038 или скайп pagan3d, если интересно

    ОтветитьУдалить
  7. Не за что, не думал, что она будет интересна. Она создавалась для маленькой группы людей, у которых была идея, но идея не выстрелила. Поэтому как можно заметить других статей на эту тему и не было =/
    Насчёт движения, если чисто в теории, то можно почитать Rupert Howell, Jonathon Wong: Apache OFBiz Development: The Beginner's Tutorial. Хорошая книга, хоть и не шибко новая. На официальном сайте тоже много вики статей. Если с практической точки зрения, то что-нибудь сделать :)
    Тут такое дело, надо различать две вещи:
    1. Ядро - это то что я пытался описать в статье, оно предоставляет великолепную инфраструктуру для разработки приложений.
    2. Набор стандартных приложений. Их очень много, они находятся в папке applications. Они предоставляют backend для ERP, CRM и пр. систем. В specialpurpose можно найти примеры fronend'а и дополнительных приложении.
    От них по моему и надо отталкиваться. Но стандартные приложения очень монструозны, потому что пытаются покрыть максимально возможную функциональность. Если зайти на страницу создания товара в стандартном бекенде, можно сума сойти от кучи вкладок и полей.
    Если Вы хотя бы примерно представляете требования к разрабатываемой системе. То возможно стоит продолжить с адаптирования этих стандартных приложений под ваши нужды.
    Насчёт практического опыта, он у меня был небольшой и печальный :) И была небольшая команда из 3х человек. В целом поднять не проблема. Просто как я уже описал могут быть проблемы со стандартными приложениями, их избыточностью. Но как у большинства проектов апатча, комьюнити очень большое и активное. Я на мейл группе задавал пару вопросов, отвечали активно и очень интересно. Мы кстати рассматривали например вариант использовать только инфраструктуру, приложения все писать самостоятельно. Не такая уж глупая и безнадёжная идея даже для небольшой команды. Вообще открытые исходники, позволяют очень гибко закастомизировать стандартные приложения, просто с апдейтами будет не шибко весело, но можно использовать только стабильные версии, которые не так часты.
    Какой-то сумбурный ответ получился, но надеюсь что-нибудь прояснит, но я к сожалению пока заморозил свою работу с офбизом. Так что на многое не могу ответить.

    ОтветитьУдалить
  8. Павел, спасибо за такой развёрнутый ответ и подсказки по проекту!
    Я как раз нахожусь на этапе выбора платформы для создание функциональных интернет приложений, поэтому хотелось бы выбрать надёжный и удобный инструмент. Возможно вы можете порекомендовать на какие проекты, помимо OFBiz, также стоит обратить внимание?

    ОтветитьУдалить