Wykład 4
Transkrypt
Wykład 4
Informatyzacja przedsiębiorstw WYKŁAD dr inż. Piotr Zabawa IBM/Rational Certified Consultant [email protected] wersja 0.1.0 07.10.2010 Wykład 4 Mechanizm zdarzeń biznesowych w środowisku Jboss Drools Wstęp Aby zrozumieć wyraźnie znaczenie praktyczne pojęcia zdarzeń biznesowych posłużymy się na wstępie uproszczonymi (co nie znaczy – zupełnie prostymi) przykładami HelloWorld od 05 do 07. Przykłady te zostały wymyślone i opracowane przez autora wykładu. Wstęp Wspomniane przykłady HelloWorld nawiązują do powitań pokazując kolejno jak: • Wyzwolić aktywność (powitanie) zdarzeniem biznesowym • Wyznaczyć autorytet grupy ludzi mierzony średnią długością sentencji powitalnych skierowanych do poszczególnych członków tej grupy wyliczoną dla wszystkich przedstawicieli grupy łącznie • Wyznaczyć autorytet poszczególnych członków grupy mierzony średnią długością sentencji powitalnych wyliczoną dla każdego członka grupy osobno Zdarzenia w Drools Omówimy następujące zagadnienia ilustrując je przykładami HelloWorld: • Jak zdefiniować zdarzenie • Jak wysłać zdarzenie • Jak kanalizować rozpływ zdarzeń – organizacja pamięci • Jak korzystać ze zdarzeń w regułach • Jak przeprowadzać analizę statystyczną zdarzeń w oderwaniu od czasu • Jak współdzielić dane statystyczne pomiędzy regułami Zdarzenia w Drools Fakty stanowią dane statyczne, jednak często w modelowaniu procesów biznesowych istnieje potrzeba zdefiniowania relacji pomiędzy tymi elementami w czasie. Jest to nazywane Complex Event Processing (CEP) lub Event Sream Processing (ESP). Wsparcie dla tego mechanizmu w Drools oferuje Drools Fusion. Na razie jednak skupimy się na samych zdarzeniach w oderwaniu od osi czasu, tzn. kolejność zdarzeń, ani ich specyficzne sekwencje nie będą na razie miały dla nas znaczenia. Zdarzenia w Drools Podejście związane z przetwarzaniem zdarzeń nawiązuje do pojęcia Event Driven Architecture. Warto się przy okazji z nim zapoznać (dla zainteresowanych osób - poza wykładem). Zdarzenia w Drools Zdarzenie składa się z: • Nagłówka zawierającego meta-dane (nazwa, czas zgłoszenia, czas trwania,…) • Ciała zawierającego informacje o zdarzeniu (UUID transakcji bakowej, kwota przelewu, konto źródłowe, docelowe,…) Pojęcie CEP odnosi się do sekwencji zdarzeń a nie do zdarzeń pojedynczych. DroolsHelloWorld_05 Przykład ten pokazuje jak utworzyć, wysłać i przechwycić event. Zdarzenia w Drools Czym jest event? Klasa Java POJO ewentualnie z finalnymi polami prywatnymi (zdarzenie nie powinno się zmieniać!) ustawianymi tylko w konstruktorze, np. public class NotificationEvent implements Serializable { private static final long serialVersionUID = 1L; public NotificationEvent(){ } @Override public String toString(){ return null; } } Zdarzenia w Drools Event jest szczególnym rodzajem faktu, który należy umieścić w sesji z silnikiem reguł. Reguła może być np. taka: // modyfikacja istniejącego faktu declare NotificationEvent @role( event ) end rule "Event notification" when NotificationEvent() from entry-point NotificationStream; then System.out.println( "Hello World!" ); end Zdarzenia w Drools Co to jest entry-point? Pamięć sesji może/powinna zostać podzielona na strumienie zdarzeń – daje to możliwość separacji i lepszego wykorzystania współbieżności mechanizmu obsługi zdarzeń przez silnik reguł biznesowych. entry-point wiąże zdarzenie ze strumieniem zdarzeń, do którego chcemy żeby było przypisane Zdarzenia w Drools Jak umieścić zdarzenie w sesji? Przed utworzeniem bazy wiedzy: KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(); config.setOption(EventProcessingOption.STREAM); Po utworzeniu sesji: WorkingMemoryEntryPoint entry = ksession.getWorkingMemoryEntryPoint("NotificationStream"); entry.insert(new NotificationEvent()); ksession.fireAllRules(); DroolsHelloWorld_06 Zdarzenia w Drools Obliczanie statystyk może dotyczyć np. policzenia prestiżu grupy ludzi mierzonego średnią wartością długości sekwencji powitalnych przypisanych każdemu z tych ludzi – czyli średnia dla grupy. Zdarzenia w Drools Do wyliczenia tej informacji statystycznej użyjemy query: query "averagePrestigeQuery" Number( $averagePrestige : doubleValue ) from accumulate( $receiver : GreetingsReceiver(), average($receiver.getLength())) end Zdarzenia w Drools W ramach collect lub accumulate można użyć też: • count • min • max • sum Zdarzenia w Drools GreetingsReceiver tomek = new GreetingsReceiver("Tomek"); ksession.insert(tomek); tomek.receiveGreeting("Hi"); GreetingsReceiver janek = new GreetingsReceiver("Janek"); ksession.insert(janek); janek.receiveGreeting("Hello"); GreetingsReceiver staszek = new GreetingsReceiver("Staszek"); ksession.insert(staszek); staszek.receiveGreeting("Good morning"); ksession.fireAllRules(); System.out.println(getAveragePrestige(ksession)); Zdarzenia w Drools private static BigDecimal getAveragePrestige(StatefulKnowledgeSession ksession){ QueryResults queryResults = ksession.getQueryResults("averagePrestigeQuery"); return BigDecimal.valueOf((Double) queryResults.iterator().next().get("$averagePrestige")); } Zdarzenia w Drools Chcemy teraz sięgnąć do HelloWorld07. Postaramy się w nim wyliczyć średnią długość sentencji powitalnych kierowanych do każdego z uczestników grupy ludzi przez inne osoby z ich otoczenia. Przy okazji zobaczymy jak definiować w Drools własne typy danych i jak można obiekty tych typów przekazywać pomiędzy regułami. Mamy zatem współdzielone dane: declare GreetingsInfo name: String averageLength: Double end Typy zostały tak dobrane, aby uniknąć konwersji Zdarzenia w Drools Zastosujemy dwie reguły, aby pokazać komunikację między nimi: • Pierwsza reguła wyznacza wartości średnie per człowiek i zapisuje je we współdzielonych obiektach natywnych Drools • Druga reguła korzysta z wyników statystyk zapisanych w obiektach natywnych Drools i wypisuje wynik do konsoli (wykorzystuje ona zresztą te dane w swoich warunkach) Zdarzenia w Drools // reguła wylicza średnią długość powitania rule "Average greetings length" dialect "mvel" no-loop true when $receiver : GreetingsReceiver() $averageLength : Double() from accumulate( GreetingReceivedEvent(receiver == $receiver.name) from entry-point GreetingsStream, average($receiver.getLength()) ) $greetingsInfo : GreetingsInfo(name == $receiver.name) then modify($greetingsInfo){ setAverageLength($averageLength) }; end Zdarzenia w Drools // reguła korzysta z wyliczonych wartości // tutaj wypisuje imię kaŜdego członka grupy // wraz z wyliczoną dla niego // średnią długością powitania rule "Report greetings" dialect "mvel" when $info : GreetingsInfo() then System.out.println( "name = "+$info.name ); System.out.println( "average = " +$info.getAverageLength() ); end Zdarzenia w Drools public class GreetingsReceiver { private String name; private String greetings = ""; public GreetingsReceiver(String name){ this.name = name; } public String getName(){ return name; } public void receiveGreeting(String greeting){ greetings = greeting; } @Override public boolean equals(final Object other){ if (this==other) return true; if (!(other instanceof GreetingsReceiver)) return false; GreetingsReceiver castOther = (GreetingsReceiver) other; return name.equals(castOther.name); } public long getLength(){ long counter = 0; for (int i=0; i<greetings.length(); i++) if (greetings.charAt(i) != ' ') counter++; return counter; } } Zdarzenia w Drools public class GreetingReceivedEvent implements Serializable { private static final long serialVersionUID = 1L; private final String receiver; public GreetingReceivedEvent(GreetingsReceiver receiver, String greeting){ this.receiver = receiver.getName(); receiver.receiveGreeting(greeting); } public String getReceiver(){ return receiver; } @Override public String toString(){ return null; } } Zdarzenia w Drools FactType greetingsInfoFactType = kbase.getFactType("com.sample", "GreetingsInfo"); // wstawienie do sesji odbiorców powitań // i obiektu natywnego Drools do przehowywania statystyk dla kaŜdego z odbiorców GreetingsReceiver tomek = new GreetingsReceiver("Tomek");// 2 ksession.insert(tomek); Object greetingsInfoTomek = greetingsInfoFactType.newInstance(); greetingsInfoFactType.set(greetingsInfoTomek, "name", tomek.getName()); greetingsInfoFactType.set(greetingsInfoTomek, "averageLength", 0.0); ksession.insert(greetingsInfoTomek); GreetingsReceiver janek = new GreetingsReceiver("Janek");// 8 ksession.insert(janek); Object greetingsInfoJanek = greetingsInfoFactType.newInstance(); greetingsInfoFactType.set(greetingsInfoJanek, "name", janek.getName()); greetingsInfoFactType.set(greetingsInfoJanek, "averageLength", 0.0); ksession.insert(greetingsInfoJanek); GreetingsReceiver staszek = new GreetingsReceiver("Staszek");// 6 ksession.insert(staszek); Object greetingsInfoStaszek = greetingsInfoFactType.newInstance(); greetingsInfoFactType.set(greetingsInfoStaszek, "name", staszek.getName()); greetingsInfoFactType.set(greetingsInfoStaszek, "averageLength", 0.0); ksession.insert(greetingsInfoStaszek); Zdarzenia w Drools // wstawienie do sesji zdarzeń róŜnych powitań // skierowanych do róŜnych osób // średnia długość powitania = (2+5+11)/3 = 6; String shortGreeting = "Hi"; // 2 String mediumGreeting = "Hello"; // 5 String longGreeting = "Good morning"; // 11 Zdarzenia w Drools WorkingMemoryEntryPoint entry = ksession.getWorkingMemoryEntryPoint("GreetingsStream"); // wysyłanie eventów entry.insert(new GreetingReceivedEvent(tomek, shortGreeting)); entry.insert(new GreetingReceivedEvent(janek, mediumGreeting)); entry.insert(new GreetingReceivedEvent(janek, longGreeting)); entry.insert(new GreetingReceivedEvent(staszek, shortGreeting)); entry.insert(new GreetingReceivedEvent(staszek, mediumGreeting)); entry.insert(new GreetingReceivedEvent(staszek, longGreeting)); ksession.fireAllRules(); Zdarzenia w Drools Wynik działania programu w konsoli: name = Staszek average = 6.0 name = Janek average = 8.0 name = Tomek average = 2.0 Zdarzenia w Drools Drools Fusion oferuje możliwość pracy ze zdarzeniami w powiązaniu z upływem czasu. Robi się to przez określanie ramek czasowych. W zastosowaniu do systemów bankowych pozwala to na wykrycie podejrzanych transakcji (mogących świadczyć np. o tym, że kto inny posługuje się kartą płatniczą lub poznał login i hasło do serwisu internetowego klienta banku). Zdarzenia w Drools rule twoLargeWithdrawals dialect „mvel” when $account : Account() Number($averageAmount : doubleValue) from accumulate( TransactionCompletedEvent(fromAccountNumber == $account.number, $ammount : ammount) over window:time(30d) from entry-point TransactionStream, average($ammount)) $t1 : TransactionCreatedEvent(fromAccountNumber == $account.number, amount > ($averageAmmount * 3.00)) over window:time(90s) from entry-point TransactionStream $t2 : TransactionCreatedEvent(this!=$t1, fromAccountNumber == $account.number, amount > ($averageAmmount * 3.00)) over window:time(90s) from entry-point TransactionStream then… Zdarzenia w Drools Są dwa rodzaje okien: • sliding time window (tylko te zdarzenia, które zaczęły się w określonym przedziale czasu) • sliding length window (tylko ostatnich N zdarzeń) Pozwalają one w warunkach reguł dopasowywać tylko te zdarzenia, które rozpoczęły się w oknie. Dodatkowo Drools w przypadku zastosowania time window w trybie STREAM usuwa te zdarzenia, które znajdują się poza oknem. Zdarzenia w Drools Drools Fusion oferuje też możliwość sprawdzania kolejności zdarzeń: this after[0, 3m] $t1 Zdarzenie $t1 miało miejsce nie później niż zaszło zdarzenie, w którym sprawdzamy ten warunek. Jest to tzw. temporal operator Zdarzenia w Drools Oprócz okien mamy w Drools Fusion do dyspozycji timery: • Real-time clock (do wykorzystania w aplikacjach) • Pseudo clock (do testowania ze względu na pełną kontrolę nad tym timerem) Zdarzenia w Drools Jeśli chodzi o testy, to oprócz wsparcia przez specjalny timer można wykorzystać też: • AgendaEventListener • AgendaFilter private void assertFired(String ruleName){ session.fireAllRules(new RuleNameEqualsAgendaFilter(ruleName)); assertTrue(trackingAgendaEventLitener.isRuleFired(ruleName)) } gdzie Zdarzenia w Drools public class TrackingAgendaListener extends DefaultAgendaListener{ List<String> rulesFiredList = new ArrayList<String>(); @Override public void afterActivationFired(AfterActivationFiredEvent event){ rulesFiredList.add(event.getActivation().getRule().getName()); } public boolean isRuleFired(String ruleName){ for(String firedRuleName : rulesFiredList) if(firedRuleName.equals(ruleName)) return true; return false; } public void reset(){ rulesFiredList.clear(); } } Zdarzenia w Drools Kolejnym przykładem, tym razem związanym z upływem czasu towarzyszącym zdarzeniom może być decyzja o wyeliminowaniu z grupy człowieka, którego zbyt często inni pozdrawiają – pewnie robią to dla żartu, co raczej nie świadczy o jego wysokich notowaniach. Pokusimy się więc o napisanie programu, który ustali regułę eliminacji, a następnie przetestujemy go.