====== 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:
{{ :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'':
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);
}
}
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ę:
Jeśli w projekcie nie ma odpowiednich plików, proszę je dołożyć zgodnie z: [[pl:dydaktyka:bim:lab4?dodatkowe_pliki_do_testowania_modelu_regulowego|opisem poniżej]].
Teraz możemy już w eclipsowym Activiti Designerze dodać do naszego service taska 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:
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 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);
}
}
W pliku konfiguracyjnym w projekcie (''activiti.cfg-mem.xml'') należy jeszcze dodać odpowiednią linijkę do połączenia z serwerem:
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:
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
}
}
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.
==== Dodatkowe pliki do projektu ====
Paczka z plikami do dodania do projektu: {{:pl:dydaktyka:bim:lab4:projekt.tar.gz|}}
├── main
│ ├── java
│ │ └── pl
│ │ └── org
│ │ └── bpmn
│ └── resources
│ └── diagrams
└── test
├── java
│ └── pl
│ └── org
│ └── bpmn
│ └── TeamScheduleTest.java
└── resources
├── activiti.cfg-mem.xml
└── lib
└── team-scheduler.jar