Jakub Ferschmann
13.3.2013

Jak jsem (ne)upgradoval jBPM3 na jBPM5



Na projektu Orinoco používáme procesní systém jBPM ve verzi 3.

JBPM_logo

Pro další vývoj jsme potřebovali mít možnost vytvářet proces programově bez použití xml definice. Ve vyšších verzích jBPM je tato funkčnost dostupná pomocí tzv. „fluent API“. Rozhodl jsem se tedy pro upgrade jBPM na nejnovější verzi, která je aktuálně 5.4 (verze ke dni vydání článku). Obvykle při přechodu knihovny na vyšší verzi, dochází také ke změně rozhraní, byl jsem proto připravený, že budu muset současnou integraci knihovny trochu upravit či přepsat. Začal jsem tím, že jsem změnil závislost na novou verzi jBPM5. Aktualizoval jsem si v Eclipsu závislosti a podíval se na výsledek. U naprosté většiny použitých tříd z jBPM3 mi IDE hlásilo, že neexistují. Ujistil jsem se, jestli nejsou třídy přesunuté či přejmenované. Nebyly. Zabrouzdal jsem do dokumentace a čím více jsem ji pročítal, tím více mi bylo jasné, že povýšení verze nebude chvilková záležitost. Totiž, změnilo se úplně všechno. Od samotného API až po celkový přístup a filozofii používání.

Popis některých změn

    • Ukládání a verzování procesů

Doposud se v jBPM3.x engine staral sám o ukládání procesů do databáze a zajištění jejich verzování. Nemuseli jste se o správu verzí procesů defakto starat. V jBPM5.x došlo k vyčlenění této funkčnosti do samostatného projektu, který nese název Drools Guvnor. Jedná se o centralizovanou repository, která funguje jako samostatná webová aplikace s vlastním GUI a nástroji pro správu velkého množství procesů či pravidel. Pokud potřebujete spravovat jen pár svých procesů, myslím si, že je to pravděpodobně kanón na vrabce.

    • Úkoly s lidskou interakcí

jBPM3.x byla zajištěna správa všech úkolů, ukládání, načítání i jejich programová obsluha. V jBPM5.x je opět vše jinak. Část pro obsluhu úkolů byla opět vyčleněna mimo jBPM engine. Nyní si můžete vybrat, jestli obsluhu úkolu budete zajišťovat lokálně, synchronně v rámci stejné transakce či asynchronně, napojením na externí službu. Asynchronní komunikaci můžete pak zajistit pomocí HornetQ, Mina či JMS.

    • Persistence

V jBPM3.x se používalo pro ukládání dat do databáze nativní rozhraní hibernate. V jBPM5.x se již používá JPA a je tedy potřeba, pokud chcete stále používat hibernate, použít rozhraní pro JPA.

    • API

Další velkou změnou je samotné API a způsob jejího použití. Uvedené rozdíly samy o sobě nejsou nic špatného, spíše naopak. Ale v okamžiku, kdy máte existující aplikaci, jako je Orinoco, pro kterou je BPM technologie základem, nechcete ji zásadně přepisovat, zvláště když vám to nepřinese mnoho výhod. Chtěl jsem spíš změnu evoluční místo revoluční.

site_logo

Activiti

Naštěstí, po různém hledání a konzultacích s některými kolegy, jsem se dostal na stopu dvěma klíčovým vývojařům jBPM3. Tom Baeyens a Joram Barrez opustili JBoss a založili nový projekt s názvem Activiti. Tento projekt navazuje na jBPM3 a jBPM4, proto jeho první verze dostala hodnotu 5. Jak autoři uvádějí, Activiti je tedy pátá verze jBPM, ale už ne pod taktovkou JBossu, ale hned celého sdružení firem (Alfresco, Camunda, SpringSource, a další). Pro mě bylo důležité, že Activiti také nabízí programové API, což byla hlavní motivace pro upgrade, s návazností na jBPM verzí 3 a 4, i se zachováním podobné filozofie.

Přechod z jBPM 3.x na Activiti 5.x

Ukažme si pár příkladů, jak nahradit jBPM3 pomocí Activiti5. Uvedené příklady jsou jen pro porovnání, v reálném projektu je lepší Activiti používat ve spojení se Springem, pro který má výbornou podporu. Nejdříve si vytvoříme instanci třídy, přes kterou zajistíme veškerou interakci s daným nástrojem. Instance je vždy vytvořena z výchozích hodnot a pro reálné použití je potřeba nejdříve ji nakonfigurovat:

jBPM3:
JbpmConfiguration myJbpmConfiguration = JbpmConfiguration.getInstance();
JbpmContext myJbpmContext = myJbpmConfiguration.createJbpmContext(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);

Activiti5:
ProcessEngine myProcessEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault().buildProcessEngine();

Nyní můžeme realizovat jednotlivé operace. Na začátek nahrajeme definici nového procesu do databáze, přičemž myInputStream vždy obsahuje načtený soubor s odpovídající definicí procesu – pro jBPM je to jPDL a pro Activiti je to BPMN 2.0.

jBPM3:
myJbpmContext.deployProcessDefinition(ProcessDefinition.parseXmlInputStream(myInputStream));

Activiti5:
myProcessEngine.getRepositoryService().createDeployment().addInputStream(myInputStream).deploy()

Jak spustit proces z již nahrané defince? myProcessKey představuje jednoznačný identifikátor pro již nahranou definici procesu.

jBPM3:
ProcessInstance myProcessInstance = myJbpmContext.newProcessInstance(myProcessKey); myProcessInstance.signal();
myJbpmContext.save(myProcessInstance);

Activiti5:
myProcessEngine.getRuntimeService().startProcessInstanceByKey(myProcessKey);

Načtení nepřiřazených úkolů, které má uživatel dostupné na základě jeho uživatelské role (roles) v aplikaci:

jBPM3:
myJbpmContext.getTaskMgmtSession().findPooledTaskInstances(roles);

Activiti5:
myProcessEngine.getTaskService().createTaskQuery().taskCandidateGroupIn(roles).list();

Přiřazení úkolu konkrétnímu řešiteli username:

jBPM3:
TaskInstance myTaskInstance = myJbpmContext.getTaskInstance(taskId); myTaskInstance.setActorId(username);
myJbpmContext.save(myTaskInstance);

Activiti5:
myProcessEngine.getTaskService().claim(taskId, username);

Získání úkolů, přiřazených uživateli (username). Úkoly jsou seřazené dle času vytvoření:

jBPM3:
Session mySession = myJbpmContext.getSession();
Criteria myCriteria = mySession.createCriteria(org.jbpm.taskmgmt.exe.TaskInstance.class, "ti"); myCriteria.add(Restrictions.eq("ti.isOpen", Boolean.TRUE)); myCriteria.add(Restrictions.not(Restrictions.eq("ti.isSuspended", Boolean.TRUE))); myCriteria.addOrder(Order.desc("ti.create")); myCriteria.add(Restrictions.eq("ti.actorId", username)); myCriteria.list();

Activiti5: myProcessEngine.getTaskService().createTaskQuery().taskOwner(username).orderByTaskCreateTime().desc().list();

Označení úkolu za vyřešený:

jBPM3:
TaskInstance myTaskInstance = myJbpmContext.getTaskInstance(taskId);
myTaskInstance.end();

Activiti5:
myProcessEngine.getTaskService().resolveTask(taskId);

Závěrem

Jediný větší problém, na který jsem při migraci narazil, je, že Activiti pro persistenci používá iBatis a jejich Query API je dosti omezené. Pokud tedy potřebujete udělat nějaký specifický dotaz do databáze, snadno narazíte. Situace se má ale údajně změnit s příchodem nové verze. V opačném případě vám nezbude nic jiného, než si vytvářet vlastní SQL dotazy mimo Activiti.

Vaše emailová adresa nebude zveřejněna

Komentáře

Děkujeme za váš komentář
Další
  • Podobný problém jsem řešil cca před dvěma lety. Naštěstí tedy při výběru BPM engine pro nový projekt. V tu dobu bylo jBPM 5 (tehdy v první verzi 5.0) naprosto nepoužitelné pro produkční nasazení. Activiti ale můžu jenom doporučit.

  • Olda

    Jestli to nebude nakonec tím, že jBPM5 není jBPM, ale Drools Flow, které použili po odchodu původního týmu založivšího si Activiti.