Springframework i Jackrabbit (JCR)
Z góry zaznaczam, że specjalnym sympatykiem JSR1 ani Jackrabbit’a nie jestem, i póki co dwa razy zastanowił się bym, czy użyć go w produkcyjnym systemie. Wydaje mi się, że nie jest to technologia wystarczająco dojrzała, w internecie można natrafić na informacje od wielu osób skarżących się na problemy przeróżnej natury, od integracji z innymi narzędziami poprzez skalowalność aż po niełatwy backup. Lecz czasem coś po prostu jest Ci “dane”, więc trzeba się z tym oswoić i starać się podejść do tego z najlepszej strony.
Pierwszym problemem, jaki musiałem rozwiązać, była integracja z Springframework. Nie ukrywam, że nie było to bezbolesne, ale wynik końcowy jest całkiem zadowalający.
W tym artykule postaram się krok po kroku opisać integrację tych technologii. Być może nie wszystkie zaproponowane rozwiązania są optymalne, jednak w miarę zdobywania dalszych doświadczeń z pewnością się nimi podzielę.
Odpalamy Jackrabbit’a
Zazwyczaj rozsądnym pomysłem jest fizyczne rozdzielenie serwera bazy danych od samej aplikacji – w tym przypadku serwerem danych będzie repozytorium Jackrabbit’a, wobec tego, uruchomimy go jako aplikację w osobnym kontenerze, by choć trochę być bliżej ideału. W przykładzie użyłem Tomcat’a w wersji 6.0.14, taki miałem pod ręką. Oczywiście całość uruchamiana była na jednym komputerze. Oto troszkę uproszczony sposób odpalenia Jackrabbit’a pod Tomcatem (jako że nie jest to sedno opisywanego problemu, uruchomienie aplikacji web’ower Jackrabbit’a uproszczone zostało do minimum, zaznaczam więc, że nie jest to rozwiązanie “produkcyjne”).
1. Z strony projektu pobrać należy pobrać archiwum WAR Jackrabbit Web Application (jackrabbit-webapp-1.5.5.war).
2. Z strony Apache Tomcat pobierz odpowiednią wersję kontenera, jak już wspominałem – użyłem wersji 6.0.14. Pobraną aplikację rozpakuj, w przykładzie użyjemy dwóch osobnych instancji Tomcata, więc skopiuj całą rozpakowaną aplikację do nowego folderu i np. zmień nazwę na np.: apache-tomcat-6.0.14-jr. W podkatalogu conf znajdziesz plik server.xml, w nim należy zmienić porty tak, by możliwe było użycie dwóch osobnych kontenerów, użyłem następującej konfiguracji:
<Server port=”8105″ shutdown=”SHUTDOWN”> … <Connector port=”8180″ protocol=”HTTP/1.1″ Shutdown connectionTimeout=”20000″ redirectPort=”8143″ URIEncoding=”UTF-8″/> … <Connector port=”8109″ protocol=”AJP/1.3″ redirectPort=”8143″ URIEncoding=”UTF-8″/> …
3. Aby Jackrabbit poprawnie działał, do katalogu lib w katalogu głównym Tomcata należy wgrać bibliotekę jsr-1.0.jar, dostępną np. tu.
4. Rozpakuj pobrany WAR jackrabbit-webapp-1.5.5.war, zmień nazwę folderu na jackrabbit i umieść go w podkatalogu webapps w instalacji Tomcata na której ma pracować JCR (nie trzeba koniecznie rozpakowywać archiwum, bez problemu powinno się udać uruchomić aplikację z archiwum, które Tomcat automatycznie rozpakuje).
5. Uruchom Tomcata (startup.sh w podkatalogu bin). Jeżeli wszystko poszło sprawnie, po przejściu na adres http://localhost:8180/jackrabbit/ powinieneś zobaczyć stronę startową Jackrabbita.
6. Po przejściu na stronę uruchomionej aplikacji Jackrabbit widoczna będzie informacja o braku repozytorium (Your content repository is not properly configured yet…). Na ekranie znajdziesz również możliwość utworzenia nowego repozytorium, w tym przykładzie jako katalog domowy repozytorium (Repository home directory) podaj addressbook i kliknij przycisk utworzenia nowego repozytorium (Create Content Repository). Nowe repozytorium powinno zostać bezproblemowo utworzone (widoczne jako folder na tym samym poziomie co instalacja Apache Tomcat). Domyślnie do przeglądania repozytorium należy zalogować się jako admin, hasło to adminId.
Kompilacja Jackrabbit’a
Dlaczego kompilacja z źródeł? Przyczyna bardzo prosta, moduł Jackrabbit Object Content Mapping nie jest dostępny jako JAR w standardowym (pobranym) buildzie. Prawdopodobnie z uwagi na potrzebę kompilacji w JDK1.5 lub nowszym.
Będzie potrzebny Maven, ja użyłem wersji 2.1.0. Jeżeli masz już Mavena, to ok, jeżeli nie to pobież go z strony projektu i rozpakuj. Z strony Jackrabbit’a należy pobrać archiwum z kodem źródłowym (jackrabbit-1.5.5-src.jar), w momencie pisania tego tekstu najnowsza dostępna wersja to 1.5.5, takiej też użyłem w przykładowym projekcie.
Po pobraniu kodu, rozpakuj go i uruchom build (dla nie znających Mavena: komenda: <lokalizacja_maven’a>/bin/mvn install wykonana w głównym katalogu kodu źródłowego Jackrabbit’a – tam gdzie znajduje się plik pom.xml). Jeżeli wszystko pójdzie dobrze po kilku minutach powinieneś zobaczyć BUILD SUCCESSFUL. Uwaga: Maven automatycznie dociągnie potrzebne biblioteki, których Jackrabbit używa, więc przy wolnym łączu internetowym build może troszkę potrwać.
Nowy projekt – oczywiście w IntelliJ IDEA
Skoro mamy już działające repozytorium, oraz skompilowaliśmy Jackrabbita pora rozpocząć nowy projekt – najlepiej w IntelliJ IDEA
.
Oto wymagane biblioteki (w wersjach, których użyłem):
- Spring Framework 2.5.6 (spring-framework-2.5.6.SEC01-with-dependencies.zip – można pobrać tutaj)
- Spring Modules 0.9 (spring-modules-0.9-with-dependencies.zip – można pobrać z tutaj)
Listę kompletną JAR’ów załączyłem na dole artykułu.
Z menu głównego wybieramy opcję utworzenia nowego projektu.
By skupić się na konkretach nie będę rozpisywał się jak stworzyć szkielet aplikacji w oparciu o Spring Framework – do artykułu dołączony jest kod źródłowy przykładowej aplikacji, więc z pewnością pomoże on na tym etapie. W dalszej części postanowiłem dla przejrzystości pominąć również listingi kodu, podaję jedynie odniesienia do plików w przykładowym projekcie.
Rozpoczynamy od edycji pliku web.xml (IntelliJ powinna utworzyć go dla nas, jeżeli nie – prawdopodobnie nie została wybrana opcja aplikacji WEB). W pliku tym definiujemy konfigurację dla Log4j, lokalizację głównego pliku konfiguracji (applicationContext.xml) oraz servlet (o nazwie addressbook – conieco sugerującej o tworzonej aplikacji
).
Do aplikacji dodajemy plik konfiguracyjny servletu (addressbook-servlet.xml), w którym to definiujemy w jaki sposób znajdowane będą widoki (jspViewResolver), mapowanie URL’i do kontrolerów (defaultHandlerMapping) oraz jeden kontroler (indexController). Dodajemy również w katalogu /web plik index.jsp. który przekieruje domyślnie do odpowiedniego kontrolera. Implementujemy oczywiście IndexController.java.
Tak skonfigurowana aplikacja dostępna jest jako skompresowane archiwum jcr-tutorial-v1.zip.
Następnie dodajemy prostą fasadę, serwis kilka bussiness obiektów. Wszystko to opieramy na tymczasowym DAO używając po prostu map. Nie poświęcę temu etapowi więcej czasu, dotyczy on raczej stworzenia prostego szkieletu aplikacji książki adresowej. Kod źródłowy załączony został jako jcr-tutorial-v2.zip, zapoznaj się z nim i zrozum go zanim przejdziesz do kolejnego kroku.
Dodajemy obsługę JCR
By móc zapisywać i czytać dane z JCR przedewszystkim musimy dodać do projektu odpowiednie biblioteki (dokładna lista znajduje się na dole postu). Najważniejsze z nich to wspomniana jcr-1.0.jar, potem odpowiednie moduły z Jackrabbita 1.5.5 – nie zapominając szczególnie o OCM – pozwalającym używać mapowania obiektów zapisywanych do repozytorium za pomocą anotacji.
Do konfiguracji w pliku applicationContext.xml dodajemy następujące beany:
jcrRepository – zapewnia zdalny dostęp do repozytorium, poprzez RMI (należy zdefiniować URL dla repozytorium – które wcześniej uruchomiliśmy w Tomcat’cie)
jcrSessionFactory – zapewnia dostęp do sesji (bardzo podobnie jak ekwiwalent dla Hibernate)
jcrTransactionManager – menadżer tranzakcji,
jcrtxProxyTemplate – definicja proxy zapewniającego wykonanie metod w odpowiednim otoczeniu tranzakcji, definiujemy tu prefiksy metod wraz z odpowiednimi atrybutami tranzakcyjności (prawdopodobnie można zkonfigurować anotacje dla metod definiujące tranzakcyjność – było by to bardziej uniwersalne rozwiązanie)
transactionRepository – repozytorium zapewniające tranzakcyjność – tu mała dygresja – tranzakcje w JCR są w powijakach – częściowo – a niekiedy wcale nie działają – szczególnie jeżeli chodzi o blokowanie
providerManager – zapewnia dostęp do sesji
jcrMapper – tu definiujemy nasze klasy, które będą zapisywane w repozytorium, klasy muszą być odpowiednio udekorowane odpowiednimi anotacjami
jcrMappingTemplate – dostęp do podstawowych funkcji zapisu, odczytu, edycji i usuwania mapowanych obiektów
Bean jcrMappingTemplate należy wpiąć w nasz dotychczasowy obiekt dostępu do danych addressbookDao. Wcześniej jednak należy opakować go w proxy w celu zapewnienia tranzakcyjności.
Dla łatwego rozszerzania funkcjonalności w trakcie dodawania nowych mapowanych klas stworzyłem abstrakcyjną klasę AbstractJcrDao zapewniającą podstawowe funkcje zapisu i odczytu danych.
Do projektu dodany został również nowy bean DaoInitializer, jego zadaniem jest jednorazowa inicjalizacja repozytorium.
Oto końcowa postać applicationContext.xml:
< ?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- JCR beans --> <bean id="jcrRepository"> <constructor -arg value="http://localhost:8180/jackrabbit/rmi"/> </bean> <bean id="jcrSessionFactory"> <property name="repository" ref="jcrRepository"/> <property name="credentials"> <bean> <constructor -arg index="0" value="admin"/> <constructor -arg index="1" value="adminId"/> </bean> </property> <property name="sessionHolderProviderManager" ref="providerManager"/> </bean> <bean id="jcrTransactionManager"> <property name="sessionFactory" ref="jcrSessionFactory"/> </bean> <bean id="jcrtxProxyTemplate" abstract="true" > <property name="proxyTargetClass" value="true"/> <property name="transactionManager" ref="jcrTransactionManager"/> <property name="transactionAttributes"> <props> <prop key="save*">PROPAGATION_REQUIRED</prop> <prop key="init*">PROPAGATION_REQUIRED</prop> <prop key="*">PROPAGATION_REQUIRED, readOnly</prop> </props> </property> </bean> <bean id="transactionRepository"> <property name="allowNonTxRepository" value="true"/> <property name="targetFactory" ref="jcrSessionFactory"/> </bean> <bean id="providerManager"/> <bean id="jcrMapper"> <constructor -arg> <util :list id="pages"> <value>com.konri.addressbook.logic.bo.Address</value> <value>com.konri.addressbook.logic.bo.Person</value> </util> </constructor> </bean> <bean id="jcrMappingTemplate"> <constructor -arg ref="jcrSessionFactory"/> <constructor -arg ref="jcrMapper"/> <property name="allowCreate" value="true"/> <property name="exposeNativeSession" value="true"/> </bean> <!-- Application beans --> <bean id="addressbookFacade"> <constructor -arg ref="addressbookService"/> </bean> <bean id="addressbookService"> <constructor -arg ref="addressbookDao"/> </bean> <bean id="addressbookDao" parent="jcrtxProxyTemplate"> <property name="target"> <bean> <property name="jcrMappingTemplate" ref="jcrMappingTemplate"/> </bean> </property> </bean> <bean scope="singleton"> <constructor -arg> <util :list> <ref bean="addressbookDao"/> </util> </constructor> </bean> </beans>
Kompletny finalny projekt dostępny jest tutaj: jcr-tutorial-v3.zip
Oto dokładne zestawienie wymaganych bibliotek:
spring-framework-2.5.6.SEC01/dist/spring.jarspring-framework-2.5.6.SEC01/dist/modules/spring-test.jar
spring-framework-2.5.6.SEC01/dist/modules/spring-webmvc.jar
spring-framework-2.5.6.SEC01/lib/jakarta-taglibs/standard.jar
spring-framework-2.5.6.SEC01/lib/j2ee/jta.jar
spring-framework-2.5.6.SEC01/lib/j2ee/activation.jar
spring-framework-2.5.6.SEC01/lib/jakarta-commons/commons-collections.jar
spring-framework-2.5.6.SEC01/lib/jakarta-commons/commons-logging.jar
spring-framework-2.5.6.SEC01/lib/log4j/log4j-1.2.15.jar
spring-framework-2.5.6.SEC01/lib/j2ee/jstl.jar
spring-framework-2.5.6.SEC01/lib/cglib/cglib-nodep-2.1_3.jar
spring-framework-2.5.6.SEC01/lib/jakarta-commons/commons-beanutils.jar
spring-modules-0.9/spring-modules-jcr.jar
jackrabbit/jackrabbit-1.5.5/jackrabbit-jcr-rmi/target/jackrabbit-jcr-rmi-1.5.0.jar
jackrabbit/jcr-1.0.jar
jackrabbit/jackrabbit-1.5.5/jackrabbit-core/target/jackrabbit-core-1.5.5.jar
jackrabbit/jackrabbit-1.5.5/jackrabbit-ocm/target/jackrabbit-ocm-1.5.3.jar
jackrabbit/jackrabbit-1.5.5/jackrabbit-jcr-commons/target/jackrabbit-jcr-commons-1.5.5.jar
jackrabbit/jackrabbit-1.5.5/jackrabbit-api/target/jackrabbit-api-1.5.0.jar
jackrabbit/jackrabbit-1.5.5/jackrabbit-spi/target/jackrabbit-spi-1.5.0.jar
jackrabbit/jackrabbit-1.5.5/jackrabbit-spi-commons/target/jackrabbit-spi-commons-1.5.5.jar
junit-4.1/junit-4.1.jar
1 – Content Repository for Java Technology API (JCR), http://jackrabbit.apache.org/










Zostaw odpowiedź!
Musisz się zalogować aby móc komentować.