====== 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