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>
<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 файл с нашими сообщениями.
О интерфейсе пока все.
Спасибо за статью. Для начала очень познавательно!
ОтветитьУдалитьНо подскажите куда двигаться дальше?
И вообще из опыта насколько реально небольшой команде разработчиков поднять и развивать эту систему для электронной коммерции?
Не за что, не думал, что она будет интересна. Она создавалась для маленькой группы людей, у которых была идея, но идея не выстрелила. Поэтому как можно заметить других статей на эту тему и не было =/
ОтветитьУдалитьНасчёт движения, если чисто в теории, то можно почитать Rupert Howell, Jonathon Wong: Apache OFBiz Development: The Beginner's Tutorial. Хорошая книга, хоть и не шибко новая. На официальном сайте тоже много вики статей. Если с практической точки зрения, то что-нибудь сделать :)
Тут такое дело, надо различать две вещи:
1. Ядро - это то что я пытался описать в статье, оно предоставляет великолепную инфраструктуру для разработки приложений.
2. Набор стандартных приложений. Их очень много, они находятся в папке applications. Они предоставляют backend для ERP, CRM и пр. систем. В specialpurpose можно найти примеры fronend'а и дополнительных приложении.
От них по моему и надо отталкиваться. Но стандартные приложения очень монструозны, потому что пытаются покрыть максимально возможную функциональность. Если зайти на страницу создания товара в стандартном бекенде, можно сума сойти от кучи вкладок и полей.
Если Вы хотя бы примерно представляете требования к разрабатываемой системе. То возможно стоит продолжить с адаптирования этих стандартных приложений под ваши нужды.
Насчёт практического опыта, он у меня был небольшой и печальный :) И была небольшая команда из 3х человек. В целом поднять не проблема. Просто как я уже описал могут быть проблемы со стандартными приложениями, их избыточностью. Но как у большинства проектов апатча, комьюнити очень большое и активное. Я на мейл группе задавал пару вопросов, отвечали активно и очень интересно. Мы кстати рассматривали например вариант использовать только инфраструктуру, приложения все писать самостоятельно. Не такая уж глупая и безнадёжная идея даже для небольшой команды. Вообще открытые исходники, позволяют очень гибко закастомизировать стандартные приложения, просто с апдейтами будет не шибко весело, но можно использовать только стабильные версии, которые не так часты.
Какой-то сумбурный ответ получился, но надеюсь что-нибудь прояснит, но я к сожалению пока заморозил свою работу с офбизом. Так что на многое не могу ответить.
Павел, спасибо за такой развёрнутый ответ и подсказки по проекту!
ОтветитьУдалитьЯ как раз нахожусь на этапе выбора платформы для создание функциональных интернет приложений, поэтому хотелось бы выбрать надёжный и удобный инструмент. Возможно вы можете порекомендовать на какие проекты, помимо OFBiz, также стоит обратить внимание?
Ну как то слишком абстрактно :)
ОтветитьУдалитьПросто можно рассматривать как веб фреймворки типа Apache Wicket, так и более специализированные штуки как Ofbiz. Вообщем трудно что-то посоветовать. Но можно подумать над Django молодой фреймворк на python для веб разработки. Сам сейчас небольшой проект на нём пишу, может чутка попозже опишу свой опыт. Вообщем интересная штука, а для небольшой команды очень как раз придётся. Понятно что там нет такого набора стандартных приложений как у Ofbiz'а. Но в следствии его невероятной простоты и удобства, вы сами можете их написать достаточно быстро. Но при этом у него есть просто произведение программерского искусства - автоматически генерируемая админка. Вообщем обзорную статью можно почитать в одном из лучших русских блогах о джанге. Но подходы и концепции там совершенно другие, может быть Вас даже больше заинтересует.
А да, насчёт статьи, она немного старовата, но основная идеология там присутствует :) но других статей тоже не проблема найти, джанга постепенно становится очень популярной.
ОтветитьУдалитьочень нужен разработчик, который знаком с ofbiz, оплатим настройку и, хотелось бы, дальнейшее сопровождение. напишите в icq 666038 или скайп pagan3d, если интересно
ОтветитьУдалитьНе за что, не думал, что она будет интересна. Она создавалась для маленькой группы людей, у которых была идея, но идея не выстрелила. Поэтому как можно заметить других статей на эту тему и не было =/
ОтветитьУдалитьНасчёт движения, если чисто в теории, то можно почитать Rupert Howell, Jonathon Wong: Apache OFBiz Development: The Beginner's Tutorial. Хорошая книга, хоть и не шибко новая. На официальном сайте тоже много вики статей. Если с практической точки зрения, то что-нибудь сделать :)
Тут такое дело, надо различать две вещи:
1. Ядро - это то что я пытался описать в статье, оно предоставляет великолепную инфраструктуру для разработки приложений.
2. Набор стандартных приложений. Их очень много, они находятся в папке applications. Они предоставляют backend для ERP, CRM и пр. систем. В specialpurpose можно найти примеры fronend'а и дополнительных приложении.
От них по моему и надо отталкиваться. Но стандартные приложения очень монструозны, потому что пытаются покрыть максимально возможную функциональность. Если зайти на страницу создания товара в стандартном бекенде, можно сума сойти от кучи вкладок и полей.
Если Вы хотя бы примерно представляете требования к разрабатываемой системе. То возможно стоит продолжить с адаптирования этих стандартных приложений под ваши нужды.
Насчёт практического опыта, он у меня был небольшой и печальный :) И была небольшая команда из 3х человек. В целом поднять не проблема. Просто как я уже описал могут быть проблемы со стандартными приложениями, их избыточностью. Но как у большинства проектов апатча, комьюнити очень большое и активное. Я на мейл группе задавал пару вопросов, отвечали активно и очень интересно. Мы кстати рассматривали например вариант использовать только инфраструктуру, приложения все писать самостоятельно. Не такая уж глупая и безнадёжная идея даже для небольшой команды. Вообще открытые исходники, позволяют очень гибко закастомизировать стандартные приложения, просто с апдейтами будет не шибко весело, но можно использовать только стабильные версии, которые не так часты.
Какой-то сумбурный ответ получился, но надеюсь что-нибудь прояснит, но я к сожалению пока заморозил свою работу с офбизом. Так что на многое не могу ответить.
Павел, спасибо за такой развёрнутый ответ и подсказки по проекту!
ОтветитьУдалитьЯ как раз нахожусь на этапе выбора платформы для создание функциональных интернет приложений, поэтому хотелось бы выбрать надёжный и удобный инструмент. Возможно вы можете порекомендовать на какие проекты, помимо OFBiz, также стоит обратить внимание?