<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 <title>Consileon Dev Blog</title>
 <link href="http://devblog.consileon.pl/atom.xml" rel="self"/>
 <updated>2012-03-01T02:06:31-08:00</updated>
 <id>http://devblog.consileon.pl/</id>
 
 <entry>
   <id>http://devblog.consileon.pl/2012/02/28/CQRS-DDD-ES-w-pigulce</id>
   <link href="http://devblog.consileon.pl/2012/02/28/CQRS-DDD-ES-w-pigulce"/>
   <title>CQRS/DDD/ES w pigułce</title>
   <content type="html">&lt;p&gt;O czym traktuje wpis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CQRS&lt;/strong&gt; - Command Query Responsibility Segregation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DDD&lt;/strong&gt; - Domain Driven Design&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ES&lt;/strong&gt; - Event Sourcing&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Koncepcja CQRS&lt;/h2&gt;

&lt;p&gt;Podział systemu na dwa obszary:
1. przetwarzanie transakcji
2. obsługa zapytań (widoki)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.infoq.com/resource/articles/cqrs_with_axon_framework/en/resources/cqrs.png&quot;&gt;Koncepcja CQRS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Architektura systemu, która wyłania się w wyniku zastosowania takiej separacji, jest zasadniczo różna względem typowych architektur bazujących na jednym modelu danych i jednej warstwie serwisowej obsługujących zarówno zapisy jak i odczyty. Dzięki separacji można niezależnie optymalizować obydwa obszary systemu.&lt;/p&gt;

&lt;p&gt;Event Sourcing i DDD to rozwiązania, które umożliwiają implementację koncepcji CQRS.&lt;/p&gt;

&lt;h2&gt;DDD&lt;/h2&gt;

&lt;p&gt;Metodyka DDD dostarcza wytyczne w jaki sposób modelować logikę biznesową. Podstawowe koncepje DDD:
 - &lt;strong&gt;Ubiquitous Language&lt;/strong&gt; - modele powstają (wyłaniają się) w wyniku dogłębnej analizy domeny i są opisywane językiem zrozumiałym dla developerów jak i ekspertów domenowych (których udział w procesie analizy domeny jest niezwykle pożądany)
 - &lt;strong&gt;Bounded Contexts&lt;/strong&gt; - zwykle w ramach jednego systemu występuje wiele domen, które należy opisywać odrębnymi modelami
 - &lt;strong&gt;Aggregates&lt;/strong&gt; - agregaty są głównymi elementami modelu, grupują powiązane ze sobą obiekty, których modyfikacja możliwa jest tylko za pośrednictwem obiektu głównego (&lt;strong&gt;Aggregate Root&lt;/strong&gt;) (AR), co zapewnia kontrolę nad spójnościa agregatu&lt;/p&gt;

&lt;h2&gt;Event Sourcing&lt;/h2&gt;

&lt;p&gt;Event Sourcing to koncepcja, w której stan obiektu reprezentowany jest jako suma zdarzeń (&lt;code&gt;Events&lt;/code&gt;) jakie zostały wygenerowane przez ten obiekt. Każda modyfikacja stanu obektu skutkuje wygenerowaniem przez obiekt zdarzenia reprezentującego tę modyfikację. Dla przykładu, obiekt &lt;code&gt;Invoice&lt;/code&gt; może być reprezentowany jako suma zdarzeń &lt;code&gt;InvoiceCreated&lt;/code&gt;, &lt;code&gt;InvoiceItemAdded&lt;/code&gt;, &lt;code&gt;InvoiceSent&lt;/code&gt;. To AR wie co zostało zmienione i jest to jawnie reprezentowane przez &lt;code&gt;Event&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Optymalizacja przetwarzania transakcji&lt;/h2&gt;

&lt;p&gt;System akceptuje wywołania operacji biznesowych w formie komend. Przetwarzanie komendy polega na wywołaniu pojedynczej metody w Aggregate Root. Metoda nie zwraca żadnego wyniku, a brak wyjątku oznacza pomyśle zakończenie operacji. Komenda nie musi być przetworzona natychmiast (może zostać zakolejkowana).&lt;/p&gt;

&lt;h3&gt;Aggregate Root wyznacza granice spójności&lt;/h3&gt;

&lt;p&gt;Aggregate Root gwarantuje integralność (spójność) danych które obejmuje. Możliwa jest dowolna optymalizacja wewnętrznej struktury agregatu dla celów przetwarzania transakcji bez konieczności uwzględniania warstwy prezentacji&lt;/p&gt;

&lt;h3&gt;Aggregate Root wyznacza granice transakcji&lt;/h3&gt;

&lt;p&gt;Transakcja nie może obejmować kilka AR, a więc AR nie może posiadać referencji do innego AR (może posiadać jedynie jego Id) Dzięki temu możliwe jest partycjonowanie AR.&lt;/p&gt;

&lt;h3&gt;Append-only storage&lt;/h3&gt;

&lt;p&gt;Zmiany stanu Aggregate Root sygnalizowane są zdarzeniami (&lt;code&gt;Event&lt;/code&gt;), które na koniec transakcji serializowane są do bazy (&lt;strong&gt;Event Store&lt;/strong&gt;). Rozwiązanie to zapewnia ogromną wydajność przetwarzania transakcji dzięki temu, że nie jest konieczne tworzenie locków na bazie danych oraz wykonywanie skomplikowanych zapytań sql. Event Store może wykorzystywać dowolny mechanizm utrwalania (baza sql, nosql, pliki) i jest łatwy w utrzymaniu (backup'y, replikacja). Oczywistą konsekwencją jest brak możliwości wykonywania zapytań sql, stąd Aggregate Root może być ładowany tylko na podstawie Id.&lt;/p&gt;

&lt;h3&gt;Asynchroniczne przetwarzanie zdarzeń&lt;/h3&gt;

&lt;p&gt;Zdarzenia są asynchronicznie propagowane do zarejestrowanych słuchaczy (&lt;code&gt;Event Handler&lt;/code&gt;), a zatem przetwarzanie ich nie wydłuża czasu trwania transakcji. W ten sposób aktualizowana jest druga strona systemu (widoki). Konsekwencją jest opóźnienie pomiędzy zatwierdzeniem transakcji a aktualizacją widoku. To samo opóźnienie może dotyczyć aktualizacji stanu innych ARs, które są wywoływane przez &lt;code&gt;Event Handler&lt;/code&gt;'a (wysłanie komendy) w wyniku zaistnienia określonego zdarzenia. Spójność pomiędzy ARs (rozumiana jako spójność transakcyjna) nie jest zatem zachowana. Gwarantowana jest natomiast ostateczna spójność (&lt;strong&gt;Eventual Consistency&lt;/strong&gt;) - każdy AR w końcu zostanie zaktualizowany.&lt;/p&gt;

&lt;h3&gt;Możliwość zatwierdzania równoległych zmian&lt;/h3&gt;

&lt;p&gt;Można odrzucać równoległe zmiany w aggregacie tylko dla określonych zdarzeń. Np. zmiana adresu użytkownika nie konfiktuje ze zmianą jego statusu. Mechanizm ten szczególnie jest przydatny jeśli klienci pracują w trybie off-line. W momencie synchronizacji z serwerem, prawdopodobieństwo wystąpienia zmian równoległych jest dużo większe.&lt;/p&gt;

&lt;h3&gt;Testowanie&lt;/h3&gt;

&lt;p&gt;Testowanie logiki biznesowej jest czytelne i efektywne. Szkielet metody testującej wygląda następująco:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// given
// create AR by replying list of events

// when
// dispatch a Command

// then
// expect given list of events or exception
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;W testach weryfikujemy zatem tylko to czy wygenerowane zdarzenia odpowiadają tym oczekiwanym.&lt;/p&gt;

&lt;h2&gt;Optymalizacja obsługi zapytań&lt;/h2&gt;

&lt;p&gt;Widoki mogą być w dowolny sposób budowane (baza sql, baza dokumentowa, nosql). W przypadku użycia bazy sql, widoki mogą być zdenormalizowane (dla zapewnienia maksymalnej wydajności zapytań). W trakcie rozwoju systemu możliwe jest łatwe tworzenie dowolnych nowych widoków / raportów. Wypełnienie nowych widoków danymi historycznymi jest możliwe dzięki odtworzeniu zdarzeń historycznych z Event Store.&lt;/p&gt;

&lt;h3&gt;Task based UI&lt;/h3&gt;

&lt;p&gt;Interfejs użytkownika odzwierciedla komendy akceptowalne przez system.&lt;/p&gt;

&lt;h2&gt;Kiedy stosować CQRS&lt;/h2&gt;

&lt;p&gt;Aby zdefiniować komendy i zdarzenia, wymagane jest dobre zrozumienie domeny i jej dekompozycja. Z uwagi na powyższe, CQRS nie koniecznie nadaje się do zastosowania dla całego systemu. Domeny dla których zastosowanie CQRS niesie największe korzyści to te, które z jednej strony stanowią o wartości biznesowej systemu, z drugiej strony wymagają optymalizacji pod kątem możliwości udostępniania (i modyfikacji) tych samych bądź powiązanych danych wielu użytkownikom systemu jednocześnie (collaborative domains).&lt;/p&gt;

&lt;h2&gt;Event Driven Architecture&lt;/h2&gt;

&lt;p&gt;Stosowanie CQRS z użyciem ES zachęca do budowania systemów sterowanych zdarzeniami. Poszczególne moduły systemu (obsługujące różne dziedziny biznesowe (Bounded Contexts)) mogą komunikować się ze sobą za pomocą zdarzeń (push integration). Każdy moduł korzysta tylko z danych lokalnych, które aktualizuje w oparciu o zdarzenia odbierane z innych modułów. Dzięki takiej architekturze, możliwe jest czasowe wyłączenie poszczególnych modułów bez konsekwencji dla całego systemu.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://pkaczor.blogspot.com/2012/02/cqrsdddes-w-piguce.html&quot;&gt;http://pkaczor.blogspot.com/2012/02/cqrsdddes-w-piguce.html&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;bio&quot;&gt;
  &lt;p&gt;Autor: &lt;a href=&quot;mailto:pawel.kaczor@consileon.pl&quot;&gt;Paweł Kaczor&lt;/a&gt;&lt;/p&gt;
  &lt;div class=&quot;foto&quot;&gt;
      &lt;img src=&quot;/images/pawel_kaczor.png&quot; alt=&quot;Paweł Kaczor&quot;/&gt;
  &lt;/div&gt;
  &lt;div class=&quot;desc&quot;&gt;
  &lt;p&gt;
    Programista, pasjonat, konsultant IT. Interesuje się nowoczesnymi framework-ami webowymi takimi jak: Seam, ZK.
    Obecnie w wolnym czasie poznaje język Scala.
  &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;



</content>
   <updated>2012-02-28T00:00:00-08:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="cqrs" label="cqrs" />
   <updated>2012-02-28T00:00:00-08:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="ddd" label="ddd" />
 </entry>
 
 <entry>
   <id>http://devblog.consileon.pl/2012/02/27/Spring-Batch</id>
   <link href="http://devblog.consileon.pl/2012/02/27/Spring-Batch"/>
   <title>Saga vs Batch Processing (Spring Batch introduction)</title>
   <content type="html">&lt;p&gt;When dealing with business process often some state transitions are not immediately executed as result of
human interaction but rather being scheduled for future execution. One example could be expiration of Payment Period (see &lt;a href=&quot;http://devblog.consileon.pl/2011/08/02/Axon-Framework-DDD-EDA-meet-together/&quot;&gt;previous blog&lt;/a&gt; for domain description).&lt;/p&gt;

&lt;p&gt;Modelling such process execution flow explicitly in the code is the right thing to do if we want to keep process logic maintainable and self describing.
That's why we should apply bpm tools or Saga pattern (if we prefer simple and light-weight solution) to do the job.&lt;/p&gt;

&lt;h2&gt;Scheduling activity within Saga&lt;/h2&gt;

&lt;p&gt;The following code shows how to schedule activity within Saga (&lt;a href=&quot;http://www.axonframework.org/&quot;&gt;Axon&lt;/a&gt; implementation):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentPeriodSaga&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractSaga&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@StartSaga&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@SagaEventHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;associationProperty&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;paymentPeriodId&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;paymentPeriodCreated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentPeriodCreatedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;associateWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;accountId&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAccountId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;getEventScheduler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;c1&quot;&gt;// trigger datetime&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValidityInterval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getEnd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; 
          &lt;span class=&quot;c1&quot;&gt;// the event to publish&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentPeriodExpiredEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAggregateId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@SagaEventHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;associationProperty&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;paymentPeriodId&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;paymentPeriodExpired&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentPeriodExpiredEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;RenewAccountCommand&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RenewAccountCommand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Builder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;accountId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getAssociatedId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;accountId&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;getCommandBus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;When (current) Payment Period is created, &lt;code&gt;RenewPaymentPeriodCommand&lt;/code&gt; is scheduled for execution on time when the period expires (after validation interval passes). This approach is clean, easy to implement and test but...requires (mind) shifting from procedural way of modelling business logic (see transaction script) towards event driven architecture (EDA). For those who are not yet ready to enter EDA and bpm, there is an old-time heavy-weight, bullet-proof way of executing scheduled tasks on certain time: batch processing.&lt;/p&gt;

&lt;h2&gt;Batch processing&lt;/h2&gt;

&lt;p&gt;Batch processing is suitable for optimizing execution of high-volume, repetitive tasks in such way that system is under heavy load only within relatively short time window (batch window). (See http://en.wikipedia.org/wiki/Batch_processing)&lt;/p&gt;

&lt;p&gt;Our goal will be to execute Payment Period renewal in batch mode. Instead of scheduling each Payment Period renewal explicitly in the business process (saga), batch job (&lt;code&gt;RenewPaymentPeriodsJob&lt;/code&gt;) scheduled to run repeatedly (every hour or day depending on requirements) will invoke &lt;code&gt;RenewPaymentPeriodCommand&lt;/code&gt; for all expired Payment Periods that it will find in database.
We will only change processing mode but will not touch business logic that will be still encapsulated inside of aggregate roots and event listeners. We will use the same commands dispatching mechanism that is used for online transactions processing avoiding creation of specialized services or using sql statements to implement batch jobs (as one could expect from batch jobs:) ).&lt;/p&gt;

&lt;p&gt;Running jobs in batch mode gives more control over system load. It can be controlled how often and when batch jobs should be run. In Saga, execution model is different, operations are executed automatically and there is no control mechanism at runtime. Which way is better..? As always, depends. But it is good to have both alternatives ready to apply when time comes...&lt;/p&gt;

&lt;p&gt;Lets see how to use &lt;strong&gt;Spring Batch&lt;/strong&gt;, modern batch framework for JVM, to apply batch processing using commands and queries as building blocks.&lt;/p&gt;

&lt;h2&gt;Spring Batch processing model&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;http://static.springsource.org/spring-batch/reference/html-single/images/spring-batch-reference-model.png&quot; alt=&quot;Batch stereotypes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The diagram above highlights the key concepts that make up the domain language of batch. A Job has one step or combines multiple steps that belong logically together in a flow. Each step has exactly one &lt;code&gt;ItemReader&lt;/code&gt;, &lt;code&gt;ItemProcessor&lt;/code&gt;, and &lt;code&gt;ItemWriter&lt;/code&gt;. A Job needs to be launched (&lt;code&gt;JobLauncher&lt;/code&gt;), and meta data about the currently running process needs to be stored (&lt;code&gt;JobRepository&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Spring Batch uses a &quot;Chunk Oriented&quot; processing style within its most common implementation. Chunk oriented processing refers to reading the data one at a time, and creating 'chunks' that will be written out, within a transaction boundary. Committing a transaction, at each commit interval, commits a 'chunk'.&lt;/p&gt;

&lt;p&gt;The data item could be line in a file or record in a database table but Spring Batch integrates modern to-object mapping frameworks so we don't have to dirty our hands by manipulating low-level data.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.springsource.org/spring-batch/reference/html-single/images/chunk-oriented-processing.png&quot; alt=&quot;Chunk oriented processing&quot; /&gt;&lt;/p&gt;

&lt;h2&gt;Spring Batch application&lt;/h2&gt;

&lt;p&gt;Our goal is to use Aggregate Roots as processing items. To build batch step we need to implement an &lt;code&gt;ItemReader&lt;/code&gt; that will fetch ARs (entities) from database by executing provided query and an &lt;code&gt;ItemProcessor&lt;/code&gt; that will build a command based on AR data and dispatch command to the system. Since batch processing is performed transactionally (chunks are automatically committed by Spring Batch with use of provided transaction manager) commands need to to be dispatched synchronously. This logic of batch step may be shared across different jobs as long as the concept of &lt;code&gt;ItemReader&lt;/code&gt; responsible of fetching ARs using provided query and &lt;code&gt;ItemProcessor&lt;/code&gt; responsible for dispatching command is preserved. What will distinguish different steps from each other is query specification and command to be executed.&lt;/p&gt;

&lt;p&gt;So lets define interface that will describe these responsibilities of batch step:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BatchStepSpecification&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getCommand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queryResultItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;QuerySpecification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getQuerySpecification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;BatchStepSpecification&lt;/code&gt; object should be able to provide query specification (executable by &lt;code&gt;ItemReader&lt;/code&gt;, more on query specifications in a moment) and build &lt;code&gt;Command&lt;/code&gt; (for each AR to be processed) executable by &lt;code&gt;ItemProcessor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we need to implement &lt;code&gt;ItemReader&lt;/code&gt; and &lt;code&gt;ItemWriter&lt;/code&gt; that will use step specification to do their job.&lt;/p&gt;

&lt;h2&gt;ItemReader&lt;/h2&gt;

&lt;p&gt;To avoid keeping all entities to be processed in memory (this could be a large set) Spring Batch offers two solutions: Cursor and Paging database &lt;code&gt;ItemReader&lt;/code&gt;s. Let's go with the letter one. For loading entities from database in a paging fashion Spring Batch provides several implementations of &lt;code&gt;AbstractPagingItemReader&lt;/code&gt; one of them being &lt;code&gt;JpaPagingItemReader&lt;/code&gt; for fetching JPA entities. &lt;code&gt;JPAPagingItemReader&lt;/code&gt; allows you to define query by providing JPQL statement. But we want our queries be more maintainable and composable. Therefore I recommend to represent JPA queries as &lt;a href=&quot;http://domaindrivendesign.org/node/87&quot;&gt;Specifications&lt;/a&gt;. This is possible with use of &lt;a href=&quot;http://www.springsource.org/spring-data/jpa&quot;&gt;Spring Data JPA&lt;/a&gt; module. Specification can be translated into JPA Query in the following way:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QuerySpecification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CriteriaBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getEntityManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getCriteriaBuilder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CriteriaQuery&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;criteriaQuery&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getResultClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Root&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;criteriaQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getResultClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;criteriaQuery&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toPredicate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;criteriaQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getEntityManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;criteriaQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;More about specifications you can read here: &lt;a href=&quot;http://blog.springsource.org/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/&quot;&gt;Advanced Spring Data JPA – Specifications And QueryDSL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Implementing &lt;code&gt;SpecificationPagingReader&lt;/code&gt; is straightforward. Main thing to do is to implement &lt;code&gt;doReadPage()&lt;/code&gt; method:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SpecificationPagingReader&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractPagingItemReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//[...]&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Specification&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;querySpecification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Transactional&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;doReadPage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//[...]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;querySpecification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setFirstResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getPage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getPageSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setMaxResults&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getPageSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getResultList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Finally, we can define our &lt;code&gt;ItemReader&lt;/code&gt; in Spring context:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;reader&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;scope=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;a.b.c.SpecificationPagingReader&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;p:entityManagerFactory-ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;entityManagerFactory&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;p:querySpecification=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;#{stepSpecification.querySpecification}&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;p:pageSize=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;2000&amp;quot;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The instance of &lt;code&gt;SpecificationPagingReader&lt;/code&gt; will be created automatically by Spring whenever batch step is executed. This is the magic of &lt;strong&gt;step&lt;/strong&gt; scope provided by Spring Batch. It allows late (dynamic) binding of properties. In our case &lt;code&gt;stepSpecification&lt;/code&gt; is unknown until particular step is executed (more details ahead).&lt;/p&gt;

&lt;h2&gt;ItemProcessor&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ItemProcessor&lt;/code&gt; interface defines just one method. The implementation in our case is simple:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EntityProcessor&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ItemProcessor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommandBus&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;commandBus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BatchStepSpecification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stepSpecification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Entity&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Entity&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Command&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stepSpecification&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getCommand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;commandBus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And Spring bean definition:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;processor&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;scope=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;step&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;a.b.c.EntityProcessor&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;p:commandBus-ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;commandBus&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;p:stepSpecification=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;#{stepSpecification}&amp;quot;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;ItemWriter&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ItemWriter&lt;/code&gt; must be provided as well but it can be empty implementation assuming all changes to ARs are flushed to database as a result of command processing within the service.&lt;/p&gt;

&lt;p&gt;Finally we are able to build batch step that will be reused by concrete batch jobs.&lt;/p&gt;

&lt;h2&gt;Batch Step&lt;/h2&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;step&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;abstractBatchStep&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;abstract=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;tasklet&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;chunk&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;reader=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;reader&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;processor=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;processor&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;writer=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;writer&amp;quot;&lt;/span&gt; 
        &lt;span class=&quot;na&quot;&gt;commit-interval=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;10&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;skip-limit=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;5&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;skippable-exception-classes&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;include&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;java.lang.Exception&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/skippable-exception-classes&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/chunk&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tasklet&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/step&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Here, we compose three beans (reader, processor, writer) and additionally provide following parameters:
 - skip-limit - the maximum number of items that will be allowed to be skipped (in case the processing of item ends with exception). Once the skip limit is reached, the next exception found will cause the step to fail.
 - commit-interval - how many items will be processed in one transaction (chunk size), value &gt; 1 might increase performance, but also could result in rollback of successfully processed ARs (if exception skip limit is reached)
 - skippable-exception-classes - exceptions that will result in skipping the processed entity instead of step failure&lt;/p&gt;

&lt;h2&gt;Job registry&lt;/h2&gt;

&lt;p&gt;Now we can define our jobs. The easiest way is to use &lt;code&gt;AutomaticJobRegistrar&lt;/code&gt; class. Registration in this case is performed automatically on application start-up, based on defined path under which spring context files containing jobs definitions are located. By putting each job bean into separate spring context file we are able to provide its &lt;code&gt;stepSpecification&lt;/code&gt; bean that will be created when job's step is executed. If all those files were imported into the same context, the &lt;code&gt;stepSpecification&lt;/code&gt; definitions would clash and override one another, but with the automatic registrar this is avoided.&lt;/p&gt;

&lt;p&gt;Please see example definition of batch job:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;job&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;renewPaymentPeriodsBatchJob&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;step&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;renewPaymentPeriodsJob&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;parent=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;abstractBatchStep&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/job&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;stepSpecification&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;a.b.c.RenewPaymentPeriodsBatchStepSpecification&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As shown above, creating new batch job is just a matter of creating new spring context file containing job id (globally unique), step id and step specification bean. Nothing more is required. The solution is powerful and simple but has one limitation. We can not create jobs with several steps, each one configured with different &lt;code&gt;BatchScopeSpecification&lt;/code&gt;. For example:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;job&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;accountsMaintenanceJob&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;parent=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;abstractBatchJob&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;step&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;step1&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;job&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;endAccountsBatchJob&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;next&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;on=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;to=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;step2&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/step&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;step&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;step2&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;job&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;renewPaymentPeriodsBatchJob&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/step&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/job&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I found a solution for this problem, in case you are interested, let me know by dropping in a comment.&lt;/p&gt;

&lt;h2&gt;Job execution&lt;/h2&gt;

&lt;p&gt;To execute a &lt;code&gt;Job&lt;/code&gt;, we need to create &lt;code&gt;JobParameters&lt;/code&gt; object and use &lt;code&gt;JobLauncher&lt;/code&gt; to run &lt;code&gt;Job&lt;/code&gt; with created parameters. Please refer to &lt;a href=&quot;http://static.springsource.org/spring-batch/reference/html/domain.html#domainJob&quot;&gt;description of artifacts&lt;/a&gt; related to batch job execution. It is important to understand differences between &lt;code&gt;Job&lt;/code&gt;, &lt;code&gt;JobInstance&lt;/code&gt; and &lt;code&gt;JobExecution&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Things to remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JobInstance&lt;/strong&gt; = the concept of a logical job run (&lt;code&gt;Job&lt;/code&gt; + &lt;code&gt;JobParameters&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JobExecution&lt;/strong&gt; = a single attempt to run &lt;code&gt;JobInstance&lt;/code&gt;. &lt;code&gt;JobInstance&lt;/code&gt; corresponding to a given execution will not be considered complete unless the execution completes successfully. There can be more than one failed &lt;code&gt;JobExecutions&lt;/code&gt; but only one successful execution of given &lt;code&gt;JobInstance&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Batch jobs scheduling&lt;/h2&gt;

&lt;p&gt;Spring Batch is not a scheduling framework. It is entirely up to the scheduler to determine when a Job should be run. There is no requirement that one &lt;code&gt;JobInstance&lt;/code&gt; be kicked off after another, unless there is potential for the two job instances to attempt to access the same data, causing issues with locking at the database level. But attempting to run the same &lt;code&gt;JobInstance&lt;/code&gt; while another is already running will result in a &lt;code&gt;JobExecutionAlreadyRunningException&lt;/code&gt; being thrown.&lt;/p&gt;

&lt;h2&gt;Other Spring Batch Goodies&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Restartability&lt;/strong&gt; - the framework periodically persists the &lt;code&gt;ExecutionContext&lt;/code&gt; at commit points. This allows the &lt;code&gt;ItemReader&lt;/code&gt; to store its state in case a fatal error occurs during the run, or even if the power goes out. &lt;code&gt;JpaPagingItemReader&lt;/code&gt; supports restart by storing item count, therefore requires item ordering to be preserved between runs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Non Sequential Step Execution&lt;/strong&gt; - conditional flow of steps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partitioning&lt;/strong&gt; - &lt;code&gt;JobOperator&lt;/code&gt; interface for common monitoring tasks such as stopping, restarting, or summarizing a Job, as is commonly done by batch operators.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;a href=&quot;http://pkaczor.blogspot.com/2012/02/saga-vs-batch-processing-spring-batch.html&quot;&gt;http://pkaczor.blogspot.com/2012/02/saga-vs-batch-processing-spring-batch.html&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;bio&quot;&gt;
  &lt;p&gt;Autor: &lt;a href=&quot;mailto:pawel.kaczor@consileon.pl&quot;&gt;Paweł Kaczor&lt;/a&gt;&lt;/p&gt;
  &lt;div class=&quot;foto&quot;&gt;
      &lt;img src=&quot;/images/pawel_kaczor.png&quot; alt=&quot;Paweł Kaczor&quot;/&gt;
  &lt;/div&gt;
  &lt;div class=&quot;desc&quot;&gt;
  &lt;p&gt;
    Programista, pasjonat, konsultant IT. Interesuje się nowoczesnymi framework-ami webowymi takimi jak: Seam, ZK.
    Obecnie w wolnym czasie poznaje język Scala.
  &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;



</content>
   <updated>2012-02-27T00:00:00-08:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="spring" label="spring" />
   <updated>2012-02-27T00:00:00-08:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="ddd" label="ddd" />
 </entry>
 
 <entry>
   <id>http://devblog.consileon.pl/2011/08/02/Axon-Framework-DDD-EDA-meet-together</id>
   <link href="http://devblog.consileon.pl/2011/08/02/Axon-Framework-DDD-EDA-meet-together"/>
   <title>Axon Framework - DDD and EDA meet together</title>
   <content type="html">&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://martinfowler.com/bliki/CQRS.html&quot;&gt;CQRS&lt;/a&gt;&lt;/strong&gt; (Command Query Responsibility Segregation) is a new approach towards building scalable and distributed systems that is based on simple pattern know as Command Query Separation (&lt;a href=&quot;http://martinfowler.com/bliki/CommandQuerySeparation.html&quot;&gt;CQS&lt;/a&gt;). In short, you should design your system in a way that it either processes a command or serves response to a query. CQRS in its core is quite simple. Just split your service interface into two parts: Query Service and Command Service and you are done. But what is important is that by making this simple separation, you make your system open to many opportunities for architecture that may otherwise not exist. In this article I will not show you what is possible when fully applying CQRS (check this &lt;a href=&quot;http://abdullin.com/cqrs/&quot;&gt;CQRS Starting Page&lt;/a&gt; for details). Instead I will present you how some design patterns can effectively be applied to standard ORM-based application when you decide to split your interface into Commands and Queries and use &lt;a href=&quot;http://code.google.com/p/axonframework/&quot;&gt;Axon&lt;/a&gt; - CQRS framework for Java. But first we must talk about &lt;a href=&quot;http://www.domaindrivendesign.org/resources/what_is_ddd&quot;&gt;DDD&lt;/a&gt; as it plays crucial role in building scalable and maintainable systems and fits very well to CQRS-based architecture.&lt;/p&gt;

&lt;h2&gt;DDD shortly&lt;/h2&gt;

&lt;p&gt;Generally DDD is about domain model that, when expressed in Ubiquitous Language, can be shared by developers and domain experts allowing them to communicate effectively. From technical point of view DDD is about modeling your core domain using aggregate roots (AR), entities, value objects and some other artifacts like domain services and repositories. What is important, when applying DDD to medium or large domains (generally complex domains are good candidates for DDD), you should not be expecting one model to arise representing all areas covered by the system. Each area will likely be modeled separately (inside its own &lt;a href=&quot;http://www.domaindrivendesign.org/node/91&quot;&gt;Bounded Context&lt;/a&gt;. This aspect is very important, especially if your application starts to grow covering more and more business activities. If you stick with one model aka EDM (Enterprise Domain Model) (that typically reassembles model of relations inside your sql database), you will end up with monolithic system not capable of adjusting to business requirements.
Therefore building application DDD-way should be seen as completely different approach comparing to standard approach (&lt;code&gt;one model to rule them all&lt;/code&gt; approach). You will need additional means to express communication between your different models (Bounded Contexts).
Thats where EDA - event based communication comes in.&lt;/p&gt;

&lt;p&gt;Let's see how to apply DDD and EDA to standard ORM-based application with use of Axon.&lt;/p&gt;

&lt;h2&gt;Axon introduction&lt;/h2&gt;

&lt;p&gt;Axon framework provides building blocks for CQRS applications. Some blocks, i.e. &lt;a href=&quot;http://martinfowler.com/eaaDev/EventSourcing.html&quot;&gt;event sourcing&lt;/a&gt;, asynchronous processing of commands (no response from Command Handler)) are optional and can be omitted. If you are not ready for (or just don't need) full CQRS, you can still benefit from other goodies such as synchronous command handling, events, sagas and others. Additionally Axon integrates deeply with Spring making configuration a trivial task (just use built in namespaces (xml part) and annotations). Unique feature of Axon is that it allows to integrate existing JPA-based application. To make your entities become Aggregate Roots extend &lt;code&gt;AbstractAggregateRoot&lt;/code&gt; class, to load your ARs from database use &lt;code&gt;GenericJpaRepository&lt;/code&gt; (or even better &lt;code&gt;HybridJpaRepository&lt;/code&gt; if you want to use EventStore). For detailed instructions see User's Guide. Lets start working with code.&lt;/p&gt;

&lt;h2&gt;Get rid of Trasaction Script&lt;/h2&gt;

&lt;p&gt;When applying DDD, we tend to build rich entities that encapsulate behavior.
In contrast to standard approach with &lt;a href=&quot;http://martinfowler.com/bliki/AnemicDomainModel.html&quot;&gt;anemic domain model&lt;/a&gt; and procedural code inside Application Services (see: &lt;a href=&quot;http://martinfowler.com/eaaCatalog/transactionScript.html&quot;&gt;Transaction Script&lt;/a&gt;), most of business logic should be handled inside Aggregate Roots. When a command comes in, it is dispatched to Command Handler whose only job should be to get AR from Repository and invoke single method on it.&lt;/p&gt;

&lt;p&gt;Lets imagine a system that manages user accounts (Account AR). One of Account's properties is &lt;code&gt;state&lt;/code&gt;. To activate an account, &lt;code&gt;AccountActivateCommand&lt;/code&gt; must be sent by the client. In order to handle this command, we must register and implement specialized Command Handler:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AccountCommandHandler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@CommandHandler&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccountActivateCommand&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accountRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;findById&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAggregateId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;That's it. Nothing special. Clean code so far.&lt;/p&gt;

&lt;h2&gt;Implementing business logic - uniformed approach&lt;/h2&gt;

&lt;p&gt;First, we need to extend our model in order to make it more interesting :) Our system must support handling user's payments related to Payment Period. The following requirements are added:&lt;/p&gt;

&lt;p&gt; 1) when Account is activated, Payment Period must be created that will be used to keep track of payments related to the Account&lt;/p&gt;

&lt;p&gt; 2) after Payment Period expires, subsequent Payment Period must be created, but expired Payment Period should still be accessible&lt;/p&gt;

&lt;p&gt;We could model our Account like this:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractAggregateRoot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{...}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@OneToMany&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paymentPeriods&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentPaymentPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{...}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentPeriod&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@ManyToOne&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;{...}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;In this model, Account is responsible for managing Payment Periods. It sounds good, we can implement account activation inside Account class:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt; 
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;validateTransitionToStatus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ACTIVE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccountActivatedEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// handle AccountActivatedEvent&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ACTIVE&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;currentPaymentPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;paymentPeriods&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(...))&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Please notice three blocks of code in this method and theirs order. This separation is dictated by CQRS-based design. You should have all these blocks in every method that is invoked by Command Handler. The blocks are:&lt;/p&gt;

&lt;p&gt; 1) validation - checking if operation is allowed and will not break consistency of the Aggregate&lt;/p&gt;

&lt;p&gt; 2) raising event - creating a Domain Event containing information about Aggregate's change&lt;/p&gt;

&lt;p&gt; 3) handling event - updating state of the Aggregate (no exceptions, no logic here!)&lt;/p&gt;

&lt;p&gt;Every change of Aggregate's state must be signaled with an Event (Domain Event). All Domain Events will be stored in Event Store (if configured) (just serialized as Blobs or Clobs to single table).&lt;/p&gt;

&lt;p&gt;If our Aggregate Root is event-sourced (is reconstructed from Event Store instead or being created by EntityManager), we should move event handling code to separate method:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@EventHandler&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;accountActivated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccountActivatedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ACTIVE&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;currentPaymentPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We will not discuss Event Sourcing in this article, as we don't want to get rid of our powerful ORM, or do we? (actually in CQRS world ORM is
persona non grata - you were warned ;)&lt;/p&gt;

&lt;h2&gt;Keep your ARs lously coupled&lt;/h2&gt;

&lt;p&gt;Going back to our model, after thinking a little bit longer, we realize that it doesn't fit well our needs... One of the requirements is to process commands related directly to Payment Periods. These commands should be dispatched directly to Payment Period entity rather than go through Account AR. Payment Period should be an AR on its own. When we think more about this (and talk with our Domain Expert (if we have one;)), the separation of Account and Payment Period will become even more obvious. We could easily imagine two services Account Service (responsible for accounts management) and Payment Service (responsible for payment registration) working independently.&lt;/p&gt;

&lt;p&gt;So we can simplify Account AR and upgrade Payment Period to AR:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractAggregateRoot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{...}&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentPaymentPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;validateTransitionToStatus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ACTIVE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccountActivatedEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;// handle AccountActivatedEvent&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ACTIVE&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;currentPaymentPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{...}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentPeriod&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractAggregateRoot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{...}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ManyToOne&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentPeriodCreated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAggregateId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{...}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now account activation is implemented partially (Payment Period is not being created). We could implement Application Service that would first call Account#activate and than create new Payment Period, but implementing business logic within Application Service layer leads to Transaction Script that we are trying to avoid (we want to avoid both ARs (Account and Payment Period) forcibly be invoked in the same transaction - it hurts system scalability).
Lets think of our ARs on higher level. They belong to different contexts/services (virtual Account Service and Payment Service). The way to communicate between different contexts (services) is to use Domain Events!&lt;/p&gt;

&lt;h2&gt;Events to the rescue&lt;/h2&gt;

&lt;p&gt;We already have implemented raising of the &lt;code&gt;AccountActivatedEvent&lt;/code&gt; in activate() method of Account AR. Now we must create Event Listener that will listen for this event and will send &lt;code&gt;PaymentPeriodCreate&lt;/code&gt; command.
With Axon it is as simple as creating new class:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt; 
&lt;span class=&quot;nd&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentService&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Autowired&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CommandBus&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;commandBus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@EventHandler&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createPaymentPeriodOnAccountActivation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccountActivatedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;commandBus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentPeriodCreateCommand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAccountId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Of course, we need to implement a command handler that will handle &lt;code&gt;PaymentPeriodCreateCommand&lt;/code&gt; by creating Payment Period AR and adding it to Repository.
Thats all. Now we have independent ARs that communicate with events.
This design leads us in direction of autonomous components (services) communicating asynchronously in publish-subscribe model (aka: push integration model), possibly via some kind of EventBus or Broker (see: &lt;a href=&quot;http://www.infoq.com/presentations/SOA-Business-Autonomous-Components&quot;&gt;Avoid a Failed SOA: Business &amp;amp; Autonomous Components to the Rescue&lt;/a&gt; by &lt;a href=&quot;http://www.udidahan.com/&quot;&gt;Udi Dahan&lt;/a&gt;)
But we will not go so far.&lt;/p&gt;

&lt;p&gt;There are many other benefits from applying EDA. One of them is ability to keep detailed history of Events:&lt;/p&gt;

&lt;h2&gt;History of Events (Audit)&lt;/h2&gt;

&lt;p&gt;By storing Events in database (Event Store) we keep log of changes. We can easily create additional table containing following data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aggregate root class&lt;/li&gt;
&lt;li&gt;aggregate root id&lt;/li&gt;
&lt;li&gt;event class&lt;/li&gt;
&lt;li&gt;user&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The table like this can serve basic reporting purposes. If we want to create more sophisticated reports in the future, we can reply events stored in Event Store and populate any report table we need. &lt;strong&gt;All&lt;/strong&gt; history of changes is kept in Event Store.&lt;/p&gt;

&lt;p&gt;Now lets see how we can model long running process with SAGA! In case you forgot the requirements, short reminder: new Payment Period must be created after the current one expires.&lt;/p&gt;

&lt;h2&gt;Enter SAGA&lt;/h2&gt;

&lt;p&gt;Saga is a stateful component (its state is persisted across invocations) that is capable of receiving events (including timeout events) (similar to Event Listener). Saga represents business process instance, in other words business process associated with particular AR(s).&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt; 
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentPeriodSaga&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractSaga&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@StartSaga&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@SagaEventHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;associationProperty&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;paymentPeriodId&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;paymentPeriodCreated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentPeriodCreatedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;associateWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;accountId&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAccountId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;getEventScheduler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;c1&quot;&gt;// trigger datetime&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValidityInterval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getEnd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; 
          &lt;span class=&quot;c1&quot;&gt;// the event to publish&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PaymentPeriodExpiredEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAggregateId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@SagaEventHandler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;associationProperty&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;paymentPeriodId&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;paymentPeriodExpired&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentPeriodExpiredEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;RenewAccountCommand&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RenewAccountCommand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Builder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;accountId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getAssociatedId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;accountId&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        
        &lt;span class=&quot;n&quot;&gt;getCommandBus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;First method will be invoked on &lt;code&gt;PaymentPeriodCreatedEvent&lt;/code&gt; and will result in creation of new Saga associated with Payment Period being created and related Account. Inside method body we schedule the &lt;code&gt;PaymentPeriodExpiredEvent&lt;/code&gt; that will be triggered when validity interval of Payment Period ends (Axon provides Quartz-based implementation of Event Scheduler) .
The second method is called when payment expiration happens (when &lt;code&gt;PaymentPeriodExpiredEvent&lt;/code&gt; is triggered by the Event Scheduler).
The only thing this method does is sending a command that will be processed by our Account Service (currentPaymentPeriod must be increased) and eventually by Payment Service (new Payment Period will be created).&lt;/p&gt;

&lt;p&gt;Lets see how easy we can test our Saga with use of Test Fixture provided by Axon:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt; 
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PaymentPeriodSagaTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AggregateIdentifier&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPaymentPeriodId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AggregateIdentifier&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accountId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Interval&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validityInterval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Interval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;plusDays&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;getFixture&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PaymentPeriodSaga&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// given&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;givenAggregate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newPaymentPeriodId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;published&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PaymentPeriodCreatedEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validityInterval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// when&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;whenTimeAdvancesTo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;validityInterval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getEnd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// then&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;expectDispatchedCommandsEqualTo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;RenewPaymentPeriodCommand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accountId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Finally, I want to discuss one more topic related to DDD.&lt;/p&gt;

&lt;h2&gt;Don't pollute your core domain model&lt;/h2&gt;

&lt;p&gt;What is common mistake DDD beginners make is that they try to apply DDD totally (put all application logic into ARs boundaries). Let's take an example and add new requirement to our application:
All entities (including Account) are separated by Sales Areas. Any operation on Account (creation, activation, etc.) can be performed only if the owning Sales Area is in status ACTIVE.&lt;/p&gt;

&lt;p&gt;First, we will modify our model by adding the following JPA mapping to the Account AR:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt; 
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AbstractAggregateRoot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{...}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ManyToOne&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SalesArea&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salesArea&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentPaymentPeriod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{...}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now lets think of the requirement. Where should we put the checking if the Sales Area is active? The &lt;code&gt;activate()&lt;/code&gt; method of Account AR seems to be the perfect place. But if we think more, we realize that Sales Area does not belong to our core domain! Checking status of the Sales Area inside Account AR will pollute the code (checking must be done before any modification of Account's state). So our new model is broken! There should be no Account -&gt; Sales Area mapping. But we can not remove it, because we reuse the same model for serving queries (we don't follow CQRS in this aspect) and we need to be able to filter Accounts by Sales Area easily.
Ok, so the better place to put the checking would be a Command Handler (&lt;code&gt;AccountCommandHandler&lt;/code&gt;). But it may be necessary to reuse this logic across different commands. What we need is some kind of interceptor that will prevent particular commands (account related or other) reaching Command Handlers. Not surprisingly Axon provides &lt;code&gt;CommandHandlerInterceptor&lt;/code&gt; interface that allows for customized command handler invocation chains. No example this time, as it is quite easy to imagine:)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://pkaczor.blogspot.com/2011/08/axon-framework-ddd-and-eda-meet.html&quot;&gt;http://pkaczor.blogspot.com/2011/08/axon-framework-ddd-and-eda-meet.html&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;bio&quot;&gt;
  &lt;p&gt;Autor: &lt;a href=&quot;mailto:pawel.kaczor@consileon.pl&quot;&gt;Paweł Kaczor&lt;/a&gt;&lt;/p&gt;
  &lt;div class=&quot;foto&quot;&gt;
      &lt;img src=&quot;/images/pawel_kaczor.png&quot; alt=&quot;Paweł Kaczor&quot;/&gt;
  &lt;/div&gt;
  &lt;div class=&quot;desc&quot;&gt;
  &lt;p&gt;
    Programista, pasjonat, konsultant IT. Interesuje się nowoczesnymi framework-ami webowymi takimi jak: Seam, ZK.
    Obecnie w wolnym czasie poznaje język Scala.
  &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;



</content>
   <updated>2011-08-02T00:00:00-07:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="cqrs" label="cqrs" />
 </entry>
 
 <entry>
   <id>http://devblog.consileon.pl/2011/02/25/bullshit-bingo-rest-i-restful</id>
   <link href="http://devblog.consileon.pl/2011/02/25/bullshit-bingo-rest-i-restful"/>
   <title>Bullshit Bingo - REST i RESTful</title>
   <content type="html">&lt;p&gt;Ostatnio nie mam trochę czasu na techniczne wpisy, także tym razem wpis będzie bardziej filozoficzny. Wczoraj zostałem zapytany o moją opinię
na temat konwencji &lt;a href=&quot;http://en.wikipedia.org/wiki/Representational_State_Transfer&quot;&gt;REST&lt;/a&gt; co zainspirowało mnie do stworzenia tego wpisu,
ku przestrodze przed bezmyślnym wciskaniem tej architektury do każdej aplikacji.&lt;/p&gt;

&lt;p&gt;REST w moim odczuciu urosło ostatnimi czasy do jednego
z największych buzzwordów w świecie IT. Mało kto tak naprawdę rozumie tę architekturę, ale każda aplikacja, każdy serwis musi być
REST-owy. Tak jakby od tego miało zależeć być albo nie być projektu. Być może w rozmowach biznesowych posiadanie REST-owej architektury
jest na tyle przekonywującym argumentem, że łatwiej pozyskać sponsorów. Nie wiem. Ja jednak proponuję każdemu krytycznym okiem
przyjrzeć się tej architekturze i samemu ocenić, czy w mojej aktualnej aplikacji jest ona w ogóle potrzebna? Czy nie jest tak, że
generuje ona więcej problemów niż pożytku? Oto kilka problemów jakie można napotkać wdrażając architekturę REST-ową.&lt;/p&gt;

&lt;h2&gt;Id-ki, Id-ki widzę...&lt;/h2&gt;

&lt;p&gt;To co od razu rzuca się w oczy w architekturze REST-owej to wszędobylskie &quot;Id-ki&quot; w adresach URL. Pół biedy, jeżeli są to identyfikatory
danych publicznych (np. ofert w sklepie), gorzej jak zaczynają się tam pojawiać identyfikatory zasobów, do których dostęp chcemy ograniczyć.
A już szczytem masochizmu jest wrzucanie identyfikatorów profili użytkowników! Przykładowo edycja profilu użytkownika w naszej aplikacji
może być dostępna pod takim URL-em:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/users/39/edit
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;No super! Piękny RESTful-owy URL. Z tym, że w kontrolerze dla takiego adresu i tak musimy zrobić walidację, że identyfikator użytkownika
podanego w adresie URL jest taki sam jak identyfikator zalogowanego użytkownika! Inaczej mogli by oni edytować profile innych użytkowników.
Także w tym przypadku użytkownik o identyfikatorze 39 i tak może wejść tylko na stronę podając parametr 39 w adresie URL, inaczej zostanie
przekierowany na jakąś stronę informującą go, że nie ma dostępu do żądanego zasobu. Jaki zatem jest sens posiadania parametru w adresie
URL, skoro i tak nie możemy go modyfikować? Nic nie zyskujemy, a możemy narazić się na kompromitację aplikacji gdy przez roztargnienie
zapomnimy o walidacji albo ją przez przypadek wyłączymy. Czy nie lepiej byłoby mieć adres:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/users/edit
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Albo jeszcze lepiej:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/edit_profile
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I nie musieć martwić się o identyfikator podany w URL-u?&lt;/p&gt;

&lt;p&gt;Sprawy tu mogą się jeszcze bardziej skomplikować. Wyobraźmy sobie, że chcemy użytkownikowi udostępnić stronę edycji zamówienia, które
on złożył. Taka storna mogłaby być dostępna pod adresem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/users/39/orders/10
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;W ten sposób sami sobie utrudniamy życie, bo nie dość, że musimy sprawdzać identyfikator użytkownika, to jeszcze musimy sprawdzić, że
zamówienie o identyfikatorze 10 należy do użytkownika o identyfikatorze 39. W tej sytuacji o wiele lepsze jest wywalenie części dotyczącej
użytkownika (która i tak jest stała):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/orders/10
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Teraz sprawdzamy jedynie czy te zamówienie należy do zalogowanego użytkownika, którego identyfikator przechowujemy gdzieś w sesji
(byle nie jawnie w pliku cookie ;)).&lt;/p&gt;

&lt;p&gt;Także piersza zasada poprawnego implementowania REST-u mówi: &lt;strong&gt;wywal z URL-i stałe parametry, a w szczególności te, które dotyczą
użytkowników&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No i warto zastanowić się nad jeszcze jedną kwestią. Czy te identyfikatory wnoszą cokolwiek użytecznego dla naszej aplikacji? Czy naprawdę
uważamy, że użytkownicy będą przeglądać oferty poprzez modyfikowanie parametru ID w adresie URL? Moim zdaniem bardzo wątpliwe.&lt;/p&gt;

&lt;h2&gt;Ładne i brzydkie adresy URL&lt;/h2&gt;

&lt;p&gt;To co jest charakterystyczne w REST-owej architekturze to jej adresy URL tzw. &lt;em&gt;pretty URL&lt;/em&gt;. No są one może i przyjazne, oraz nie kłują w oczy
wszędobylskimi znakami ? oraz &amp;amp;. Problem pojawia się wtedy, kiedy nasza witryna dostaje się pod strzechy eskertów od SEO. Pierwsze
co każą Ci zmienić w takiej aplikacji to właśnie adresy URL.&lt;/p&gt;

&lt;p&gt;Stara zasada SEO mówi, że w adresie URL &lt;strong&gt;musi&lt;/strong&gt; znaleźć się to do czego ta strona prowadzi. Przykładowo załóżmy, że poniższy adres
prowadzi do strony oferty zakupu Mac Booka Pro (nie nie jestem funboyem Apple'a, nie mam ani Mac Booka, ani iPhone'a ani niczego innego
z nadgryzionym jabłkiem w logo ;)):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/offers/131
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ekspert od SEO powie Ci &quot;Panie, na taki URL to ty się nigdy nie wypromujesz, trzeba go będzie przepisać&quot;. No i z twojego pięknego URL-a
robi się:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/oferty/131/Mac-Book-Pro
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Także suma sumarum na końcu i tak będziesz musiał ręcznie te piękne URL-e przepisywać (albo zaimplementować sprytny sposób ich przepisywania
coby nie robić tego ręcznie ;)) i nie będą one już takie piękne.&lt;/p&gt;

&lt;h2&gt;Pokaż mi swój identyfikator, a powiem ci jaką masz sprzedaż&lt;/h2&gt;

&lt;p&gt;Jest jeszcze jedna ciekawa osobliwość posiadania identyfikatorów w adresach URL. Otóż okazuje się, że mogą one udostępniać na zewnątrz
informacje, którymi niekoniecznie chcielibyśmy się dzielić. Jeżeli przykładowo nasza strona pozwala podglądać publiczne profile użytkowników
np. pod adresem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/profiles/39
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Inkrementując nasz identyfikator do momentu, aż otrzymamy 404 dowiemy się iluż zarejestrowanych użytkowników posiada dany portal (i autentycznie
jest kilka takich portali gdzie to można zrobić, ale nie wymienię ich nazwy ;)).&lt;/p&gt;

&lt;p&gt;W ten sposób możemy wyciągać z witryn informacje nie tylko o ilości zarejestrowanych użytkowników, ale i posiadanych ofert itd. Także trzeba być
tego świadomym, że nie tylko to co jest na stronie jest informacją, ale i to co jest w adresie URL. I trzeba się zastanowić czy chcemy tę
informacje upubliczniać. Może się skończyć na tym, że nasze REST-owe identyfikatory i tak będziemy zasłaniać jakimiś losowymi wartościami na podstawie
których będziemy identyfikować zasób i w tym przypadku REST-owość może stać się bardziej uciążliwa niż pomocna.&lt;/p&gt;

&lt;h2&gt;Polimorfizm URL-i i metody HTTP&lt;/h2&gt;

&lt;p&gt;Konwencja REST-owa zbudowana jest na bazie czterech metod protokołu HTTP: GET, POST, PUT oraz DELETE. Już tutaj pojawia się pierwszy problem, gdyż
przeglądarki internetowe z reguły wykorzystuję jedynie GET oraz POST. Stąd wszelkie formularze REST-owe do aktualizacji danych czy linki do ich
usuwania muszą stosować różne sztuczki, aby zasymulować użycie metod PUT lub DELETE w żądaniach GET i POST. Czasami to stwarza problemy, zwłaszcza
początkującym, przy konfiguracji routingów.&lt;/p&gt;

&lt;p&gt;Konwencja ta niestety nie wyjaśnia do końca co powinno się dziać, jeżeli jakaś operacja się nie powiedzie. Przykładowo wchodzimy na ekran edycji
złożenia zamówienia:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;GET /orders/new
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Konwencja REST mówi nam, iż utworzenie nowego zasobu (w tym wypadku o nazwie &lt;code&gt;order&lt;/code&gt;) powinno zostać wykonane za pomocą metody POST ale pod
adres kolekcji zasobów:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;POST /orders
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I taki adres będzie ukryty w naszym formularzu (w atrybucie &lt;code&gt;action&lt;/code&gt; elementu &lt;code&gt;form&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Takie działanie może i jest sensowne z punktu widzenia REST-a, ale z punktu widzenia działania witryny i przeglądarki internetowej jest wielce
niepożadana. Dlaczego? Jeżeli nasz formularz jest nieprawidłowy nie chcemy lądować na stronie z listą, chcemy ponownie wyswietlić formularz
wraz z odpowiednimi komunikatami o błędach. Innymi słowy nie chcemy być na &lt;code&gt;/orders&lt;/code&gt; tylko ciągle na &lt;code&gt;/orders/new&lt;/code&gt;. Niestety przeglądarka
zmieni nam adres, mimo iż pozostajemy w tym samym miejscu. Jeszcze gorzej jest, jeżeli w ogóle nie udostępniamy strony o adresie &lt;code&gt;/orders&lt;/code&gt;
(czy chcemy użytkownikom udostępniać stronę z listą wszystkich zamówień złożonych w systemie?). Jeżeli użytkownik w tym momencie zrobi zakładkę, aby
późiej powrócić i poprawić formularz zamiast formularza przywita go piękne 404, a przecież wcześniej był na tym formularzu i go wypełniał!&lt;/p&gt;

&lt;p&gt;O tym problemie &lt;a href=&quot;http://michalorman.pl/blog/2010/03/zmieniajacy-sie-url-po-bledach-walidacji-w-rails/&quot;&gt;pisałem już kiedyś&lt;/a&gt; na moim blogu.&lt;/p&gt;

&lt;h2&gt;Parametrów kilka&lt;/h2&gt;

&lt;p&gt;Do tej pory skupiałem się na architekturze REST-owej w kontekście witryny internetowej. A co w przypadku serwisów webowych? Wydaje się, że
architektura ta jest wręcz stworzona do takich serwisów. O ile człowiek nie będzie łaził po stronie manipulując identyfikatorami o tyle
aplikacja korzystająca z serwisu nie będzie miała z tym problemu. Jednak w praktyce niekoniecznie wszystko wygląda tak różowo.&lt;/p&gt;

&lt;p&gt;W całej swojej karierze deweloperskiej nie spotkałem jeszcze serwisu udostępniającego jakieś poważne usługi (np. płatności), którego żądania
przyjmują tylko 1 parametr albo wcale. Zwykle takie żadania przyjmują kilka parametrów, a mi zdażało się pisać takie co przyjmowały 10-15
parametrów. I to wcale nie jest nic dziwnego! Oprócz samych parametrów żadania często wymagane są jakieś dane identyfikacyjne, jakieś tokeny,
odciśki SSH czy dane statystyczne.&lt;/p&gt;

&lt;p&gt;Wszystko to powoduje, że nasze piękne URL-e przestają być już piękne i nie ma w tym nic złego, w końcu te URL-e nie są przeznaczone dla
ludzi. Maszyny, przynajmniej jak na razie, nie mają zmysłów ani gustów i im nie przeszkadzają takie długie adresy. W związku z tym jaki sens
jest upierać się przy REST-owości w takich serwisach?&lt;/p&gt;

&lt;h2&gt;A morał jest krótki i niektórym znany...&lt;/h2&gt;

&lt;p&gt;Ok, czy to oznacza, iż architektura REST jest zła i nie powinno się z niej korzystać? Nie, absolutnie nie! Trzeba tylko umieć rozgraniczyć,
kiedy architektura REST jest naprawdę potrzebna i sensowna, a kiedy jest tylko zwyczajnym buzzwordem. Trzeba być świadomym tego, co taka
architektura ze sobą przynosi i czy w naszej aplikacji będzie ona miała zastosowanie i będzie w ogóle wykorzystywana.&lt;/p&gt;

&lt;p&gt;Obecnie wszystkie projekty muszą być REST-owe, musi być JSON i musi być coś z &lt;em&gt;Enterprise&lt;/em&gt; w nazwie. Ta buzzwordowa karuzela już bardziej
działa na nerwy niż przynosi coś sensownego, ale co zrobić, prawa marketingu są nieubłagane i aby mieć inwestorów trzeba wykazać się
innowacyjnością, nawet za cenę funkcjonalności, bezpieczeństwa czy łatwości programowania i utrzymania. Ja osobiście REST i RESTful dokładam
do swojej listy &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzzword_bingo&quot;&gt;Bullshit Bingo&lt;/a&gt; i Tobie też polecam zastanowienie się nad tym, czy faktycznie
ten REST jest Ci potrzebny w Twojej aplikacji, aby potem na spotkaniu ktoś nie wstał i nie zawołał &lt;strong&gt;Bullshit!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://michalorman.pl/blog/2011/02/bullshit-bingo-rest-i-restful/&quot;&gt;http://michalorman.pl/blog/2011/02/bullshit-bingo-rest-i-restful/&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;bio&quot;&gt;
  &lt;p&gt;Autor: &lt;a href=&quot;http://michalorman.pl&quot;&gt;Michał Orman&lt;/a&gt;&lt;/p&gt;
  &lt;div class=&quot;foto&quot;&gt;
    &lt;a href=&quot;http://michalorman.pl&quot;&gt;
      &lt;img src=&quot;/images/michal_orman.png&quot; alt=&quot;Michał Orman&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  &lt;div class=&quot;desc&quot;&gt;
  &lt;p&gt;
Programista, konsultant IT. Pasjonuje się nowoczesnymi technologiami i metodami wytwarzania oprogramowania, oraz pedantycznie dba o jakość kodu w swoich projektach. Obecnie poświęca swój czas na zgłębianie tajników korporacyjnej Javy EE oraz frameworka Ruby on Rails, a zawodowo zajmuje się szeroko pojętym konsultingiem.
  &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

</content>
   <updated>2011-02-25T00:00:00-08:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="rest" label="rest" />
 </entry>
 
 <entry>
   <id>http://devblog.consileon.pl/2011/02/23/JPA-stronicowanie-wynikow-kwerendy</id>
   <link href="http://devblog.consileon.pl/2011/02/23/JPA-stronicowanie-wynikow-kwerendy"/>
   <title>JPA - stronicowanie wyników kwerendy</title>
   <content type="html">&lt;p&gt;Interfejs kwerendy zdefiniowany w JPA (&lt;code&gt;javax.persistence.Query&lt;/code&gt;) umożliwia stronicowanie listy wyników (&lt;strong&gt;paging&lt;/strong&gt;). Służą do tego metody:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;setFirstResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startPosition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;setMaxResults&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Wynikiem kwerendy ze stronicowaniem jest podzbiór obiektów czyli strona, której numer i rozmiar określają odpowiednio parametry &lt;code&gt;startPosition&lt;/code&gt; i &lt;code&gt;maxResult&lt;/code&gt;.
Stronicowanie przyspiesza działanie aplikacji (mniejsza ilość danych do przetwarzania) oraz ułatwia użytkownikowi nawigację i wyszukiwanie określonych rekordów. Jednak jak to zwykle w świecie ORM bywa, każde rozwiązanie ma swoje &quot;problemy&quot;, które w przypadku stronicowania objawiają się wraz z użyciem mechanizmu ładowania wyprzedzającego elementów kolekcji.&lt;/p&gt;

&lt;h2&gt;Ładowanie wyprzedzające ze stronicowaniem&lt;/h2&gt;

&lt;p&gt;Ładowanie wyprzedzające generalnie polega na wykonaniu kwerendy w taki sposób, aby razem z głównymi obiektami pobrane zostały obiekty powiązane (pojedyncze obiekty bądź kolekcje). Najbardziej popularnym sposobem ładowania wyprzedzającego jest złączenie tabel w kwerendzie (&lt;strong&gt;join fetching&lt;/strong&gt;). Niestety, metoda ta nie nadaje się do zapytań ze stronicowaniem. Zobaczmy dlaczego.&lt;/p&gt;

&lt;h2&gt;Przykład&lt;/h2&gt;

&lt;p&gt;Mamy obiekt zamówienia (&lt;code&gt;Order&lt;/code&gt;) zawierający pozycje (&lt;code&gt;LineItem&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ... ...&lt;/span&gt;
  &lt;span class=&quot;nd&quot;&gt;@OneToMany&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LineItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lineItems&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

 &lt;span class=&quot;nd&quot;&gt;@Temporal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Date&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orderDate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Załóżmy, że chcemy wyświetlić użytkownikowi stronę z listą zamówień. Jednocześnie dla każdego zamówienia chcemy załadować jego pozycje. W tym celu tworzymy kwerendę na obiekcie &lt;code&gt;Order&lt;/code&gt;, ze stronicowaniem oraz ze złączeniem kolekcji &lt;code&gt;lineItems&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;em&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;SELECT o FROM Order o LEFT JOIN FETCH o.lineItems&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setFirstResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setMaxResults&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getResultList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Złączenie definiujemy w JPQL za pomocą klauzuli &lt;code&gt;JOIN FETCH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oczekiwania wobec dostawcy JPA&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Załóżmy, że w bazie mamy 3 zamówienia z różną ilością pozycji.
Oczekujemy, że zarządca utrwalania (&lt;code&gt;EntityManager&lt;/code&gt;) wykona pojedyncze zapytanie sql (z klauzulą &lt;code&gt;left outer join&lt;/code&gt;) i zwróci listę zawierającą 3 obiekty &lt;code&gt;Order&lt;/code&gt;. Dodatkowo oczekujemy, że w każdym obiekcie &lt;code&gt;Order&lt;/code&gt;, kolekcja &lt;code&gt;lineItems&lt;/code&gt; będzie załadowana.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Realizacja&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hibernate&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Hibernate co prawda zwraca oczekiwany wynik, ale generuje podejrzanie brzmiący komunikat:
&lt;code&gt;firstResult/maxResults specified with collection fetch; applying in memory!&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EclipseLink&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;EclipseLink zwraca 2 obiekty &lt;code&gt;Order&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wyjaśnienie&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Aby zrozumieć co się stało, zobaczmy jak dokładnie wygląda wynik naszej kwerendy na poziomie rekordów bazy danych bez uwzględnienia stronicowania:&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt; order_id     &lt;/th&gt;
&lt;th&gt; line_id    &lt;/th&gt;
&lt;th&gt; cust_name       &lt;/th&gt;
&lt;th&gt; sku &lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt; 1            &lt;/td&gt;
&lt;td&gt; 1          &lt;/td&gt;
&lt;td&gt; Jan Kowalski    &lt;/td&gt;
&lt;td&gt; 232342342   &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt; 1            &lt;/td&gt;
&lt;td&gt; 2          &lt;/td&gt;
&lt;td&gt; Jan Kowalski    &lt;/td&gt;
&lt;td&gt; 345345443   &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt; 2            &lt;/td&gt;
&lt;td&gt; 3          &lt;/td&gt;
&lt;td&gt; Paweł Kaczor    &lt;/td&gt;
&lt;td&gt; 655624323   &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt; 3            &lt;/td&gt;
&lt;td&gt; 4          &lt;/td&gt;
&lt;td&gt; Jerzy Dudek     &lt;/td&gt;
&lt;td&gt; 673454345   &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt; 3            &lt;/td&gt;
&lt;td&gt; 5          &lt;/td&gt;
&lt;td&gt; Jerzy Dudek     &lt;/td&gt;
&lt;td&gt; 563425676   &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt; 3            &lt;/td&gt;
&lt;td&gt; 6          &lt;/td&gt;
&lt;td&gt; Jerzy Dudek     &lt;/td&gt;
&lt;td&gt; 234576854   &lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;Widzimy, że w wyniku złączenia, otrzymujemy zbiór rekordów liczebnością przekraczający ilość zamówień. Jeśli z takiego zbioru będziemy chcieli wyciągnąć stronę o rozmiarze 3, otrzymamy tylko pierwsze &lt;strong&gt;trzy&lt;/strong&gt; rekordy zawierające zamówienia z id 1 i 2. Zamówienie z id 3 zostanie wykluczone. Otrzymamy zatem &lt;strong&gt;dwa&lt;/strong&gt; zamówienia zamiast &lt;strong&gt;trzech&lt;/strong&gt;! Wniosek: stronicowanie na poziomie rekordów w kwerendzie ze złączeniem &lt;code&gt;outer join&lt;/code&gt;  jest niedokładne.&lt;/p&gt;

&lt;p&gt;Teraz już wiemy dlaczego EclipseLink zwrócił dwa zamówienia. Ale jakim sposobem Hibernate zwrócił trzy zamówienia? Odpowiedź jest prosta (ale bolesna). Hibernate omija problem poprzez załadowanie wszystkich rekordów z tabeli i wyselekcjonowanie strony w pamięci (stąd magiczne: &quot;applying in memory&quot;!). Rozwiązanie to zwiększa zużycie zasobów (procesora i pamięci), co przeczy głównemu celowi (zwiększeniu wydajności), dla którego stosujemy stronicowanie. Należy poszukać lepszego rozwiązania.&lt;/p&gt;

&lt;h2&gt;Ładowanie wsadowe ze stronicowaniem&lt;/h2&gt;

&lt;p&gt;Ładowanie wsadowe (&lt;strong&gt;batch fetching&lt;/strong&gt;) to bardziej zaawansowany sposób pobierania wyprzedzającego. Obiekty powiązane nie są ładowane w kwerendzie głównej, ale w dodatkowej kwerendzie, której ostateczna postać zależy od wybranego (o ile dostawca na to pozwala) typu ładowania wsadowego.&lt;/p&gt;

&lt;p&gt;Stronicowanie wykonywane jest bezproblemowo w kwerendzie głównej, która nie zawiera klauzuli &lt;code&gt;outer join&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Konfiguracja -  Hibernate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dla kolekcji &lt;code&gt;lineItems&lt;/code&gt; specyfikujemy ładowanie wsadowe używając adnotacji &lt;code&gt;@org.hibernate.annotations.BatchSize&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@OneToMany&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@BatchSize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LineItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lineItems&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Parametr &lt;code&gt;size&lt;/code&gt; w adnotacji &lt;code&gt;@BatchSize&lt;/code&gt; oznacza ilość elementów kolekcji, jaka zostanie załadowana w pojedynczej kwerendzie sql.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Konfiguracja -  EclipseLink&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ten sam sposób ładowania wsadowego konfigurujemy w EclipseLink następująco:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@OneToMany&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@BatchFetch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BatchFetchType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;IN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LineItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lineItems&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Realizacja&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tworzymy kwerendę JPA tym razem bez klauzuli &lt;code&gt;JOIN FETCH&lt;/code&gt;. W celu lepszego zobrazowania działania pobierania wsadowego, dodajemy warunek na pole &lt;code&gt;orderDate&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;em&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;SELECT o FROM Order o WHERE o.orderDate = CURRENT_DATE&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setFirstResult&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setMaxResults&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getResultList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Wygenerowane zostają dwa zapytania sql:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;kwerenda główna zamówień (ze stronicowaniem)&lt;/li&gt;
&lt;/ul&gt;


&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sql&quot;&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ORDER_DATE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;OFFSET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;kwerenda dodatkowa - załadowanie wsadowe pozycji zamówień&lt;/li&gt;
&lt;/ul&gt;


&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sql&quot;&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LineItem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;li&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ORDER_ID&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ORDER_ID&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Jak widzimy, w celu załadowania pozycji tylko dla zamówień pobranych w kwerendzie głównej, w kwerendzie dodatkowej została użyta klauzula &lt;strong&gt;&lt;code&gt;IN&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optymalizacja - EclipseLink&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;EclipseLink pozwala skonfigurować trzy typy pobierania wsadowego: (&lt;code&gt;IN, JOIN, EXISTS&lt;/code&gt;)
Typ &lt;code&gt;IN&lt;/code&gt; już znamy. Mankamentem jest tutaj ograniczona ilość elementów kolekcji, które mogą być załadowane w jednej kwerendzie sql. Efektywniejszym rozwiązaniem jest użycie kryteriów selekcji z kwerendy głównej w kwerendzie dodatkowej (typ &lt;code&gt;JOIN&lt;/code&gt; - &quot;The original query's selection criteria is joined with the batch query&quot;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Konfiguracja:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@OneToMany&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@BatchFetch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BatchFetchType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;JOIN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LineItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lineItems&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Wygenerowana kwerenda dodatkowa wygląda następująco:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sql&quot;&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LineItem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;li&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order_id&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ORDER_DATE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Widzimy, że problematyczna klauzula &lt;code&gt;IN&lt;/code&gt; zastąpiona została kryterium wyboru identycznym jak w kwerendzie głównej.&lt;/p&gt;

&lt;h2&gt;Podsumowanie&lt;/h2&gt;

&lt;p&gt;W powyższym artykule przedstawiłem w jaki sposób stosować stronicowanie (&lt;strong&gt;paging&lt;/strong&gt;) w kwerendach JPA. Szczegółowo omówiłem problem stronicowania, kiedy w kwerendzie używane jest ładowanie wyprzedzające elementów kolekcji.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://pkaczor.blogspot.com/2011/02/jpa-stronicowanie-wynikow-kwerendy.html&quot;&gt;http://pkaczor.blogspot.com/2011/02/jpa-stronicowanie-wynikow-kwerendy.html&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;bio&quot;&gt;
  &lt;p&gt;Autor: &lt;a href=&quot;mailto:pawel.kaczor@consileon.pl&quot;&gt;Paweł Kaczor&lt;/a&gt;&lt;/p&gt;
  &lt;div class=&quot;foto&quot;&gt;
      &lt;img src=&quot;/images/pawel_kaczor.png&quot; alt=&quot;Paweł Kaczor&quot;/&gt;
  &lt;/div&gt;
  &lt;div class=&quot;desc&quot;&gt;
  &lt;p&gt;
    Programista, pasjonat, konsultant IT. Interesuje się nowoczesnymi framework-ami webowymi takimi jak: Seam, ZK.
    Obecnie w wolnym czasie poznaje język Scala.
  &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;



</content>
   <updated>2011-02-23T00:00:00-08:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="orm" label="orm" />
 </entry>
 
 <entry>
   <id>http://devblog.consileon.pl/2011/01/03/mockowanie-z-uzyciem-Spring-i-Mockito</id>
   <link href="http://devblog.consileon.pl/2011/01/03/mockowanie-z-uzyciem-Spring-i-Mockito"/>
   <title>Mockowanie z użyciem Spring i Mockito</title>
   <content type="html">&lt;p&gt;W poniższym wpisie chciałbym przedstawić jak efektywnie skonfigurować testy integracyjne w Spring z użyciem mocków.&lt;/p&gt;

&lt;h2&gt;Testy integracyjne w Spring&lt;/h2&gt;

&lt;p&gt;Tworzenie i uruchamianie testów integracyjnych w Spring jest dziecinnie proste dzięki dobrodziejstwom jakie dostarcza &lt;a href=&quot;http://static.springsource.org/spring/docs/3.0.x/reference/testing.html#testcontext-framework&quot;&gt;Spring TestContext Framework&lt;/a&gt;.
Zakładając, że plik xml konfiguracji kontekstu aplikacji Spring (Spring Application Context) w module aplikacji, który chcemy testować, nazwaliśmy &lt;code&gt;applicationContext.xml&lt;/code&gt;, uruchomienie testu integracyjnego JUnit dla takiego moduły wygląda następująco:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@RunWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SpringJUnit4ClassRunner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ContextConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;locations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&amp;quot;classpath:applicationConfig.xml&amp;quot;&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DoSthTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shouldDoSth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Jest to najprostszy sposób na przetestowanie poprawności pliku konfiguracji kontekstu aplikacji, w którym zdefiniowane są zależności pomiędzy klasami zarządzanymi przez Spring.&lt;/p&gt;

&lt;p&gt;No dobrze, ale chcemy przetestować konkretny serwis, który stworzyliśmy. Zatem do naszego testu wstrzykujemy serwis i tworzymy dla niego metodę testującą:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Autowired&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoSthService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceToTest&lt;/span&gt; 

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shouldReturnSth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// when&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceToTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;doSth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// then&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Tworzenie mocka&lt;/h2&gt;

&lt;p&gt;Nasz serwis korzysta z kilku innych serwisów, w tym z serwisu &lt;code&gt;HttpClient&lt;/code&gt;, który w naszym teście musimy zastąpić mockiem.
Jednym ze sposobów jest ręczna zmiana zależności w kodzie testu:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shouldReturnSth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// given&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mockHttpClient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;serviceToTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setHttpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mockHttpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Sposób ten ma jednak kilka wad:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Dla każdego testu (metody testującej) musimy sami tworzyć i przypisywać mocka&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Serwis musi dostarczać settera dla zależności mockowanej, podczas gdy w przypadku wstrzykiwania zależności za pomocą adnotacji setter nie jest wymagany&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mockujemy tylko konkretną zależność między dwoma beanami. Jeżeli serwis który mockujemy jest używany przez inny serwis biorący udział w teście (np. &lt;code&gt;DoSthService&lt;/code&gt; używa &lt;code&gt;HttpClient&lt;/code&gt; i &lt;code&gt;OtherService&lt;/code&gt;, a &lt;code&gt;OtherService&lt;/code&gt; używa &lt;code&gt;HttpClient&lt;/code&gt;) , musimy mockować każdą zależność osobno&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Lepszym rozwiązaniem jest podmiana serwisu &lt;code&gt;HttpClient&lt;/code&gt; bezpośrednio w kontekście aplikacji. Oczywiście nie możemy zmienić pliku &lt;code&gt;applicationContext.xml&lt;/code&gt;, musimy stworzyć odrębny kontekst aplikacji i wskazać go w konfiguracji naszego testu:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@RunWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SpringJUnit4ClassRunner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ContextConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;locations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&amp;quot;classpath:applicationConfig-test.xml&amp;quot;&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DoSthTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shouldReturnSth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// when&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceToTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;doSth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;No dobrze, ale jak stworzyć mocka w pliku konfiguracji kontekstu aplikacji, skoro nasz mock nie jest utworzoną przez nas osobną klasą, ale został wygenerowany automatycznie przez framework &lt;a href=&quot;http://mockito.org&quot;&gt;Mockito&lt;/a&gt; ( &lt;code&gt;Mockito.mock(HttpClient.class)&lt;/code&gt; ) ?&lt;/p&gt;

&lt;p&gt;Rozwiązanie jest proste. Metodę &lt;code&gt;Mockito.mock&lt;/code&gt; należy zadeklarować jako metodę fabrykującą naszego mocka podając jako argument tej metody klasę obiektu mockowanego:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;httpClient&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;org.mockito.Mockito&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;factory-method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;mock&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;constructor-arg&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;org.apache.http.client.HttpClient&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt; 
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Dostrajanie konfiguracji&lt;/h2&gt;

&lt;p&gt;Kopiowanie całej konfiguracji kontekstu aplikacji w celu nadpisania jednego serwisu prowadzi do redundancji kodu (kodu konfiguracji), a zatem zwiększa koszt utrzymania aplikacji.
Jeżeli chcemy tego uniknąć, możemy zdefiniować dwa pliki konfiguracji kontekstu aplikacji w konfiguracji naszego testu:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@ContextConfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;locations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&amp;quot;classpath:applicationConfig.xml&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s&quot;&gt;&amp;quot;classpath:applicationConfig-test.xml&amp;quot;&lt;/span&gt; 
&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DoSthTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;W pliku &lt;code&gt;applicationConfig-test.xml&lt;/code&gt; definiujemy tylko mocki, które zastąpią istniejące beany w konfiguracji głównej.
Zwróćmy uwagę na kolejność deklaracji plików konfiguracji. Ważne jest aby deklaracja pliku konfiguracji mocków następowała po deklaracji pliku głównego konfiguracji.&lt;/p&gt;

&lt;h2&gt;Autowstrzykiwanie mocków&lt;/h2&gt;

&lt;p&gt;W przypadku gdy korzystamy z autowstrzykiwania zależności, nasz mock będzie dostępny tylko gdy wstrzykiwanie zależności odbywa się na podstawie nazwy. A zatem mock nie zostanie znaleziony jeśli używamy adnotacji &lt;code&gt;@Autowired&lt;/code&gt;, dla której wstrzykiwanie odbywa się na podstawie typu. Rozwiązaniem może być dodanie kwalifikatora wskazującego nazwę zależności:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Autowired&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@Qualifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;httpClient&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Jednak lepszym rozwiązaniem jest zastosowanie adnotacji &lt;code&gt;@javax.annotation.Resource&lt;/code&gt;. W tym przypadku zależność najpierw wyszukiwana jest po nazwie, a w przypadku braku pasującej nazwy, po typie.&lt;/p&gt;

&lt;h2&gt;Włączanie/wyłączanie mocków&lt;/h2&gt;

&lt;p&gt;Czasami ten sam test integracyjny chcemy uruchamiać zarówno z serwisem w postaci mocka jak i z serwisem rzeczywistym. Jeśli stosujemy opisaną powyżej metodę nadpisywania beanów z konfiguracji głównej mockami z konfiguracji testowej, włączenie/wyłączenie mocka można łatwo osiągnąć modyfikując plik konfiguracji mocków (dodając bądź usuwając mocka). Jednak co z kodem sterującym zachowaniem mocka (patrz kod poniżej)?&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Autowired&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoSthService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceToTest&lt;/span&gt; 

&lt;span class=&quot;nd&quot;&gt;@Resource&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shouldReturnSth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// given&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//httpClient must be a mock!&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpUriRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;willReturn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// when&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serviceToTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;doSth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Uruchomienie tego kodu na obiekcie nie będącym mockiem zakończy się wyjątkiem. Należy zatem kod sterujący mockiem wykonać warunkowo tylko jeśli obiekt rzeczywiście jest mockiem. Sprawdzenie implementujemy następująco:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// given&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAssignableFrom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpUriRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;willReturn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Współdzielenie mocków&lt;/h2&gt;

&lt;p&gt;Jeżeli chcielibyśmy użyć tego samego mocka w kilku metodach testowych musimy pamiętać o zresetowania stanu mocka przed każdym testem.
Najprostszym rozwiązaniem jest dodanie do klasy testu metody &lt;code&gt;resetMocks&lt;/code&gt; z adnotacją &lt;code&gt;@org.junit.Before&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Before&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;resetMocks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Mockito&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Mockowanie częściowe&lt;/h2&gt;

&lt;p&gt;Na koniec przedstawię w jaki sposób utworzyć w konfiguracji kontekstu aplikacji częściowego mocka, czyli obiekt, którego zachowanie tylko w części chcemy mockować (np. zmieniając tylko rezultat wywołania metody):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;httpClient&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;org.apache.http.client.HttpClient&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;httpClientPartiallyMocked&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;org.mockito.Mockito&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;factory-method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;spy&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;constructor-arg&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;httpClient&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt; 
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;W tym przypadku tworzymy mocka przy pomocy metody fabrykującej &lt;code&gt;Mockito.spy&lt;/code&gt;, podając jako argument tej metody referencję do istniejącego bean-a.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://pkaczor.blogspot.com/2010/12/mockowanie-przy-uzyciu-spring-i-mockito.html&quot;&gt;http://pkaczor.blogspot.com/2010/12/mockowanie-przy-uzyciu-spring-i-mockito.html&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;bio&quot;&gt;
  &lt;p&gt;Autor: &lt;a href=&quot;mailto:pawel.kaczor@consileon.pl&quot;&gt;Paweł Kaczor&lt;/a&gt;&lt;/p&gt;
  &lt;div class=&quot;foto&quot;&gt;
      &lt;img src=&quot;/images/pawel_kaczor.png&quot; alt=&quot;Paweł Kaczor&quot;/&gt;
  &lt;/div&gt;
  &lt;div class=&quot;desc&quot;&gt;
  &lt;p&gt;
    Programista, pasjonat, konsultant IT. Interesuje się nowoczesnymi framework-ami webowymi takimi jak: Seam, ZK.
    Obecnie w wolnym czasie poznaje język Scala.
  &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;



</content>
   <updated>2011-01-03T00:00:00-08:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="spring" label="spring" />
   <updated>2011-01-03T00:00:00-08:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="testowanie" label="testowanie" />
 </entry>
 
 <entry>
   <id>http://devblog.consileon.pl/2010/12/09/spring-dm-czyli-spring-i-osgi</id>
   <link href="http://devblog.consileon.pl/2010/12/09/spring-dm-czyli-spring-i-osgi"/>
   <title>Spring DM - czyli Spring i OSGi</title>
   <content type="html">&lt;p&gt;Uff, ale dawno nie wrzuciłem żadnego posta na bloga. Czas nadrobić nieco zaległości, a ostatnie tygodnie stały u
mnie pod znakiem &lt;strong&gt;Spring'a&lt;/strong&gt;. Obecnie przyglądam się integracji tego frameworka z ciekawą technologią &lt;a href=&quot;http://en.wikipedia.org/wiki/OSGi&quot;&gt;OSGi&lt;/a&gt;,
która ostatnio staje się coraz bardziej modna. Spring w swojej rodzinie frameworków posiada jeden o nazwie
&lt;a href=&quot;http://www.springsource.org/osgi&quot;&gt;Spring Dynamic Modules&lt;/a&gt;, który służy do integracji komponentów Spring'owych z
platformą OSGi (i na odwrót).&lt;/p&gt;

&lt;h2&gt;Krótko o platformie OSGi&lt;/h2&gt;

&lt;p&gt;Jak dotąd platforma Java nie dorobiła się porządnego wsparcia dla modularnych aplikacji. Oczywiście istnieją
wzorce pozwalające nam na modularyzację aplikacji, jednakże z punktu widzenia maszyny wirtualnej jest to wciąż
jedna, monolityczna aplikacja. Czego zatem brakuje Javie, do pełnej modularyzacji?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jasnej definicji tego, czym jest moduł.&lt;/li&gt;
&lt;li&gt;Określania zakresu widoczności modułu dla innych modułów.&lt;/li&gt;
&lt;li&gt;Określenia cyklu życia modułu.&lt;/li&gt;
&lt;li&gt;Określenia sposobów interakcji modułów.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Jak łatwo się domyśleć technologia OSGi została opracowana w celu wypełnienia tej niszy i oferuje platformę,
która zapewnia nam funkcjonalności, których brakuje w standardowej Javie.&lt;/p&gt;

&lt;p&gt;To co oferuje nam OSGi to lekką platformę dla komponentowych, oraz zorientowanych na usługi aplikacji w
ramach wirtualnej maszyny Javy (JVM). Możemy dynamicznie, w czasie działania aplikacji, modyfikować rejestr
komponentów (&lt;em&gt;bundle&lt;/em&gt; - w terminologii OSGi) dodając nowe i podmieniając istniejące komponenty. Platforma
OSGi zapewnia nam pełną izolację komponentów, dzięki czemu mamy pewność, że nie będą one przeszkadzały
sobie nawzajem (np. poprzez konflikty wersji zależnych bibliotek JAR).&lt;/p&gt;

&lt;h2&gt;Krótko o Spring DM&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Spring Dynamic Modules&lt;/strong&gt; łączy technologię Spring z platformą OSGi. Framework ten nie przynosi żadnej
dodatkowej funkcjonalności dla OSGi a jedynie obserwuje rejestrowane komponenty tworząc dla nich Spring'owe
konteksty aplkiacji. Framework ten potrafi udostępnić komponenty Spring'owe jako komponenty OSGi (widoczne
dla innych komponentów OSGi), oraz dowolny komponent OSGi, nawet nie Spring'owy, zainstalować w kontenerze
Springa.&lt;/p&gt;

&lt;h2&gt;Integracja Spring'a z OSGi&lt;/h2&gt;

&lt;p&gt;No dobra, starczy tej teorii, zobaczmy jak wygląda integracja tych platform w kodzie.&lt;/p&gt;

&lt;p&gt;Będziemy potrzebować dwóch komponentów, z których jeden będzie dostarczał usługę, a drugi z niej korzystał.
Oba komponenty utworzone zostaną jako osobne komponenty OSGi.&lt;/p&gt;

&lt;p&gt;Najpierw musimy określić interfejs dla naszej usługi:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AuthorizationService&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authorize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Teraz możemy utworzyć trywialną implementację tego interfejsu:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DefaultAuthorizationService&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AuthorizationService&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authorize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;secret&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Kolejny krok to utworzenie konfiguracji Spring'owej. Framework Spring DM wymaga, aby taka konfiguracja
znajdowała się w katalogu &lt;code&gt;META-INF/spring&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;beans&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.springframework.org/schema/beans&amp;quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xmlns:xsi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xmlns:context=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.springframework.org/schema/context&amp;quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xmlns:osgi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.springframework.org/schema/osgi&amp;quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-1.2.xsd&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;context:component-scan&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;base-package=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;demo.springdm&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;osgi:service&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;interface=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;demo.springdm.api.AuthorizationService&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;demo.springdm.api.impl.DefaultAuthorizationService&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/osgi:service&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/beans&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Najważniejszym elementem tej konfiguracji jest &lt;code&gt;&amp;lt;osgi:service&amp;gt;&lt;/code&gt;, który deklaruje Springowy komponent
jako komponent OSGi widoczny dla innych komponentów zainstalowanych w ramach tej platformy. Spring DM
za nas zainstaluje komponent w rejestrze usług (ang. &lt;em&gt;service registry&lt;/em&gt;) platformy OSGi przez co stanie
się on dostępny dla innych komponentów.&lt;/p&gt;

&lt;p&gt;Ok, moduł dostarczający usługe jest gotowy, teraz możemy przejść do modułu korzystającego z usługi.
Utwórzmy zatem komponent Spring'owy do którego wstrzykniemy komponent zainstalowany w platformie OSGi:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AuthorizationClient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AuthorizationService&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authorizationService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@PostConstruct&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authorize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Authorization for foo:bar : &amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authorizationService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authorize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Authorization for foo:secret : &amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authorizationService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;authorize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;secret&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Jak widać, jest to typowa klasa POJO z kilkoma adnotacjami typowymi dla Spring'a. Nic magicznego się
tutaj nie dzieje. Aby kod ten działał potrzebujemy jednak komponentu &lt;code&gt;authorizationService&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;beans&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.springframework.org/schema/beans&amp;quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xmlns:xsi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xmlns:context=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.springframework.org/schema/context&amp;quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xmlns:osgi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.springframework.org/schema/osgi&amp;quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-1.2.xsd&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;context:component-scan&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;base-package=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;demo.springdm&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;osgi:reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;authorizationService&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;interface=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;demo.springdm.api.AuthorizationService&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/beans&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;W tym wypadku importujemy komponent OSGi jako komponent Spring'owy za pomocą &lt;code&gt;&amp;lt;osgi:reference&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Teraz pozostaje już tylko zainstalowanie komponentów w kontenerze OSGi (ja korzystam z
&lt;a href=&quot;http://www.eclipse.org/equinox/&quot;&gt;Equinoxa&lt;/a&gt;):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;java -jar org.eclipse.osgi_3.6.1.R36x_v20100806.jar -console

osgi&amp;gt; ss

Framework is launched.

id    State       Bundle
0     ACTIVE      org.eclipse.osgi_3.6.1.R36x_v20100806
1     ACTIVE      org.springframework.osgi.io_2.0.0.M1
2     ACTIVE      org.springframework.osgi.core_2.0.0.M1
3     ACTIVE      org.springframework.osgi.extender_2.0.0.M1
4     ACTIVE      org.springframework.aop_3.0.5.RELEASE
5     ACTIVE      org.springframework.asm_3.0.5.RELEASE
6     ACTIVE      org.springframework.beans_3.0.5.RELEASE
7     ACTIVE      org.springframework.context_3.0.5.RELEASE
8     ACTIVE      org.springframework.context.support_3.0.5.RELEASE
9     ACTIVE      org.springframework.core_3.0.5.RELEASE
10    ACTIVE      org.springframework.expression_3.0.5.RELEASE
11    ACTIVE      com.springsource.org.aopalliance_1.0.0
12    ACTIVE      com.springsource.net.sf.cglib_2.1.3
13    ACTIVE      com.springsource.slf4j.api_1.6.1
                  &lt;span class=&quot;nv&quot;&gt;Fragments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;15
14    ACTIVE      com.springsource.slf4j.org.apache.commons.logging_1.6.1
15    RESOLVED    com.springsource.slf4j.nop_1.6.1
                  &lt;span class=&quot;nv&quot;&gt;Master&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;13
16    ACTIVE      com.springsource.ch.qos.logback.core_0.9.24
17    ACTIVE      com.springsource.ch.qos.logback.classic_0.9.24

osgi&amp;gt; install file:bundles/spring-demo/spring-dm-demo-producer-1.0.jar
Bundle id is 18

osgi&amp;gt; start 18 

osgi&amp;gt; install file:bundles/spring-demo/spring-dm-demo-consumer-1.0.jar
Bundle id is 19

osgi&amp;gt; start 19

osgi&amp;gt; Authorization &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;foo:bar : &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;
Authorization &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;foo:secret : &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;

osgi&amp;gt; ss

Framework is launched.

id    State       Bundle
0     ACTIVE      org.eclipse.osgi_3.6.1.R36x_v20100806
...
18    ACTIVE      demo.springdm.spring-dm-demo-producer_1.0.0
19    ACTIVE      demo.springdm.spring-dm-demo-consumer_1.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Działa. Pierwsza integracja Spring'a z OSGi za pomocą frameworka Spring Dynamic Modules
gotowa.&lt;/p&gt;

&lt;h2&gt;Podsumowanie&lt;/h2&gt;

&lt;p&gt;Platforma OSGi ma na celu wypełnienie luki jaka istnieje w Javie dotyczącej wsparcia dla
aplikacji wielomodułowych. To co oferuje nam Java, czyli różne typy archiwów (JAR, WAR czy EAR)
okazują się niewystarczające. OSGi jasno definiuje czym jest moduł, jaki jest jego cykl życia
oraz izoluje moduły od siebie udostępniając tylko te usługi, które trzeba.&lt;/p&gt;

&lt;p&gt;Spring Dynamic Modules to framework, który pozwala instalować komponenty Springo'we w kontenerze
OSGi a także używać dostępnych komponentów i wstrzykiwać je do komponentów Springa. Całość
dzieje się jedynie z pomocą odrobiny deklaracji w plikach XML-owych.&lt;/p&gt;

&lt;p&gt;Działający kod aplikacji powstałej do tego posta można znaleźć pod adresem:
&lt;a href=&quot;https://github.com/michalorman/michalorman.github.com/tree/master/przyklady/spring-dm-czyli-spring-i-osgi&quot;&gt;https://github.com/michalorman/michalorman.github.com/tree/master/przyklady/spring-dm-czyli-spring-i-osgi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Znajduje się tam także Equinox skonfigurowany pod Spring DM 2.0.0.M1 oraz Spring'a 3.0.5.RELEASE
(a nie 3.0.0.RC1 z jaką domyślnie jest Spring DM).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://michalorman.pl/blog/2010/12/spring-dm-czyli-spring-i-osgi/&quot;&gt;http://michalorman.pl/blog/2010/12/spring-dm-czyli-spring-i-osgi/&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;bio&quot;&gt;
  &lt;p&gt;Autor: &lt;a href=&quot;http://michalorman.pl&quot;&gt;Michał Orman&lt;/a&gt;&lt;/p&gt;
  &lt;div class=&quot;foto&quot;&gt;
    &lt;a href=&quot;http://michalorman.pl&quot;&gt;
      &lt;img src=&quot;/images/michal_orman.png&quot; alt=&quot;Michał Orman&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  &lt;div class=&quot;desc&quot;&gt;
  &lt;p&gt;
Programista, konsultant IT. Pasjonuje się nowoczesnymi technologiami i metodami wytwarzania oprogramowania, oraz pedantycznie dba o jakość kodu w swoich projektach. Obecnie poświęca swój czas na zgłębianie tajników korporacyjnej Javy EE oraz frameworka Ruby on Rails, a zawodowo zajmuje się szeroko pojętym konsultingiem.
  &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

</content>
   <updated>2010-12-09T00:00:00-08:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="spring" label="spring" />
   <updated>2010-12-09T00:00:00-08:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="osgi" label="osgi" />
 </entry>
 
 <entry>
   <id>http://devblog.consileon.pl/2010/11/04/jquery-pure-html-templates</id>
   <link href="http://devblog.consileon.pl/2010/11/04/jquery-pure-html-templates"/>
   <title>jQuery - czyste szablony HTML</title>
   <content type="html">&lt;p&gt;Ta historia będzie o &lt;a href=&quot;http://github.com/mpapis/jquery-pure-templates/&quot;&gt;jquery pure templates&lt;/a&gt; - czyste wzorce HTML w jquery.&lt;/p&gt;

&lt;p&gt;This article is also available in english version (see link at the bottom of this article).&lt;/p&gt;

&lt;p&gt;Podczas pisania kodu na nowym projekcie znalazłem świetną bibliotekę &lt;a href=&quot;http://beebole.com/pure/&quot;&gt;Pure napisaną przez BeeBole&lt;/a&gt; i miałem właśnie o niej pisać. Ale wraz z użyciem jej znajdowałem coraz więcej problemów. Najpierw chciałem naprawić tą bibliotekę, ale w środku debugowania okazało się to nie takie proste, 20 kilobajtów źródeł ciężko zmienić.&lt;/p&gt;

&lt;p&gt;Zdecydowałem się więc napisać swoją bibliotekę opisującą podobną ideę, ale wykorzystując inne podejście. Moja biblioteka jest nadal &lt;em&gt;czysta&lt;/em&gt;, nie wymaga jednak śmiesznych znaczników HTML żeby wypełnić go danymi, wystarczy napisać kod który zawiera selektory jquery. Moja pierwsza próba została wykonana z użyciem selektorów jquery bezpośrednio w definicji danych:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;a#my&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;my url&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;a#my@href&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;#/url&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Po napisaniu działającego kodu wzorcowego tylko w 3 godziny pomyślałem że dobrze by było mapować klucze z danych na selektory w jquery, tak żeby serwer nie musiał zwracać selektorów jako kluczy w JSON:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;nx&quot;&gt;data2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;my url&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;link&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;#/url&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Do tego potrzebna jest prosta mapa:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;nx&quot;&gt;map2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;a#my&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;link&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;a#my@href&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Więc jak to uruchomić ? Proste :). Załącz bibliotekę w swoim kodzie (najlepiej w nagłówku):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;jquery-pure-template.js&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I wywołaj render na elementach wybranych przez jquery:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;.user&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Lub używając mapy:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;.user&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Do działania potrzebny jest wzorzec (template) HTML:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;user&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;#&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/a&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;na koniec otrzymamy oto taki wynik w HTML:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;js&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;user&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;#/url&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/a&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;To był prosty przykład, ale ta biblioteka działa także z tablicami oraz zagnieżdżonymi tablicami, po prostu dajcie temu szanse, i pamiętajcie: nie zmuszajcie HTML do określania logiki, niech dane posterują logiką nie widok.&lt;/p&gt;

&lt;p&gt;Oczywiście założone rozwiązanie może mieć kilka problemów, najważniejszy to wydajność. Tablice powyżej 500 elementów zaczynają zwalniać, ale używając zagnieżdżonych tablic można spokojnie operować na 5000 elementów na w miarę nowoczesnym komputerze. Dodatkowo dodanie &lt;code&gt;id&lt;/code&gt; do zbiorów może przyśpieszyć kod nawet o 20% (znajdowanie elementów przez jquery).&lt;/p&gt;

&lt;p&gt;Wiem że istnieją szybsze rozwiązania, które mogą bez problemu wyświetlić duże ilości danych, ale moje jest proste, tylko 70 linii kodu, to czyni je niesamowicie prostym w utrzymaniu i modyfikacji. Łatwiej pracować z mniejszą ilością kodu :).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://niczsoft.com/2010/11/jquery-pure-templates/&quot;&gt;http://niczsoft.com/2010/11/jquery-pure-templates/&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;bio&quot;&gt;
  &lt;p&gt;Autor: &lt;a href=&quot;http://michalorman.pl&quot;&gt;Michał Papis&lt;/a&gt;&lt;/p&gt;
  &lt;div class=&quot;foto&quot;&gt;
    &lt;a href=&quot;http://niczsoft.com/&quot;&gt;
      &lt;img src=&quot;/images/michal_papis.jpeg&quot; alt=&quot;Michał Papis&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  &lt;div class=&quot;desc&quot;&gt;
  &lt;p&gt;
Programista, konsultant IT, administrator. 
Pasjonuje się programowaniem oraz administracja systemami (Linux). 
W wolnym czasie zajmuje się zdobywaniem wiedzy z zakresu IT, a szczególnie programowania. 
Obecnie realizuje projekty najczęściej używając frameworków Ruby on Rails oraz jQuery.
  &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;

</content>
   <updated>2010-11-04T00:00:00-07:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="jquery" label="jquery" />
 </entry>
 
 <entry>
   <id>http://devblog.consileon.pl/2010/10/29/zk-ajax-dla-kazdego</id>
   <link href="http://devblog.consileon.pl/2010/10/29/zk-ajax-dla-kazdego"/>
   <title>ZK - Ajax dla każdego</title>
   <content type="html">&lt;p&gt;ZK opiera swoją architekturę na technologii Ajax. Komunikacja klient-serwer z wykorzystaniem Ajax-a w całości realizowana jest przez silnik ZK  i jest niewidoczna/przezroczysta z punktu widzenia aplikacji używającej ZK. To framework decyduje kiedy wysłać żądanie do serwera oraz kiedy i jakie elementy strony odświeżyć. Dodając do tego warstwę komponentów i zdarzeń, którą dostarcza ZK, otrzymujemy interfejs programistyczny znany z frameworków desktopowych (np. Swing). Mamy zatem obiekt kontrolera, którego metody wywoływane są w wyniku akcji użytkownika oraz komponenty GUI, którymi kontroler steruje. Kontroler może bezpośrednio wywoływać metody komponentów, bądź modyfikować obiekty modelu, z których komponenty GUI  korzystają. Zarówno komponenty GUI jaki i obiekty modelu definiują zdarzenia jakie wysyłają w wyniku akcji użytkownika bądź wykonania określonego kodu.
Tworzenie aplikacji w ZK nie różni się zatem znacząco od tworzenia aplikacji desktopowej. Co więcej, ZK oferuje nowoczesne rozwiązania niedostępne w starszych technologiach takich jak Swing (np. wbudowany data binding, język tworzenia interfejsu użytkownika oparty na &lt;a href=&quot;https://developer.mozilla.org/En/XUL&quot;&gt;XUL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tyle teorii, zobaczmy jak wygląda kod aplikacji opartej o ZK analizując konkretne przykłady.&lt;/p&gt;

&lt;h2&gt;Ajax w akcji&lt;/h2&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PersonListCtrl&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GenericForwardComposer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

 &lt;span class=&quot;c1&quot;&gt;//GUI components autowired by ZK&lt;/span&gt;
 &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Listbox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;personsList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Textbox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Combobox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Checkbox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;married&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

 &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onAddPerson&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;personBean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;personBean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setFirstname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;personBean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setColor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;personBean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setMarried&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;married&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isChecked&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;//save person to database and add it to the listbox&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;addPersonRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;personBean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;addPersonRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Listitem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPersonItem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Listitem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Listbox&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;geItems&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newPersonItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;W powyższym przykładzie kontroler w metodzie obsługującej zdarzenie &lt;code&gt;onAddPerson&lt;/code&gt; tworzy obiekt osoby (obiekt klasy Person) i wypełnia go danymi wprowadzonymi na stronie przez użytkownika. Dane te odczytywane są bezpośrednio z komponentów GUI (textbox, combobox, checkbox). ZK umożliwia wstrzyknięcie komponentów GUI do kontrolera na podstawie zgodności nazwy zmiennej w kontrolerze i atrybutu id komponentu GUI.
Po zapisaniu obiektu osoby do bazy, wywoływana jest metoda &lt;code&gt;addPersonRecord&lt;/code&gt;, w której nazwa osoby (String) dodawana jest do listy wyświetlanej na ekranie (komponent &lt;code&gt;personsList&lt;/code&gt; klasy Listbox). Rezultatem modyfikacji komponentu Listbox będzie odświeżenie listy na stronie przeglądarki. Dzięki technologii Ajax odświeżony zostanie tylko fragment strony zawierający listę osób.
Jak widać, mechanizm odświeżania działa automatycznie i nie wymaga żadnej ingerencji ze strony obiektu kontrolera.&lt;/p&gt;

&lt;h2&gt;Inteligentny model&lt;/h2&gt;

&lt;p&gt;Komponenty GUI przeznaczone do wyświetlania/edycji danych (listbox, combobox, grid, tree) mogą być powiązane z zewnętrznym obiektem modelu przechowującym dane. Rezultatem użycia modelu jest odseparowanie logiki związanej z tworzeniem/modyfikacją danych od logiki ich wyświetlania (zgodnie z wzorcem &lt;strong&gt;MVC&lt;/strong&gt;). Dla komponentu Listbox przewidziano interfejs ListModel i dostarczono wiele implementacji tego interfejsu przeznaczonych do obsługi różnych typów kolekcji np. ListModelArray, ListModelList, ListModelSet, ListModelMap. Po przypisaniu modelu do listy, kontroler nie musi odwoływać się bezpośrednio do komponentu listy. Wszelkie operacje (dodanie, usunięcie elementu, zmiana selekcji) dokonuje na obiekcie modelu:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;addPersonRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ListModelList&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getModel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Model w momencie modyfikacji (dodanie/usunięcie elementu) generuje zdarzenie, w którym zawarta jest szczegółowa informacja o zaistniałej zmianie. Komponent listy posiada obiekt nasłuchujący na zdarzenia pochodzące z modelu, dzięki czemu jest informowany o tym kiedy i w jakim zakresie musi dokonać odświeżenia swojego stanu. Modyfikacje w warstwie modelu są zatem automatycznie odzwierciedlane na stronie (w przeglądarce).&lt;/p&gt;

&lt;h2&gt;Widok&lt;/h2&gt;

&lt;p&gt;Logiką wyświetlenia danych dostarczonych przez model zajmuje się &lt;strong&gt;renderer&lt;/strong&gt;. W naszym przykładzie metoda &lt;code&gt;render&lt;/code&gt;, którą musi zaimplementować renderer listy wygląda następująco:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Listitem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;personName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setLabel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;personName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Tworzenie własnego renderera zwykle nie jest jednak konieczne. Prostszą i efektywniejszą metodą jest użycie mechanizmów bindowania w kodzie strony:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;listbox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;model=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{controller.model}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;listitem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;self=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{each=&amp;#39;person&amp;#39;}&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{person.name}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/listbox&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Stosując &lt;strong&gt;data binding&lt;/strong&gt; w wygodny sposób tworzymy powiązanie między komponentami GUI i danymi z modelu, które te komponenty mają wyświetlać.
W podanym przykładzie lista nie jest skomplikowana, wyświetla bowiem tylko nazwę osoby. Jednak stworzenie bardziej skomplikowanej listy (np. dodanie większej ilości kolumn, zagnieżdżenie w wierszu innych komponentów) nie stanowi najmniejszego problemu. Przykład poniżej:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;listbox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;model=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{controller.model}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;listitem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;self=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{each=&amp;#39;person&amp;#39;}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;listcell&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{person.name}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;listcell&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;combobox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{person.color}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;comboitem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Red&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;comboitem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Blue&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;/combobox&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/listcell&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;listcell&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;checkbox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;checked=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{person.married}&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Are you married?&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/listcell&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/listitem&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/listbox&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;MVC bez limitów&lt;/h2&gt;

&lt;p&gt;Jak już wspomniałem, przedstawiony powyżej przykład to realizacja dobrze znanego wszystkim programistom wzorca MVC (Model-View-Controller).
Dzięki jego zastosowaniu kontroler odwołuje się tylko do modelu, a stan modelu odzwierciedlany jest po stronie widoku.
Zauważmy, że widok może odzwierciedlać różnego rodzaju informacje biznesowe, nie tylko dane przeznaczone do wyświetlenia w tabeli, liście czy drzewie. Na przykład może to być informacja o uprawnieniach zalogowanego użytkownika. W zależności czy użytkownik posiada określone uprawnienia czy nie, stan aktywności poszczególnych kontrolek (np. przycisków akcji) może być inny. Co więcej na stan aktywności niektórych kontrolek może wpływać więcej niż jedna informacja.&lt;/p&gt;

&lt;p&gt;Jak zrealizować zatem taką funkcjonalność nie łamiąc zasad MVC czyli nie manipulując właściwościami komponentów GUI z poziomu kontrolera?
Jak się okazuje, technologia &lt;strong&gt;data binding&lt;/strong&gt; dostępna w ZK w zupełności wystarcza do realizacji tego typu wymagań.&lt;/p&gt;

&lt;p&gt;Załóżmy zatem, że wymaganiem jest aby przycisk &lt;code&gt;Delete&lt;/code&gt; znajdujący się pod listą był aktywny tylko jeśli użytkownik posiada odpowiednie uprawnienia oraz został wybrany element na liście.
W modelu przechowywać będziemy obiekt klasy Person, który reprezentuje aktualnie wybraną z listy osobę oraz obiekt klasy AccessMode reprezentujący
tryb dostępu do danych (VIEW / EDIT) dla bieżącej strony dla aktualnie zalogowanego użytkownika.&lt;/p&gt;

&lt;p&gt;Dane modelu będziemy przechowywać w kontrolerze (alternatywnie moglibyśmy umieścić je w jakiejś oddzielnej klasie modelu, do której nasz widok miałby dostęp).
Mamy zatem następujące pola w kontrolerze:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccessMode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessMode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Tworzymy, metodę która zwróci nam informację o tym czy akcja &lt;code&gt;Delete&lt;/code&gt; jest dostępna dla zalogowanego użytkownika:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isDeleteDisabled&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessMode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AccessMode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;VIEW&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;W następujący sposób tworzymy przycisk &lt;code&gt;Delete&lt;/code&gt;, którego stan (enabled/disabled) chcemy powiązać z danymi znajdującymi się w modelu:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;disabled=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{controller.deleteDisabled, load-after=&amp;#39;listBox.onSelect&amp;#39;}&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Delete&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Atrybutem &lt;strong&gt;load-after&lt;/strong&gt; specyfikujemy kiedy komponent powinien odświeżyć swój stan. W naszym przypadku odświeżenie musi nastąpić  po każdorazowej zmianie selekcji na komponencie listy.&lt;br/&gt;
Zwracam uwagę, iż atrybut &lt;code&gt;load-after&lt;/code&gt; jest konieczny gdyż, ani model ani kontroler nie informują widoku o zmianie danych.
Jest to wygodne rozwiązanie, widok aktualizuje się automatycznie w momencie wystąpienia określonego zdarzenia (Więcej o zdarzeniach w ZK napisałem w artykule &lt;a href=&quot;http://devblog.consileon.pl/2010/10/29/zk-tworzenie-aplikacji-sterowanej-zdarzeniami&quot;&gt;ZK - tworzenie aplikacji sterowanej zdarzeniami&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;Podsumowanie&lt;/h2&gt;

&lt;p&gt;Chciałbyś napisać aplikację internetową z użyciem Ajax-a i nie wiesz jak się za to zabrać? Dotychczas tworzyłeś aplikacje desktopowe i nie chcesz uczyć się wszystkiego od początku?. Framework ZK będzie dla Ciebie idealnym rozwiązaniem. Tak reklamuje się ZK i jak pokazałem w powyższym artykule, nie ma w tym wiele przesady.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://pkaczor.blogspot.com/2010/10/zk-ajax-dla-kazdego.html&quot;&gt;http://pkaczor.blogspot.com/2010/10/zk-ajax-dla-kazdego.html&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;bio&quot;&gt;
  &lt;p&gt;Autor: &lt;a href=&quot;mailto:pawel.kaczor@consileon.pl&quot;&gt;Paweł Kaczor&lt;/a&gt;&lt;/p&gt;
  &lt;div class=&quot;foto&quot;&gt;
      &lt;img src=&quot;/images/pawel_kaczor.png&quot; alt=&quot;Paweł Kaczor&quot;/&gt;
  &lt;/div&gt;
  &lt;div class=&quot;desc&quot;&gt;
  &lt;p&gt;
    Programista, pasjonat, konsultant IT. Interesuje się nowoczesnymi framework-ami webowymi takimi jak: Seam, ZK.
    Obecnie w wolnym czasie poznaje język Scala.
  &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;



</content>
   <updated>2010-10-29T00:00:00-07:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="zk" label="zk" />
   <updated>2010-10-29T00:00:00-07:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="framework" label="framework" />
   <updated>2010-10-29T00:00:00-07:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="mvc" label="mvc" />
 </entry>
 
 <entry>
   <id>http://devblog.consileon.pl/2010/10/28/zk-tworzenie-aplikacji-sterowanej-zdarzeniami</id>
   <link href="http://devblog.consileon.pl/2010/10/28/zk-tworzenie-aplikacji-sterowanej-zdarzeniami"/>
   <title>ZK - tworzenie aplikacji sterowanej zdarzeniami</title>
   <content type="html">&lt;p&gt;Większość frameworków/bibliotek odpowiedzialnych za obsługę interfejsu użytkownika definiuje zdarzenia i dostarcza szereg mechanizmów służących do przechwytywania i przetwarzania zdarzeń.
Komponenty GUI oferowane przez te biblioteki specyfikują jakie zdarzenia i kiedy są przez nie generowane oraz na jakie zdarzenia i w jakim celu nasłuchują. Zdarzenia to nie tylko akcje wykonane przez użytkownika takie jak kliknięcie myszką na przycisk, wybór elementu na liście czy naciśnięcie klawisza Enter w polu tekstowym. Komponenty używają bowiem zdarzeń również do komunikacji między sobą dzięki czemu powiązania między komponentami są luźne. Jest to jeden z ważnych czynników  zwiększających elastyczność architektury systemu.&lt;/p&gt;

&lt;p&gt;Dlaczego zatem nie wprowadzić zdarzeń do warstwy logiki biznesowej? Nie jest to nowa koncepcja, aczkolwiek rzadko spotykana w typowych aplikacjach biznesowych. Wynika to m.in. z braku odpowiedniego wsparcia ze strony frameworków aplikacyjnych. Z nadejściem JEE 6 sytuacja może się zmienić. Specyfikacja CDI definiuje prosty w użyciu model generowania i odbierania zdarzeń (podobny model od dawna oferuje Seam). Model ten jest uniwersalny i może mieć zastosowanie w różnych obszarach/warstwach aplikacji.
W poniższym artykule zaprezentuję rozwiązanie jakie w tym zakresie oferuje &lt;a href=&quot;http://www.zkoss.org&quot;&gt;framework ZK&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Przykładowa aplikacja&lt;/h2&gt;

&lt;p&gt;W typowej aplikacji biznesowej mamy zwykle do zaimplementowania obsługę grupy operacji określanej skrótem CRUD (tworzenie, zmiana, usuwanie) wykonywanych przez użytkownika na różnych obiektach biznesowych. W naszej przykładowej aplikacji zamodelujmy obsługę ról użytkowników. Mamy zatem ekran z listą ról oraz ekran edycji/tworzenia roli wyświetlany w oknie dialogowym. Rysunki poniżej.&lt;/p&gt;

&lt;h2&gt;Zdarzenie biznesowe&lt;/h2&gt;

&lt;p&gt;Zdarzenie w ZK definiowane jest jako obiekt klasy &lt;strong&gt;Event&lt;/strong&gt; posiadający nazwę (name). Dodatkowo zdarzenie może mieć przypisany komponent docelowy (target) oraz dowolny obiekt z danymi (data).&lt;/p&gt;

&lt;p&gt;Zdefiniujmy zatem zdarzenia biznesowe, które chcielibyśmy obsłużyć w naszej aplikacji:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;onEdit - użytkownik wykonał akcję mającą na celu przejście do trybu edycji wybranego obiektu biznesowego&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;onAdd - użytkownik wykonał akcję mającą na celu przejście do trybu edycji nowego obiektu biznesowego&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;onDelete - użytkownik wykonał akcję mającą na celu usunięcie wybranego obiektu biznesowego&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;onSave - użytkownik wykonał akcję mającą na celu zapisanie zmian (modyfikacja bądź utworzenie obiektu biznesowego)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Przypisanie zdarzenia biznesowego do akcji użytkownika&lt;/h2&gt;

&lt;p&gt;W naszej aplikacji edycja następuje po kliknięciu na element listy, a tworzenie elementu po naciśnięciu przycisku znajdującego się pod listą. W obu przypadkach nastąpi wyświetlenie okna edycji (popup).&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;window&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;windowRoleList&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;apply=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;${roleListCtrl}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;listbox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;model=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{controller.listModel}&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;selectedItem=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{controller.selected}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;listitem&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;self=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{each=&amp;#39;role&amp;#39;}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;listcell&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{role.name}&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;forward=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;onClick=onEdit&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/listcell&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;listcell&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{role.description}&amp;quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;/listitem&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/listbox&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;toolbar&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;btnAdd&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;forward=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;onClick=onAdd&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;btnDelete&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;forward=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;onClick=onDelete&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/toolbar&amp;gt;&lt;/span&gt;
 &lt;span class=&quot;nt&quot;&gt;&amp;lt;/window&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Zwróćmy uwagę na atrybuty &lt;strong&gt;forward&lt;/strong&gt; zdefiniowane dla linku (element a w wierszu listy) i przycisku pod listą (element button).&lt;br/&gt;
W atrybucie forward podajemy nazwę zdarzenia jakie zostanie propagowane w górę drzewa komponentów. Propagowanie odbywa się poprzez utworzenie nowego zdarzenia (o nazwie podanej po znaku równości) zawierającego zdarzenie źródłowe (o nazwie podanej przed znakiem równości). W naszym przypadku zdarzeniem źródłowym jest zdarzenie systemowe &lt;code&gt;onClick&lt;/code&gt; (kliknięcie na przycisk/link). W przypadku nie podania zdarzenia źródłowego, przyjmowane jest domyślne, różne w zależności od komponentu dla jakiego specyfikujemy atrybut forward.
W przypadku linka i przycisku jest to zdarzenie &lt;code&gt;onClick&lt;/code&gt;. Zatem oba zapisy są tożsame:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;forward=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;onClick=onAdd&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;forward=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;onAdd&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Zdefiniowaliśmy zatem, po jakiej akcji użytkownika nastąpi określone zdarzenie biznesowe.
Odseparowaliśmy tym samym logikę interfejsu użytkownika od logiki biznesowej. Gdybyśmy dla innego obiektu biznesowego edycję chcieli przeprowadzić nie w osobnym oknie, lecz w tym samym, w którym znajduje się lista wyboru, zdarzenie &lt;code&gt;onEdit&lt;/code&gt; moglibyśmy zdefiniować jako następstwo wyboru elementu na liście:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;listbox&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;model=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{controller.listModel}&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;selectedItem=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{controller.selected}&amp;quot;&lt;/span&gt; 
   &lt;span class=&quot;na&quot;&gt;forward=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;onSelect=onEdit&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;W obu przypadkach logika obsługi zdarzenia &lt;code&gt;onEdit&lt;/code&gt; będzie taka sama.
Przyjrzyjmy się zatem jak obsłużyć zdarzenie w kodzie aplikacji.&lt;/p&gt;

&lt;h2&gt;Obsługa zdarzenia&lt;/h2&gt;

&lt;p&gt;Zdarzenie propagowane jest aż do komponentu okna (window) i następnie zostaje przekazane do kontrolera podpiętego pod to okno (kontroler definiujemy atrybutem &lt;b&gt;apply&lt;/b&gt;). W naszym przypadku kontroler jest bean-em zarządzanym przez Springa o nazwie &lt;code&gt;roleListCtrl&lt;/code&gt; (użycie Spring-a jest opcjonalne).&lt;/p&gt;

&lt;p&gt;Kontroler musi zdefiniować metodę odpowiadającą nazwie zdarzenia:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onEdit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Wiemy zatem, w jaki sposób nasze zdarzenie biznesowe może być utworzone i obsłużone w wyniku akcji podjętej przez użytkownika.
Spyta ktoś, czym to rozwiązanie różni się od klasycznego podejścia używanego w innych frameworkach np. Seam, gdzie możemy analogicznie wywołać metodę w kontrolerze:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;#{controller.onEdit()}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Rozpatrzmy różnice między oboma podejściami z punktu widzenia interfejsu wywołania/obsługi komunikatu :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Seam&lt;/strong&gt;: w metodzie możemy przekazywać dowolną ilość argumentów, dowolnego typu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ZK&lt;/strong&gt;: W zdarzeniu możemy przekazać jeden obiekt danych (możemy np. przekazać wybraną rolę:  &lt;code&gt;forward=onEdit(role)&lt;/code&gt;) . Typem danych jest zawsze Object. Porównując zatem z wywołaniem metody, mamy ograniczoną ilość argumentów i konieczność rzutowania typu po stronie kontrolera. Zwykle jednak nie ma konieczności przekazywania większej liczby argumentów (w naszym przykładzie nie musimy przekazywać roli w zdarzeniu, gdyż framework potrafi wstrzyknąć do kontrolera wybraną rolę automatycznie).
Podkreślić należy, że oba problemy wynikają z ograniczeń jakie niesie ze sobą propagowanie zdarzeń przy przy pomocy atrybutu forward. Nie istnieje tutaj możliwość stworzenia własnej klasy zdarzenia. Gdy wysyłamy zdarzenie w kodzie aplikacji (przykład później),problemy powyższe nie istnieją.&lt;/p&gt;

&lt;p&gt;Przejdźmy w końcu do przykładów, gdzie zdarzenia zaczynają pokazywać swoją moc:)&lt;/p&gt;

&lt;h2&gt;Zdarzenia w akcji&lt;/h2&gt;

&lt;p&gt;W przypadku gdy wyświetlamy kilka okien na stronie możemy używać zdarzeń do komunikacji między oknami sterując w ten sposób logiką aplikacji.&lt;/p&gt;

&lt;p&gt;Dodajmy na naszej stronie z listą ról nowy panel z listą użytkowników. Lista ról niech wyświetla tylko role dla wybranego użytkownika (z możliwością ich edycji). Obie listy umieszczamy w oddzielnych oknach (komponentach window) dzięki czemu obie listy możemy obsługiwać oddzielnymi kontrolerami (w obu oknach chcemy obsłużyć logikę CRUD).
Odświeżenie listy ról po wybraniu użytkownika obsługujemy następująco:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;c1&quot;&gt;//Kontroler użytkowników&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onSelect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//inform role list window on selection change&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roleListWindow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFellow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;windowRoleList&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Events&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;postEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;onUserSelected&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roleListWindow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getSelected&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()));&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;c1&quot;&gt;//Kontroler ról&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onUserSelected&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;selectedUser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;refreshList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Dzięki temu, że zdefiniowaliśmy zdarzenie &lt;code&gt;onUserSelected&lt;/code&gt;,  możemy zarejestrować słuchaczy obserwujących to zdarzenia w celu zaimplementowania dodatkowej logiki. Jako przykład wykorzystam wbudowany w ZK mechanizm sterowania bindowaniem danych. Co to jest bindowanie? Jest to możliwość bezpośredniego połączenia warstwy modelu (danych) z kontrolkami wyświetlającymi/modyfikującymi te dane. W ZK bindowanie może obejmować dowolne właściwości kontrolek np. stan kontrolki (czy kontrolka jest aktywna czy nie (enabled/disabled)).
Dodajmy zatem wymaganie w naszej aplikacji aby usunięcie roli było możliwe tylko dla użytkownika nowo utworzonego (dla którego pole registered = false). Musimy zatem przy zmianie selekcji użytkownika deaktywować bądź aktywować przycisk Delete pod listą ról.
Korzystając z możliwości mechanizmu bindowania ZK konfigurujemy właściwość &lt;code&gt;disabled&lt;/code&gt; dla przycisku Delete:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;btnDelete&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;forward=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;onDelete&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;disabled=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;@{controller.selectedUser.registered, load-after=&amp;#39;windowRoleList.onUserSelected&amp;#39;}&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Użycie &lt;strong&gt;@&lt;/strong&gt; w wyrażeniu oznacza zastosowanie bindingu. W naszym przypadku odczytujemy pole &lt;code&gt;registered&lt;/code&gt; z obiektu użytkownika przekazanego uprzednio do kontrolera (patrz kod wyżej). Za pomocą parametru &lt;code&gt;load-after&lt;/code&gt;, wskazujemy zdarzenie, po nastąpieniu którego nastąpi odświeżenie przycisku (odczytanie danych z modelu). Framework automatycznie zarejestruje odpowiedniego listenera w komponencie naszego okna nasłuchującego na zdarzenie &lt;code&gt;onUserSelected&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Zauważmy co zyskujemy. Kod obsługujący zdarzenie onUserSelected jest czysty. Modyfikujemy w nim tylko stan modelu, czyli ustawiamy zmienną selectedUser i odświeżamy listę ról (model). Zarówno przy odświeżaniu listy ról jak i odświeżaniu przycisku na stronie zostanie odczytane pole selectedUser. Kontroler nie musi wiedzieć kiedy odświeżyć przycisk, nie musi nawet wiedzieć o jego istnieniu.&lt;/p&gt;

&lt;h2&gt;Zdarzenia globalne&lt;/h2&gt;

&lt;p&gt;Przedstawione dotychczas zdarzenia były wysyłane do konkretnego komponentu. Nie zawsze jest to pożądane. Jedną z podstawowych cech architektur sterowanych zdarzeniami jest możliwość niezależnego wysyłania i odbierania zdarzeń. Emitent zdarzenia nie musi znać odbiorców. Odbiorca nie musi wiedzieć skąd zdarzenie pochodzi. Jak tę funkcjonalność zrealizować w ZK pokaże znowu na konkretnym przykładzie.
Dodajmy na górze naszej strony panel zawierający nazwę zalogowanego użytkownika. Pojawia się kwestia odświeżenia zawartości panelu w momencie modyfikacji nazwy aktualnie zalogowanego użytkownika. W kontrolerze obsługującym okno edycji użytkownika implementujemy obsługę zdarzenia &lt;code&gt;onSave&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onSave&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//save user&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;....&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//inform potential listeners&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;EventQueue&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lookup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventQueues&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;QUEUE_GLOBAL&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserChangedEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;onUserChanged&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Wysyłamy zatem zdarzenie &lt;code&gt;onUserChanged&lt;/code&gt; bez adresata do kolejki o zdefiniowanej przez nas nazwie &lt;code&gt;QUEUE_GLOBAL&lt;/code&gt;.
Zauważmy, że tym razem zdefiniowaliśmy własną klasę zdarzenia (&lt;code&gt;UserChangedEvent&lt;/code&gt;), w której przekazujemy obiekt użytkownika. Standardowo, rejestracja w kontrolerze słuchacza zdarzeń z tej kolejki wygląda w następujący sposób:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;c1&quot;&gt;//kontroler nagłówka&lt;/span&gt;
 &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onCreate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EventQueues&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;QUEUE_GLOBAL&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventQueues&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DESKTOP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;//process event &lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Rejestrację można uprościć i jednocześnie ułatwić kontrolerowi obsługę zdarzenia tworząc klasę pośrednicząca w odbieraniu i przekazywaniu zdarzeń do wybranego przez nas komponentu (nazwijmy ją &lt;code&gt;EventsManager&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EventsManager&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EventQueues&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventQueues&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DESKTOP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;Events&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;sendEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;});&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Teraz w kontrolerze nagłówka możemy w sposób standardowy zaimplementować obsługę zdarzenia &lt;code&gt;onUserChanged&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// kontroler nagłówka&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onCreate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;//register self (window component) as listener of events from QUEUE_GLOBAL&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EventsManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;QUEUE_GLOBAL&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onUserChanged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UserChangedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;eqauls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getUserWorkspace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getLoggedInUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;c1&quot;&gt;//refresh component displaying user name&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;   
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Używając zdarzeń globalnych uwalniamy emitenta zdarzenia od konieczności znajdowania komponentu docelowego. Jednocześnie uwalniamy odbiorcę od rejestracji nasłuchiwania w konkretnym komponencie. Wystarczy, że obie strony uzgodnią kanał (kolejkę) komunikacji.
Zdarzenia globalne domyślnie wysyłane i odbierane są w kontekście strony przeglądarki (desktop-level).&lt;br/&gt;
ZK pozwala również funkcjonować zdarzeniom w kontekście całej aplikacji. Dzięki temu strona może zostać odświeżona pomimo braku akcji ze strony zalogowanego użytkownika (np. na skutek operacji wykonanej przez scheduler-a uruchomionego na serwerze). Umożliwia to technologia &lt;strong&gt;Push Server&lt;/strong&gt;, oferowana wewnątrz ZK.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://pkaczor.blogspot.com/2010/10/zk-tworzenie-aplikacji-sterowanej.html&quot;&gt;http://pkaczor.blogspot.com/2010/10/zk-tworzenie-aplikacji-sterowanej.html&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;bio&quot;&gt;
  &lt;p&gt;Autor: &lt;a href=&quot;mailto:pawel.kaczor@consileon.pl&quot;&gt;Paweł Kaczor&lt;/a&gt;&lt;/p&gt;
  &lt;div class=&quot;foto&quot;&gt;
      &lt;img src=&quot;/images/pawel_kaczor.png&quot; alt=&quot;Paweł Kaczor&quot;/&gt;
  &lt;/div&gt;
  &lt;div class=&quot;desc&quot;&gt;
  &lt;p&gt;
    Programista, pasjonat, konsultant IT. Interesuje się nowoczesnymi framework-ami webowymi takimi jak: Seam, ZK.
    Obecnie w wolnym czasie poznaje język Scala.
  &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;



</content>
   <updated>2010-10-28T00:00:00-07:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="zk" label="zk" />
   <updated>2010-10-28T00:00:00-07:00</updated>
   <category scheme="http://devblog.consileon.pl/" term="framework" label="framework" />
 </entry>
 
</feed>
