Archive for Implementacja NGinn
Jak wywołać dowolny web service bez WSDL i bez generowania kodu
W dzisiejszym artykule zajmiemy się wywoływaniem web services z poziomu procesu NGinn, przy okazji poznając alternatywny sposób korzystania z web services. Jak pewnie doskonale wszyscy wiedzą, web services są komponentami (uslugami) dostępnymi zdalnie, za pośrednictwem niezależnego od platformy protokołu SOAP. Wywołanie web service jest niczym innym jak przekazaniem komunikatu SOAP oraz odebraniem odpowiedzi (też SOAP), najczęściej za pomocą protokołu http.
Jednak gdy programista chce w swojej aplikacji wywołać jakiś web service zwykle nie posługuje się on bezpośrednio SOAPem, ale używa dostępnych w danym języku narzędzi ‘opakowujących’ które pozwalają mu wywoływać web service tak jakby to było wywołanie lokalnej funkcji . W Microsoft.Net posługujemy się na przykład narzędziem WSDL.exe które na podstawie XML-owego opisu usługi (WSDL) generuje nam kod w C# (lub VB) który opakowuje nam web service do postaci ‘strawnej’ dla .Net, czyli obiektu określonej klasy z interfejsem zgodnym z definicją web service. Jest to na tyle wygodne i uniwersalne że w typowych zastosowaniach możemy polegać tylko na wsdl.exe i w ogóle zapomnieć o XML-u i http pod spodem. Ale czasami wsdl.exe nie wystarcza – na przykład gdy nie wiemy z góry jaki web service będziemy wywoływać.
WSDL.exe generuje kod opakowujący web service, co oznacza że struktura web service jest wkompilowana w program który z danej usługi korzysta – i gdy usługa się zmieni to należy ponownie wygenerować kod wsdl.exe’m i skompilować aplikację od nowa. Świetnie, ale gdy aplikacja musi się komunikować z web service którego nie znamy na etapie kompilacji to WSDL.exe do niczego się nie przyda. NGinn jest właśnie takim przypadkiem ponieważ oferuje możliwość wywoływania web services w ramach procesów. Wszelkie potrzebne do wywołania informacje czerpie z definicji procesu a na podstawie tych informacji tworzy i wysyła komunikaty SOAP.
Żeby wywołać web service z procesu NGinn trzeba posłużyć się zadaniem ‘XmlHttp’. Jest to zadanie pozwalające na wykonywanie wywołań http, między innymi takich które zawierają XML. Żeby wywołać web service trzeba tylko zadbać o to aby ten XML był SOAP-em o strukturze której spodziewa się nasz web service – czyli zapewnić ‘binding’ między danymi na których operuje nasz proces a odpowiednimi polami w komunikacie SOAP. Na aktualnym etapie rozwoju NGinn robi się to za pomocą transformacji XSLT. Żeby było łatwiej to wszystko przyswoić posłużmy się przykładem. Jest taka publicznie dostępna usługa http://aspspider.info/trutech/MarketWatch.asmx która pozwala m.in . zapytać się o kursy akcji, indeksy giełdowe i kursy metali szlachetnych. Udostępnia ona operację ‘GetQuote’ za pomocą której będziemy odpytywać się o kursy akcji. Po wejściu na w/w stronę ukazuje się nam opis usługi (standardowo wygenerowany przez ASP.Net) z WSDL oraz z przykładami komunikatów SOAP. No i przykładowy komunikat SOAP dla zapytania o kurs akcji Microsoftu wygląda tak:
[xml]
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetQuote xmlns="http://trutech.com/marketwatch/">
<credValidate>
<Email>trutech.shared@gmail.com</Email>
<Password>trutech.shared@gmail.com</Password>
</credValidate>
<MyScript>
<sScriptCode>MSFT</sScriptCode>
</MyScript>
</GetQuote>
</soap12:Body>
</soap12:Envelope>
[/xml]
Gdy wyślemy taki XML POST-em na adres http://aspspider.info/trutech/MarketWatch.asmx dostaniemy odpowiedź która wygląda mniej-więcej tak:
[xml]
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetQuoteResponse xmlns="http://trutech.com/marketwatch/">
<GetQuoteResult>true</GetQuoteResult>
<MyScript>
<sScriptCode>MSFT</sScriptCode>
<sScriptName>MSFT – Microsoft Corpora</sScriptName>
<sScriptNameFromServer>Microsoft Corpora</sScriptNameFromServer>
<sScriptStockExchange>NasdaqNM</sScriptStockExchange>
<sScriptLastPrice>28.22 Rs</sScriptLastPrice>
<sScriptPreviousClose>28.22 Rs</sScriptPreviousClose>
<sScriptChangeInRupees>0.00 Rs</sScriptChangeInRupees>
<sScriptChangeInPercentage>0.00 %</sScriptChangeInPercentage>
<sScriptVolume>0 Sh</sScriptVolume>
<sScriptDaysRange>N/A – N/A</sScriptDaysRange>
<sScriptYearRange>14.87 Rs – 29.35 Rs</sScriptYearRange>
<sScriptToolTipText>
Registration Code : MSFT
Company Name : MSFT – Microsoft Corpora
Exchange : NasdaqNM ( 2009-10-29 )
Last Price : 28.22 Rs
Previous Close : 28.22 Rs
Change (INR) : 0.00 Rs
Change (%) : 0.00 %
Trading Volume : 0 Shares
Price Range (Day) : N/A – N/A
Price Range (52 Weeks) : 14.87 Rs – 29.35 Rs
Query Time : 2009-10-30 17:02:57
</sScriptToolTipText>
<sScriptWebLink>http://in.finance.yahoo.com/q?s=MSFT</sScriptWebLink>
<sScriptLastTradeDate>2009-10-29</sScriptLastTradeDate>
<sScriptLastTradeTime>4:00pm</sScriptLastTradeTime>
<sScriptQueryDateTime>2009-10-30 17:02:57</sScriptQueryDateTime>
<bScriptDataFound>true</bScriptDataFound>
<lScriptRowId>0</lScriptRowId>
</MyScript>
</GetQuoteResponse>
</soap:Body>
</soap:Envelope>
[/xml]
Interesująca nas cena jest w polu ‘sScriptLastPrice’. Niektórzy pewnie się zastanawiają co oznacza ‘Rs’ obok ceny – otóż jest to indyjski web service podający ceny w Rupiach. Bardzo przepraszam ale innego nie znalazłem.
Skoro już wiemy jak wyciągnąć cenę akcji Microsoftu w Rupiach to zbudujmy proces NGinn który dokładnie to robi – tj na wejściu bierze symbol spółki giełdowej i odpytuje się o cenę akcji tej spółki. Proces ten będzie miał jedną zmienną wejściową – symbol spółki, i jedną zmienną wyjściową – kurs akcji wyrażony w rupiach.
[xml]
<?xml version="1.0" encoding="utf-8"?>
<process version="1" name="RupeeStockQuote" xmlns="http://www.nginn.org/WorkflowDefinition.1_0.xsd">
<variables>
<variable name="symbol" type="string" required="true" dir="In" />
<variable name="price" type="string" required="true" dir="Out" />
</variables>
<body>
<places>
<place id="start" type="Start" />
<place id="end" type="End" />
</places>
<tasks>
<task id="T1" type="XmlHttp">
<variables>
<variable name="symbol" type="string" dir="In" required="true"/>
<variable name="price" type="string" required="true" dir="Out" />
</variables>
<parameters>
<param variable="Url"><value>http://aspspider.info/trutech/MarketWatch.asmx</value></param>
<param variable="RequestType"><value>SOAP</value></param>
<param variable="ResponseType"><value>SOAP</value></param>
<param variable="HttpMethod"><value>POST</value></param>
<param variable="RequestBodyXsl"><value>GetQuote_Request.xsl</value></param>
<param variable="ResponseBodyXsl"><value>GetQuote_Response.xsl</value></param>
<param variable="RequestHeaders"><expr>{"Content-Type": "application/soap+xml; charset=utf-8"}</expr></param>
</parameters>
</task>
</tasks>
<flows>
<flow from="start" to="T1" />
<flow from="T1" to="end" />
</flows>
</body>
</process>
[/xml]
Proces ten jest bardzo uproszczony – ma tylko jedno zadanie. W normalnej sytuacji miałby on dalszy ciąg, czyli logikę która w jakiś sposób działa w oparciu o cenę akcji i np. wysyła do kogoś powiadomienie. Ale to tylko przykład więc musi być prosty.
Widać tu że zadanie ‘T1’ posługuje się dwiema transformacjami XSL – pierwsza z nich jest zapisana w pliku ‘GetQuote_Request.xsl’ i służy do wygenerowania komunikatu SOAP wywołującego web service, druga, w pliku ‘GetQuote_Response.xsl’ służy do przetworzenia odpowiedzi web service na postać zrozumiałą dla NGinn. Oto te transformacje:
GetQuote_Request.xsl
[xml]
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes" />
<xsl:template match="/data">
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetQuote xmlns="http://trutech.com/marketwatch/">
<credValidate>
<Email>trutech.shared@gmail.com</Email>
<Password>trutech.shared@gmail.com</Password>
</credValidate>
<MyScript>
<sScriptCode><xsl:value-of select="symbol" /></sScriptCode>
</MyScript>
</GetQuote>
</soap12:Body>
</soap12:Envelope>
</xsl:template>
</xsl:stylesheet>
[/xml]
Na wejściu dostaje ona XML zawierający dane zadania ‘T1’, czyli coś w rodzaju
[xml]<data><symbol>MSFT</symbol></data>[/xml]
Na wyjściu natomiast powstaje komunikat SOAP o strukturze opisanej wcześniej – nasza transformacja po prostu wstawia zmienną ‘symbol’ w polu ‘sScriptCode’. Po wygenerowaniu komunikatu SOAP zadanie T1 wysyła go w treści requestu ‘POST’ pod adres web service. Następnie odbiera odpowiedź, czyli opisany wyżej komunikat ‘GetQuoteResponse’, który z kolei poddaje transformacji ‘GetQuote_Response.xsl’:
[xml]
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:quot="http://trutech.com/marketwatch/">
<xsl:output method="xml" encoding="utf-8" indent="yes" />
<xsl:template match="/soap:Envelope/soap:Body/quot:GetQuoteResponse">
<data>
<price><xsl:value-of select="quot:MyScript/quot:sScriptLastPrice" /></price>
</data>
</xsl:template>
</xsl:stylesheet>
[/xml]
Widać że na wyjściu otrzymamy bardzo prosty XML w rodzaju
[xml]<data><price>28.08 Rs</price></data>[/xml]
NGinn na podstawie tego XML zaktualizuje zmienne zadania T1, czyli przypisze otrzymaną od web service wartość kursu akcji do zmiennej ‘price’. Na tym zadanie ‘T1’ się kończy, a razem z nim cały przykładowy proces.
Jest to bardzo elastyczna metoda wywoływania web services, mamy praktycznie całkowitą kontrolę nad strukturą przekazywanych komunikatów. Niestety, dzieje się to kosztem dość żmudnego przygotowywania XSL-i – nie ma żadnego narzędzia które by to zrobiło za nas (pewnie takie narzędzie korzystało by z WSDL, my nie musimy bo mamy przykłady XML-i). Warto tu wspomnieć o narzędziach które przydają się do tworzenia w/w xsl-i i w ogóle do testów web services. Pierwszym z tych narzędzi jest pakiet ‘Curl’ (http://curl.haxx.se/ ) umożliwiający wysyłanie po http dowolnych żądań (a w szczególności naszych komunikatów SOAP). Drugim jest pakiet ‘nxslt’ (http://www.xmllab.net/downloads/nxslt/) umożliwiający testowanie działania transformacji XSLT. Posługując się tymi dwoma narzędziami jesteśmy w stanie szybko ustalić jaka powinna być struktura komunikatów SOAP i wygenerować odpowiednie arkusze XSL. W ten sam sposób możemy obsłużyć nie tylko wywołania web services, ale np protokół XML-RPC czy inne, oparte na XML/HTTP protokoły komunikacji co czyni zadanie XmlHttp dość uniwersalnym narzędziem.
Taki sposób używania web services nie jest oczywiście zarezerwowany dla NGinn – można to robić w każdej innej sytuacji, nawet gdy mamy do dyspozycji Visual Studio z automatycznym generowaniem kodu klienta web service. Po co? Otóż są pewne typy danych których standardowa serializacja SOAP oferowana przez Microsoft.Net nie potrafi obsłużyć – na przykład typy nullable, nieprawidłowo też są obsługiwane pola typu ‘DateTime’ w innej niż UTC strefie czasowej. Innym przykładem są niestandardowe nagłówki http lub rozszerzenia SOAP – za pomocą opisanego tu sposobu możemy generować wywołania z dowolnymi nagłówkami i z dowolną treścią, podczas gdy w ‘normalny’ sposób musieli byśmy pisać w C#/VB dodatkowy kod obsługujący przypadki niestandardowe. No a poza tym jest to świetny sposób testowania web services bez pisania kodu.
Ponieważ opisany tu mechanizm wywoływania web services jest bardzo prosty, można wręcz powiedzieć że prymitywny, rodzi się pytanie czy jest to rozwiązanie docelowe czy tylko prowizoryczne? Na pewno brakuje mu kilku rzeczy – między innymi automatycznej kontroli zgodności schematów danych z definicją web service, można by się też pokusić o jakiś graficzny edytor pozwalający mapować zmienne zadania na parametry we/wy usługi lub też automatycznie generować XSL-e na podstawie jakiegoś prostszego mapowania. Ale mimo to uważam że jest to elastyczne i proste w użyciu narzędzie które da się zastosować w wielu sytuacjach.
O dokumentach, procesach, bazach danych i JSON. No i o przyszłości.

Mówiąc o jakichkolwiek procesach biznesowych wcześniej czy później trafiamy na pojęcie dokumentu. To wokół dokumentu buduje się procesy – dokumentem może być jakiś papier wędrujący przez organizację, faktura, sprawa zarejestrowana w jakimś systemie, nadesłany przez klienta email z zażaleniami i tak dalej. Jeśli chcemy taki proces ‘zinformatyzować’ to również dokumenty muszą zostać w jakiś sposób przetworzone na postać cyfrową i zarejestrowane w jakimś systemie.
W przypadku NGinn nastawiłem się na maksymalną elastyczność w temacie dokumentów do których odnoszą się procesy – mianowicie elastyczność wynika z całkowitego pominięcia tego tematu. Procesy w NGinn operują na danych (zostało to opisane we wcześniejszych artykułach) i te dane zawierają wszystkie informacje istotne z punktu widzenia działania procesu. W szczególności założyłem że będą mogły zawierać odnośnik do odpowiedniego dokumentu w systemie zewnętrznym – na przykład numer faktury w systemie księgowym albo numer zgłoszenia w systemie wsparcia klientów. Sam system NGinn nie wnika w to w jakim systemie i w jaki sposób są te dokumenty przechowywane, natomiast oferuje mechanizmy integracyjne pozwalające na wymianę danych z takimi systemami i pobieranie odpowiednich informacji o dokumencie jeśli to konieczne. To pozwala twierdzić że NGinn znakomicie nadaje się do rozszerzania możliwości istniejących w firmie systemów oraz do ich integrowania, ale zupełnie pomija przypadek gdy odpowiedniego systemu nie ma w firmie a dokumenty dopiero czekają na bazę danych która je przechowa. No właśnie, co wtedy?
Pierwszy pomysł to wykorzystanie danych procesu. NGinn pozwala na definiowanie własnych struktur danych dla procesów, więc nic nie stoi na przeszkodzie żeby danymi procesu był cały dokument. Projektujemy strukturę danych tak żeby uwzględnić wszystkie pola dokumentu i wtedy zawartość naszego ‘dokumentu’ jest przechowywana przez NGinn razem z innymi informacjami o procesie. Zaletą jest to że nie musimy się martwić o przechowywanie tych danych, wad za to jest kilka – przede wszystkim dokument istnieje tylko razem z procesem i ‘zamiera’ gdy proces się kończy, dodatkowo NGinn bardzo ogranicza to co można zrobić z danymi procesu. Tak więc ten sposób jest dobry tylko dla prostych struktur danych, nieistotnych poza procesem który ich dotyczy.
Druga możliwość wiąże się z pewną rozbudową NGinn. Otóż w tej chwili NGinn przechowuje stan procesów i zadań w postaci JSON. Każde zadanie zapisywane jest w postaci pojedynczego dokumentu JSON zawieracjącego wszystkie jego dane i informacje potrzebne do działania procesu. Dzięki temu mamy elastyczność i dowolność w definiowaniu struktury procesu i jego danych. No i jeśli byśmy chcieli rozszerzyć NGinn o bazę dokumentów to możemy po prostu wykorzystać ten sam mechanizm i przechowywać dowolne dokumenty w postaci JSON – wtedy zarówno dokument jak i proces toczący się wokół niego były by obsługiwane w ten sam sposób, jako dokumenty JSON. Pomysł interesujący według mnie, ale czy są z nim jakieś problemy?
Są, ale to raczej ‘wyzwania’ niż problemy. Chodzi o to że w tej chwili do przechowywania dokumentów JSON ze stanem procesu jest wykorzystywana baza danych (MSSQL) – jest tam jedna tabela z polem tekstowym (ntext) do której wrzucany jest cały dokument JSON. Dla NGinn to jest wystarczające, ale gdybyśmy w ten sposób chcieli przechowywać dokumenty z danymi które chcemy przeszukiwać to szybko się okaże że szukanie odpada, nie da się efektywnie takiej postaci danych przeszukiwać a i aktualizacja jest mocno ograniczona. Rozwiązaniem według mnie jest przejście na nierelacyjną bazę danych, na przykład taką przeznaczoną do obsługi dokumentów JSON. Takich baz danych jest bardzo wiele, komercyjnych i darmowych. Powstały jako ‘back-end’ dla serwisów internetowych obsługujących bardzo duży ruch i wymagacjących ogromnej skalowalności bazy danych – np bardzo popularnych serwisów społecznościwych i sprawdzają się w takich zastosowaniach o wiele lepiej niż bazy relacyjne. Dodatkowo są bardzo elastyczne jeśli chodzi o struktury danych, a to jest wg mnie kluczowa sprawa przy obsłudze dużych (raczej bardzo dużych) ilości dokumentów. Wystarczy pomyśleć co się dzieje gdy chcemy np zmienić strukturę istniejącej bazy relacyjnej:
- czas: jeśli tabela jest bardzo duża (miliony rekordów) to dodanie nowej kolumny może trwać bardzo długo gdyż może wywołać zwiększenie rozmiaru rekordu i reorganizację całego pliku bazy danych. Długo to znaczy np 5 godzin, gdy nasze ‘okienko serwisowe’ to 30 minut.
- dostępność: na czas aktualizacji bazy relacyjnej musimy wyłączyć aplikację gdyż operacje modyfikacji bazy wykluczają jej odczyt (blokada dostępu do tabel na czas aktualizacji)
- inne problemy: np konieczność zdejmowania i ponownego zakładania constraintów przy operacjach na danych, czasami prosta aktualizacja wymaga niezmiernie skomplikowanych operacji na więzach integralności i indeksach
- no właśnie, indeksowanie, nie można z nim przesadzić gdyż nadmierne poindeksowanie danych spowalnia ich aktualizację
W przypadku bazy dokumentów JSON nie musimy w żaden sposób zmieniać jej struktury gdy modyfikujemy strukturę naszych dokumentów, zaś indeksowanie jest w trybie ‘offline’ czyli nie wpływa na wydajność aktualizacji. Dodając do tego fakt że JSON jest podstawowym formatem dokumentu w NGinn użycie tekstowej bazy dokumentów wydaje się naturalnym pomysłem. A dobrym kandydatem wydaje mi się baza ‘Persevere’ (http://www.persvr.org/) – jest to kompletna baza danych dokumentów JSON z dostępem poprzez HTTP/REST i z rozbudowanymi funkcjami ‘bazodanowymi’ takimi jak indeksowanie, przeszukiwanie czy zarządzanie zawartością bazy. Polecam zapoznanie się choć pobieżnie z jego możliwościami oraz potencjalnymi zastosowaniami, wg mnie to bardzo otwiera oczy na możliwości tych ‘nowych’ baz danych oraz ich potencjalne zastosowania. Jak by wyglądał NGinn po przejściu na bazę Persevere (lub inną tego typu)? Przede wszystkim myślę że wtedy nie będzie używał żadnej bazy relacyjnej, a po drugie myślę że byłby wydajniejszy i bardziej elastyczny, zwłaszcza gdyby dać możliwość dowolnego indeksowania dokumentów i danych w procesach w celu ich łatwego przeszukiwania. Na pewno poświęcę temu niedługo więcej czasu.
P.S. Bazy w rodzaju Persevere zwykle nie obsługują transakcji rozproszonych ani two-phase commit. To jest duże ograniczenie dla NGinn i główny problem do przezwyciężenia. Chodzi o to że w tej chwili NGinn wykorzystuje transakcje rozproszone (transactionscope) do zapewnienia spójności stanu procesu w razie jakichś problemów.
Castle Windsor zamiast Spring
Poprzednia wersja NGinn została zbudowana w oparciu o kontener Spring. I to było dobre, Spring jest naprawdę świetnym narzędziem z doskonałą dokumentacją, przykładami oraz funkcjonalnością. Ale okazało się że wiele komponentów które wykorzystuję w NGinn bazuje na innym kontenerze – Castle Windsor. Ponieważ używanie dwóch różnych kontenerów w jednym projekcie to trochę kiepski pomysł, postanowiłem przejść na Castle Windsor. Pierwsze próby były trudne, bo Windsor jest o wiele słabiej udokumentowany niż Spring a poza tym ma ewidentnie skromniejsze możliwości jeśli chodzi o konfigurację komponentów. Ale ma też dobre cechy – jest bardzo ‘lekki’ i nadaje się dobrze do konfiguracji programistycznej (Spring głównie poprzez XML). W dodatku jest dla niego rozszerzenie o nazwie Binsor, pozwalające konfigurować komponenty w oparciu o język Boo (Binsor jest przykładem DSL, czyli Domain Specific Language, zbudowanego w oparciu o Boo). Boo jest także wykorzystywany jako podstawowy język skryptowania procesów w NGinn – zatem decyzja mogła być tylko jedna: wchodzę w to. I na razie nie żałuję, choć pojawiło sie kilka trudności na horyzoncie – przede wszystkim brak możliwości wystawienia komponentów z kontenera poprzez .Net remoting tak jakbym chciał. Ale myślę że da się to łatwo przeskoczyć.
A co na horyzoncie: wykorzystanie prawdziwego ESB do przekazywania komunikatów w NGinn – flirtuję z projektem Rhino Service Bus. To on właśnie wymaga Castle Windsora. A poniżej można zobaczyć jak wygląda konfiguracja NGinn w Boo:
import System.IO
import Castle.Facilities.FactorySupport from Castle.MicroKernel
import Castle.Facilities.Startable from Castle.MicroKernel
import Rhino.ServiceBus.Impl from Rhino.ServiceBus
import NGinnBPM.Lib.Interfaces
import NGinnBPM.Lib.Interfaces.MessageBus
import NGinnBPM.Services
import NGinnBPM.Lib.Services
import NGinnBPM.Dao
import System.Collections.Generic
cfg = Kernel.Resolve(NGinnBPM.Lib.Util.DefaultConfigProvider)
baseDir = cfg.ResolveVariable("ng.configdir")
endp = cfg.ResolveVariable("NGinn.MessageQueue")
connstr = cfg.ResolveVariable("NGinn.ConnectionString")
Facility FactorySupportFacility
Facility StartableFacility
Facility ("remoting", Castle.Facilities.Remoting.RemotingFacility, {
@isServer: true,
@registryUri: "nginn.kernel.rem",
@remotingConfigurationFile: "NGinnBPM.Engine.WindsorHost.exe.config"
})
Facility("rhino.esb", RhinoServiceBusFacility, {
bus: {
@endpoint: endp,
@threadCount: 1
},
messages: {
add: {@name: "NGinnBPM.Runtime.Events", @endpoint: endp}
}
})
Component("TaskInstanceFactory", ITaskInstanceFactory, TaskInstanceFactory)
Component("TaskDefinitionFactory", ITaskDefinitionFactory, TaskDefinitionFactory)
Component("ProcessScriptManager", IProcessScriptManager, NGinnBPM.Services.Scripting.BooScript.BooProcessScriptManager, LifestyleType.Singleton,
BaseDirectory: "${baseDir}\\temp\\BooProcessScriptManager")
Component("TaskInstanceRepository", ITaskInstanceRepository, RawSqlTaskInstanceRepository, LifestyleType.Singleton,
ConnectionString: connstr)
Component("MessageBus", IMessageBus, NGinnBPM.Services.MessageBus.SqlMessageHub, LifestyleType.Singleton,
Endpoint: "sql://nginn/MQueue", ConnectionStrings: {"nginn": connstr})
Component("PackageRepository", IProcessPackageRepository, NGinnBPM.Services.FSProcessPackageRepository, LifestyleType.Singleton,
BaseDirectory: "${baseDir}\\PackageRepository")
Component("MessageCorrelationResolver", IMessageCorrelationIdResolver, NGinnBPM.Dao.RawSqlMessageCorrelationIdResolver, LifestyleType.Singleton,
ConnectionString: connstr)
Component("LockManager", IProcessInstanceLockManager, LocalProcessInstanceLockManager, LifestyleType.Singleton)
Component("Environment", INGEnvironment, NGinnBPM.Runtime.NGEnvironment, LifestyleType.Singleton)
Component("NGEngine", NGinnBPM.Runtime.NGEngine)
Konfigurując nasze komponenty w pliku konfiguracyjnym, czy jest to XML czy Boo, zyskujemy ważną rzecz – nie wprowadzamy zależności w kodzie, nawet gdy komponenty pochodzą z jakichś zewnętrznych assembly. Czyli nie musimy już mieć w kodzie miejsca które odwołuje się do wszystkich wykorzystywanych bibliotek, takie miejsce jest tylko w konfiguracji. I nie myślmy za dużo o tym że Boo to też kod, tylko w innym języku.
Script.Net czy Boo?
Jakiś czas temu do ‘oskryptowywania’ procesów zacząłem używać języka Script.Net którego autorem jest ukraiński programista – Petro Protsyk. Script.Net jest prostym w użyciu skryptem szczególnie nadającym się do wbudowywania w aplikacje, tylko że wersje na których pracowałem ciągle pozostawiały wiele do życzenia jeśli chodzi o jakość. Ilość błędów przekreślała produkcyjne użycie. Dlatego zacząłem patrzeć na język Boo – to już nie jest skrypt tylko język kompilowany, jednak pozwalający na łatwą dynamiczną kompilację i posiadający bardzo przyjazną skryptową składnię. Za pomocą Boo zrobiłem prosty moduł reguł biznesowych oraz view engine dla ASP.Net MVC pozwalającą generować dynamiczny JSON i generalnie jestem wielkim zwolennikiem tego języka, więc przyjąłem że w 2.0 Nginn Boo będzie używany również do programowania procesów. Implementacja tego jest jednak trudna, a w dodatku Petro Protsyk ostatnio wypuścił nową wersję Script.Net więc widać że chłopak się zajmuje swoim projektem i zmierza do wersji stabilnej – w rezultacie mam dylemat który język wybrać. Jak to bywa w informatyce, gdy nie wiadomo które rozwiązanie wybrać najczęściej wybiera się oba na raz i robi się system pluginów pozwalający użyć albo jednego albo drugiego, ale ja chciałbym uniknąć wykonywania podwójnej roboty. Na razie potestuję więc tego Script.Neta.