|
|
pl:dydaktyka:bim:lab4 [2013/11/18 16:45] kkluza [Service task Get available hours używający funkcji klasy w Javie] |
pl:dydaktyka:bim:lab4 [2019/06/27 15:50] |
====== Lab 4: Service task w modelu BPMN ====== | |
| |
Na początek w eclipsie proszę stworzyć nowy fragment modelu, tak by łatwo można było go testować. | |
Fragment ten będzie mógł zostać dołączony potem do naszego modelu z poprzednich zajęć. | |
| |
{{ :pl:dydaktyka:bim:lab4:eclipse3-process0.png |}} | |
| |
Nasz model będzie się składał z podprocesu, którego zadaniem jest dokonać ewaluacji merytorycznej oferty. | |
Podprocesy są użyteczne, gdyż: | |
- pozwalają na hierarchiczne modelowanie (w wielu narzędziach można wyświetlać podprocesy w postaci zwiniętej, nie ukazującej ich wewnętrznej logiki, dzięki czemu widzimy tylko model wysokiego poziomu), | |
- tworzą nową przestrzeń dla zdarzeń, tzn. zdarzenia rzucane przy wykonywaniu podprocesu mogą być przechwytywane przez zdarzenia brzegowe podprocesu (a widoczność jest ograniczona do podprocesu). | |
| |
Istotne: W Activiti podproces musi się zaczynać pojedynczym zdarzeniem startowym i kończyć co najmniej jednym zdarzeniem końcowym, mimo że specyfikacja BPMN 2.0 pozwala omijać zdarzenia początkowe i końcowe w podprocesach. | |
| |
==== Service task "Get available hours" używający funkcji klasy w Javie ==== | |
| |
Na początek poznamy możliwości uruchomienia określonej funkcji w Javie (z dołączonej klasy). | |
| |
Ogólnie service task wywołujący określoną funkcję w kodzie XML procesu wygląda następująco: | |
| |
<code xml><serviceTask id="javaService" name="My Java Service Task" activiti:expression="#{object.function()}" /></code> | |
| |
{{ :pl:dydaktyka:bim:lab4:eclipse3-add-library-ts.png?440|}} | |
W naszym przypadku będziemy chcieli zasymulować pobranie liczby dostępnych osobogodzin z zewnętrznego systemu. | |
W tym celu udostępniona została biblioteka z klasą ''TeamSchedule'' naśladującą takie zachowanie: {{:pl:dydaktyka:bim:lab4:team-scheduler.jar|}}. | |
Biblioteka ta zwraca dostępną liczbę osobogodzin (de facto funkcja ''getAvailableHours()'' zwraca losowo liczby: 14, 47, 78, zaś funkcja ''getAvailableHours(int priority)'' zwraca konkretną z liczb w zależności od znaku podanego argumentu). | |
Dodajemy bibliotekę do naszego projektu w miejscu ''/src/test/resources/lib/''. | |
Działanie naszego Team Schedulera można przetestować przy użyciu testu ''TeamScheduleTest'', który sprawdza 2 dostępne funkcje klasy ''TeamSchedule'': | |
| |
<code java TeamScheduleTest.java> | |
package pl.org.bpmn; | |
import static org.junit.Assert.assertEquals; | |
import static org.junit.Assert.assertTrue; | |
import java.util.Arrays; | |
import pl.org.bpmn.TeamSchedule; | |
import org.junit.Test; | |
| |
public class TeamScheduleTest { | |
| |
@Test | |
public void testAvailableHours(){ | |
TeamSchedule schedule = new TeamSchedule(); | |
for(int i=0; i<10; i++) { | |
int avHrs = schedule.getAvailableHours(); | |
assertTrue(Arrays.asList(14, 47, 78).contains(avHrs)); | |
} | |
} | |
| |
@Test | |
public void testAvailableHoursWithPriority(){ | |
TeamSchedule schedule = new TeamSchedule(); | |
assertEquals(schedule.getAvailableHours(-10), 14); | |
assertEquals(schedule.getAvailableHours(0), 47); | |
assertEquals(schedule.getAvailableHours(10), 78); | |
} | |
| |
}</code> | |
| |
| |
Ponieważ chcemy mieć dostęp do obiektu klasy ''TeamSchedule'', potrzebujemy go stworzyć przy uruchamianiu silnika activiti. W tym celu w pliku konfiguracyjnym w projekcie (np. ''activiti.cfg-mem.xml''), dodajemy odpowiednią linijkę: | |
| |
<code xml><bean id="schedule" class="pl.org.bpmn.TeamSchedule"/></code> | |
| |
Teraz możemy już w eclipsowym Activiti Designerze dodać odpowiednie wywołanie: | |
| |
{{:pl:dydaktyka:bim:lab4:eclipse3-java-expression.png?600 |}} | |
| |
==== Testowanie modelu ==== | |
| |
Aby uprościć testowanie modelu skorzystamy z klasy ''[[http://www.activiti.org/javadocs/org/activiti/engine/test/ActivitiRule.html|Activiti Rule]]'', która służy do inicjalizacji silnika procesowego dla JUnit testów. | |
| |
Proszę dodać poniższy plik do projektu: | |
| |
<code java CheckOfferTest.java> | |
package pl.org.bpmn; | |
| |
import java.util.List; | |
import org.activiti.engine.*; | |
import org.activiti.engine.runtime.ProcessInstance; | |
import org.activiti.engine.task.Task; | |
import org.activiti.engine.test.ActivitiRule; | |
import org.activiti.engine.test.Deployment; | |
import org.junit.*; | |
| |
public class CheckOfferTest { | |
| |
@Rule | |
public ActivitiRule activitiRule = new ActivitiRule("activiti.cfg-mem.xml"); | |
| |
@Test | |
@Deployment(resources={"diagrams/CheckOffer.bpmn20.xml"}) | |
public void executeJavaExpression() { | |
RuntimeService runtimeService = activitiRule.getRuntimeService(); | |
TaskService taskService = activitiRule.getTaskService(); | |
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("process-key"); | |
List<Task> taskList; | |
do { | |
taskList = taskService.createTaskQuery() | |
.processInstanceId(processInstance.getId()).list(); | |
if(taskList.size()!=0) { | |
System.out.println("Completing task: " + taskList.get(0).getName()); | |
taskService.complete(taskList.get(0).getId()); | |
} | |
} while(taskList.size()!=0); | |
} | |
} | |
</code> | |
| |
W pliku konfiguracyjnym w projekcie (''activiti.cfg-mem.xml'') należy jeszcze dodać odpowiednią linijkę do połączenia z serwerem: | |
<code xml> | |
<bean id="processEngineConfiguration" | |
class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration"> | |
<property name="jdbcUrl" value="jdbc:h2:tcp://localhost/activiti" /> | |
<property name="jdbcDriver" value="org.h2.Driver" /> | |
<property name="jdbcUsername" value="sa" /> | |
<property name="jdbcPassword" value="" /> | |
</bean> | |
</code> | |
| |
W powyższym kodzie plik konfiguracyjny uruchamia silnik procesowy w trybie //StandaloneInMemProcessEngineConfiguration//. Jest to tryb wygodny do testów jednostkowych, gdyż w tym trybie baza danych zostanie utworzona w trakcie uruchamiania silnika i następnie skasowana przy wyłączaniu. | |
| |
** Sprawdzenie zmiennych procesu ** | |
| |
Proszę rozbudować powyższy test, tak aby wyświetlał zmienną zwracaną do procesu przez zadanie **Get available hours** i testował jej wartość, analogicznie jak to miało miejsce przy testowaniu klasy ''TeamSchedule''. | |
| |
DO SPRAWOZDANIA: | |
* Proszę w sprawozdaniu umieścić model oraz rozbudowany test. | |
| |
==== Rozbudowa modelu o logikę niskopoziomową w Javie ==== | |
| |
Zanim wprowadzimy nową klasę Javy do ewaluacji merytorycznej (na kolejnych zajęciach częściowo zostanie to zastąpione regułami), potrzebujemy mieć wartości zmiennych, będących potencjalnymi czynniki analizy merytorycznej. | |
| |
Dla klasy **Set offer factors** ustawiamy więc 3 zmienne (potencjalne czynniki analizy merytorycznej): | |
* **Project Attractiveness** -- atrakcyjność merytoryczna zamówienia. | |
* **Required Hours** -- przewidywana liczba osobogodzin do wykonania projektu opisanego w ofercie. | |
* **Team Skills** -- określa poziom kompetencji potencjalnego zespołu do wykonania projektu. | |
| |
{{ :pl:dydaktyka:bim:lab4:eclipse3-factor-parameters.png |}} | |
| |
Proszę wprowadzić stosowne zmiany do jUnit testu, by wczytać 3 dowolne wartości do powyższych zmiennych. | |
| |
Następnie będziemy chcieli przeprowadzić analizę merytoryczną oferty na podstawie danych od użytkownika (z zadania **Set offer factors**) oraz pobranych automatycznie (z zadania **Get available hours**). | |
| |
Proszę zamienić zadanie **Show result** na service task (po najechaniu na element z menu kontekstowego **change element type** należy wybrać **change to service task**) i zmienić nazwę na **Evaluate offer**. Do tego zadania podepniemy klasę w Javie, która przetworzy dane z procesu. | |
| |
Specyfikacja logiki zadania w Javie możliwa jest poprzez wybranie odpowiedniej klasy w polu ''activiti:class'' w zadaniu typu //Service task//. Klasa taka musi implementować interfejs ''JavaDelegate'' i nadpisywać funkcję ''public void execute(DelegateExecution execution)'', która jest wywoływana w momencie uruchamiania tasku. | |
| |
Proszę stworzyć klasę Evaluation.java: | |
| |
<code java Evaluation.java> | |
| |
package pl.org.bpmn; | |
import org.activiti.engine.delegate.DelegateExecution; | |
import org.activiti.engine.delegate.JavaDelegate; | |
| |
public class Evaluation implements JavaDelegate { | |
@Override | |
public void execute(DelegateExecution execution) { | |
//TODO | |
} | |
} | |
</code> | |
| |
W klasie tej przy pomocy metody ''execution.getVariable()'' proszę odczytać wartości potencjalnych czynników analizy merytorycznej (pobranych wcześniej od użytkownika oraz możliwości produkcyjnych zespołu -- dostępnych osobogodzin), a następnie zaimplementować poniżej opisaną logikę, która zwróci (przy pomocy ''execution.setVariable()'') odpowiedni wynik analizy merytorycznej. | |
| |
Załóżmy, że wynik analizy merytorycznej jest negatywny, w każdym z następujących przypadków: | |
* Niewystarczające kompetencje zespołu. | |
* Przewidywane, wymagane zasoby przekraczają możliwości produkcyjne zespołu o 50%. | |
* Przewidywane, wymagane zasoby przekraczają możliwości produkcyjne zespołu i oferta jest mało atrakcyjna. | |
| |
Po zaimplementowaniu odpowiedniej logiki pozostaje dodać klasę do odpowiedniego zadania, które ma ona obsługiwać: | |
| |
{{ :pl:dydaktyka:bim:lab4:eclipse3-task-eval.png |}} | |
| |
Na koniec należałoby wyświetlić wynik analizy w zadaniu **Show checking result**, \\ np. przy użyciu odpowiedniego komunikatu w polu ''description''. | |
| |
Proszę uruchomić test i sprawdzić zachowanie stworzonego procesu powiązanego z klasą w Javie. | |
| |
DO SPRAWOZDANIA: | |
* Proszę w sprawozdaniu umieścić model, rozbudowany test. | |
* Proszę umieścić screenshot z eclipsa z rezultatem testu. | |
| |
==== Obsługa błędów/wyjątków ==== | |
{{ :pl:dydaktyka:bim:lab4:eclipse3-error-throwing.png|}} | |
W przypadku, gdy wynik analizy merytorycznej jest negatywny, proces powinien zostać przerwany, gdyż dalsze przetwarzanie oferty jest zbyteczne. W takim wypadku można użyć zdarzenia końcowego, które sygnalizuje błąd, a następnie przechwycić ten błąd innym zdarzeniem np. na krawędzi podprocesu. \\ | |
Aby zdarzenia te się poprawnie komunikowały istone jest, by nadać im jednakowy ''errorCode'' w konfiguracji.\\ | |
W ten sposób możemy przerwać wykonywanie podprocesu, po czym zakończyć cały proces (tak jak na rysunku obok). | |
Proszę dodać do modelu obsługę błędu, po czym przetestować jego działanie. | |
| |
==== Uzupełnienie procesu o elementy konieczne do uruchomienia w Activiti Explorerze ==== | |
| |
Na kolejnych zajęciach przygotujemy paczkę do wdrożenia tak rozbudowanego projektu w Activiti Explorerze. | |
Jeśli jednak chcemy uruchomić proces w Activiti Explorerze musimy jeszcze uzupełnić zadania typu //User task// o użytkownika ''assignee'', tak byśmy mogli zatwierdzać odpowiednie zadania. | |
| |