09 lipca 2007

Java Persistence - Rozdziały 5.5 Zarządzanie transakcjami oraz 5.6 Konteksty trwałe zarządzane przez serwer

4 komentarzy
Kolejne rozdziały JPA tym razem dotyczące transakcji, więc nie jest lekko. Wydaje mi się, że temat transakcji zazwyczaj traktowany jest po macoszemu i wielu (wliczając mnie) odetchnęło z ulgą dowiedziawszy się, że w Java EE istnieje możliwość deklaratywnego oznaczania miejsc ich rozpoczęcia. Podobnie ma się sprawa z usługą bezpieczeństwa. Bezpieczeństwo i transakcyjność to dwie z wielu usług, które dostarcza serwer aplikacyjny Java EE i właśnie ich dostępność i prostota konfiguracji i użycia jest argumentem za jego stosowaniem (ponowie uwaga do użytkowników Spring Framework - można zestawić podobny zestaw usług, ale czy warto się tym parać?). Pora rozpoznać temat integracji usług w JPA.

Rozdział 5.5 Zarządzanie transakcjami

W zależności od typu transakcyjnego zarządcy trwałego, transakcje związane z operacjami zarządcy mogą być zarządzane przez JTA albo przez egzemplarz (resource-local) EntityTransaction. Użycie terminu angielskiego - resource-local - lokalny względem zasobu, który uczestniczy w transakcji, idealnie oddaje późniejszą konfigurację jednostki trwałej (PU). O tym później.

Egzemplarz EntityTransaction odpowiada transakcji związanej z zasobem, który z kolei związany jest z encjami zarządzanymi przez zarządcę trwałego.

Zarządca trwały, którego transakcje zarządzane są przez JTA nazywany jest zarządcą trwałym JTA (ang. JTA entity manager).

Zarządca trwały, którego transakcje zarządzane są przez aplikację przez interfejs EntityTransaction nazywany jest lokalnym zarządcą trwałym (ang. resource-local entity manager).

Zarządca trwały zarządzany przez kontener (serwer aplikacyjny Java EE) musi być zarządcą trwałym JTA. Zarządcy trwali JTA są określeni jedynie dla wykorzystania w środowisku serwera aplikacyjnego Java EE.

Zarządca trwały zarządzany przez aplikację może być JTA lub lokalny.

Określenie typu transakcyjnego zarządcy trwałego odbywa się podczas konstruowania fabryki zarządcy trwałego.

W środowisku kontenerów aplikacji internetowych oraz EJB wymagane jest wsparcie dla zarządcy encji JTA oraz lokalnego. W ramach kontenera EJB zazwyczaj wykorzystywany jest zarządca encji JTA. W środowisku Java SE wymagane jest jedynie wsparcie dla lokalnych zarządców encji.

Zarządca encji JTA uczestniczy w transakcji JTA, która rozpoczyna się i jest zatwierdzana zewnętrznie w stosunku do zarządcy encji i jest przekazana do zarządcy zasobów związanego z encjami uczestniczącymi w transakcji.

Lokalny zarządca encji może korzystać z zasobów serwera bądź lokalnych w celu nawiązania połączenia do bazy danych i jest nieświadomy istnienia transakcji JTA, które mogą być aktywne w danej chwili.

Interfejs EntityTransaction dostarcza metody do zarządzania transakcjami w trybie lokalnym. Metoda EntityManager.getTransaction() zwraca interfejs EntityTransaction. W momencie wystąpienia wyjątku, który oznaczony jest jako wycofujący transakcję, lokalny zarządca encji musi oznaczyć transakcję jako wyłącznie do wycofania.

Jeśli wywołanie EntityTransaction.commit() zakończy się wyjątkiem, dostawca JPA musi wycofać transakcję.

Specyfikacja przedstawia opis interfejsu javax.persistence.EntityTransaction.

@Test
public void testEntityTransactionAPI() {
Osoba encja = new Osoba("Jacek", "Laskowski");
try {
em.persist(encja);
// Podczas flush nastąpi przesłanie zmian stanu encji do bazy danych
em.flush();
assert false : "Oczekiwano wyjątku javax.persistence.TransactionRequiredException, gdyż nie rozpoczęto transakcji";
} catch (TransactionRequiredException oczekiwany) {
}
EntityTransaction et = em.getTransaction();
assert et.isActive() == false : "Transakcja nie powinna już być aktywna - nie wywołano begin()";
et.begin();
assert et.isActive() : "Transakcja musi już być aktywna - wywołano begin()";
assert et.getRollbackOnly() == false : "Transakcja musi być w stanie umożliwiającym jej zatwierdzenie";
et.setRollbackOnly();
assert et.getRollbackOnly() : "Transakcja musi być w stanie wykluczającym jej zatwierdzenie";
em.persist(encja);
try {
// Podczas zatwierdzania transakcji nastąpi przesłanie zmian stanu encji do bazy danych
et.commit();
assert false : "Oczekiwano wyjątku javax.persistence.RollbackException, gdyż transakcja oznaczona jedynie do wycofania";
} catch (RollbackException oczekiwany) {
}
}

W sekcji 5.5.3 Example (strona 120) przedstawiono kompletny przykład aplikacji korzystającej z zarządcy encji zarządzanego przez aplikację w trybie lokalnym.

5.6 Kontekst trwały zarządzany przez kontener (serwer)

Użycie zarządcy encji zarządzanego przez kontener nakłada na serwer konieczność zarządzania stadiami rozwojowymi kontekstu trwałego w sposób niezauważalny dla aplikacji. Serwer zarządza transakcjami związanymi z zarządcą za pomocą JTA.

Kontekst trwały zarządzany przez kontener może trwać tak długo jak aktywna jest pojedyńcza transakcja bądź być związany z wieloma transakcjami. Związek między długością życia kontekstu trwałego a transakcjami określany jest przez typ wyliczeniowy javax.persistence.PersistenceContextType, który definiowany jest podczas tworzenia zarządcy trwałego. Specyfikacja odnosi się do tych typów zarządców trwałych jako kontekst trwały pojedyńczej transakcji (ang. transaction-scoped persistence context) lub kontekst trwały rozszerzony (ang. extended persistence context), odpowiednio.

Długość życia kontekstu trwałego jest zadeklarowana używając adnotacji @PersistenceContext lub poprzez element persistence-context-ref w deskryptorze aplikacji. Domyślnie zakłada się użycie PersistenceContextType.TRANSACTION.

@javax.persistence.PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager em;

Wykorzystanie elementu persistence-context-ref w deskryptorze aplikacji - WEB-INF/web.xml lub META-INF/ejb-jar.xml (przykład za specyfikacją Java EE sekcja EE.5.12 Persistence Unit References strona 100).

<persistence-context-ref>
<description>Persistence context for the inventory management application.</description>
<persistence-context-ref-name>persistence/InventoryAppDB</persistence-context-ref-name>
<persistence-unit-name>InventoryManagement</persistence-unit-name>
<persistence-context-type>Extended</persistence-context-type>
</persistence-context-ref>

Wartości elementu persistence-context-type to wartości Transaction lub Extended. Jedynym źródłem informacji o elemencie to dokument dostępny pod adresem http://java.sun.com/xml/ns/javaee/javaee_5.xsd.

5.6.1 Kontekst trwały zarządzany przez kontener pojedyńczej transakcji

Kontekst trwały zawsze związany jest z fabryką zarządców encji.

Aplikacja może uzyskać dostęp do zarządcy encji zarządzanego przez kontener, pojedyńczej transakcji, w trybie JTA (ciekawie prezentuje się opis w języku angielskim - a container-managed entity manager with transaction-scoped persistence context bound to the JTA transaction) poprzez mechanizm DI lub odszukanie w JNDI.

Zarządca encji pojedyńczej transakcji jest założony domyślne (w przypadku braku elementu type adnotacji @PersistenceContext) bądź zdefiniowany przez PersistenceContextType.TRANSACTION.

Nowy kontekst trwały rozpoczyna się, podczas pierwszego wywołania operacji na zarządcy encji (interfejs EntityManager) zarządzanym przez kontener w zasięgu aktywnej transakcji JTA i tylko wtedy, kiedy nie istnieje zarządca trwały już związany z aktywną transakcją. Kontekst trwały jest tworzony i związywany natychmiast z aktywną transakcją JTA.

Kontekst trwały kończy się, podczas zakończenia związanej transakcji JTA (zatwierdzenie bądź wycofanie) i kiedy wszystkie encje związane z danym zarządcą trwałym, z którym związany jest kontekst, przejdą w stan odłączony (ang. detached).

Jeśli zarządca trwały jest wywołany poza zasięgiem aktywnej transakcji, encja utworzona na podstawie danych z bazy danych przechodzi natychmiast w stan odłączony.

5.6.2 Rozszerzony kontekst trwały zarządzany przez kontener (serwer)

Rozszerzony kontekst trwały zarządzany przez kontener może być jedynie utworzony w ramach stanowego ziarna sesyjnego EJB. Kontekst istnieje od momentu, w którym utworzone zostanie ziarno, które deklaruje zależność od zarządcy encji typu PersistenceContextType.EXTENDED i określa się go mianem przypisany (ang. bound) do stanowego ziarna sesyjnego EJB. Zależność na rozszerzonym kontekście trwałym jest deklarowana poprzez adnotację @PersistenceContext lub element persistence-context-ref w deskryptorze.

Kontekst trwały jest zamykany po pomyślnym wywołaniu metody stanowego ziarna EJB udekorowanej przez adnotację @Remove lub jeśli egzemplarz ziarna zostanie zniszczony w inny sposób, np. niektóre metody zakończone wyjątkiem są sygnałem dla kontenera o bezużyteczności egzemplarza ziarna, które są następnie niszczone.

Jeśli stanowe ziarno tworzy (w dowolny sposób - DI lub JNDI) inne stanowe ziarno, które ma zadeklarowaną zależność od rozszerzonego kontekstu trwałego, rozszerzony kontekst trwały pierwszego ziarna jest dziedziczony przez (przekazane do) drugiego ziarna i zostaje przypisane do niego, i tak kolejno do następnych ziaren stanowych EJB bez względu na istnienie aktywnej transakcji w trakcie tworzenia ziaren (dla przypomnienia utworzone w ten sposób encje na bazie danych z bazy danych są natychmiast w stanie odłączony).

Jeśli kontekst trwały został odziedziczony przez ziarna stanowe EJB, kontener zamknie kontekst dopiero, kiedy wszystkie ziarna zostaną usunięte (z potencjalnym wywołaniem metody @Remove) lub w inny sposób zniszczone.

5.6.3 Przekazywanie kontekstu trwałego

Pojedyńczy kontekst trwały może odpowiadać jednemu lub wielu egzemplarzom zarządców encji JTA (związanym z tą samą fabryką zarządców encji - zarządcy encji pochodzący z różnych fabryk zarządców encji nigdy nie dzielą tego samego kontekstu trwałego).

Kontekst trwały jest przekazywany do egzemplarzy zarządców encji wraz z przekazywaniem transakcji JTA.

Przekazywanie kontekstów trwałych ma miejsce wyłącznie w ramach środowiska lokalnego. Konteksty trwałe nie są przekazywane do zewnętrznych (zdalnych) warstaw.

5.6.3.1 Wymagania związane z przekazywaniem kontekstu trwałego

Przy braku aktywnej transakcji JTA wywołanie komponentu nie wiąże się z przekazaniem kontekstu trwałego. Wywołanie zarządcy encji pojedyńczej transakcji (PersistenceContextType.TRANSACTION) z poziomu tego komponentu wiąże się ze stworzeniem nowego kontekstu trwałego. Wywołanie rozszerzonego zarządcy encji (PersistenceContextType.EXTENDED) wiąże się z użyciem istniejącego rozszerzonego zarządcy encji związanego z tym komponentem. Jeśli zarządca encji jest wywołany w ramach transakcji JTA, kontekst trwały zostanie z nią związany.

Przy wywołaniu komponentu, podczas przekazania transakcji JTA do niego, wyróżniamy 2 sytuacje. Jeśli komponent to stanowe ziarno sesyjne EJB, z którym związany jest rozszerzony kontekst trwały oraz istnieje inny kontekst trwały związany z transakcją JTA, zostanie rzucony wyjątek javax.ejb.EJBException. W przeciwnym przypadku, jeśli istnieje kontekst trwały związany z transakcją JTA, nastąpi przekazanie i korzystanie z tego kontekstu trwałego.

Dla mnie ostatnie sekcje specyfikacji, szczególnie ta związana z przekazywaniem kontekstu trwałego, pozostawia wiele pytań do odpowiedzi. Czas odszukać odpowiedzi w książkach o JPA. Wracam do Pro EJB 3 - Java Persistence API z Apress. Po 2 otwierających rozdziałach książka wydaje się być napisana lekko i przyjemnie.

08 lipca 2007

Java Persistence w Spring Framework wspomaganym przez Eclipse z Spring IDE oraz m2eclipse

2 komentarzy
We wtorek za 2 tygodnie - 17.07.2007 - prowadzę prezentację o Java Persistence API podczas spotkania grupy Warszawa JUG. Nie mam wiele czasu, ale tyle pomysłów, aby zaprezentować JPA, że zacząłem przygotowywania już teraz, aby powiedzieć wiele na temat i w zaplanowanym czasie.

Jedną z rzeczy, które zamierzam pokazać jest integracja JPA z popularnymi szkieletami programistycznymi jak Spring Framework czy JBoss Seam. 1-2 przykłady, kilka(naście) słów i przechodzę do kolejnego tematu. Wymaga to przygotowania przykładów zawczasu i przećwiczenia ich we wszystkich możliwych wariantach, bo kiedy wystąpi wyjątek dobrze jest mieć rozwiązanie w zapasie. Kiedy przygotowywałem poszczególne sekcje prezentacji i zatrzymałem się na Spring Framework natrafiłem na artykuł, który zaplanowałem przeczytać już jakiś czas temu - Introduction to Spring 2 and JPA. Nie mogłem wyobrazić sobie lepszego momentu na lekturę niż właśnie dzisiaj, teraz.

W tym samym czasie pojawiły się nowe wersje narzędzi Eclipse IDE 3.3 oraz Spring IDE 2.0, a poza tym, właśnie podczas rozpoznawania SCA zatrzymałem się na rozpoznawaniu implementation.spring i konstruowaniu aplikacji z jego wykorzystaniem. Najwyższa pora popróbować się z JPA wspartym przez Spring Framework i Spring IDE i utworzyć aplikację do dalszych eksperymentów technologicznych.

Korzystając z Spring IDE tworzę projekt - File > New > Other... > Spring > Spring Project.

, gdzie podaję wymagane informacje, m.in. nazwa projektu to spring-jpa.

Na bazie wspomnianego artykułu tworzę encję Prezenter (pakiet pl.jaceklaskowski.spring.ranking.entity)

package pl.jaceklaskowski.spring.ranking.entity;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Prezenter implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private long id;

private String imie;
private String nazwisko;

public Prezenter() {
}

public Prezenter(String imie, String nazwisko) {
this.imie = imie;
this.nazwisko = nazwisko;
}

public long getId() {
return id;
}

public String getImie() {
return imie;
}

public void setImie(String imie) {
this.imie = imie;
}

public String getNazwisko() {
return nazwisko;
}

public void setNazwisko(String nazwisko) {
this.nazwisko = nazwisko;
}

}

oraz interfejs usługi zarządzania prezenterami PrezenterService.

package pl.jaceklaskowski.spring.ranking;

import java.util.List;

import pl.jaceklaskowski.spring.ranking.entity.Prezenter;

public interface PrezenterService {
public void save(Prezenter prezenter);

public void delete(Prezenter prezenter);

public List<Prezenter> findAll();

public Prezenter findByPrezenterId(long id);
}

Ideą jest utworzenie aplikacji, która mogłaby zamodelować sytuację, w której encja Prezenter jest w relacji 1-do-wielu z encją Prezentacja, która jest w relacji 1-do-wielu z encją Ocena. Rozbudowanie aplikacji pozostawię na później, a w tej notatce zajmę się jedynie sposobem uruchomienia JPA w Spring Framework i to jak najmniejszym wysiłkiem bazując na lekturze artykułu.

Podczas lektury artykułu natrafiłem na kilka elementów, które są dla mnie niezrozumiałe. Dlaczego modeluje się encję Employee z dwoma identyfikatorami - JPA (identyfikator technologiczny) oraz numer (identyfikator biznesowy). Dlaczego public List<Employee> findByEmployeeNumber(String empno) zwraca listę Employee, zamiast pojedyńczą encję Employee? Dlaczego public Employee save(Employee emp) zwraca Employee? I w końcu, dlaczego klasa encji Employee i Address nie implementują interfejsu java.io.Serializable, co jest zalecane (żeby nie napisać jest dobrą praktyką)? Pytania pozostaną zapewne bez odpowiedzi, ale nie ma to większego znaczenia w realizacji postawionego na dzisiaj celu.

Tworzę implementację PrezenterService - PrezenterDAO, która reprezentuje warstwę dostępu do danych (ang. Data Access Object).

package pl.jaceklaskowski.spring.ranking;

import java.util.List;

import org.springframework.orm.jpa.support.JpaDaoSupport;

import pl.jaceklaskowski.spring.ranking.entity.Prezenter;

public class PrezenterDAO extends JpaDaoSupport implements PrezenterService {

public void delete(Prezenter prezenter) {
getJpaTemplate().remove(prezenter);
}

public List<Prezenter> findAll() {
return getJpaTemplate().find("select p from Prezenter p");
}

public Prezenter findByPrezenterId(long id) {
return getJpaTemplate().find(Prezenter.class, id);

}

public void save(Prezenter prezenter) {
getJpaTemplate().persist(prezenter);
}

}

I w tym momencie, korzystając z klasy JpaDaoSupport wchodzę na tereny Spring Framework. To jest jeden z tych momentów, w którym znacząco dostrzegam zaletę wprowadzenia systemu kontrolującego zależności i pozwalającego mi na deklaratywne ich definiowanie - Apache Maven 2 (M2) czy Apache Ivy. Skoro tworzę aplikację w IDE, oczekuję wsparcia podświetlenia składni i takie tam. Jednakże jego użyteczność kończy się, w przypadku stosowania odwołań do typów, które nie są dla niego widoczne. Chcę skorzystać z typów dostarczanych przez Spring Framework, ale...nie mam go zainstalowanego na moim komputerze, więc nawet, gdybym chciał to jak miałbym zdefiniować zależność w Eclipse? Pozostaje jedynie zainstalować Springa, ale po co miałbym tracić na to czas? Potrzebna mi wyłącznie biblioteka, a nie cała wersja dystrybucyjna projektu, nieprawdaż? Korzystając z m2 wystarczyłoby przecież zadeklarować zależność w pliku konfiguracyjnym projektu - pom.xml - i m2 pobrałaby mi ją. Wyraźnie przyspieszenie mojej produktywności - zrzucić obowiązki na usługę, która się w danej kwestii specjalizuje. Bezcenne.

Nie, nie dam rady tak marudzić. Włączam Maven > Enable Dependency Managemenet na projekcie korzystając z zainstalowanej wtyczki m2eclipse (kompletnie nie wiem, co się zaraz stanie, ale coś mi mówi, że może być tylko lepiej). O! Tego się nie spodziewałem - tworzy się pom.xml dla projektu. Wspaniale!

Wciskam przycisk Next > i co widzę? Możliwość zdefiniowania zależności projektowych. Brawo!

Skrzętnie korzystam z okazji i deklaruję zależność od biblioteki specyfikacji JPA (opieram się o bibliotekę dostarczaną przez projekt Apache Geronimo)


oraz Spring Framework.

Wyszukiwanie zależności przegląda moje lokalne repozytorium, więc na razie zadowolony jestem z posiadania możliwości dodania wersji Spring Framework 2.0.2 chociaż istnieje już wersja 2.0.6. Zaraz się i tym zajmę. Ostatecznie kończę deklarowanie zależności z następującymi bibliotekami.


Wciskam przycisk Finish i zostaje stworzony plik pom.xml, który zaraz modyfikuję o zmianę związaną z aktualną wersją Spring Framework 2.0.6. Okazuje się, że wtyczka wykonuje jakieś skomplikowane czynności, bo Eclipse nie odpowiada. Zmroziła go moja pomysłowość?! ;-)

...po 15-20 sekundach...

Jest! Plik pom.xml jest automatycznie otwarty, wprowadzam zmianę i zapominam o przejściowych problemach. Wracamy do JpaDaoSupport i PrezenterService, i w ogóle Spring Framework i JPA.

Zalet integracji JPA w Spring Framework nie będę przedstawiał - artykuł robi to wyśmienicie. Jednego nie można nie wspomnieć - Spring Framework znacznie upraszcza tworzenie aplikacji, nie tylko korzystającej z JPA.

Pora na plik konfiguracyjny Spring Framework domyślnie nazywany beans.xml, chociaż bardziej znamienne nazwy są zawsze na miejscu. Może spring-jpa-beans.xml jako zlepek nazwy projektu (która sama w sobie nie jest czymś wyrafinowanym) i beans.xml jako dziedzictwo domyślnej nazwy?

Do tworzenia pliku XML skorzystamy z świeżo zainstalowanej wtyczki Spring IDE 2.0. Niech się przekonam, gdzie Spring IDE umiejscowi plik w strukturze projektu zarządzanego przez m2 (powinno być src/main/resources, ale skąd Spring IDE miałby to wiedzieć, że projekt jest kontrolowany przez m2).

Wybieram menu New > Other... > Spring > Spring Bean Definition


Ach, to jest rozwiązanie - panel pytający użytkownika o podanie miejsca położenia pliku. No tak - czego możnaby oczekiwać?! Sprytnie.

Ostatecznie przekopiowujemy zawartość spring.xml z artykułu z odpowiednimi modyfikacji. Ciekawostką wtyczki Spring IDE jest podpowiedź przy uzupełnianiu wartości atrybutów ziaren Spring.

Dostawcą JPA w przykładzie jest TopLink JPA. Możliwi dostawcy JPA w Spring Framework to Hibernate JPA, Apache OpenJPA oraz TopLink JPA (więcej w liście typów w pakiecie org.springframework.orm.jpa.vendor).

Dodaję nowe zależności w projekcie - HSQL oraz TopLink Essentials JPA - korzystając z pomocy mojego artykułu Nauka Java Persistence z Apache Maven 2 i dostawcami JPA: OpenJPA, Hibernate i TopLink, w którym opisałem sposób dodawania zależności od TopLink Essentials.

Tworzę JUnit test, który rozszerza AbstractJpaTests, więc konieczne dodaję kolejną zależność spring-mock-2.0.6.jar do projektu.

package pl.jaceklaskowski.spring.ranking;

import org.springframework.test.jpa.AbstractJpaTests;

import pl.jaceklaskowski.spring.ranking.entity.Prezenter;

public class PrezenterServiceIntegrationTest extends AbstractJpaTests {
private PrezenterService prezenterService;

private long janKowalskiId;

public void setPrezenterService(PrezenterService prezenterService) {
this.prezenterService = prezenterService;
}

protected String[] getConfigLocations() {
return new String[] { "classpath:/spring-jpa-beans.xml" };
}

protected void onSetUpInTransaction() throws Exception {
Prezenter prezenter = new Prezenter("Jan", "Kowalski");
prezenterService.save(prezenter);
janKowalskiId = prezenter.getId();
}

public void testPrezenterExists() {
Prezenter prezenter = prezenterService.findByPrezenterId(janKowalskiId);
assertEquals("Jan", prezenter.getImie());
assertEquals("Kowalski", prezenter.getNazwisko());
}
}

Uruchomienie testu potwierdza poprawne zestawienie środowiska (poza brakiem konfiguracji log4j, ale kto by się tym przejmował teraz).

log4j:WARN No appenders could be found for logger (org.springframework.util.ClassUtils).
log4j:WARN Please initialize the log4j system properly.
[TopLink Config]: 2007.07.08 10:12:51.312--ServerSession(9182681)--Thread(Thread[main,5,main])--The alias name for the entity class [class pl.jaceklaskowski.spring.ranking.entity.Prezenter] is being defaulted to: Prezenter.
[TopLink Config]: 2007.07.08 10:12:51.328--ServerSession(9182681)--Thread(Thread[main,5,main])--The table name for entity [class pl.jaceklaskowski.spring.ranking.entity.Prezenter] is being defaulted to: PREZENTER.
[TopLink Config]: 2007.07.08 10:12:51.343--ServerSession(9182681)--Thread(Thread[main,5,main])--The column name for element [private long pl.jaceklaskowski.spring.ranking.entity.Prezenter.id] is being defaulted to: ID.
[TopLink Config]: 2007.07.08 10:12:51.359--ServerSession(9182681)--Thread(Thread[main,5,main])--The column name for element [private java.lang.String pl.jaceklaskowski.spring.ranking.entity.Prezenter.imie] is being defaulted to: IMIE.
[TopLink Config]: 2007.07.08 10:12:51.359--ServerSession(9182681)--Thread(Thread[main,5,main])--The column name for element [private java.lang.String pl.jaceklaskowski.spring.ranking.entity.Prezenter.nazwisko] is being defaulted to: NAZWISKO.
[TopLink Info]: 2007.07.08 10:12:51.531--ServerSession(9182681)--Thread(Thread[main,5,main])--TopLink, version: Oracle TopLink Essentials - 2.0 (Build 40 (03/30/2007))
[TopLink Config]: 2007.07.08 10:12:51.531--ServerSession(9182681)--Connection(6131844)--Thread(Thread[main,5,main])--connecting(DatabaseLogin(
platform=>HSQLPlatform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
[TopLink Config]: 2007.07.08 10:12:51.781--ServerSession(9182681)--Connection(5555373)--Thread(Thread[main,5,main])--Connected: jdbc:hsqldb:mem:spring-jpa
User: SA
Database: HSQL Database Engine Version: 1.8.0
Driver: HSQL Database Engine Driver Version: 1.8.0
[TopLink Config]: 2007.07.08 10:12:51.781--ServerSession(9182681)--Connection(20738936)--Thread(Thread[main,5,main])--connecting(DatabaseLogin(
platform=>HSQLPlatform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
[TopLink Config]: 2007.07.08 10:12:51.781--ServerSession(9182681)--Connection(29422309)--Thread(Thread[main,5,main])--Connected: jdbc:hsqldb:mem:spring-jpa
User: SA
Database: HSQL Database Engine Version: 1.8.0
Driver: HSQL Database Engine Driver Version: 1.8.0
[TopLink Info]: 2007.07.08 10:12:51.859--ServerSession(9182681)--Thread(Thread[main,5,main])--file:/C:/.eclipse/sandbox/spring-jpa/target/-spring-jpaPU login successful
[TopLink Fine]: 2007.07.08 10:12:51.875--ServerSession(9182681)--Connection(30844270)--Thread(Thread[main,5,main])--CREATE TABLE PREZENTER (ID NUMERIC(19) NOT NULL, IMIE VARCHAR(255), NAZWISKO VARCHAR(255), PRIMARY KEY (ID))
[TopLink Fine]: 2007.07.08 10:12:51.890--ServerSession(9182681)--Connection(26750913)--Thread(Thread[main,5,main])--CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT NUMERIC(38), PRIMARY KEY (SEQ_NAME))
[TopLink Fine]: 2007.07.08 10:12:51.890--ServerSession(9182681)--Connection(6775863)--Thread(Thread[main,5,main])--SELECT * FROM SEQUENCE WHERE SEQ_NAME = 'SEQ_GEN_TABLE'
[TopLink Fine]: 2007.07.08 10:12:51.890--ServerSession(9182681)--Connection(20092482)--Thread(Thread[main,5,main])--INSERT INTO SEQUENCE(SEQ_NAME, SEQ_COUNT) values ('SEQ_GEN_TABLE', 1)
[TopLink Fine]: 2007.07.08 10:12:51.984--ClientSession(31212095)--Connection(181086)--Thread(Thread[main,5,main])--UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [50, SEQ_GEN_TABLE]
[TopLink Fine]: 2007.07.08 10:12:51.984--ClientSession(31212095)--Connection(181086)--Thread(Thread[main,5,main])--SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ?
bind => [SEQ_GEN_TABLE]

Zaprezentowany w artykule plik persistence.xml ukazuje działanie Spring Framework i jego modułu integrującego JPA, którzy symulują działanie serwera aplikacyjnego Java EE, w którym nie definiuje się klas (za pomocą elementu class), które podlegają zarządzaniu przez zarządcę trwałego (kolejny argument, że Spring Framework jest/staje się semi-serwerem aplikacyjnym Java EE).

Na zakończenie pełny plik konfiguracyjny projektu - pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>spring-jpa</groupId>
<artifactId>spring-jpa</artifactId>
<version>0.0.1</version>
<repositories>
<repository>
<id>java.net</id>
<name>java.net Maven Repository</name>
<url>https://maven-repository.dev.java.net/nonav/repository</url>
<layout>legacy</layout>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jpa_3.0_spec</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jpa</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-mock</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.7</version>
</dependency>
<dependency>
<groupId>toplink.essentials</groupId>
<artifactId>toplink-essentials</artifactId>
<version>2.0-40</version>
</dependency>
</dependencies>
</project>

Na dzisiaj wystarczy - szkielet aplikacji ze Spring i JPA stworzony. Cały projekt dostępny jest jako spring-jpa.zip. Wystarczy rozpakować, zaimportować do wybranego IDE (w paczce zawarte są pliki konfiguracyjne dla Eclipse IDE) i uruchomić test jednostkowy PrezenterServiceIntegrationTest. Zakładając istnienie wtyczek i dostępności zależności powinno działać.

Kolejne odsłony tematu planuję rozszerzyć o przedstawienie sposobu uaktywnienia Apache OpenJPA w Spring Framework jako dostawcy JPA oraz wykorzystanie bazy danych PostgreSQL lub Derby (zamiast HSQL). Planuję również przejść ścieżkę integracji aplikacji opartej na Spring i JPA z SCA, a później rozpoznanie integracji Spring z iBatis (tyle się o nim pisze).

Automatyczne sprawdzanie uaktualnień wtyczek w Eclipse IDE 3.3

0 komentarzy
W Eclipse 3.3 istnieje możliwość takiej konfiguracji narzędzia, w której o określonej porze zostanie sprawdzone istnienie nowych wersji wtyczek - Window > Preferences... > Install/Update > Automatic Updates. Po wybraniu opcji Automatically find new updates and notify me Eclipse wykona żmudną pracę sprawdzenia uaktualnień za nas.

Ciekawym rozwiązaniem jest również ustawienie Eclipse, aby akceptował uaktualnienia zgodne wstecz (ang. compatible) oraz podczas odszukiwania zmian automatycznie akceptował serwery lustrzane (ang. mirrors).

Przy takiej konfiguracji pozostaje jedynie dodawanie nowych adresów z aktualizacjami, a resztą zajmie się Eclipse. Wspanile - kolejny kłopot z głowy.

Po chwili okazało się, że pojawiła się nowa wtyczka dla Groovy oraz poprawka do samego środowiska Eclipse 3.3.

Dobrze byłoby poznać listę wtyczek z jakich korzystają inni. Moja zawiera AJDT, Derby, DTP, EMF, Findbugs, Geronimo Eclipse Plugin, GEF, Glassfish Eclipse Plugin, Groovy, M2Eclipse, Mylyn, Spring IDE, Subclipse, TestNG, VE oraz WTP.

Zastanawiam się, w jaki sposób możnaby udostępnić listę moich miejsc publikacji wtyczek, aby inni mogli z niej skorzystać? Jakiś pomysł? Nie wszystkie miejsca są dostępne w Help > Software Updates > Find and Install..., ponieważ niektóre wtyczki instaluję samodzielnie poprzez mechanizm Extension Location (opisywałem go w jednym z moich artykułów Zarządzanie wtyczkami w Eclipse - Extension Location).

07 lipca 2007

Powrót do lektury specyfikacji JPA - rozdziały 5.2-5.4 na przykładzie

0 komentarzy
Postanowiłem powrócić do lektury specyfikacji Java Persistence API 1.0 (JPA) od przykładu, który znalazłem w NetBeans IDE 6.0 zbudowanego ze źródeł z 30 czerwca (ostatnia rozwojowa wersja NetBeans IDE to 6.0M10). Przykład dostępny jest w menu New Project > Samples > Enterprise > Customer CMP. Przykład opisany jest jako

Demonstrates using Java Persistence APIs based on the new Java Persistence specification. The EJB interacts with a relational database to store and display information about customer subscriptions to periodicals.

Szybko okazało się, że przykład nie działa poprawnie z Glassfish v2 b50f, gdyż wymagany do poprawnego działania JPA wpis test w JNDI nie istnieje. Skorzystałem z instrukcji uruchomienia źródła danych do PostgreSQL 8.2.3 opisanych w moim artykule Aplikacja korporacyjna z JPA w trybie JTA z GlassFish i PostgreSQL i po modyfikacji pliku konfiguracyjnego JPA - persistence.xml - o wartość elementu jta-data-source mogłem uruchomić przykład.

Analiza kodów źródłowych ujawniła niezalecany sposób tworzenia aplikacji przy pomocy umieszczania elementów języka Java w ramach stron JSP. Dodatkowo, właśnie ze względu na umieszczenie części logiki aplikacji w plikach JSP, zamiast korzystać z wstrzeliwania zależności, przykład proponuje wyszukiwanie bezstanowego sesyjnego ziarna EJB za pomocą JNDI i rejestrowania nazwy logicznej ziarna w deskryptorze aplikacji internetowej (/WEB-INF/web.xml). To przede wszystkim zadecydowało o zaniechaniu prezentacji wiadomości z rozdziału 5 specyfikacji JPA na przykładzie Customer CMP z NetBeans IDE.

Zapewne zostało zauważone moje użycie słowa ziarno odpowiadające angielskiemu bean. Rozpocząłem używanie ziarno w kontekście dawnej nazywanych komponentów EJB. Po ostatniej prezentacji Michała Grzejszczaka zatytułowanej Open Terracotta - klastrowanie dla Javy na spotkaniu Warszawa JUG, gdzie użył tego określenia w stosunku do ziaren w Spring Framework, jakoś przypadło mi to do gustu i tak zostało.

Mimo nierekomendowanych rozwiązań projektowych, warto zapoznać się z kodem źródłowym przykładowej aplikacji Customer CMP. Czytanie kodu źródłowego zawsze dostarcza ciekawych informacji (nawet takich, że nie należy tworzyć aplikacji w ten sposób). I tak w kodzie źródłowym ziarna EJB - enterprise.customer_cmp_ejb.ejb.session.CustomerSession napisano:

Why a facade?
1. session beans are thread safe, and EMs are not necessarily; so injecting a EM into a SessionBean makes it safe.
2. Tx management is taken care of by container
3. of course, because it's a facade [we can combine operations].

Bardzo ważna jest uwaga #1 o odporności ziaren sesyjnych na wielowątkowość. Kontener dba o jednoczesny dostęp do ziarna i nie dopuszcza do sytuacji, w której dwa wątki równocześnie modyfikują jego stan, więc mamy gwarancję, że ziarno sesyjne używane jest przez pojedyńczy wątek. To gwarantuje korzystanie z zarządcy trwałego (ang. entity manager) w ramach pojedyńczego wątku, a to ma niebagatelne znaczenie, gdyż zarządca musi być używany wyłącznie przez jeden wątek (czytaj: nie jest odporny na wielowątkowość).

Druga uwaga jest równie ważna, co stanowi argument dla korzystania z serwera aplikacyjnego Java EE w ogólności - zarządzanie usługami przez kontener. Poza usługą monitora transakcji, inną usługą, która jest udostępniana przez serwer Java EE, niwelując naszą potrzebę poznawania szczegółów, a jedynie kilka adnotacji lub elementów w XML, jest bezpieczeństwo. Za pomocą deklaratywnego podejścia do wykorzystania usług transakcji i bezpieczeństwa możemy konfigurować je na potrzeby naszej aplikacji. (Jeśli ktoś teraz powie, że Spring Framework też to potrafi, więc nie potrzeba serwera aplikacyjnego Java EE, to właśnie wzbudzi mój entuzjazm, który wiąże się z określeniem Spring Framework jako pewnego rodzaju serwera aplikacyjnego, z czym wielu nie może się zgodzić).

Trzecia, ostatnia uwaga jest jedynie wzmocnieniem sensowności stosowania wzorca projektowego - Fasada, który polega na upraszczaniu publicznego interfejsu przez ukrycie zawiłych interakcji wewnętrznych między usługodawcami, np. usługą JPA, transakcji, bezpieczeństwa, itp. Wszystko, to jest ukryte w pojedyńczym wywołaniu metody znacząco upraszczając konstruowanie aplikacji klienckiej. Wydaje się, że nie ma o czym debatować o wzorcach projektowych w kontekście JPA, ale nieprawda. Właśnie konstrukcja oprogramowania przy wykorzystaniu Java EE 5, którego częścią jest JPA poprzez EJB3, wymusza stosowanie dobrych praktyk projektowych, a stosowanie wzorca Fasada jest tego potwierdzeniem. Zamiast wymuszać na twórcy klienta (w tym przypadku jest to niewyrafinowana strona JSP, ale możnaby wyobrazić sobie coś bardziej zaawansowanego, np. zdalny klient aplikacyjny w postaci aplikacji desktopowej) znajomość tajników konstrukcji oprogramowania ze wszystkimi detalami z Java EE, które związane byłyby z rozpoczęciem transakcji przed dostępem do danych przechowywanych w bazie (za pomocą JPA), wystarczy jedynie skorzystać z pojedyńczej metody udostępnianej przez bezstanowe ziarno sesyjne EJB, które jest dostępne zdalnie poprzez mechanizm klienta aplikacyjnego (ang. Java EE client application), ale również możnaby udostępnić ją jako część usługi sieciowej (ang. web service) za pomocą prostej modyfikacji polegającej na udekorowaniu klasy ziarna adnotacją @WebService.

W klasie ziarna CustomerSession można zobaczyć dostęp do zarządcy trwałego poprzez adnotację @PersistenceContext.

@javax.persistence.PersistenceContext(unitName="persistence_sample")
private EntityManager em ;

W tym przykładzie istnieje wyłącznie pojedyńcza jednostka trwała związana o nazwie persistence_sample, więc element unitName nie jest konieczny.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="persistence_sample" transaction-type="JTA">
<provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
<jta-data-source>jdbc/poligon</jta-data-source>
<properties>
<property name="toplink.ddl-generation" value="drop-and-create-tables"/>
</properties>
</persistence-unit>
</persistence>

W przypadku większej ilości definicji PU w pliku konfiguracyjnym JPA użycie elementu unitName byłoby konieczne.

Zarządca trwały dla wybranego kontekstu trwałego pobierany jest z fabryki zarządców. Klasa rozpoczynająca interakcję z JPA to javax.persistence.Persistence (oczywiście wszystko zależy od środowiska, w którym pracuje nasza aplikacja oraz od trybu w jakim wykorzystywane jest JPA - zarządca encji zarządzany przez kontener lub aplikację). W środowisku Java SE mamy do dyspozycji jedynie tryb zarządzania zarządcy encji przez aplikację. Dodam, że aplikacją może być kontener EJB albo szerzej serwer aplikacyjny Java EE, więc klientami mogą być ziarna EJB, komponenty zarządzane JSF, servlety, itp, które będą już korzystali z JPA zazwyczaj w trybie zarządzania zarządcy przez kontener. Różnica między trybami była już przedstawiana w poprzednich odcinkach lektury specyfikacji JPA, ale jest to tak istotna sprawa w poprawnym użyciu JPA, że będzie jeszcze o tym nie raz.

Zobaczmy w jaki sposób dowolna aplikacja może rozpocząć korzystanie z JPA (zakładamy, że wykorzystywaną konfiguracją JPA jest powyższy plik persistence.xml, gdzie zdefiniowano pojedyńcze PU - persistence_sample).

EntityManagerFactory emf = javax.persistence.Persistence.createEntityManagerFactory("persistence_sample");

Skoro każda aplikacja ma taką możliwość, serwer aplikacyjny Java EE również. I właśnie zrozumienie tej kwestii upraszcza zrozumienie kolejnych elementów JPA. Użycie serwera aplikacji Java EE jest jedynie podyktowane potrzebą uproszczenia tworzenia aplikacji, a to jest realizowane przez automatyczne zarządzanie zasobami przez serwer.

Po wywołaniu statycznej metody Persistence.createEntityManagerFactory.createEntityManagerFactory(String persistenceUnitName) otrzymujemy dostęp do gotowej fabryki zarządców - javax.persistence.EntityManagerFactory w aplikacji, która decyduje się samodzielnie zarządzać zarządcami encji (tryb zarządcy trwałego zarządzanego przez aplikację).

Powyższe wywołanie jest realizowane w ramach środowiska serwera za pomocą adnotacji @PersistenceUnit.

@PersistenceUnit(unitName="persistence_sample")
EntityManagerFactory emf;
Korzystając z zarządców zarządzanych przez kontener (w środowisku Java EE), aplikacja korzysta z fabryki pośrednio za pośrednictwem serwera poprzez mechanizm wstrzeliwania zależności lub z drzewa JNDI. Kontener zarządza komunikacją (użyciem) z fabryką zarządców w sposób niezauważalny dla aplikacji, nie wymagając jakichkolwiek dodatkowych kroków po stronie jej twórcy.

W przeciwieństwie do środowiska Java EE, gdzie zlecamy zarządzanie dostępem do kontekstu trwałości kontenerowi, w przypadku korzystania z zarządców trwałości zarządzanych przez aplikację (w środowisku Java SE lub w szczególnych przypadkach w Java EE) aplikacja musi korzystać bezpośrednio z fabryki zarządców, aby zarządzać zarządcą trwałości i cyklem życia kontekstu trwałego za pomocą klas Persistence oraz EntityManagerFactory.

Zarządca trwałości jest zaprojektowany do działania w środowisku jednowątkowym i nie może być współdzielony wśród wielu równolegle wykonujących się wątków. Jest to bardzo istotna cecha zarządców, która często jest zapominana, a która uwidacznia się w środowisku serwera aplikacji, np. w kontenerze servletów, gdzie istnieją różne zasięgi istnienia obiektów - request, session oraz application. Z tego powodu, w środowisku servletów, obiekty o zasięgu request muszą korzystać z wstrzeliwania zależności bądź bezpośrednio korzystać z fabryki zarządców do tworzenia kontekstu trwałego.

Dostęp do zarządcy trwałości zarządzanego przez kontener (w środowisku Java EE) jest możliwy poprzez wstrzeliwanie zależności (adnotacje @PersistenceUnit lub @PersistenceContext) lub bezpośrednie wyszukanie w JNDI. Cała odpowiedzialność za zarządzanie cyklem rozwojowym kontekstu trwałego (utworzenie, zamknięcie) oraz związanego z nim zarządzcy trwałości spoczywa na kontenerze (serwerze Java EE) i jest niewidoczne dla aplikacji (poza wynikającymi uproszczeniami w sposobie dostępu do nich).

Adnotacja @PersistenceContext służy do wstrzelenia zarządcy trwałego odpowiedzialnego za jednostkę trwałości o nazwie wskazanej przez opcjonalny element unitName. Element type określa typ kontekstu trwałego w odniesieniu do transakcji:
  • TRANSACTION - (domyślna wartość) kontekst trwały pojedyńczej transakcji
  • EXTENDED - kontekst trwały jest przedłużony, tj. trwający dłużej niż pojedyńcza transakcja
Przy następującej deklaracji:

@PersistenceContext
EntityManager em;

ustanawiamy zależność aplikacji od zarządcy trwałego dla jednostki trwałej, która jest jedna w aplikacji (plik persistence.xml zawiera wyłącznie pojedyńczy element persistence-unit) w trybie pojedyńczej transakcji.

Poniższa deklaracja jest analogiczna z tym, że korzysta z wyszukania zarządcy w drzewie JNDI.

@Stateless
@PersistenceContext(name="persistence_sample")
public class CustomerSessionBean implements CustomerSessionRemote {

@Resource SessionContext ctx;

public Customer searchForCustomer(String id){
EntityManager em = (EntityManager) ctx.lookup("persistence_sample");
...
}

}

Dostęp do zarządcy trwałego zarządzanego przez aplikację uzyskiwany jest przez aplikację od fabryki zarządców. Korzysta się wtedy z metod interfejsu EntityManagerFactory i jest identyczne bez względu, czy aplikacja uruchamiana jest w ramach środowiska Java SE, czy Java EE (można sobie wyobrazić servlet bedący zarządzcą zarządcy trwałego, który zazwyczaj korzysta z mechanizmu wstrzeliwania zależności, czyli zarządcy trwałego zarządzanego przez serwer).

Każda fabryka zarządców trwałych zwraca egzemplarze zarządców trwałych, które są skonfigurowane w ten sam sposób (korzystają z tej samej bazy danych, używają tych samych ustawień początkowych, itp.).

Może istnieć wiele egzemplarzy fabryki zarządców trwałych. Taki przypadek może wystąpić podczas korzystania z wielu baz danych, skoro zazwyczaj pojedyńczy egzemplarz zarządcy trwałego korzysta z pojedyńczej bazy danych (zastanawiam się nad użyciem słowa typical w notatce na stronie 115 specyfikacji - czy może być inaczej niż pojedyńczy zarządca - pojedyńcza baza danych?).

Metody interfejsu EntityManagerFactory są odporne na wielowątkowość. Metody służą do utworzenia zarządców trwałych zarządzanych przez aplikację. Zakończenie pracy z fabryką przez aplikację powinno być odnotowane przez wywołanie metody EntityManagerFactory.close(), co powoduje, że wszyscy zarządcy trwali utworzeni przez właśnie zamkniętą fabrykę uważani są również za zamkniętych. Metoda EntityManagerFactory.isOpen() pozwala na sprawdzenie stanu fabryki. Podczas tworzenia zarządcy metodą EntityManagerFactory.createEntityManager(Map map) przekazywana mapa zawiera standardowe właściwości posiadające nazwę rozpoczynającą się od javax.persistence oraz właściwości specyficzne dla dostawcy JPA, które muszą być nazwane inaczej niż rozpoczynając się od javax.persistence.

05 lipca 2007

Przegląd specyfikacji modelu montażu SCA 1.0 (SCA Assembly Model 1.0)

0 komentarzy
Przez ostatnie dni walczyłem z kilkoma rzeczami jednocześnie (a obiecywałem sobie, żeby nie). Pracowałem nad wtyczką NetBeans dla Geronimo, która wciąż z niewiadomych mi powodów nie potrafi związać projektu internetowego w NetBeans z Geronimo, więc z praktycznego punktu widzenia jest bezużyteczna, jak i czytałem kilka specyfikacji. Biorąc pod uwagę, że niedługo powinienem podejść do egzaminu z SCBCD 5 (mam już kod promocyjny do rejestracji) powinienem raczej zająć się dokończeniem specyfikacji JPA 1.0 czy EJB 3.0. Mimo wszystko nie mogłem oprzeć się lekturze specyfikacji modelu montażu SCA (SCA Assembly Model 1.0), a to przede wszystkim dlatego, że po kilku przykładowych aplikacjach z SCA w wykonaniu Apache Tuscany - SCAlanie z JAX-WS, SCA z językami skryptowymi w wykonaniu Apache Tuscany, Jetty i Maven 2 oraz SCAlenie (kompozyt) z Apache Tuscany i Apache Maven - coraz bardziej mnie ta specyfikacja zdumiewa. Rozpoznając ją nie sposób nie dotknąć innych technologii i języków - JAX-WS, EJB 3.0, JPA 1.0, BPEL, Groovy, JBI czy Spring Framework, a wspomina się również o OSGi - więc bardzo pomaga wzmocnić swoją ogólną wiedzę. Sposób integracji jest również bardzo nowatorski i wyjątkowo prosty z punktu widzenia programisty, więc nic tylko poznać szczegóły. I właśnie wyjaśnienia szczegółów oczekiwałem od lektury specyfikacji modelu montażu SCA 1.0.

Cała specyfikacja - SCA_AssemblyModel_V100.pdf - zajmuje 90 stron i jest to najgorsza, pod względem wyglądu, specyfikacja, jaką przyszło mi ostatnio czytać. Treść czyta się płynnie i nie zawiera górnolotnych stwierdzeń zrozumiałych dla wtajemniczonych, więc pod tym względem jest w porządku.

Spróbuję streścić zawartość specyfikacji w pojedyńczej notatce.

Podwalinami SCA jest postrzeganie rozwiązań biznesowych jako zbioru usług, które można scalić w celu udostępnienia kolejnej, bardziej specjalizowanej funkcjonalności. Rozwiązania mogą zawierać nowe usługi stworzone specjalnie na potrzeby nowej aplikacji, jak również wykorzystać już istniejące usługi (jest to generalnie podejście architektoniczne zwane SOA - Service-Oriented Architecture, ale jako, że jest wyłącznie podejście musi mieć podwaliny technologiczne i właśnie SCA jest ich częścią).

SCA udostępnia model tworzenia oraz integracji usług za pomocą różnorodnych technologii. W SCA komponenty mogą być tworzone przy pomocy wielu języków programowania - Java, C++ i BPEL oraz szkieletów programistycznych, które są związane z nimi, np. Spring Framework (istnieje również możliwość rozbudowywania środowiska uruchomieniowego SCA o nowe technologie). Do ich integracji SCA udostępnia całą gamę technologii komunikacyjnych oraz upraszczających dostęp do usług - JAX-WS, JMS, RPC.

Specyfikacja modelu montażu SCA składa się z artefaktów, które definiują konfigurację domeny SCA za pomocą SCAleń, które z kolei zawierają komponenty usługowe, powiązania oraz zależne artefakty, które opisują jak one są związane za pomocą łączy.

Podstawowym elementem SCA jest komponent (ang. component), który jest elementarną jednostką konstrukcji w SCA. Komponent składa się z egzemplarzy implementacji dostarczających funkcje biznesowe. Funkcja biznesowa dostarczana jest do użycia przez komponenty jako usługa (ang. service). Implementacje mogą opierać się o usługi dostarczane przez inne komponenty, które nazywamy referencjami (ang. references). Implementacje mogą udostępniać możliwość zmiany wartości właściwości (ang. properties), które wpływają na przebieg działania oferowanej funkcji biznesowej. Komponent jest, więc konfiguracją implementacji przez ustawienie wartości właściwości oraz przez związanie referencji do usług udostępnianych przez inne komponenty.

SCA umożliwia tworzenie komponentów za pomocą języków Java, C++, BPEL oraz języków skryptowych PHP czy JavaScript jak i języków deklaratywnych XQuery oraz SQL.

SCA opisuje zawartość i montaż aplikacji w postaci SCAleń. SCAlenia mogą zawierać komponenty, usługi, referencje, deklaracje właściwości oraz wiązania, które opisują połączenia między tymi elementami. SCAlenie może korzystać z komponentów stworzonych w dowolnej, dostępnej w środowisku uruchomieniowym SCA, technologii umożliwiając w ten sposób dopasowanie technologii realizacji funkcji biznesowej do jej wymagań i możliwości wytwórczych zespołu projektowego. Z kolei, SCAlenia mogą być używane jako gotowe implementacje, które mogą stanowić składowe kolejnych SCAleń umożliwiając w ten sposób hierarchiczne budowanie rozwiązań biznesowych, gdzie usługi wyższego poziomu są złożeniami usług niższego poziomu. W ten sposób można konstruuować rozwiązania bardziej specjalizowane i dopasowane do potrzeb klienta na bazie dostępnych usług.

SCAlenia są uruchamiane w ramach domeny SCA (ang. SCA domain). Zazwyczaj, domena reprezentuje zbiór usług udostępniających obszar funcjonalności biznesowej kontrolowanej przez pojedyńczą organizację.

SCA definiuje format pliku konfiguracyjnego XML. Deskryptory XML są przenośną reprezentacją statycznych bytów SCA.

Komponent jest podstawowym elementem funkcji biznesowej w zestawie SCA (ang. SCA assembly), które są łączone w gotowe rozwiązania biznesowe przez SCAlenia.

Komponenty są gotowymi do użycia instancjami implementacji. Komponenty dostarczają i korzystają z usług. Więcej niż jeden komponent może używać i konfigurować tą samą implementację na potrzeby jej wymagań.

Komponenty są zadeklarowane jako podelementy SCAlenia w pliku - deskryptorze XML o rozszerzeniu .composite. Komponent jest reprezentowany przez element component będącym potomkiem elementu composite. Element composite może zawierać wiele elementów component. Obowiązkowym atrybutem elementu component jest atrybut name nadający nazwę komponentu, która musi być unikatowa w ramach SCAlenia (wewnątrz composite).

<component name="DictionaryServiceComponent">
<implementation.java class="pl.jaceklaskowski.sca.dictionary.DictionaryServiceImpl" />
<reference name="englishPolishDictionaryService" target="EnglishPolishDictionaryServiceComponent" />
<reference name="germanPolishDictionaryService" target="GermanPolishDictionaryServiceComponent" />
</component>

<component name="EnglishPolishDictionaryServiceComponent">
<implementation.java class="pl.jaceklaskowski.sca.dictionary.EnglishPolishDictionaryServiceImpl" />
</component>

<component name="GermanPolishDictionaryServiceComponent">
<implementation.script script="pl/jaceklaskowski/sca/dictionary/groovy/GermanPolishDictionaryServiceImpl.groovy"/>
</component>

Element component może zawierać wiele elementów implementation.*, które wskazują na implementację używaną przez komponent. Komponent bez implementacji nie może być uruchomiony, jednakże może służyć jako możliwość nakreślania usług przy konstruowaniu komponentów metodą "od góry - w dół", czyli od ogólnego spojrzenia do szczegółów.

Komponent może zawierać wiele elementów services jako sposób konfiguracji usług komponentu. Usługi możliwe do konfiguracji są zdefiniowane przez implementację.

Element service posiada obowiązkowy atrybut name, który nadaje nazwę usłudze i która musi odpowiadać nazwie usługi zdefiniowanej przez implementację.

<service name="DictionaryService" promote="DictionaryServiceComponent">
<interface.java interface="pl.jaceklaskowski.sca.dictionary.DictionaryService" />
</service>

Usługa posiada wiele elementów interfejs, które opisują operacje udostępniane przez usługę. Jeśli nie określono interfejsu usługi, wtedy obowiązującym interfejsem jest interfejs określony przez implementację. Jeśli określono interfejs, to musi być on podzbiorem interfejsu udostępnianego przez implementację, czyli określać podzbiór operacji zdefiniowanych przez implementację.

Element service może zawierać wiele elementów binding. Jeśli nie podano żadnego elementu binding, wtedy binding wyznaczany jest przez implementację.

Element component może posiadać wiele elementów reference, które określają referencje komponentu do usług. Referencje, które mogą być konfigurowane, określone są przez implementację. Element reference posiada obowiązkowy atrybut name, który przypisuje nazwę referencji, która z kolei musi odpowiadać nazwie referencji zdefiniowanej przez implementację.

Referencja może posiadać wiele elementów interface, które opisują operacje wymagane przez referencję. Jeśli nie podano żadnego elementu interface, obowiązujący interfejs określony jest przez implementację. Interfejs musi określać zbiór interfejsów dostarczanych przez implementację, tj. dostarczać operacje zdefiniowane przez implementacje dla danej referencji.

Element reference zawiera wiele elementów binding. Podobnie jak przy elemencie binding dla service, tak i tutaj albo podajemy zbiór zgodny z możliwościami implementacji, albo przy jego braku wyznaczany jest na podstawie implementacji. Element binding może określać endpoint, który będzie jego realizacją. Referencja nie może korzystać równocześnie z endpoint w ramach wiązania oraz docelowymi endpoint określonymi przez atrybut target. Jeśli atrybut target jest określony, elementy binding mogą jedynie definiować typy wiązania określone przez atrybut target. Jeśli endpoint są określone w elementach binding, wtedy każdy endpoint musi korzystać z typu binding, w którym jest zdefiniowany. Dodatkowo, w takim przypadku każdy element binding potrzebuje określić pewien endpoint.

Element component może zawierać wiele elementów property, które są używane do konfiguracji właściwości implementacji. Właściwości do konfiguracji i ich typy określone są przez implementację.

Implementacja może zadeklarować wielowartościową właściwość.

Wartość właściwości może być określona w jeden z poniższych sposobów:
  • Jako wartość dostarczoną jako zawartość elementu property.
  • Jako referencję do właściwości innego SCAlenia, który zawiera ten komponent. Referencja jest tworzona przez atrybut source jako wyrażenie XPath.
  • Jako adres URI będący wskazaniem na plik, którego zawartość jest traktowana jako wartość właściwości, za pomocą atrybutu file.
Jeśli korzysta się z kilku definicji wartości właściwości, atrybut source ma pierwszeństwo nad atrybutem file.

Opcjonalnie, typ właściwości może być określony na jeden z poniższych sposobów:
  • przez pełną nazwę typu zdefiniowanego w schemacie XML za pomocą atrybutu type.
  • przez pełną nazwę globalnego elementu w schemacie XML za pomocą atrybutu element.
Typ właściwości musi być zgodny z typem właściwości w implementacji.

Element property posiada obowiązkowy atrybut name, który przypisuje nazwę właściwości, która musi odpowiadać nazwie właściwości zdefiniowanej przez implementację.

Implementacje komponentów są konkretnymi implementacjami funkcji biznesowych, które udostępniają usługi i/lub korzystają z innych. Dodatkowo, implementacje mogą udostępniać możliwość zmiany własnych właściwości.

SCA posiada wiele typów implementacji, takich jak Java, BPEL, C++. Wybór typu określa użycie technologii implementacji, która poza wykorzystaniem języka programowania (Java lub C++), może określać szkielet programistyczny lub środowisko uruchomieniowe, np. Spring Framework lub EJB.

Dostępne są następujące typy implementacyjne:
  • implementation.java
  • implementation.bpel
  • implementation.composite
  • implementation.spring
  • implementation.ejb
Atrybuty są specyficzne dla elementu określającego typ implementacyjny, i tak dla implementation.java istnieje atrybut class, dla implementation.bpel atrybut process oraz dla implementation.composite atrybut name.

<interface.java interface="pl.jaceklaskowski.sca.dictionary.PolishEnglishDictionaryService" />
<implementation.script script="pl/jaceklaskowski/sca/dictionary/groovy/GermanPolishDictionaryServiceImpl.groovy" />

Usługi, referencje oraz właściwości są zmiennymi elementami implementacji. SCA określa je jako typ komponentu. Typ komponentu jest wyznaczany na podstawie implementacji, jednakże wiele z elementów może zostać nadpisanych przez komponent, który korzysta z i konfiguruje implementację według własnych potrzeb. Typ składa się z oferowanych usług, referencji do innych usług poprzez wiązania oraz właściwości, które mogą być modyfikowane. Możliwość przesłaniania (nadpisywania) charakterystyki komponentu, jak ustawianie właściwości czy referencji, może być ograniczona, np. udostępniane interfejsy mogą być jedynie zawężane (muszą być zgodne z typami obsługiwanymi przez implementację).

Sposoby deklarowania usług, referencji oraz właściwości zależą od typu implementacyjego. W przypadku języka Javy (implementation.java) można skorzystać z adnotacji.

Podczas działania, postać egzemplarza implementacji zależy od wykorzystywanej technologii. Logika biznesowa działania egzemplarza są niezmienne i zależy od implementacji, lecz wartości właściwości i referencje pochodzą również od komponentu, który konfiguruje implementację na własne potrzeby.

Wyznaczanie typu komponentu jest realizowane dwukrokowo. Najpierw przeglądana jest implementacja, wliczając w to sprawdzenie adnotacji. Kolejny krok to uzupełnienie informacji o dane zawarte w pliku typu komponentu (ang. component type file). Bez względu na możliwość realizacji pierwszego (brak wsparcia dla analizy typu czy adnotacji), czy drugiego kroku (brak pliku), po ich wykonaniu środowisko uruchomieniowe oczekuje kompletnej informacji o typie. W typach implementacyjnych, gdzie możliwe jest korzystanie z analizy typu (prześwietlanie - ang. reflection) oraz adnotacji wykorzystanie pliku typu komponentu będzie znikome.

Nazwa pliku typu komponentu odpowiada nazwie pliku implementacji z rozszerzeniem .componentType. Typ komponentu jest definiowany przez element componentType wewnątrz pliku. Miejsce położenia pliku jest związane z mechanizmami wspieranymi przez typ implementacyjny i jest opisany w odpowiadającej mu specyfikacji modelu implementacji SCA.

Interfejs definiuje funkcje biznesowe. Funkcje są dostarczane przez usługi i używane przez referencje. Usługa dostarcza funkcjonalność biznesową dokładnie jednego interfejsu, która może być używana przez inne komponenty. Każdy interfejs definiuje operacje z komunikatami wejściowymi (ang. request (input) message) i komunikaty zwrotne (ang. response (output) message). Komunikaty mogą być typami prostymi bądź złożonymi.

SCA wspiera typy interfejsów zdefiniowane przez następujące języki:
  • interfejsy Java (element interface.java)
  • portType w WSDL 1.1 (element interface.wsdl)
  • interfejsy WSDL 2.0 (element interface.wsdl)
Istnieje możliwość rozbudowy systemu interfejsów poprzez mechanizm rozszerzeń SCA.

Wyróżniamy interfejsy lokalne i zdalne. Interfejs zdalny może być wywołany przez klienta uruchomionego na systemie zewnętrznym w stosunku do usługi. Interfejs Java jest zdalny, jeśli zostanie udekorowany przez adnotację @Remotable. Interfejsy zdefiniowane przez WSDL są zawsze zdalne.

Komunikaty wejściowe operacji określonych przez interfejs zdalny są zawsze przekazywane przez wartość. Istnieje możliwość oznaczenia komunikatu (parametru) wejściowego jako przekazywanego przez referencję, dla klientów, którzy uruchamiani są w tej samej przestrzeni, co usługa, mimo korzystania z interfejsu zdalnego przez adnotację @AllowsPassByReference.

Lokalne interfejsy nie mogą być opublikowane przez zdalne usługi komponentów. Interfejs lokalny może być wywoływany przez klientów z tej samej przestrzeni, gdzie uruchomiony jest komponent realizujący interfejs. Brak adnotacji @Remotable w interfejsie jest oznaczeniem interfejsu jako lokalny.

Wybór typu interfejsu - lokalny vs zdalny - wiąże się ze sposobem interakcji między usługobiorcą a usługodawcą. Interfejs zdalny przewidziany jest do komunikacji pojedyńczej, bardzo złożonej, w przeciwieństwie do interfejsu lokalnego, który wykorzystywany jest do komunikacji bardzo drobnej w sensie jej złożoności biznesowej.

SCAlenie (ang. composite) służy do połączenia elementów SCA w logiczną całość. SCAlenie jest podstawową jednostką konstrukcji w ramach domeny SCA. SCAlenie składa się ze zbioru komponentów, usług, referencji i wiązań, które łączą je oraz zbioru właściwości, które służą do konfiguracji komponentów.

SCAlenia mogą być typem implementacyjnym komponentów (implementation.composite).

Zawartość SCAlenia może być używana w ramach innego przez włączenie, co powoduje, że cała zawartość jest dostępna we włączającym SCAleniu jak, gdyby była umieszczona w nim bezpośrednio.

SCAlenie może być używane jako jednostka dystrybucji. SCAlenia dostarczają elementy do domeny SCA. SCAlenie może zostać zainstalowane w domenie SCA albo poprzez włączenie, albo jako implementacja.

SCAlenie zdefiniowane jest w pliku o rozszerzeniu .composite. SCAlenie jest reprezentowane przez element composite. Jedynym obowiązkowym atrybutem elementu composite jest name, który nadaje nazwę SCAleniu.

Nadanie nazwy SCAleniu już istniejącego SCAlenia w domenie SCA może spowodować konflikt nazw. Za pomocą opcjonalnego atrybutu targetNamespace możliwe jest uniknięcie konfliktu.

SCAlenia zawierają właściwości, usługi, komponenty, referencje, wiązania i włączenia.

Komponenty zawierają gotowe do użycia implementacje, które dostarczają logikę biznesową SCAlenia. Komponenty oferują usługi i wymagają dostępności usług wskazanych przez referencje. Usługi SCAlenia definiują usługi publiczne dostarczane przez SCAlenie. Referencje SCAlenia reprezentują zależności od innych, zewnętrznych usług. Wiązania opisują połączenia między usługami i referencjami komponentów w SCAleniu. Włączone SCAlenia (włączenia) dostarczają elementy, które rozbudowują możliwości SCAlenia.

Z usługami SCAlenia związane jest pojęcie promocji (ang. promotion) jednej usługi komponentu w ramach SCAlenia, co oznacza, że usługa SCAlenia jest w zasadzie dostarczana przez jeden z komponentów w ramach SCAlenia. Podobnie jest z właściwościami. Wszystkie promocje, które korzystają z tego samego komponentu, korzystają z tej samej instancji komponentu.

Referencje SCAlenia są definiowane przez promocję referencji definiowanych przez komponenty zawarte w SCAleniu. Każda z referencji wymaga, aby odpowiadający jej komponent był osiągalny poza SCAleniem. Referencje definiowane są za pomocą elementu reference z obowiązkowymi atrybutami name oraz promote.

Usługi SCAlenia są definiowane przez promocję usług definiowanych przez komponenty zawarte w SCAleniu. Usługi definiowane są za pomocą elementu service z atrybutami name oraz promote.

Łącza SCA (ang. SCA wires) w SCAleniu łączą referencje źródłowe komponentu z usługami komponentu docelowego. Jednym ze sposobów definicji wiązania jest konfiguracja referencji komponentu korzystając z atrybutu target elementu reference. Innym sposobem jest wykorzystanie elementu wire. Oba sposoby są semantycznie identyczne.

SCAlenia mogą być typem implementacyjnym komponentu przez użycie elementu implementation.composite. W takiej konfiguracji, komponenty we włączanym SCAleniu nie mogą być bezpośrednio wykorzystane przez włączające SCAlenie. Włączające SCAlenie może jedynie wiązać usługi i referencje SCAlenia włączanego oraz ustawiać wartości właściwości. Wewnętrzna budowa włączanego SCAlenia jest niewidoczna dla SCAlenia włączającego.

SCA udostępnia możliwość budowania SCAlenia jako rozłącznych elementów projektowych, które ostatecznie będą złączone w pojedyńczą logiczną jednostkę. W ten sposób budowa SCAlenia może zostać rozłożona na kilka zespołów programistycznych.

Za pomocą elementu include treść konfiguracji SCAlenia (zawartość pliku .composite) jest włączana do pliku konfiguracyjnego włączającego SCAlenia. Korzystając z takiej możliwości należy zapewnić unikalność nazw składowych SCAlenia.

Jedynym i obowiązkowym parametrem elementu include jest name, który definiuje nazwę SCAlenia, które będzie włączane teksowo, tj. zawartość załączanego pliku composite SCAlenia będzie włączona (wklejona) w miejscu wystąpienia elementu include.

SCAlenia mogą składać się z kilku typów implementacyjnych, np. jeden komponent będzie reprezentowany przez typ implementacyjny Java, podczas gdy kolejny przez proces BPEL.

SCA umożliwia ograniczanie typów implementacyjnych komponentów przez element constrainigType. Pozwala to na tworzenie SCAleń "odgórnie", tj. architekt definiuje strukturę SCAlenia, włączając w to wymaganą postać implementacji, zanim implementacja zostanie zbudowana.

Element constrainigType jest wyrażony jako zbiór usług, referencji i właściwości. Jako, że constrainigType jest niezależny od implementacji nie może zawierać żadnych informacji wprowadzanych do komponentu podczas jego budowy, w szczególności niedozwolone jest użycie binding, policySet, wartości właściwości oraz domyślnych informacji o wiązaniach.

Definicja constrainingType jest łączona z komponentem przez atrybut constrainingType elementu component.

Wiązania (ang. bindings) są używane przez usługi i referencje. Referencje korzystają z wiązań do opisania mechanizmu dostępu wykorzystywanego przy wywołaniu usługi. Usługi korzystają z wiązań, aby opisać mechanizm dostępu, który klienci muszą użyć do jego wywołania.

Istnieje wiele typów wiązań, np. usługa SCA, usługa sieciowa, bezstanowy komponent sesyjny EJB, procedura składowana, usługa EIS. Środowisko uruchomieniowe SCA musi udostępniać wsparcie dla wiązań będących usługą SCA oraz usługą sieciową.

Wiązanie zdefiniowany jest przez element binding w ramach elementu service lub reference w deskryptorze SCAlenia (plik composite).

Wiązanie SCA jest wiązaniem wyrażonym za pomocą elementu binding.sca. Używane do konfiguracji współpracy referencji i usług w ramach pojedyńczej domeny SCA. Realizacja wiązania jest specyficzna dla środowiska uruchomieniowego SCA. Wiązanie SCA jest domyślnym wiązaniem dla usług i referencji bez jawnie podanego elementu binding. Wykorzystanie binding.sca sprowadza się do sytuacji nadpisywania konfiguracji, lub kiedy definiowany jest zbiór binding i binding.sca byłby jednym z nich.

Wiązanie usług sieciowych opisane jest w dokumencie SCA Web Services Binding V1.00.

Wiązanie JMS opisane jest w dokumencie SCA JMS Binding V1.00.

Domena SCA reprezentuje pełną konfigurację uruchomieniową, potencjalnie rozproszoną na kilka połączonych węzłów (serwerów).

Domena SCA definiuje zakres widoczności dla działania mechanizmów SCA. Połączenia SCA mogą być używane wyłącznie dla komponentów w ramach pojedyńczej domeny SCA. Połączenia z usługami spoza domeny muszą korzystać z mechanizmu wiązań. W ogólności, sposób realizacji usługi od strony klienta powinien być nieistotny i realizacja usługi za pomocą SCA jest jedynie szczegółem implementacyjnym.

Typowa domena SCA reprezentuje obszar funkcjonalności biznesowej zarządzanej przez pojedyńczą organizację. Domena może reprezentować całe przedsiębiorstwo, albo pojedyńczy departament.

Domena SCA może wymagać innych elementów do poprawnej pracy. XML jest domyślnym typem artefaktów SCA, których elementem głównym jest composite, componentType, constrainingType lub definitions. Inne typy XML, pliki binarne specyficzne dla języka programowania wykorzystywanego do budowy komponentów SCA mogą być wymagane przez SCAlenia. SCA definiuje format dystrybucji SCAleń, w którym zip jest formatem domyślnym. Nie określa się, czy instalacja dystrybucji spowoduje utrzymanie formatu po instalacji. Środowisko uruchomieniowe SCA oparte o platformę Java EE może zmodyfikować format zip do pakietu EAR.

Zakłada się, że w korzeniu drzewa artefaktów SCA istnieje katalog META-INF zawierający plik o nazwie sca-contribution.xml, który zawiera listę SCAleń w ramach przekazania (ang. contributions). Możliwe układy katalogów i plików w ramach przekazania zawierają katalog, pakunek OSGi, spakowany katalog (w formacie zip, gzip, itp.), lub plik jar (z możliwymi odmianami jak WAR, EAR).

Nie istnieje możliwość zagnieżdżania przekazań w dystrybucji, tj. przekazania nie zawierają przekazań. Jeśli plik jar, który jest przekazaniem, zawiera inne pliki jar nie są one traktowane jako przekazania, a jedynie jako wewnętrzne składowe.

Podkreśla się, że do instalacji i wykorzystania przekazania nie wymagana jest żadna modyfikacja podczas instalacji.

Przekazania mogą być samowystarczalne, tj. wszystkie artefakty konieczne do uruchomienia zawartości przekazania zawarte są wewnątrz przekazania. Możliwa jest sytuacja, w której artefakty przekazania korzystają z artefaktów zewnętrznych w stosunku do pliku przekazania (inne artefakty SCA, pliki WSDL, XSD, klasy Java, skrypty BPEL, itp.).

Do rozwiązywania referencji zewnętrznych służą m.in. atrybuty wsdlLocation i schemaLocation czy mechanizmy dystrybucji OSGi. Jeśli atrybuty są wykorzystywane muszą być podstawą do rozwiązania referencji.

SCA udostępnia mechanizm rozwiązywania (położenia) artefaktów, który wykorzystywany jest w sytuacji braku innych wskazań na położenie potrzebnych artefaktów czy następuje rozwiązywanie położenia różnych przekazań używających różnych technologii do ich budowy - np. łączenie przekazania opartego o OSGi, które korzysta z przekazania opartego o usługi opisane przez WSDL.

Mechanizm rozwiązywania (położenia) artefaktów SCA polega na założeniu, że przekazanie, które korzysta z zewnętrznych artefaktów deklaruje ich potrzebę przez element import w deskryptorze przekazania. Przekazanie może określić dostępność swoich artefaktów za pomocą elementu export w deskryptorze.

Przekazanie może zawierać dokument, który deklaruje dostępne SCAlenia, udostępniane i zewnętrzne artefakty w pliku deskryptorze przekazania SCA. Deskryptor znajduje się w META-INF/sca-contribution.xml w korzeniu przekazania. Uzupełnieniem pliku sca-contribution.xml jest opcjonalny plik META-INF/sca-contribution-generated.xml, który w przypadku wystąpienia kolizji deklaracji ma niższy priorytet.

Deskryptor sca-contribution.xml składa się z opcjonalnych elementów deployable, import i export. Istnienie elementu deployable wskazuje na SCAlenia, które są uruchamiane jako byty podstawowe w ramach domeny, podczas gdy pozostałe SCAlenia są jedynie wykorzystywane przez inne SCAlenia.

Zakłada się, że typowe działanie mechanizmu rozwiązywania artefaktów zależnych ogranicza się do zbioru przekazań w ramach pojedyńczego środowiska uruchomieniowego na jednym serwerze. Zakłada się jednak możliwość rozwiązywania artefaktów uruchomionych w ramach innego środowiska uruchomieniowego SCA, a stąd, aby środowiska SCA wspierały URI przekazań jako specyfikację położenia (opcjonalny atrybut location elementu import). Format adresu URI jest specyficzny dla środowiska SCA.

Rozdział 2.2 Koncepcje SCA kończy dokument i jest doskonałym jego podsumowaniem. Przedstawia fundamentalne pojęcia związane ze specyfikacją SCA i warto się z nim zapoznać podczas rozpoznawania specyfikacji (chociażby, aby upewnić się, że moje zrozumienie tematu i jego przedstawienie tutaj odpowiadają rzeczywistości). Wydawać by się mogło, że rozdział powinien znaleźć się na początku dokumentu, jednakże terminologia jest również wyjaśniona w międzyczasie, a podsumowanie dobrze uporządkowuje pojęcia i zrozumienie specyfikacji SCA.

Wiązanie (ang. binding) - używane przez usługi i referencje. Referencje korzystają z wiązań do określenia sposobu dostępu podczas wywołania usługi, z którą są związane. Usługi korzystają z wiązań do opisania mechanizmu dostępu, jaki powinien być wykorzystany przez klientów korzystających z nich.
Wyróżniamy wiązania: usługa SCA, usługa sieciowa, bezstanowe ziarno sesyjne EJB, procedura składowana, usługa EIS. Istnieje możliwość rozbudowania środowiska SCA o dodatkowe wiązania.

Komponent (ang. component) jest przygotowanym egzemplarzem implementacji SCA, który udostępnia i korzysta z usług. Wyróżniamy technologie implementacyjne: Java, BPEL, C++. Istnieje możliwość rozbudowania środowiska SCA o nowe typy implementacyjne. Wybór podstawowej technologii implementacyjnej pozostawia się dostawcom SCA.

Usługa (ang. service) - lokalna (ang. local) i zdalna (ang. remotable) deklaruje dostępne usługi dostarczane przez implementację. Usługa może być dostarczana jako zdalna usługa SCA, usługa sieciowa, bezstanowe sesyjne ziarno EJB, usługa EIS, itp. Usługi korzystają z wiązań do opisania sposobu ich udostępniania.
Zdalne usługi określone są przez WSDL lub interfejs Javy udekorowany przez adnotację @Remotable. Przekazywanie parametrów odbywa się przez wartość.
Lokalna usługa określona jest przez interfejs Javy nieudekorowany przez adnotację @Remotable. Przekazywanie parametrów odbywa się przez referencję (w sensie programowania w Javie nie SCA).

Referencja (ang. reference) reprezentuje usługę, która jest wykorzystywana przez implementację podczas realizacji własnych funkcjonalności biznesowych. Referencje są określane przez interfejsy.

Implementacja (ang. implementation) określa technologię informatyczną służącą do dostarczania realizacji usług, np. klasa Javy, proces BPEL, transformata XSLT, klasa C++. SCAlenie przedstawia również implementację.

Interfejs (ang. interface) definiuje funkcje biznesowe dostarczane przez usługi. Interfejs jest wykorzystywany przez komponenty za pomocą referencji. SCA wspiera dwa typy interfejsów: interfejsy w Javie oraz portType w WSDL.
Istnieją interfejsy dwukierunkowe (ang. bi-directional), które posiadają funkcje, które muszą być dostarczane przez obie strony komunikacji, które można wykorzystać do modelowania komunikacji zwrotnej (ang. callback).

SCAlenie (ang. composite) - podstawowa jednostka konstrukcji w SCA. SCAlenie jest bytem złożonym z komponentów, usług, referencji i łączeń. Za ich pomocą dostarczane są przekazania do domeny SCA.

Włączenie (ang. composite inclusion) - część definicji SCAlenia oparta o pewne inne SCAlenie. Włączenie następuje tekstowo.

Właściwość (ang. property) umożliwia konfigurację implementacji. Definiowane specyficzne do użytego typu implementacyjnego, np. adnotacji w Javie lub plik componentType.

Domena (ang. domain) jest zbiorem usług udostępniających funkcje biznesowe kontrolowane przez pojedyńczą organizację. W SCA domenę można traktować również jako SCAlenie, tj. domena udostępnia usługi i referencje oraz łącza, które je spajają.

Łącze (ang. wire) łączy referencje z usługami. Poprawnymi źródłami łączy w SCAleniu są referencje komponentu i usługi SCAlenia, podczas gdy odbiorcami mogą być usługi komponentu i referencje SCAleń. Odbiorcy mogą być zdalni w stosunku do domenu SCA.

Nie nudzę?! Raczej tak. Pora skończyć z SCA (ale tylko na chwilę zanim rozpoznam implementation.spring) i wrócić do lektury specyfikacji JPA, a później EJB. Tak zupełnie przy okazji - pojawiły się egzaminy JPA i EJB 3 - Basic na javaBLACKbelt. Kiedyś deklarowałem stworzenie ich, ale nie starczyło zapału i cieszę się, że pojawili się chętni (poprawiać kogoś jest o wiele prościej ;-)) Ciekawym Waszych wyników - ja jeszcze poczytam specyfikację zanim wystawię się na publiczną falę krytyki.

04 lipca 2007

Wtyczka NetBeans dla Geronimo - pora na malutki rozgłos

0 komentarzy
To już bodajże rok, kiedy krążę nad stworzeniem wtyczki NetBeans dla Geronimo i wydaje się, że dojrzałem, aby myśl zrealizować. Wiadomość rozpoznająca zainteresowanie tematem - NetBeans plugin for Geronimo - anyone interested? - wysłana była 26 lipca 2006 i aby uczcić tę datę czymś niezwykłym, np. zakończeniem prac nad wersją 1.0, powinienem zakasać rękawy i zabrać się do intensywnej pracy.

Właśnie wczoraj moim oczom po raz pierwszy ukazała się lista aplikacji internetowych, jakie są zainstalowane na Geronimo 2.0 SNAPSHOT poprzez wtyczkę w NetBeans IDE. Wtyczka może już wiele, ale nie na tyle, aby opublikować ją jako wersję 1.0. Jeszcze daleko jej do tego numeru wydania, ale o tym za momencik.

Pracuję z wersją rozwojową NetBeans IDE 6.0 zbudowaną ze źródeł. To również nie należało do trywialnych zadań, ale się udało. Można oczywiście skorzystać z wersji oznaczonej jako Milestone np. 9 albo ostatni 10, ale skompletować źródła do niej to wyczyn nie na miarę moich możliwości (innymi słowy przerosłoby mnie). Budowa NB ze źródeł to przestrzeganie zaleceń opisanych na stronie WorkingWithNetBeansSources, co w zasadzie sprowadza się do serii poleceń cvs. Zainteresowani budową NB zapraszam do kontaktu, a teraz kilka słów o moim nowym dokonaniu.

Rozpoczynamy od zainstalowania wtyczki. Jak? Hmmm, pewnie jest jakiś sposób, ale na razie nie bardzo rozpoznany przeze mnie. Korzystam z możliwości NetBeans do uruchamiania projektu wtyczki.

Kolejnym krokiem jest zdefiniowanie serwera w NetBeans IDE w widoku Services.


Wciskamy przycisk Next>.

Zdefiniowanie niepoprawnej ścieżki do katalogu serwera kończy się komunikatem błędu.


Poprawna ścieżka umożliwia kontynuowanie definicji serwera.


Wybieramy przycisk Next>, a później Finish.


Naszym oczom ukaże się zdefiniowany Apache Geronimo. Ale to nie koniec.


Z instancją mamy związane menu pod prawym przyciskiem myszy. Uruchamiamy serwer wybierając menu Start.


Co kończy się pomyślnie uruchomionym serwerem z listą zainstalowanych aplikacji internetowych.


Możemy dowolną z aplikacji internetowych uruchomić w przeglądarce (menu Open in Browser) bądź odinstalować (menu Undeploy).


Na koniec zatrzymujemy serwer (menu Stop).

Niestety mimo licznych zalet, dobrze, kilku zalet są i wady. Pierwsza z nich to poleganie na wartościach domyślnych, których zmiana...mówiąc delikatnie...wprowadza wtyczkę w zakłopotanie. A najbardziej dokuczliwy mankament, który ostudzi zapał jej instalowania i używania, to niemożność stworzenia aplikacji, która jako środowisko uruchomieniowe miałaby zdefiniowane Geronimo. Ot, taka mała niedogodność, nad którą zamierzam popracować w kolejnych dniach.

Dla zainteresowanych rozwojem wtyczki zapraszam do śledzenia zgłoszenia GERONIMODEVTOOLS-131 oraz popróbowania się z wtyczką budując jej źródła. Do dyskusji zachęcam na grupie rozwojowej Apache Geronimo.

01 lipca 2007

XII spotkanie Warszawskiej Grupy Użytkowników Technologii Java (Warszawa-JUG)

3 komentarzy
Warszawska Grupa Użytkowników Technologii Java (Warszawa-JUG) zaprasza na XII spotkanie, które odbędzie się w nadchodzący wtorek 03.07.2007 o godzinie 18:00 w sali 4420 na Wydziale MIMUW przy ul. Banacha 2 w Warszawie.

Temat prezentacji: Open Terracotta - klastrowanie dla Javy
Prowadzący: Michał Grzejszczak

Tematem prezentacji będzie przedstawienie możliwości klastrowania programów w Javie za pomocą projektu Terracotta. Projekt Terracottę można traktować jako rozproszoną stertę (z rozproszonym odśmiecaczem) maszyny wirtualnej. Nie dość, że upraszcza to model programowania dla wielu komputerów, to nie wymaga poznawania żadnego API, bo go w ogóle nie ma. Projekt Terracotta można stosować do klastrowania POJO, ale również do już istniejących aplikacji wykorzystujących Spring Framework, czy sesji HTTP w kontenerach servletów. Jest to ogólne rozwiązanie, które można użyć właściwie wszędzie.
W toku prezentacji wgłębimy się w specyfikę działania Terracotty na prostych przykładach i pokażemy w działaniu przykłady bardziej skomplikowane. Nie zabraknie też poglądowych informacji o architekturze całego systemu i miejscu jakie na rynku oprogramowania Terracotta może zająć.

Prezentację poprowadzi Michał "migi" Grzejszczak, "świeży" absolwent wydziału MIM UW. Migi interesuje się raczej wygodnym programowaniem niźli samą Javą, a to od momentu pierwszego spotkania z porządnym IDE (dla Javy :) parę lat temu. Migi od początku pracy z Javą siedzi po stronie serwera i rzadko stamtąd wychodzi, ale stara się, żeby nie przysłaniało mu to widoku na świat (nie tylko informatyki).

Planowany czas prezentacji to 1,5 godziny z 15 minutową dyskusją.

Zapraszam w imieniu Warszawa-JUG!