Lab 5: Business Rule task w modelu BPMN

Wdrażanie procesu w Activiti Explorerze

Na poprzednich zajęciach przygotowaliśmy i przetestowaliśmy następujący model:

Będziemy chcieli wdrożyć go w Activiti Explorerze, a następnie uruchomić.
W tym celu potrzebujemy stworzyć odpowiednie pliki jar oraz bar przy użyciu anta.

Dla struktury katalogów, jak na rysunku obok, plik build.xml może wyglądać następująco:

build.xml
<?xml version="1.0" encoding="UTF-8"?>
 
<project name="javatest.setup" default="create.javatest">
 
	<property name="dist.dir" value="./dist" />
 
	<target name="dist.rmdir">
		<delete dir="${dist.dir}" />
	</target>
 
	<target name="dist.mkdir" depends="dist.rmdir">
		<mkdir dir="${dist.dir}" />
	</target>
 
	<target name="javatest.jar" depends="dist.mkdir">
		 <jar destfile="${dist.dir}/javatest.jar"
		       basedir="../../../target/classes"
		       includes="pl/org/bpmn/**"
		  />
	</target>
 
	<target name="javatest.bar" depends="dist.mkdir">
		 <jar destfile="${dist.dir}/javatest.bar"
		       basedir="."
		 	     includes="diagrams/*.bpmn20.xml"
		  />
	</target>
 
	<target name="create.javatest" depends="dist.mkdir, javatest.jar, javatest.bar"/>
 
</project>

Po przygotowaniu odpowiednich klas Javy do obsługi niskopoziomowej logiki procesu, należy stworzyć odpowiednio pliki jar oraz bar (proszę pamiętać o unikalnych nazwach klas i plików!), a następnie:

  1. Proszę skopiować plik jar do katalogu z bibliotekami dla Activiti Explorera:
    /opt/activiti-5.10/apps/apache-tomcat-6.0.32/webapps/activiti-explorer/WEB-INF/lib.
  2. Po poprawnym przesłaniu pliku należy przeładować Activiti Explorer, aby przeczytał biblioteki.
  3. Jeśli wszystko wykonało się poprawnie, można będzie w Activiti Explorerze wdrażać procesy
    zawierające odniesienia do zdefiniowanych przez nas klas Javy.

Rozbudowa modelu o reguły Droolsa

Rozbudujemy teraz model z poprzednich zajęć o zadanie regułowe. Dodamy je jako dodatkowy element analizy merytorycznej oferty (Check offer).

W zadaniu Evaluate offer określaliśmy odgórnie, czy kompetencje zespołu są wystarczające. W rzeczywistości same kompetencje zespołu bez informacji o trudności projektu, nie dają podstaw do rzetelnej ewaluacji. Proszę przed zadaniem Evaluate offer dodać zadanie regułowe Estimate risk, które oszacuje ryzyko projektu na podstawie dwóch czynników:

  • kompetencji zespołu – zmiennej teamSkills, zdefiniowanej na poprzednich zajęciach w zadaniu Set offer factors, oraz
  • spodziewanej trudności projektu – zmiennej projectDifficulty (proszę dodać tę zmienną typu long do zadania Set offer factors).

Dla oszacowania ryzyka zdefiniujemy prostą klasę modelu ryzyka (najlepiej dopisać inicjały do nazwy klasy, tak aby nie było konfliktów nazw):

Model.java
package pl.org.bpmn;
 
import java.io.Serializable;
 
public class Model implements Serializable {
 
	private static final long serialVersionUID = 1L;
 
	public Integer Kompetencje;
	public Integer Trudnosc;
	public String Ryzyko = "Brak danych";
 
	public String toString() {
		return Ryzyko;		
	}
}

oraz zdefiniujemy reguły Droolsa do szacowania ryzyka, oparte o powyższy model:

rules.drl
package pl.org.bpmn
 
import pl.org.bpmn.Model;
 
rule "rule1"
    when
        m : Model(Kompetencje > 7, Trudnosc < 5)
    then
        m.Ryzyko = "Niskie";
end
 
rule "rule2"
    when
        m : Model(Kompetencje < 5, Trudnosc > 5)
    then
        m.Ryzyko = "Wysokie";
end
 
rule "rule3"
    when
        m : Model(Kompetencje > 7, Trudnosc > 5)
    then
        m.Ryzyko = "Srednie";
end

W tym momencie możemy usunąć warunek dot. kompetencji zespołu z zadania Evaluate offer i zamiast niego wprowadzić warunek związany z wysokim ryzykiem projektu (np. projectRisk == „Wysokie”).

Opcjonalnie: Aby przetestować opisane reguły bez Activiti możemy do naszego projektu dołożyć klasę RuleRunner do uruchomienia reguł, a nasze reguły przetestować przy użyciu klasy RuleRunnerTest. Do tego celu potrzebujemy w eclipsie dołączyć odpowiednie pliki biblioteczne do src/main/resources/liblib.zip.

Ustawienia dla zadania regułowego:

  • Input variables: ${modelRyzyka}
  • Result variable: projectRisk

Do pliku pom.xml proszę dołożyć odpowiednie dependencje jak na listingu w części Dodatkowe pliki....

Uruchomienie modelu z regułami w eclipsie

Aby móc przetestować nasz proces, musimy jeszcze w projekcie do pliku konfiguracyjnego dla silnika (activiti.cfg-mem-rules.xml) dodać dla processEngineConfiguration:

    <property name="customPostDeployers">
      <list>
        <bean class="org.activiti.engine.impl.rules.RulesDeployer" />
      </list>
    </property>

Proszę do testu stworzonego na poprzednich zajęciach dopisać testowanie zadania regułowego. Fragment klasy testującej proces z regułami, może wyglądać następująco:

public class RuleRunnerProcessTest {
 
	@Rule
	public ActivitiRule activitiRule = new ActivitiRule(
			"activiti.cfg-mem-rules.xml");
 
	@SuppressWarnings("unchecked")
	@Test
	@Deployment(resources = { "diagrams/CheckOfferWithRules.bpmn20.xml", "rules/rules.drl" })
	public void testProcessWithRules() {
		Map<String, Object> variableMap = new HashMap<String, Object>();
 
		Model modelRyzyka = new Model();
		modelRyzyka.Kompetencje = 2;
		modelRyzyka.Trudnosc = 8;		
 
		variableMap.put("modelRyzyka", modelRyzyka);
 
		ProcessInstance processInstance = activitiRule.getRuntimeService()
				.startProcessInstanceByKey("processandrules", variableMap);
		assertNotNull(processInstance);
 
		Collection<Object> ruleOutputList = (Collection<Object>) activitiRule
				.getRuntimeService().getVariable(processInstance.getId(),"projectRisk");
		assertNotNull(ruleOutputList);
 
		for(Object obj : ruleOutputList){
			if(obj instanceof Model) {
				assertEquals("Wysokie", ((Model) obj).Ryzyko);	
				System.out.println("ruleOutput: " + obj);
			}
		}	
	}
}

Póki co przekazywaliśmy instancję Modelu bezpośrednio przez jUnit test. Docelowo chcielibyśmy, by tworzyła się ona w czasie działania procesu. Możemy zatem odpowiedni obiekt stworzyć w Javie, tak jak na poprzednich zajęciach lub przy okazji zadania pobierającego od użytkownika czynniki podlegające analizie merytorycznej – jako task listener. Do stworzenia task listenera potrzeba przygotować klasę np. pl.org.bpmn.AddModel, która implementuje interfejs TaskListener i w funkcji notify zawrzeć odpowiednią logikę działania tworzącą obiekt modelu ryzyka.

public class AddModel implements TaskListener {
  public void notify(DelegateTask delegateTask) {
    // Custom logic goes here
  }
}

Następnie tak przygotowaną klasę możemy wybrać w zakładce Listeners w zadaniu Set offer factors:

Proszę zmodyfikować test, tak by nie przekazywał instancji modelRyzyka do procesu, tylko korzystał z instancji stworzonej w przygotowanym task listenerze.

DO SPRAWOZDANIA:

  • Proszę w sprawozdaniu umieścić model oraz rozbudowany test.
  • Proszę umieścić screenshot z eclipsa z rezultatem testu, jak również kod klasy pl.org.bpmn.AddModel.

Wdrożenie i uruchomienie procesu z regułami w Activiti Explorerze

Jeśli silnik actviti został skonfigurowany tak, by mógł uruchamiać zadania regułowe Droolsa, to będziemy mogli uruchomić proces w Activiti Explorerze.

Podobnie jak poprzednio, do wdrożenia procesu i reguł potrzebne będą archiwa jar oraz bar (proszę pamiętać o unikalnych nazwach klas i plików!). W archiwum bar dołożymy oprócz diagramu również plik z regułami. Następnie należy:

  1. Przekopiować plik jar do katalogu z bibliotekami dla Activiti Explorera.
  2. Po poprawnym przesłaniu pliku należy przeładować silnik Activiti Explorera, aby przeczytał biblioteki.
  3. Jeśli wszystko wykonało się poprawnie, w Activiti Explorerze powinno się dać wdrożyć procesy zawierające zadania regułowe.

CZĘŚĆ NIEOBOWIĄZKOWA

Dla zainteresowanych osób, poniżej przedstawiono możliwości integracji modeli stworzonych w trakcie zajęć oraz dalszej rozbudowy procesu.

Integracja procesu "Check offer" (z lab. 3 i 4) z procesem rozwijanym w lab. 1 i 2

Jeśli założymy, że podproces Check offer jest osobną całością, która mogłaby być użyta w różnych procesach, sensownie będzie go wydzielić jako Call Activity. W tym celu zostawimy na diagramie procesu jedynie elementy z wnętrza podprocesu Check offer:

Upewniwszy się, że nasz podproces działa, zapewniamy mu unikalne id, tak byśmy mogli się do niego odwoływać z innego procesu.

Aby odwołać się z Check offer w postaci Call Activity (jak na powyższym rysunku) do odpowiedniego podprocesu, który zostanie wykonany, ustawiając następujące parametry w Main Config dla Call Activity:

  • Called element – id zdefiniowanego podprocesu,
  • Input parameters – ew. zmienne, które chcielibyśmy przekazać do podprocesu,
  • Output parameters – zmienne, które chcielibyśmy przekazać z podprocesu do procesu macierzystego (w source: nazwa zmiennej wyniku analizy merytorycznej w podprocesie; w target: nazwa zmiennej wyniku analizy merytorycznej w procesie macierzystym).

W przypadku, gdy wynik analizy merytorycznej jest negatywny, proces powinien zostać przerwany, gdyż dalsze przetwarzanie oferty jest zbyteczne. Na poprzednich zajęciach w tym celu sygnalizowaliśmy błąd przy użyciu zdarzenia końcowego wyrzucającego błąd w podprocesie. W naszym procesie macierzystym wypada zatem ten błąd przechwycić. Przy użyciu zdarzenia na krawędzi aktywności wychwytującego błąd, będziemy mogli odpowiednio obsłużyć przerwanie procesu. Aby zdarzenia błędów poprawnie się komunikowały istone jest, by nadać im jednakowy errorCode w konfiguracji (ważne: errorCode nie może być samą liczbą!).

Proszę dodać do modelu odpowiednią obsługę błędu, po czym przetestować działanie modelu dla różnych scenariuszy.

Dodanie wyceny oferty

Przed akceptacją zarządu potrzebujemy dokonać jeszcze wyceny oferty. Możemy to zrobić na podstawie reguł stworzonych w narzędziu Drools do ustalania modyfikatora oszacowania wyceny oferty.

Proszę dołożyć w tym celu odpowiednie zadania:

1) Zadanie Add factors typu „user task” pobierające od użytkownika odpowiedzialnego za przetwarzanie oferty brakujące dane – do reguł potrzebne nam będą wartości następujących zmiennych:

  • priceValuationClientPotential – potencjał klienta (wymaga zapytania użytkownika),
  • priceValuationCooperationChance – perspektywa współpracy z klientem (wymaga zapytania użytkownika),
  • priceValuationVisualAtractiveness – atrakcyjność wizualna (wymaga zapytania użytkownika),
    a także obliczające następujące zmienne:
  • priceValuationRealAttractiveness – atrakcyjność merytoryczna, ma wartość zmiennej projectAttractiveness z podprocesu Check Offer,
  • priceValuationTeamAvailability – dostępność zespołu (do wyznaczenia na podstawie requiredHours oraz availableHours z podprocesu Check Offer,),
  • priceValuationOfferValue – wartość oferty (do obliczenia na podstawie wymaganej liczby osobogodzin, np. 100*requiredHours).

Do obliczenia wybranych wartości przy zakończeniu zadania typu „user task” można użyć task listenera dla eventu complete albo dodatkowego zadania skryptowego. W przypadku implementacji poprzez task listener, należy stworzyć odpowiednią klasę np. pl.org.bpmn.PriceConditions, która implementuje interfejs TaskListener i w funkcji notify obliczyć, po czym wyeksportować odpowiednie zmienne do procesu.

public class PriceConditions implements TaskListener {
  public void notify(DelegateTask delegateTask) {
    // Custom logic goes here
  }
}

2) Zadanie regułowe Price offer, które uruchomi odpowiednie reguły i ustali wielkość priceValuationPriceModification, czyli wartość modyfikatora oszacowania wyceny oferty (ma ona zostać wyznaczona na podstawie zdefiniowanych reguł w pliku drl lub odpowiedniej tabeli decyzyjnej).

Tak przygotowany proces wyglądałby wtedy następująco:

Inne możliwości dalszej rozbudowy procesu...

  • Możliwość ręcznej poprawy wyceny oferty (w przypadku braku akceptacji zarządu).
  • Zmiana zadania Check formal condition na zadanie regułowe i podpięcie do niego odpowiednich reguł.
  • Dodanie zadań przygotowujących dokumenty dla klienta na potrzeby oferty.
  • Dodanie zadania informującego o przyczynie przerwania przetwarzania oferty
    (np. nie spełniamy warunków formalnych, negatywna analiza merytoryczna, brak możliwości wyceny itp.).

Dodatkowe pliki do testowania modelu regułowego

RuleRunner.java
package pl.org.bpmn;
 
import java.io.File;
 
import pl.org.bpmn.Model;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderErrors;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatelessKnowledgeSession;
 
public class RuleRunner {
 
	public static String runRules(Model modelRyzyka) throws Exception {
 
		KnowledgeBase kbase = readKnowledgeBase();
		StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
		ksession.execute(modelRyzyka);
		System.out.println("Done firing.. --> result = " + modelRyzyka.Ryzyko);
		return modelRyzyka.Ryzyko;
	}
 
	private static KnowledgeBase readKnowledgeBase() throws Exception {
		KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
		kbuilder.add(
				ResourceFactory.newClassPathResource("rules" 
				        + File.separator + "rules.drl"),
				ResourceType.DRL);
		KnowledgeBuilderErrors errors = kbuilder.getErrors();
		if (errors.size() > 0) {
			for (KnowledgeBuilderError error : errors) {
				System.err.println(error);
			}
			throw new IllegalArgumentException("Could not parse knowledge.");
		}
		KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
		kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
		return kbase;
	}
 
}
RuleRunnerTest.java
package pl.org.bpmn;
 
import static org.junit.Assert.assertEquals;
 
import pl.org.bpmn.RuleRunner;
import pl.org.bpmn.Model;
import org.junit.Test;
 
public class RuleRunnerTest {
 
	@Test
	public void testNiskiegoRyzyka(){
		Model modelRyzyka = new Model();
		modelRyzyka.Kompetencje = 8;
		modelRyzyka.Trudnosc = 3;
		String ryzyko = null; 
		try {
			 ryzyko = RuleRunner.runRules(modelRyzyka);
		} catch (Exception e) {
			e.printStackTrace();
		}	
		assertEquals("Niskie", ryzyko);		
	}
 
	@Test
	public void testWysokiegoRyzyka(){
		Model modelRyzyka = new Model();
		modelRyzyka.Kompetencje = 2;
		modelRyzyka.Trudnosc = 7;
		String ryzyko = null; 
 
		try {
			 ryzyko = RuleRunner.runRules(modelRyzyka);
		} catch (Exception e) {
			e.printStackTrace();
		}
 
		assertEquals("Wysokie", ryzyko);		
	}
 
	@Test
	public void testSredniegoRyzyka(){
		Model modelRyzyka = new Model();
		modelRyzyka.Kompetencje = 8;
		modelRyzyka.Trudnosc = 8;
		String ryzyko = null; 
 
		try {
			 ryzyko = RuleRunner.runRules(modelRyzyka);
		} catch (Exception e) {
			e.printStackTrace();
		}
 
		assertEquals("Srednie", ryzyko);		
	}
 
	@Test
	public void testBrakuRegulOkreslajacychRyzyko(){
		Model modelRyzyka = new Model();
		modelRyzyka.Kompetencje = 5;
		modelRyzyka.Trudnosc = 5;
		String ryzyko = null; 
 
		try {
			 ryzyko = RuleRunner.runRules(modelRyzyka);
		} catch (Exception e) {
			e.printStackTrace();
		}
 
		assertEquals("Brak danych", ryzyko);		
	}
 
}

W pliku pom.xml powinny zostać dołożone następujące elementy:

  <properties>
	<drools-version>5.3.0.Final</drools-version>
  </properties>
  <dependencies>
    		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-core</artifactId>
			<version>${drools-version}</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-compiler</artifactId>
			<version>${drools-version}</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-decisiontables</artifactId>
			<version>${drools-version}</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-transformer-xstream</artifactId>
			<version>5.0.1</version>
		</dependency>
  </dependencies>
  <repositories>
		<repository>
			<id>jboss-public-repository-group</id>
			<name>JBoss Public Maven Repository Group</name>
			<url>http://repository.jboss.org/nexus/content/groups/public-jboss/</url>
		</repository>
  </repositories>  
pl/dydaktyka/bim/lab5.txt · ostatnio zmienione: 2017/07/17 08:08 (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