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ęć.

Nasz model będzie się składał z podprocesu, którego zadaniem jest dokonać ewaluacji merytorycznej oferty. Podprocesy są użyteczne, gdyż:

  1. 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),
  2. 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:

<serviceTask id="javaService" name="My Java Service Task" activiti:expression="#{object.function()}" />

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

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);	
	}
 
}

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ę:

<bean id="schedule" class="pl.org.bpmn.TeamSchedule"/>

Jeśli w projekcie nie ma odpowiednich plików, proszę je dołożyć zgodnie z: opisem poniżej.

Teraz możemy już w eclipsowym Activiti Designerze dodać do naszego service taska odpowiednie wywołanie:

Testowanie modelu

Aby uprościć testowanie modelu skorzystamy z klasy Activiti Rule, która służy do inicjalizacji silnika procesowego dla JUnit testów.

Proszę dodać poniższy plik do projektu:

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);
	}
}

W pliku konfiguracyjnym w projekcie (activiti.cfg-mem.xml) należy jeszcze dodać odpowiednią linijkę do połączenia z serwerem:

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

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.

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:

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ć:

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

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: 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
pl/dydaktyka/bim/lab4.txt · ostatnio zmienione: 2019/06/27 15:50 (edycja zewnętrzna)
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0