29 maja 2008

java.io.File.toURI().toURL() oraz słów kilka o Ajaxie w Wicket

1 komentarzy
Przeglądając ostatnie zmiany w Apache Geronimo natrafiłem na zmianę związaną z java.io.File.toURL(), którą Jason zmigrował do File.toURI().toURL(). Kilkakrotnie już trafiałem na taką konwersję i nigdy nie wiedziałem, jaki jest dokładnie powód. Dzisiaj postanowiłem sprawdzić javadoc dla tych metod i okazało się, że:
  • Java 6 "przyłożyła" adnotację @Deprecated do metody File.toURL() z komentarzem, że metoda nie dbała o poprawość zwracanego URL, który mógł zawierać niedozwolone znaki, np. #, &, {, + czy ?.
  • Jako poprawne wywołanie tej metody wskazano na właśnie File.toURI().toURL()
Dobrze wiedzieć te ciekawostki, aby nie popaść w kłopoty podczas wdrożenia aplikacji, czy podczas egzaminu SCJP 6. Od tej pory nie korzystamy z File.toURL. Nigdy!

W ten sposób wykonanie poniższego kawałka kodu:
 String sciezka = "że#to&nie{powinno+działać?";
File pathFile = new File(sciezka);
System.out.println("1. " + pathFile.toURL());
System.out.println("2. " + pathFile.toURI().toURL());
zwróci:
 1. file:/C:/sandbox/że#to&nie{powinno+działać?
2. file:/C:/sandbox/że%23to&nie%7Bpowinno+działać%3F
Jak można zauważyć pierwszy z URLi jest niepoprawny i próba otwarcia takiego adresu spowoduje wyjątek. Dodatkowo dokumentacja java.net.URL również wskazuje na klasę java.net.URI, właśnie ze względu na brak dbałości o poprawność zwracanych adresów URL. Więcej informacji o niedozwolonych znakach w adresie (ze względu na szczególne ich znaczenie) można znaleźć w RFC 2396: Uniform Resource Identifiers (URI): Generic Syntax w sekcji 2.4.3. Excluded US-ASCII Characters.

W trakcie lektury książki Wicket in Action natrafiłem na wzmianki o wsparciu Ajaxa. Dla ustalenia uwagi moją znajomość JavaScript i innych technologii klienckich określiłbym bliską zeru, więc każdorazowe wsparcie ze strony szkieletów aplikacyjnych w tym zakresie witanych jest przeze mnie z wielkim entuzjazmem. Tak jest z JavaServer Faces, i tak jest z Apache Wicket. Oba rozwiązania mają swoje zalety, gdzie podstawową zaletą JSF podczas porównania z Wicket jest możliwość uruchamiania JSP i jego znaczników, gdzie właśnie to jest podnoszone jako zaleta Wicketa, w którym użycie JSP jest niezwykle utrudnone, jeśli w ogóle w pełni możliwe. Zwróciłem na to uwagę podczas wykorzystania znaczników jpivot dla kostek OLAP, gdzie musiałem zwizualizować kilka takich struktur i jedynym sposobem mogło być wykorzystanie JSF.

Wracając do Wicketa, bo o nim chciałem wspomnieć i jego wsparciu Ajaxa, uruchomienie wstawek ajaksowych sprowadza się do wykorzystania odpowiedników ajaksowanych dla kontrolek akcyjnych - łącze (ang. link) czy przycisk (ang. button), np. org.apache.wicket.ajax.markup.html.AjaxFallbackLink dla org.apache.wicket.markup.html.link.Link (de facto AjaxFallbackLink rozszerza Link) oraz org.apache.wicket.ajax.markup.html.form.AjaxFallbackButton dla org.apache.wicket.markup.html.form.Button. Zaletą stosowania typów AjaxFallback* jest jednoczesna obsługa żądań ajaksowych i nieajaksowych, zlecając Wicketowi decyzję jaką obsługę wybrać do możliwości klienta (przeglądarki).

Dla zniecierpliwionych, podaję 5-minutowy "przepis" na ajaksową aplikację z Wicket. Jedyne co potrzeba, to Apache Maven 2 (dalej m2) oraz Eclipse IDE z wtyczką M2Eclipse, do pracy z projektami kontrolowanymi przez m2.

1. Utworzenie projektu wicket-ajax-demo
 $ mvn archetype:create \
-DarchetypeGroupId=org.apache.wicket \
-DarchetypeArtifactId=wicket-archetype-quickstart \
-DarchetypeVersion=1.4-m1 \
-DgroupId=pl.jaceklaskowski.wicket \
-DartifactId=wicket-ajax-demo
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO] task-segment: [archetype:create] (aggregator-style)
[INFO] ------------------------------------------------------------------------
...
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating OldArchetype: wicket-archetype-quickstart:1.4-m1
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: pl.jaceklaskowski.wicket
[INFO] Parameter: packageName, Value: pl.jaceklaskowski.wicket
[INFO] Parameter: basedir, Value: c:\projs\sandbox
[INFO] Parameter: package, Value: pl.jaceklaskowski.wicket
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: artifactId, Value: wicket-ajax-demo
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] OldArchetype created in dir: c:\projs\sandbox\wicket-ajax-demo
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
2. Import projektu do Eclipse

Importuję projekt wicket-ajax-demo do Eclipse z pomocą File > Import > Maven Projects.


3. Podniesienie wersji Wicket do 1.4-m1 w pom.xml

Poprawiam pom.xml o podniesienie wersji Wicket do 1.4-m1.
 <wicket.version>1.4-m1</wicket.version>
Eclipse zatroszczy się pobraniem wymaganych zależności projektowych. Dobrze być wtedy w Sieci, aby mogły być pobrane. Zaleca się skorzystanie z menu Maven > Download Sources, aby Eclipse pobrał źródła zależności, w tym i Wicketa z Sieci, co umożliwi podejrzenie jak to działa bezpośrednio w kodzie.

Podniesienie wersji spowoduje, że Eclipse oznaczy kilka klas jako błędne, co będzie wymagało kolejnego uaktualnienia pom.xml o wersję kompilatora do 1.5 za pomocą konfiguracji wtyczki maven-compiler-plugin.
 <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
Po zmianie najlepiej należy ponownie zaimportować projekt (wcześniej go usuwając) bądź urchomić polecenie mvn eclipse:eclipse z linii poleceń w katalogu projektu i F5 (Refresh) w Eclipse.

4. Modyfikacja strony domowej aplikacji - HomePage.html
 <html>
<head>
<title>Wicket Quickstart Archetype Homepage</title>
</head>
<body>
<strong>Wicket Quickstart Archetype Homepage</strong>
<br/><br/>
<span wicket:id="message">message will be here</span>
<br>
<a href="#" wicket:id="link">Wciśnij mnie</a>, a napis wyżej się zmieni.
</body>
</html>
5. Modyfikacja strony domowej aplikacji - pl.jaceklaskowski.wicket.HomePage

Dodanie wicketowego elementu do strony HTML wymaga odpowiedniej zmiany w klasie strony.
 package pl.jaceklaskowski.wicket;

import java.util.Calendar;
import java.util.Locale;

import org.apache.wicket.PageParameters;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.util.convert.IConverter;

public class HomePage extends WebPage {

private static final long serialVersionUID = 1L;

private Label label;
private Link link;

public HomePage(final PageParameters parameters) {

label = new Label("message", "If you see this message wicket is properly configured and running");
label.setOutputMarkupId(true);
add(label);
add(new AjaxFallbackLink("link") {

@Override
public void onClick(AjaxRequestTarget target) {
label.getModel().setObject("Wcisnieto mnie o " + Calendar.getInstance(new Locale("pl")).getTime());
if (target != null) {
target.addComponent(label);
}
}

});
}
}
Sprawdzenie target != null jest konieczne dla sytuacji, w której żadanie było nieajaksowe (klient nie wspiera Ajaxa).

Konieczne jest również wywołanie metody label.setOutputMarkupId(true), której brak spowoduje:
 java.lang.IllegalArgumentException: cannot update component that does not have setOutputMarkupId property set to true. 
Component: [Component id = message, page = pl.jaceklaskowski.wicket.HomePage, path = 1:message.Label, isVisible = true, isVersioned = true]
at org.apache.wicket.ajax.AjaxRequestTarget.addComponent(AjaxRequestTarget.java:343)
at pl.jaceklaskowski.wicket.HomePage$1.onClick(HomePage.java:31)
at org.apache.wicket.ajax.markup.html.AjaxFallbackLink$1.onEvent(AjaxFallbackLink.java:73)
at org.apache.wicket.ajax.AjaxEventBehavior.respond(AjaxEventBehavior.java:161)
at org.apache.wicket.ajax.AbstractDefaultAjaxBehavior.onRequest(AbstractDefaultAjaxBehavior.java:298)
at org.apache.wicket.request.target.component.listener.BehaviorRequestTarget.processEvents(BehaviorRequestTarget.java:100)
at org.apache.wicket.request.AbstractRequestCycleProcessor.processEvents(AbstractRequestCycleProcessor.java:91)
at org.apache.wicket.RequestCycle.processEventsAndRespond(RequestCycle.java:1174)
at org.apache.wicket.RequestCycle.step(RequestCycle.java:1251)
at org.apache.wicket.RequestCycle.steps(RequestCycle.java:1352)
at org.apache.wicket.RequestCycle.request(RequestCycle.java:499)
at org.apache.wicket.protocol.http.WicketFilter.doGet(WicketFilter.java:375)
at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:199)
...
podczas uruchomienia aplikacji bez tego wywołania.

6. Uruchomienie aplikacji z mvn jetty:run

Ostatecznie należy sprawdzić poprawność wprowadzonych zmian uruchamiając aplikację z wybranym kontenerem servletów. Jako, że korzystam z m2 stawiam na Jetty, którego uruchomienie i uruchomienie aplikacji webowej sprowadza się do wykonania polecenia mvn clean jetty:run (dodałem clean dla zapewnienia, że klasy zostały skompilowane w odpowiedniej wersji javy).
 $ mvn clean jetty:run
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'jetty'.
[INFO] ------------------------------------------------------------------------
[INFO] Building quickstart
[INFO] task-segment: [clean, jetty:run]
[INFO] ------------------------------------------------------------------------
...
[INFO] [jetty:run]
[INFO] Configuring Jetty for project: quickstart
[INFO] Webapp source directory = C:\projs\sandbox\wicket-ajax-demo\src\main\webapp
[INFO] web.xml file = C:\projs\sandbox\wicket-ajax-demo\src\main\webapp\WEB-INF\web.xml
[INFO] Classes = C:\projs\sandbox\wicket-ajax-demo\target\classes
2008-05-29 23:09:58.908::INFO: Logging to STDERR via org.mortbay.log.StdErrLog
[INFO] Context path = /wicket-ajax-demo
[INFO] Tmp directory = determined at runtime
[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml
[INFO] Web overrides = none
[INFO] Webapp directory = C:\projs\sandbox\wicket-ajax-demo\src\main\webapp
[INFO] Starting jetty 6.1.9 ...
2008-05-29 23:09:58.987::INFO: jetty-6.1.9
2008-05-29 23:09:59.112::INFO: No Transaction manager found - if your webapp requires one, please configure one.
INFO - Application - [WicketApplication] init: Wicket core library initializer
...
INFO - WebApplication - [WicketApplication] Started Wicket version 1.4-m1 in development mode
********************************************************************
*** WARNING: Wicket is running in DEVELOPMENT mode. ***
*** ^^^^^^^^^^^ ***
*** Do NOT deploy to your live server(s) without changing this. ***
*** See Application#getConfigurationType() for more information. ***
********************************************************************
2008-05-29 23:09:59.924::INFO: Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server
Przechodzę na adres http://localhost:8080/wicket-ajax-demo.

Wciśnięcie łącza Wciśnij mnie spowoduje zmianę napisu na zawierający datę wykonania obsługi wciśnięcia.

Niewielka aplikacja, a jak cieszy. I ta prostota Wicketa w kontekście obsługi Ajaxa - zero JavaScripu czy podobnie (!)

Pytanie konkursowe: Jaka jest rola typów AjaxFallback{Link,Button} w Wicket?

Do zobaczenia na JAVArsovii w nadchodzącą sobotę 31.05.2008 w godzinach 9:00-19:00, gdzie podczas mojej prezentacji o Apache Wicket pokaże tą i inne aplikacje webowe z nim na pokładzie. Jutro audycja w Polskim Radio EURO o 9:00. Ciekawym Waszych opinii odnośnie naszego radiowego występu (więcej o nim we wczorajszej notatce 3 dni do JAVArsovii 2008, audycja w Polskim Radiu, @Override i skróty netbeansowe). Wracam do lektury książki Wicket in Action.

28 maja 2008

3 dni do JAVArsovii 2008, audycja w Polskim Radiu, @Override i skróty netbeansowe

3 komentarzy
Ostatnie dni przed JAVArsovią 2008 to okres zwiększonej aktywności przy domykaniu spraw organizacyjnych i wspólna praca w ramach Kapituły JAVArsovii prowokuje do ciekawych przemyśleń i daje niezapomniane wrażenia. Jako potwierdzenie przytoczę słowa jednego kapitułowicza, którego pozwolę jednak pozostawić anonimowym:

Tak sobie myślę, że nasz "komitet" jest doskonałym case-study do rozprawy o procesie "docierania" się ludzi w zespole. Kłótni coraz mniej a coraz więcej jednomyślności.

Nie ma to jak zespół całkowicie niezależnych osób, którzy skrzyknęli się, aby zorganizować najciekawszą konferencję javową w Polsce. Czy się uda, okaże się już niebawem, gdyż jutro (29.05.2008 o 23:59) zamykamy obowiązkową rejestrację na konferencję, a ona sama odbędzie się już w nadchodzącą sobotę 31.05.2008 w godzinach 9:00-19:00. Konferencja jest całkowicie bezpłatna, a rejestracja pozwala nam na oszacowanie Waszej liczności. Jedynie zarejestrowani otrzymają wikt i opierunek ;-) Będą koszulki, coś do jedzenia i picia, dużo zabawy i technologii javowych, aby na zakończenie wśród uczestników wyłonić szczęśliwców, którzy zostaną zaproszeni na imprezę podsumowującą konferencję w Lolku na pobliskich Polach Mokotowskich. Do tej pory zarejestrowało się 296 (!) uczestników konferencji, więc śmiało można założyć, że będzie to konferencja wielu naj - najtłoczniejsza sobotnia impreza w Warszawie, najciekawsza konferencja javowa w Warszawie (Polsce?), najmniej skomercjalizowana i takie tam. Możnaby tak najować, a chodzi przede wszystkim o zaktywizowanie polskiej społeczności javowej. Jak będzie zależy od Was - co można było zrobić organizacyjnie zostało (prawie) zrobione. Teraz sami uczestnicy są odpowiedzialni za spędzony na konferencji czas. Nawołuję do zwiększonej niż zwykle aktywności w postaci pytań do prelegentów, prowokowania rozmów podczas wystąpień i w trakcie przerw, poznawania nie tylko technologii, ale i ludzi, którzy się tym parają. Od nas samych zależy, jaki ostatecznie będzie oddźwięk konferencji i czy sobotni czas na JAVArsovii będzie produktywny. Dziękuję wszystkim zarejestrowanym i zachęcam pozostałych do naśladowania, i rejestracji.

A zapomniałbym o jeszcze jednej ciekawostce związanej z JAVArsovią. Kapituła otrzymała zaproszenie do Polskiego Radia na piątkowy porannek w godzinach 9:00-10:00 do audycji Czwarty Wymiar, gdzie Michał Margiel, Mariusz Lipiński i Ja(cek Laskowski) wystąpią jako jej przedstawiciele. Ma być ciekawie i tyle wiadomo. Chętnych zapraszam na częstotliwość radia 92,0 MHz w Warszawie (mapka częstotliwości na stronie Polskiego Radia EURO). Na pewno nie zabraknie wpadek, więc warto wysłuchać. Będzie ubaw po pachy! ;-)

Na zakończenie trochę bardziej technicznie. Nie miałem świadomości roli jaką pełni adnotacja @Override w Javie, aż do momentu, kiedy rozpocząłem lekturę książki Wicket in Action autorstwa Martijn Dashorst oraz Eelco Hillenius (aktualnie dostępna w Manning Early Access Program oraz bibliotece Warszawa JUG). Na @Override natrafiłem już w pierwszym rozdziale, w którym przeczytałem, że jest to wskazówka dla kompilatora o zamierzeniu przesłonięcia odpowiedniej metody klasy macierzystej. Zaintrygowało mnie to, aby sprawdzić javadoc dla @Override, gdzie doczytałem, że adnotacją oznaczamy metody, które oczekujemy, że przesłaniają metody nadrzędnej klasy, a w przypadku braku takowych, kompilator powinien (musi?) poinformować o braku stosownym komunikatem. Niby Eclipse usilnie przekonywał mnie do stosowania @Override, ale nigdy nie zebrałem się w sobie do sprawdzenia jej celu stosowania. Teraz mam to za sobą. Wniosek: warto czytać książki, a Wicket in Action zapowiada się niezwykle ciekawie. Na jej barkach chciałbym przedstawić kilka ciekawostek podczas mojej prezentacji na konferencji JAVArsovia 2008 - Apache Wicket w przykładach. Zapraszam! Jako kontrprelegentów mam Marka Newtona z JBoss.org o JBoss AS 5.0 oraz Arkadiusza Rosińskiego (Javart) o profilowaniu javy, więc rywalizacja o uczestników spotkania wysoka.

Na zakończenie wspomnę o ciekawych skrótach klawiszowych Ctrl+; oraz Ctrl+Shift+; w NetBeans IDE 6, które przedstawił Geertjan Wielenga w artykule Two NetBeans Keyboard Shortcuts That Will Change Your Life (Forever). Ciekawe, czy są ich odpowiedniki w Eclipse? Wydają się być wręcz niezastąpione.

W międzyczasie zająłem się rozpoznawaniem Apache MyFaces Trinidad. Pierwsza moja aplikacja demonstracyjna z tr:tree oraz TreeModel spełniła pokładane nadzieje, więc już niedługo (pewnie po konferencji JAVArsovia) należy oczekiwać wzmianek o Trinidadzie. I nie będzie o geografii, a całkowicie technicznie. Nigdy nie miałem dobrego zdania o Apache MyFaces i wydaje mi się, że było to podyktowane opiniami innych osób niż własnymi, a pierwsze spotkanie z nim i Trinidadem było jak najbardziej pozytywne. Już niedługo przekonam się o ich wartości na własnej skórze. Pierwsze lody przełamane, idę dalej w las ;-)

Do zobaczenia na konferencji JAVArsovia 2008!

Pytanie konkursowe: Czy adnotacja @Override jest obowiązkowa, jeśli przesłaniamy metody klasy macierzystej w Javie 5? Nagroda czeka na szczęśliwców 31. maja 2008 na ul. Ilji Miecznikowa 1.

24 maja 2008

Deklaratywne bezpieczeństwo w JSF z JAAS i Jetty

2 komentarzy
Podczas kolejnej wycieczki technologicznej przyszło mi się zmierzyć z tematem, o który byłem pytany wielokrotnie, ale nie znałem odpowiedzi - deklaratywne uwierzytelnianie i autoryzacja w aplikacjach webowych z JSF. W ogóle temat bezpieczeństwa w aplikacjach korporacyjnych był traktowany przeze mnie po macoszemu i nigdy nie doczekał się odpowiedniego traktowania. Aż do dzisiaj, kiedy to postanowiłem nie odpuszczać, aż do znalezienia rozwiązania.

Zadanie: Zabezpieczenie aplikacji webowej opartej o JavaServer Faces (JSF) przed nieuprawnionym dostępem, czyli wprowadzenie mechanizmu deklaratywnego uwierzytelnienia i autoryzacji.

Rozwiązanie znajdziesz w moim nowym artykule Deklaratywne bezpieczeństwo w JSF z JAAS i Jetty. Miłej lektury!

Pytanie konkursowe: Za pomocą jakich 3 głównych elementów deskryptora wdrożenia WEB-INF/web.xml definiuje się bezpieczeństwo deklaratywnie? Nagrodą jest bezpłatny udział w konferencji JAVArsovia 2008 w nadchodzącą sobotę 31 maja 2008. Pamiętaj o rejestracji!

23 maja 2008

Wprowadzenie do JAXB

1 komentarzy
Uczestnictwo w spotkaniach grupy Warszawa JUG daje mi tą nieocenioną możliwość poznawania technologii praktycznie praktycznie bez uderzenia w klawiaturę (podwójnie użycie praktycznie świadome). Brzmi jak masło maślane, ale kiedy zastanowić się nad tym chwilę, to wszystko z pewnością stanie się jasne. Podczas spotkań większość z prezentowanego materiału okraszona jest mnóstwem przykładów, które nierzadko pisane są na bieżąco w IDE. W tym sensie mam praktyczne poznanie technologii, a że jedynie patrzę i rozmawiam z innymi uczestnikami, a nie stukam po klawiaturze, to i praktycznie odbywa się to bez mojego aktywnego obcowania z nią. Po prostu wymarzony sposób na poznawanie nowych technologii, mając bardzo ograniczony na nie "budżet" czasowy. Tak samo stało się ze specyfikacją JAXB (The Java Architecture for XML Binding), gdzie Łukasz Dywicki podczas jednego ze spotkań Warszawa JUG, a później podczas konferencji WarsJava 2007 zaprezentował JAXB. Niby wszystko proste i łatwe do użycia, ale kiedy ostatnio dostałem zadanie zapisania danych do bazy danych i plików, chwilę trwało zanim po JPA (Java Persistence API) pojawił się i JAXB. W końcu udało mi się namierzyć projekt, w którym mógłbym skorzystać z JAXB, więc i mogłem go praktycznie wypróbować.

Zadanie do wykonania: zapis/odczyt danych do/z plików XML z użyciem aktualnego modelu danych aplikacji (który wcześniej wykorzystałem przy mapowaniu opartym o JPA).

Zainteresowany rozwiązaniem? Zajrzyj do mojego nowego artykułu Wprowadzenie do JAXB. Chętnie wysłucham Twoich spostrzeżeń odnośnie zaprezentowanego podejścia.

Dzisiejsze pytanie konkursowe to: Jaka adnotacja JPA odpowiada funkcjonalnie adnotacji @XmlTransient w JAXB? Nagrodą jest bezpłatny udział w konferencji JAVArsovia 2008, gdzie takie i wiele innych ciekawostek będzie prezentowane. Koniecznie się zarejestruj!

21 maja 2008

JAVArsovia 2008 - II edycja Konferencji Java grupy Warszawa JUG

0 komentarzy
31 maja 2008 w godzinach 9:00 - 19:00 w gmachu Wydziału Biologii Uniwersytetu Warszawskiego przy ul. Ilji Miecznikowa 1 Warszawska Grupa Użytkowników Technologii Java (Warszawa JUG) oraz osoby zaprzyjaźnione (PJUG, Szczecin JUG, Trójmiasto JUG, Poznań JUG, Wrocław JUG) zapraszają na jedyną w swoim rodzaju konferencję javową w Polsce - JAVArsovię 2008.

Motto konferencji: Miłośnicy Java dla miłośników Java aka Ciekawie o Javie.

Podczas sobotniej konferencji zaprezentowanych zostanie wiele różnych technologii javowych i szkieletów aplikacyjnych. Z czterema równoległymi ścieżkami, każda z czterema prezentacjami, pragniemy, aby konferencja była ciekawa technologicznie oraz odzwierciedlała nasze własne, lokalne doświadczenia z Javą. JAVArsovia 2008 to idealne miejsce, aby spotkać się z innymi praktykami technologii, nawiązać kontakty, porozmawiać o bieżących problemach (technologicznych) i poznać nowe narzędzia, które usprawnią Twoje projekty. Sądzimy, że udział w konferencji JAVArsovia 2008 pozwoli każdemu popróbować się z nowościami technologicznymi lub zgłębić już poznane.

Wstęp wolny! Dobra zabawa gwarantowana!

Rejestracja na konferencję jest bezpłatna acz obowiązkowa i trwa do 29 maja! Na zakończenie konferencji zaplanowane jest spotkanie podsumowujące JAVArsovię na Polach Mokotowskich. Liczba miejsc ograniczona, więc czym wcześniej się zarejestrujesz, tym większa szansa na udział w niej.

Pierwsza edycja JAVArsovii 2007 była hitem czerwca 2007 i zaktywizowała polską scenę javową. Naszą intencją jest zorganizowanie największej i najbardziej hucznej konferencji javowej w Polsce w 2008 roku. Bądź jej częścią!

Zapraszam w imieniu Kapituły JAVArsovii 2008 i grupy Warszawa JUG!

20 maja 2008

Dalsze atrakcje aplikacji JDA z JPA - @Enumerated

3 komentarzy
Kolejny dzień potyczek z aplikacją zbudowaną przy pomocy JDA (Java Desktop Application). Właśnie niedawno natrafiłem na ten skrót i zaskoczyła mnie zbieżność z JPA (Java Persistence API), którą również wykorzystuję w aplikacji Przychodnia. Jednym z zadań, jakie zaplanowałem w kontekście udoskonalania aplikacji była migracja pola plec typu boolean na typ wyliczeniowy (enum). Pamiętałem jeszcze z lektury specyfikacji i rozmów na grupach, że typy wyliczeniowe nie są czymś nadzwyczajnym dla JPA, więc nie spodziewałem się wielkich trudności, a mimo to i tak owa prostota rozwiązania zaskoczyła mnie.

Najpierw lektura specyfikacji JPA - rozdział 9.1.21 Enumerated Annotation (strona 181):

Domyślnie pole typu wyliczeniowego utrwalane (zapisywane) jest w bazie danych po jego liczbie porządkowej (wywołanie metody java.lang.Enum.ordinal()). Za pomocą adnotacji @Enumerated określany jest sposób mapowania pola typu wyliczeniowego, jako uzupełnienie dane z adnotacji @Basic (oczywiście adnotacja nie jest konieczna w wielu sytuacjach, więc najczęściej bazuje się na wartościach domyślnych adnotacji @Basic). Klasa java.lang.Enum, która reprezentuje wszystkie typy wyliczeniowe w Javie dostarcza dwie metody - ordinal() oraz name(), które uczestniczą w mapowaniu pola wyliczeniowego na struktury bazodanowe. Pierwsza z nich ordinal() wywoływana jest przy konfiguracji ORDINAL dla adnotacji @Enumerated, a druga przy wartości STRING. Domyślnie zakłada się ORDINAL, więc wszystkie typy wyliczeniowe będą zapisywane w bazie jako liczby porządkowe. Za pomocą STRING do bazy trafiają nazwy wartości typu wyliczeniowego.

Adnotacje posiadają swoje odpowiedniki w postaci elementów w pliku konfiguracji mapowania JPA, którym domyślnie jest META-INF/orm.xml (inne można wskazać za pomocą elementu mapping-file w META-INF/persistence.xml - "sercu" konfiguracji JPA lub nie wprost poprzez element jar-file). Odpowiednikiem xmlowym (zawartym w pliku orm.xml) adnotacji @Enumerated jest element enumerated (podelement basic), który akceptuje dwie wartości ORDINAL (domyślna) i STRING.

W mojej aplikacji Przychodnia zadeklarowałem typ wyliczeniowy pl.jaceklaskowski.przychodnia.model.Plec (początkowo był on w ramach klasy Pacjent, ale okazało się, że JDA narzekał na zagnieżdzenie klas wykorzystywanych do mapowania JPA).
 package pl.jaceklaskowski.przychodnia.model;

public enum Plec {

KOBIETA('K', "Kobieta"), MEZCZYZNA('M', "Mężczyzna");

private final char kod;
private final String nazwa;

Plec(char kod, String nazwa) {
this.kod = kod;
this.nazwa = nazwa;
}

public char kod() {
return kod;
}

@Override
public String toString() {
return this.nazwa;
}

public static Plec valueOf(int cyfra) {
return cyfra % 2 == 0 ? KOBIETA : MEZCZYZNA;
}

}
W pliku mapowania JPA - META-INF/orm.xml - wystarczyło dodać
 <basic name="plec" optional="false">
<enumerated>STRING</enumerated>
</basic>
aby do bazy trafiały napisy w postaci MEZCZYZNA lub KOBIETA. Nie rozwiązałem jeszcze kwestii zapisywania samych kodów typów, np. M dla mężczyzny, a K dla kobiety (ma ktoś pomysł jak to zrealizować, aby kod typu był wartością w bazie danych?).

Przy okazji zastosowania typu wyliczeniowego skorzystałem ze statycznego importu (import static) i kod stał się bardziej przejrzysty i czytelny. Wychodzę z założenia, że najpierw działający kod w wersji 1.0, a później jego uaktrakcyjnianie w kolejnych wersjach. W ten sposób podszedłem właśnie do zadania aplikacji Przychodnia.

Dodatkowo pola plec oraz wiek są ustawiane na podstawie obowiązkowego peselu, co sprowadziło się do następującej konstrukcji:
 protected void ustawWiekPlecNaPodstawiePeselu() {

// za Wikipedią o wieku w PESELu:
// http://pl.wikipedia.org/wiki/PESEL#P.C5.82e.C4.87
// Numeryczny zapis daty urodzenia przedstawiony jest w następującym porządku:
// dwie ostatnie cyfry roku, miesiąc i dzień.
// Dla odróżnienia poszczególnych stuleci przyjęto następującą metodę kodowania:
// * dla osób urodzonych w latach 1900 do 1999 - miesiąc zapisywany jest w sposób naturalny
// * dla osób urodzonych w innych latach niż 1900 - 1999 dodawane są do numeru miesiąca następujące wielkości:
// o dla lat 1800-1899 - 80
// o dla lat 2000-2099 - 20
// o dla lat 2100-2199 - 40
// o dla lat 2200-2299 - 60
int rokDzisiaj = Calendar.getInstance().get(Calendar.YEAR);
int rok = Integer.valueOf(this.pesel.substring(0, 2));
int miesiac = Integer.valueOf(this.pesel.substring(2, 4));
if (miesiac > 80) {
miesiac -= 80;
rok += 1800;
} else if (miesiac > 60) {
miesiac -= 60;
rok += 2200;
} else if (miesiac > 40) {
miesiac -= 40;
rok += 2100;
} else if (miesiac > 20) {
miesiac -= 20;
rok += 2000;
} else {
rok += 1900;
}
this.wiek = rokDzisiaj - rok;
if (this.wiek <= 0) {
throw new IllegalArgumentException("Niedozwolony wiek " + this.wiek + " [PESEL:" + this.pesel + "]");
}

// za Wikipedią o płci w PESELu:
// http://pl.wikipedia.org/wiki/PESEL#P.C5.82e.C4.87
// Informacja o płci osoby, której zestaw informacji jest identyfikowany,
// zawarta jest na 10 (przedostatniej) pozycji numeru PESEL.
// * cyfry parzyste (0, 2, 4, 6, 8) – oznaczają płeć żeńską
// * cyfry nieparzyste (1, 3, 5, 7, 9) – oznaczają płeć męską
this.plec = Plec.valueOf(Integer.valueOf(this.pesel.substring(9, 10)) % 2);
}
Coś mi mówi, że można byłoby to zrealizować prościej, ale nic na szybko nie przyszło mi do głowy, poza owym if-(else if)*-else. Sugestie mile widziane.

Niestety, sądziłem, że ta zmiana pociągnie za sobą zmiany w tworzonej dynamicznie tabeli przez JDA, tak że użytkownik będzie mógł wybrać płeć pacjenta z listy tworzonej na podstawie możliwych wartości typu wyliczeniowego, ale nic takiego się nie wydarzyło. W zamian pole stało się niemodyfikowalne, co w zasadzie jest równie dobre, gdyż jego wartość i tak wyliczana jest na podstawie obowiązkowego peselu.

Jutro zaplanowałem zabawę z formatowaniem danych w JTable podczas wyświetlania (renderery komórek) i edycji (edytory komórek).

18 maja 2008

Tworzenie aplikacji desktopowej z Java Desktop Application w NetBeans IDE 6.1 - część 2

2 komentarzy
W poprzednim artykule Tworzenie aplikacji desktopowej z Java Desktop Application w NetBeans IDE 6.1 przedstawiłem procedurę tworzenia aplikacji desktopowej "podpierając się" asystentem Java Desktop Application dostępnym w NetBeans IDE 6.1 oraz Java Persistence API (JPA) jako mechanizmem tworzenia odpowiednich struktur bazodanowych automatycznie. Głównym mankamentem aplikacji był brak korzystania z modelu z projektu przychodnia-model, gdzie pewne wartości były generowane automatycznie, a samo mapowanie encji odbywało się poprzez wydzielony plik mapowania META-INF/orm.xml.

W artykule Tworzenie aplikacji desktopowej z Java Desktop Application w NetBeans IDE 6.1 - część 2 przedstawiłem kolejne etapy udoskonalania aplikacji o wykorzystanie własnego modelu z projektu przychodnia-model z konfiguracją JPA z projektu przychodnia-generatortabel. Dużo dobrej zabawy w wykonaniu NetBeans IDE 6.1 i projektem Java Desktop Application.

W ramach artykułów powstaje wiele zadań do rozwiązania, które mogą być doskonałym sposobem na pogłębienie wiedzy w zakresie NetBeans IDE i tworzenia aplikacji desktopowych korzystających z Java Persistence API (JPA). Zadania należą do kategorii łatwiejszych, a to gwarantuje, że zadanie nie ma szansy się niepowieść, a tym samym i satysfakcje z wykonanego zadania. Same zalety, a jak wiedza wzrasta. Rozwiązania opublikuję w kolejnych wpisach.

p.s. Nadchodząca konferencja JAVArsovia 2008 31-ego maja 2008 może jeszcze bardziej uatrakcyjnić Twoje programistyczne "wypady" i tego typu doświadczeń technologicznych będzie znacznie więcej. Jeśli jesteś zainteresowany/-a pogłębieniem wiedzy praktycznie, to właśnie udział w konferencji JAVArsovia 2008 jest doskonałym miejscem, aby się popróbować z nowościami technologicznymi lub zgłębić już poznane. Jako przedstawiciel Kapituły JAVArsovii 2008 gorąco zapraszam do rejestracji na stronie konferencji i aktywnego uczestniczenia w niej. Do 31 maja już niedaleko, a skoro JAVArsovia pretenduje do miana największej konferencji javowej w Polsce, to na pewno nie może Cię na niej zabraknąć. Wstęp wolny! Dobra zabawa gwarantowana!

16 maja 2008

Tworzenie aplikacji desktopowej z Java Desktop Application w NetBeans IDE 6.1

4 komentarzy
W środę żona zrobiła mi niesamowitą niespodziankę! Bodajże na miesiąc przed spytała jedynie, czy mam czas w środę o 19:00 i tyle. Jakbym mógł odmówić mojej Agatce?! Oczywiście wszystkie możliwe inicjatywy przeniosłem zaraz na bezpieczniejszy czwartek i cierpliwie czekałem. W końcu okazało się, że owa niespodzianka to wyjście do Teatru Bajka na przedstawienie Goło i wesoło. Jeju, ale to było przedstawienie! Prawie umarłem ze śmiechu. Płacz murowany! Do tej pory kiedykolwiek przypomnę ją sobie, nie mogę powstrzymać się ze śmiechu. To jest obowiązkowy spektakl, dla każdego lubującego się w dobrze dobranym humorze z doborową obsadą aktorską. Po prostu cudeńko. Nie będę rozpisywał się na temat sztuki, bo szkoda byłoby uchylić chociażby rąbek tajemnicy na temat sztuki. Wystarczy, że napiszę, że jestem gotów iść na nią kolejny raz. Normalnie Góra, Żmuda, Bugi, Gustaw (szczególnie w drugiej części), Norbert i Kierownik to mistrzostwo świata w dobrym humorze. Ubaw po pachy! Dzięki Agata za wspaniały wybór!

A wracając do Javy to ostatnio natrafiłem na ciekawe zadanie, które dotyczyło stworzenia aplikacji desktopowej korzystającej z bazy danych. Czasu na jej utworzenie nie było wiele i wskazano na NetBeans IDE 6.1 jako narzędzie, które w ciekawy sposób upraszcza jej tworzenie za pomocą projektu Java Desktop Application. Podczas rozpoznawania funkcjonalności NetBeans natrafiłem na ciekawy wycinek specyfikacji Java Persistence API (JPA), gdzie mapowanie relacji jeden-do-wielu (adnotacja @OneToMany lub element <one-to-many>) można sprowadzić do konfiguracji opartej o 2 tabele z pojedyńczym kluczem obcym (z domyślnie wykorzystywanych 3 tabel z dwoma kluczami obcymi w tabeli łączącej). Użyteczna funkcjonalność NetBeans IDE 6.1 i ciekawostka JPA to równie ciekawie spędzony czas, co zaowocowało nowym artykułem Tworzenie aplikacji desktopowej z Java Desktop Application w NetBeans IDE 6.1. Zapraszam do lektury, a znudzonych siedzeniem przed kompem na spektakl Goło i wesoło. Koniecznie napisz, jak podobał się spektakl. Dla mnie był najwyższych lotów.

p.s. Kilka godzin temu przejeżdżałem koło podwrocławskiej wioski Jugowiec (a może to było miasto?). Jakby na przypomnienie, że niedługo JAVArsovia 2008 (dokładnie za 2 tygodnie - 31 maja 2008), gdzie jugowiczów będzie co nie miara. Ciekawe, czy będzie ktoś z podwrocławskiego Jugowca? Zapraszam! Wstęp wolny.

13 maja 2008

NetBeans Blogging Contest: Winner!

2 komentarzy
Dzisiaj zostałem nagrodzony dwukrotnie. Najpierw wystąpiłem na spotkaniu Warszawa JUG z prezentacją o Apache Wicket, gdzie padło tyle pytań i pojawiło się tyle wyjątków, że w ostatnim miesiącu nie zdobyłem tyle wiedzy, co właśnie podczas 31. spotkania! Przygotowuję się z tematem o Apache Wicket i OSGi na nadchodzącą konferencję JAVArsovia 2008, która jest organizowana przez grupę Warszawa JUG w dniu 31 maja 2008 w Warszawie i nie ukrywałem, że możliwość wystąpienia z Wicketem na 2 tygodnie przed konferencją jest dla mnie próbą generalną mojego przygotowania oraz zebrania dodatkowych wskazówek o potencjalnych obszarach, które należy uzupełnić, omówić, itp. Wierzę, że znalazły się osoby na spotkaniu, którym Wicket przypadł do gustu, a niewielka liczba przykładów gwarantuje, że jeszcze ciekawiej przed nimi. Z przyjemnością zebrałbym garść uwag odnośnie mojego wystąpienia i co powinienem dopracować, aby moje wystąpienie na JAVArsovia 2008 otrzymało miano najciekawiej prowadzonej (świadomie nie napisałem najciekawszej, gdyż temat jednego może zainteresować, a drugiego skłonić do odespania ostatniej niedospanej nocy - pozdrowienia dla dzisiejszych śpiochów! ;-))

Na chwilę przed wystąpieniem miałem możliwość zajrzeć do następującej wiadomości zatytułowanej NetBeans Blogging Contest: Winner!:

Jacek:

Congratulations! Your blog entry on NetBeans 6.1 was selected by our panel of judges as a winner of our NetBeans Blogging Contest.
[...]
Your blog entry will be promoted on the NetBeans Blogging Contest homepage.

Zamarłem! Nie żebym nie liczył na wygraną, bo w końcu uczestniczyłem w konkursie i udało mi się znaleźć czas na ewaluację kilku funkcjonalności właśnie, aby ostatecznie zostać wyróżnionym, ale kiedy już zostałem nagrodzony, to o tym wszystkim się zapomina, a nawet jest się zaskoczonym. Na stronie NetBeans IDE 6.1 Blogging Contest można znaleźć szczegóły konkursu i jego nagrodzonych, wśród których jestem i ja z tematem o wsparciu Spring Framework przez NetBeans IDE 6.1. Tym samym chciałbym podziękować jury, a w szczególności stałej czytelniczce i członkini jury - Magdalenie Gołdyn. Dziękuję Magda za cierpliwość i wskazanie!

11 maja 2008

31. spotkanie Warszawskiej Grupy Użytkowników Technologii Java (Warszawa JUG)

0 komentarzy
Warszawska Grupa Użytkowników Technologii Java (Warszawa JUG) zaprasza na 31. spotkanie, które odbędzie się 13.05.2008 o godzinie 18:00 w sali 5440 Wydziału MIMUW przy ul. Banacha 2 w Warszawie.

Temat prezentacji: Apache Wicket w przykładach
Prowadzący: Jacek Laskowski

Apache Wicket jest szkieletem webowym, którego podwalinami jest zarzucenie JSP jako technologii prezentacji, stworzenie warstwy modelu opartego o POJO i zniesienie konieczności stosowania wszechobecnego XMLa. Na spotkaniu Jacek zaprezentuje wiele przykładów, za pomocą których zamierza zademonstrować siłę Wicketa i przekonać do jego spróbowania w projektach. Odświeżające odseparowanie HTMLa od JSP, całkowite zaniechanie XMLa do konfiguracji aplikacji oraz wiele ciekawych rozwiązań wspierających tworzenia aplikacji webowych jak silne typowanie obiektów o zasięgu sesji czy aplikacji, wstrzymywanie żądania i przekierowywanie na stronę uwierzytelnienia są ciekawą alternatywą dla innych, znanych szkieletów webowych. Wiele z rozwiązań Wicket z pewnością sprowokuje do poszukiwania ich odpowiedników w obecnie korzystanych szkieletach. Mnóstwo przykładów gwarantuje praktyczne zrozumienie Wicketa.

Prezentację poprowadzi Jacek Laskowski, który jest pasjonatem tworzenia aplikacji javowych korzystających z Java EE i projektów otwartych. Jacek jest członkiem zespołów Apache Geronimo, Apache OpenEJB, Apache ServiceMix, Apache XBean oraz Apache ActiveMQ. Jestem założycielem i liderem Warszawskiej Grupy Użytkowników Technologii Java (Warszawa JUG). Bierze aktywny udział w wielu forach dyskusyjnych i otwartych projektach. Własne doświadczenia z językiem Java, Java EE i otwartymi projektami opisuje w Notatniku Projektanta Java EE. Służbowo pracuje na stanowisku konsultanta oprogramowania w IBM Polska.

Planowany czas prezentacji to 1,5 godziny, po której planuje się 15-30-minutową dyskusję.

Wstęp wolny!

Zapraszam w swoim imieniu i grupy Warszawa JUG!

09 maja 2008

Umiędzynarodowienie w Apache Wicket

3 komentarzy
Tempo rozwoju Apache Wicket może zdumiewać, gdyż jeszcze nie tak dawno podnosiłem wersję Wicket mojej testowej aplikacji do 1.3.3, a już mamy 1.4-m1. Z pomocą Apache Maven 2 temat sprowadził się do uaktualnienia pom.xml, w którym podniosłem wersję Wicketa oraz Spring Framework (do wersji 2.5.4). Jedną z głównych zmian w Wicketa 1.4 jest skorzystanie z mechanizmu wzorców w Javie (Java Generics), tak że musiałem zmienić sygnaturę metody WicketDemoApplication.getHomePage(), która zawęziła typ zwracanych obiektów do Class<? extends org.apache.wicket.Page>.
 c:\projs\sandbox\wicket-demo\src\main\java\pl\jaceklaskowski\wicket\WicketDemoApplication.java:[66,20] getHomePage() 
in pl.jaceklaskowski.wicket.WicketDemoApplication cannot override getHomePage() in org.apache.wicket.Application;
attempting to use incompatible return type
found : java.lang.Class<?>
required: java.lang.Class<? extends org.apache.wicket.Page>
Do mojej prezentacji Wicketa na kolejnym,wtorkowym spotkaniu Warszawa JUG pozostało już niewiele dni, więc kończąc lekturę książki Pro Wicket (Apress) trafiłem na rozdział przedstawiający mechanizm i18n - umiędzynarodowienia, który polega na przeniesieniu napisów do plików properties z przypisaniem im identyfikatorów wraz z ich wykorzystaniem w dedykowanych znacznikach <wicket:message>. Identyfikatory są niezmienne i zapisane są w ciele stron aplikacji, podczas gdy ich wartości zależne od języka są zapisane w plikach properties. Wskazanie miejsca umiędzynarodowienia polega na skorzystaniu ze znacznika <wicket:message key="..." />, który odszuka właściwego napisu dla podanego klucza (identyfikatora) wskazanego przez atrybut key. Pozostaje przedstawić, gdzie pliki properties powinny się znajdować. Jest kilka wariantów umieszczenia pliku properties, co daje nam różne poziomy granulacji komunikatów dla pojedyńczego formularza, pojedyńczej strony, dla całej grupy stron czy dla całej aplikacji. Nie wnikając w niepotrzebne szczegóły, umieszczę napisy w pliku odpowiadającym nazwie strony. Cała radość w "czystość" plików HTML została nieznacznie zaburzona wprowadzeniem specyficznych dla Wicketa znaczników <wicket:message>.

Oto strona DaneOsobowe.html ze znacznikami <wicket:message/>:
 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns:wicket="http://wicket.apache.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Wicket Demo App</title>
</head>
<body>
<strong>Wicket Demo App</strong>
<span wicket:id="komunikaty"></span>
<form wicket:id="daneOsobowe" action="">
<table>
<tr>
<td><wicket:message key="imie" />:</td>
<td colspan="2"><input type="text" wicket:id="imie" /></td>
</tr>
<tr>
<td><wicket:message key="nazwisko" />:</td>
<td colspan="2"><input type="text" wicket:id="nazwisko" /></td>
</tr>
<tr>
<td><wicket:message key="login" />:</td>
<td colspan="2"><input type="text" wicket:id="login" /></td>
</tr>
<tr>
<td><wicket:message key="miejscowosc" />:</td>
<td><select wicket:id="miejscowosc">
<option>Cokolwiek</option>
</select></td>
<td><input type="submit" value="Zatwierdź" /></td>
</tr>
</table>
</form>
</body>
</html>
i odpowiadający jej plik DaneOsobowe.properties z wartościami kluczy:
 imie=Imi\u0119
nazwisko=Nazwisko
login=Login
miejscowosc=Miejscowo\u015b\u0107
W zasadzie to plik DaneOsobowe.properties powinienem był nazwać DaneOsobowe_pl.properties, aby wskazać jaki język plik zawiera, ale dla prostoty przykłady postanowiłem zaniechać tego.

I to tyle. Teraz wystarczy przenieść wszystkie napisy do odpowiednich plików properties i stworzyć wiele ich wersji językowych dla każdego wspieranego przez aplikację języka. I to bez modyfikacji kodu źródłowego! Wprowadzenie obsługi wersji językowej dla kolejnego języka sprowadza się teraz do dodania kolejnego pliku properties.

Pytanie konkursowe: Jaka jest geneza akronimu i18n, który tłumaczy się jako umiędzynarodowienie? Nagród nie przewiduje się.

07 maja 2008

Blok inicjalizujący egzemplarz w Javie

6 komentarzy
W ostatnim wydaniu gazetki [JavaSpecialists] Issue 159 - The Law of Sudden Riches od dr Heinz M. Kabutza można było spotkać następujący kod:
 import java.util.concurrent.*;

public class BankAccountTest {
public static void main(String[] args) {
final BankAccount account = new BankAccount(1000);
for (int i = 0; i < 2; i++) {
new Thread() {
{
start();
}

public void run() {
while (true) {
account.deposit(100);
account.withdraw(100);
}
}
};
}
ScheduledExecutorService timer =
Executors.newSingleThreadScheduledExecutor();
timer.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println(account.getBalance());
}
}, 1, 1, TimeUnit.SECONDS);
}
}
U "naszego" Wiktora Gworka natrafiłem na Poznajemy nowe języki: Scala, czyli jak wypisać elementy z listy, gdzie zaprezentował taki oto fragment kodu źródłowego:
 List lista = new ArrayList() {{
add(1); add(2); add(3); add(4);
}};
I dlaczego ja o tym?! Ciekawostką Javy jest sposób inicjalizacji klas. Jest kilka jej etapów, a tym, co łączy przytoczone przykłady dr Kabutza oraz Wiktora to użycie bloku inicjującego egzemplarz (8.6 Instance Initializers). W skrócie: blok egzemplarza wykonywany jest po wykonaniu konstruktora klas nadrzędnych, a przed wykonaniem konstruktora. Bardzo przydatna funkcjonalność, szczególnie w trakcie prototypowania. Wciąż o niej zapominam, więc teraz już zostanie w aktach.

Dodatkowo, jakbyśmy się zmówili z Mariuszem, który na swoim blogu umieścił wpis SCJP - Konstruktory i inicjalizacja. Jakby komukolwiek było mało wiedzy odnośnie tematu inicjowania klas, to może zapoznać się z nim u Mariusza. A warto, gdyż (potencjalnie) ma to ogromne znaczenie w kontenerach IoC/DI, gdzie inicjalizację klasy deleguje się do kontenera. Pierwszy raz natrafiłem na tę informację w książce Pro Wicket (Apress, 2006), gdzie autor umieścił ciekawy komentarz o unikatowej dla mnie wartości edukacyjnej, w którym wspomina właśnie o kolejności inicjalizacji pól klas pochodnych - DI and Field Initialization (strona 160). Streszczając komentarz, sprawa sprowadza się do zapamiętania, że wartości pól klas pochodnych zostaną w pełni zainicjowane (a tym samym nastąpi przypisanie wartości ich pól) po inicjalizacji klasy nadrzędnej i mimo przypisania im wartości przez kontener, ich docelowa wartość będzie tą, którą przypisaliśmy. Innymi słowy, jeśli w klasie nadrzędnej wywołamy "maszynerię" IoC/DI, która ustawi parametry klas pochodnych (tak się działo chociażby w wersji Wicket przed wersją 1.3, która jest tematem książki), to ta praca pójdzie do kosza przy ich faktycznej inicjalizacji (wartości zostaną nadpisane). Gdyby ktoś zechciał zilustrować problem w zgrabnym przykładzie z przyjemnością opublikowałbym go w kolejnym wpisie. Tak na prawdę, to nie wiem, czy taka sytuacja może w ogóle zajść, gdzie kontenery IoC (Spring, HiveMind, XBean, PicoContainer) tworzą egzemplarz za pomocą bezparametrowego konstruktora i wywołują metody set na nim lub po prostu wywołują konstruktor o zadanej licznie i typach parametrów. W takim przypadku ustanowienie domyślnej wartości polom egzemplarza nie ma znaczenia. Chyba, że się mylę? Wyprowdzi mnie ktoś z błędu?

Pytanie konkursowe: Gdzie użyto bloku inicjującego egzemplarz w kodzie dr Kabutza?

p.s. Jutro jestem na Uniwersytecie Śląskim w Sosnowcu, gdzie przedstawię produkty IBM w kontekście Korporacyjnej Javy 5 (pisałem o tym w IBM WAS 7.0 Open Beta oraz IBM RAD 7.5 Open Beta na moim wykładzie na Uniwersytecie Śląskim w Sosnowcu). Z pewnością nie zabraknie wielu przykładów z WAS7 i RAD7.5, których zauważyłem, że wiele dostarczono wraz z nimi. Już je zainstalowałem i nie mogę doczekać się, kiedy w końcu je zademonstruję w działaniu. Wyjeżdżam wcześniej, więc rozpoczynam jak zaplanowano o 11:30. Serdecznie zapraszam!

06 maja 2008

Egzaminy OSGi i EJB3 na JavaBlackBelt oraz łyk OSGi - rozdział 4.4 Kontekst pakunku specyfikacji OSGi 4.1

1 komentarzy
No i proszę, do czegoś się przydałem. W ostatnim wydaniu JavaBlackBelt Newsletter - May 08 pojawiała się wzmianka o możliwości zdobycia zielonego pasa bez wymaganego wcześniej udzielania się w serwisie (Get your green belt for free), a w sekcji Moderator spotlight pojawiłem się i ja!

Congratulations to all our moderators for their hard work, with special mention going to:
  • Benedikt for the Java 3D - Basic exam, JAX-B exam and c# exams
  • Jacek & Toni for the OSGi exam
  • Amzad for racking up 10.000 contributions points
  • Yours truly for the Algo - Basic and Programming Aptitude exams
Ten Jacek od egzaminu OSGi to nieskromnie mówiąc właśnie ja! Pracuję nad tym, aby w końcu egzamin pojawił się publicznie z pełną punktacją i aby wreszcie można było rozpoznać zadowolenie użytkowników z jakości pytań. Może Ty spróbuj tym razem i napisz, co Ci się podobało, a czego brakuje? Zamieniam się w słuch.

Skoro mnie wspomniano pomyślałem, że może w końcu uda mi się zdać egzamin z EJB 3.0 Basic, który ostatnio oblałem. Z niektórymi pytaniami się nie zgodziłem i napisałem do autorów, ale na razie ni widu ni słychu, a ja co oblałem to moje. Szczęśliwie upłynął już czas od ostatniego podejścia wymagany, aby podejść ponownie i tym razem...Congratulation, you successfully passed this test! z wynikiem 15/ 17 = 88% poprawnych odpowiedzi. Poległem na 2 pytaniach i na swoje usprawiedliwienie napiszę, że przy jednym dłużej się zastanawiałem (i nawet miałem wybrać poprawną odpowiedź!). Dwa pytania na których poległem to:

During stateful session bean activation, transient fields are treated the same way as during serialization, that is, transient fields are initialized to their default values (null for object references, 0 for Integers, etc).

oraz

Which of the following code snippets mark the OrderManager as a remote interface and OrderManagerBean as a stateless session bean?

Odpowiedzi nie podam z oczywistych względów, ale mogę skierować do rozdziału 4.2.1 Instance Passivation and Conversational State (strona 63) oraz 4.6.6 Session Bean's Business Interface (strona 94) w specyfikacji EJB 3.0. Na uwagę zasługuje fakt, że autorem drugiego pytania o biznesowym interfejsie zdalnym oraz wielu innych, które mi się przytrafiły podczas egzaminu jest nasz rodak - Mateusz Kwasniewski. Gość już wymiata w technologiach javowych mając brązowy pas.

W euforii podszedłem do kolejnego egzaminu w ścieżce do zielonego pasa, którym okazał się być egzamin Java SE Collections i który mnie w tylu miejscach zaskoczył, że ostatecznie wynik wcale mnie nie zdziwił Sorry, you failed this test, tj. 14/ 22 = 63% poprawnych przy 80% wymaganych. Na dzisiaj dosyć tego egzaminowania.

Pora wrócić do przyjemniejszych rzeczy, jak na przykład specyfikacji OSGi. Zacznę od odpowiedzi na pytanie MZ, który w komentarzu do wpisu Specyfikacja OSGi - rozdział 4.3 Obiekt Bundle - dokończenie zapytał:

Jest w końcu metoda start/stop(int) czy start/stop(BundleContext) czy obie?

Odpowiedź zdawkowa: obie.

Odpowiedź znacznie dłuższa: W konteście OSGi głównymi bytami, z którymi przyjdzie nam pracować będą typu org.osgi.framework.Bundle lub org.osgi.framework.BundleActivator. Pierwszy z nich Bundle reprezentuje pakunek OSGi i za pomocą odpowiednich metod możemy ustawić go w odpowiednim stanie (wyróżniamy 6 stanów pakunku/etapów w cyklu rozwojowym pakunku). Jednymi z takich metod, które zmieniają etap rozwojowy pakunku są start() oraz stop(). Obie należą do typu (interfejsu) org.osgi.framework.Bundle.
Za pomocą BundleActivator dostawca pakunku ma możliwość zmiany sposobu uruchomienia i zatrzymania pakunku. Podczas uruchomienia pakunku wywoływana jest metodą start(BundleContext context) aktywatora, podczas gdy przy jego zatrzymaniu wywołana zostanie metoda stop(BundleContext context). Aktywator pakunku deklarowany jest w manifeście w nagłówku Bundle-Activator.
W zależności od kontekstu rozmowy metody start() oraz stop() mogą pojawić się odnośnie aktywatora pakunku, albo samego pakunku. Uwaga na kontekst!

A skoro jestem przy kontekście to zajmę się tematem rozdziału 4.4 Kontekst pakunku specyfikacji OSGi 4.1.

Między Platformą OSGi a samym pakunkiem istnieje więź w postaci kontekstu uruchomieniowego - egzemplarza org.osgi.framework.BundleContext. Kontekst jest tworzony podczas uruchomienia pakunku i przekazywany jako parametr wejściowy do wspomnianej wcześniej metody start(BundleContext context) aktywatora. Każdy pakunek ma swój prywatny egzemplarz BundleContext, który nie powinien być przekazywany innym pakunkom i służy do instalowania innych pakunków OSGi, pozyskiwania informacji o środowisku i zainstalowanych pakunkach, rejestracji usług dostarczanych przez pakunek oraz korzystania z usług innych oraz nasłuchiwania zdarzeń emitowanych przez Platformę OSGi. Zakończenie wykonania metody stop(BundleContext context) powoduje wyłączenie BundleContext i jego późniejsze użycie zawsze zakończy się zgłoszeniem wyjątku IllegalStateException.

BundleContext dostarcza metod, za pomocą których możemy pozyskać informację o pakunkach zainstalowanych w Platformie OSGi.
  • getBundle() - zwraca egzemplarz Bundle związany z BundleContext (pakunek kontekstowy).
  • getBundles() - zwraca tablicę zainstalowanych pakunków
  • getBundle(long) - zwraca egzemplarz Bundle wskazywany przez identyfikator lub null, jeśli nie istnieje pakunek o danym identyfikatorze.
Platforma OSGi powinna dostarczać prywatną przestrzeń trwałą dla pakunku w postaci systemu plików, gdzie metoda getDataFile(String filename) zwraca plik odpowiadający podanej nazwie (filename) lub null, jeśli Platforma nie wspiera prywatnej przestrzeni plikowej. Wywołanie metody getDataFile z pustą nazwą pliku zwróci katalog główny przestrzeni.

Interfejs BundleContext udostępnia metodę getProperty(String), która zwraca wartość parametru systemowego lub javowego (podobnie, jak ma to miejsce w Javie z System.getProperty(String)). Wyróżnia się następujące parametry systemowe, wszystkie rozpoczynające się od przedrostka org.osgi.framework: version (dla wersji OSGi 4.1 wartością będzie 1.3), vendor, language, executionenvironment, processor, os.version, os.name, bootdelegation, system.packages oraz rozpoczynające się od przedrostka org.osgi.supports: framework.extension (cecha obowiązkowa implementacji OSGi), więc wartość true), bootclasspath.extension, framework.fragment (cecha obowiązkowa, więc wartość true), framework.requirebundle (cecha obowiązkowa, więc wartość true).

BundleContext udostępnia wiele innych metod, np. służących do rejestracji słuchaczy (obiektów nasłuchujących), pobranie referencji do usług czy ich zwolnienie, o czym będzie w kolejnych odsłonach specyfikacji OSGi.

W dużej mierze polecenia wydawane podczas pracy z wybraną Platformą OSGi to właśnie odpowiedniki wspomnianych metod, więc ich korzystanie rozpoczyna się już od pierwszych chwil pracy z Platformą OSGi, czy to jest Apache Felix, Eclipse Equinox czy Knoplerfish.

Przyjrzyjmy się poleceniu ps w Apache Felix (bądź ss w Equinox) w formie (aktywatora) pakunku, którego wynikiem jest właśnie lista zainstalowanych pakunków.
 package pl.jaceklaskowski.osgi;

import java.util.Dictionary;
import java.util.logging.Logger;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class AktywatorPakunku implements BundleActivator {

Logger log = Logger.getLogger(AktywatorPakunku.class.getName());

public void start(BundleContext context) throws Exception {
for (Bundle pakunek : context.getBundles()) {
Dictionary headers = pakunek.getHeaders();
String stan = zwrocNazweStanu(pakunek.getState());
String linia = String.format("[%1$4d] [%2$-11s] [ ?] %3$s (%4$s)", pakunek.getBundleId(), stan,
headers.get("Bundle-Name"), headers.get("Bundle-Version"));
System.out.println(linia);
}
}

/**
* TODO: Dokończyć obsługę pozostałych stanów
*
* @param identyfikator
* identyfikator stanu
* @return nazwa stanu dla podanego identyfikatora stanu (tylko dla ACTIVE oraz STARTING)
*/
private String zwrocNazweStanu(int identyfikator) {
switch (identyfikator) {
case Bundle.ACTIVE:
return "Active";
case Bundle.STARTING:
return "Starting";
}
return null;
}

public void stop(BundleContext bundleContext) throws Exception {
}

}
Pora zademonstrować przykład w akcji.
 jlaskowski@work /cygdrive/c/apps/felix
$ java -Dfelix.cache.profile=osgi-bundlecontext -jar bin/felix.jar

Welcome to Felix.
=================

DEBUG: WIRE: 1.0 -> org.osgi.service.packageadmin -> 0
DEBUG: WIRE: 1.0 -> org.osgi.service.startlevel -> 0
DEBUG: WIRE: 1.0 -> org.ungoverned.osgi.service.shell -> 1.0
DEBUG: WIRE: 1.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 1.0 -> org.apache.felix.shell -> 1.0
DEBUG: WIRE: 2.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 2.0 -> org.apache.felix.shell -> 1.0
DEBUG: WIRE: 3.0 -> org.osgi.service.obr -> 3.0
DEBUG: WIRE: 3.0 -> org.osgi.framework -> 0
-> DEBUG: WIRE: 3.0 -> org.apache.felix.shell -> 1.0

-> version
1.0.4
-> install file:/c:/projs/osgi/osgi-bundlecontext/target/osgi-bundlecontext-1.0.jar
Bundle ID: 4
-> start 4
DEBUG: WIRE: 4.0 -> org.osgi.framework -> 0
[ 0] [Active ] [ ?] System Bundle (1.0.4)
[ 1] [Active ] [ ?] Apache Felix Shell Service (1.0.1)
[ 2] [Active ] [ ?] Apache Felix Shell TUI (1.0.1)
[ 3] [Active ] [ ?] Apache Felix Bundle Repository (1.0.3)
[ 4] [Starting ] [ ?] OSGi 4.4 Kontekst pakunku (1.0)
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.4)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.1)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.1)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.3)
[ 4] [Active ] [ 1] OSGi 4.4 Kontekst pakunku (1.0)
-> shutdown
Pytanie konkursowe: Jaka jest zależność między interfejsami Bundle, BundleActivator i BundleContext? A teraz trochę trudniejsze: Dlaczego w wyniku działania aktywatora stan pakunku "OSGi 4.4 Kontekst pakunku" jest Starting? Nagród materialnych nie przewiduje się, ale publiczne wychwalenie wiedzy jak najbardziej. Ciekawe odpowiedzi, np. zawierające kod źródłowy, planuję umieścić w kolejnych wpisach (za zgodą autora).

05 maja 2008

IBM WAS 7.0 Open Beta oraz IBM RAD 7.5 Open Beta na moim wykładzie na Uniwersytecie Śląskim w Sosnowcu

0 komentarzy
W końcu pojawiła się wersja długooczekiwanego przeze mnie IBM WebSphere Application Server Network Deployment 7.0 Open Beta (WAS7). Wraz z nią pojawił się głos na forum, że właśnie oto rozpoczął się wyścig między tytanami dwóch światów - komercyjnego w postaci firmy IBM oraz otwartego w postaci projektu JBoss (teraz to również przedstawiciel świata komercyjnego po przejęciu przez firmę RedHat, ale dla uatrakcyjnienia sytuacji zapomnę o tym na chwilę).

Thank you for pre-registering for this Beta Program.
We are pleased to announce that this program is now active and available.

You may review the features and highlights on the Open Beta overview page at this URL:
https://www14.software.ibm.com/iwm/web/cc/earlyprograms/websphere/wasndv7/

The Code and Library downloads may also be found at these URLs:
https://www14.software.ibm.com/iwm/web/cc/earlyprograms/websphere/wasndv7/download.shtml
https://www14.software.ibm.com/iwm/web/cc/earlyprograms/websphere/wasndv7/library.shtml

For support issues related to this Open Beta program, we have also provided a discussion forum, which you may access from this URL:
https://www14.software.ibm.com/iwm/web/cc/earlyprograms/websphere/wasndv7/support.shtml

Welcome the Open Beta, and we look forward to helping you have a positive experience with this early version of IBM WebSphere Application Server Network Deployment V7.0


Co mnie mile zaskoczyło w tym wydaniu WAS7 to nie tylko wsparcie dla Korporacyjnej Javy 5, ale również sam format dystrybucji oprogramowania. Istnieje możliwość pobrania WASa w formacie VMware OVF (Open Virtual Machine Format), który jest całkowitym novum dla mnie. Okazuje się, że za pomocą narzędzia napisanego w Javie - OVF Tool - na podstawie pliku xmlowego OVF można utworzyć pełne środowisko uruchomieniowe. Jakby mało było wrażeń w formcie OVF jest dystrybuowana wersja WAS ND 7.0 na systemie linuksowym - SUSE Linux Enterprise Server (SLES) 10.1, więc nie tylko pobawię się nowiuteńkim WASem 7.0, ale również i Linuksem bez straty czasu na jego instalację i konfigurację. Mała rzecz a cieszy!
 C:\apps\VMware-ovftool 2008-05-05 18:00:15,25
> ovftool.bat "C:\WAS_7.0\was.7000.linux.vmware.ovf.binary\WebSphere_V7\WebSphere_V7.ovf"
"C:\Documents and Settings\jlaskowski\My Documents\My Virtual Machines"
Opening OVF descriptor:c:\WAS_7.0\was.7000.linux.vmware.ovf.binary\WebSphere_V7\WebSphere_V7.ovf
Virtual Machine: WebSphere_V7
Annotation: This virtual appliance is the full beta version of WebSphere Application Server Network Deployment (WAS ND)
V7.0 and SUSE Linux Enterprise Server (SLES) 10.1. Included in the package are configuration screens for the SLES opera
ting system and for WAS ND V7.0. This virtual appliance provides a great opportunity to try out the latest WebSphere Ap
plication Server product and to explore the new features and topologies being offered.

build id: WAS70.VM:open0816.06
Virtual Hardware: 1 CPU, 1024 memory
Validating access to: WebSphere_V7-0.vmdk
...
Poza tym z wydaniem WAS ND 7.0 wydano również nowszą wersję IBM Rational Application Developer for WebSphere Software v7.5 Open Beta (RAD7.5), gdzie napisano:

Latest News: 2008-05-02

A new beta code drop is now available! This release contains many fixes and improvements over the previous beta for development of Java EE 5, SOA, Web 2.0, Portal and J2C client applications.

This new release also includes a beta version of the WebSphere Application Server V7.0 test environment (in addition to a WebSphere Application Server V6.1 and V6.0 test environment). For those who would like to trial the full WebSphere Application Server Network Deployment V7.0 we have added a link to an Open Beta for that product to the Related Offerings section (below).

Oznacza to, że dla poszukujących wrażeń nie potrzebne jest pobranie WASa 7.0 - dystrybucja RAD7.5 z dnia 02.05.2008 zawiera również środowisko testowe WAS 7.0, poza WAS 6.0 i 6.1 (!) Tego było mi trzeba. Właśnie otrzymałem nowiuteńki sprzęt i nie ma co ukrywać, ale pierwszym poważniejszym oprogramowaniem jakie zainstaluję na nim będzie właśnie WAS 7.0 Open Beta i RAD 7.5 Open Beta (poza BESTplayerem i innymi ważnymi narzędziami ;-))

A wszystko to, jakby pod moje wystąpienie. W nadchodzący czwartek - 8 maja - w Instytucie Informatyki Uniwersytetu Śląskiego w Sosnowcu (ul. Będzińska 39, aula A4) zaprezentuję temat Aplikacje korporacyjne Java EE w 5 minut? Zastosowanie produktów IBM w praktyce. Tematem spotkania będą produkty programistyczne i środowiska serwerowe oferowane przez firmę IBM, jak właśnie wspomniane IBM WebSphere Application Server Network Deployment V7.0 Open Beta oraz IBM Rational Application Developer for WebSphere Software v7.5 Open Beta. Wymienione zostały również produkty IBM WebSphere Application Server 6.1 z rozszerzeniem EJB 3.0 i Web Services, Apache Geronimo...ekhm...IBM WebSphere Application Server Community Edition z Eclipse IDE (wtyczka Geronimo Eclipse Plugin 2.1.0 właśnie się pojawiła) oraz rodzina rozwiązań spod sztandaru SOA - IBM WebSphere Process Server oraz IBM WebSphere Integration Developer, ale znając poruszany temat i moją pierwszą wizytę w Sosnowcu z Korporacyjną Javą 5 i narzędziami ją wspierającymi nie sądzę, aby się nam udało je zobaczyć w akcji. To nie mają być prezentacje sprzedażowe, a zaprezentowanie od strony praktycznej procesu tworzenia aplikacji, oraz pokazanie możliwości narzędzi wspierających ten proces, więc skoro warsztaty i skoro Java EE 5 to na pewno temat wykraczający przydzielone 1,5 godziny. Tylko skąd pojawiła się ta godzina rozpoczęcia prezentacji - 11:30 - skoro pociąg z Warszawy wjeżdza o 11:35 do Sosnowca (!) Jeszcze się nie zaczęło, a już wpadka. Oczywiście poprawna godzina rozpoczęcia to 12:00. Zapraszam!

04 maja 2008

Aplikacja webowa jako pakunek OSGi ze Spring Dynamic Modules

4 komentarzy
Wciąż chodzi mi po głowie sposób tworzenia aplikacji webowej na platformie OSGi, gdzie jej elementy składowe będą faktycznie pakunkami OSGi, które można włączać/wyłączać/uaktualniać dynamicznie bez konieczności jej zatrzymywania, o wielu wersjach tej samej biblioteki w środowisku nie wspominając. Po ostatnim spotkaniu z OSGi skończyło się na skorzystaniu ze Spring Dynamic Modules (Spring-DM). Jednym ze sztandarowych cech najnowszej wersji Spring-DM 1.1.0 M2 jest wsparcie dla uruchamiania aplikacji webowych jako pakunków OSGi. To jest dokładnie to, czego szukałem (chociaż przyznaję, że nie starałem się jakoś specjalnie znaleźć coś innego niż właśnie Spring-DM).

Lekturę dokumentacji Spring-DM rozpocząłem od rozdziału 8. Web Support. Wcześniej czytałem pozostałe rozdziały, więc od razu przeszedłem do sedna. To właśnie lektura rozdziału uzmysłowiła mi, że format dystrybucji aplikacji webowej - plik war - jest faktycznie jarem, a pakunek osgi to właśnie plik jar z odpowiednimi nagłówkami w manifeście (META-INF/MANIFEST.MF). Idąć tym tropem można sobie wyobrazić sytuację, w której umieszczając obowiązkowy nagłówek OSGi - Bundle-SymbolicName - do manifestu w paczce aplikacji webowej nadajemy jej cechy pakunku w środowisku OSGi. Pozostaje jeszcze powiązać oba światy pod względem ich wymagań, np. widoczności klas i pomysł uruchamiania aplikacji webowych jako pakunków OSGi zostanie zmaterializowany. Właśnie taki cel obrał projekt Spring-DM.

Kwintesencją Spring-DM jest następujący zapis w jego dokumentacji:

Spring-DM addresses these problems by bridging the web container and the OSGi space so loading is no longer a concern. Unique in its functionality, the web support in Spring-DM integrates directly with the web container so the WAR processing is literally handled by the server.[...] In short, everything that the target container supports is available to the OSGi WAR through Spring-DM.

Początkowo trudno było mi zrozumieć, co autor miał na myśli, ale teraz po dniu pracy ze Spring-DM sprawa wydaje się być całkowicie oczywista (zakładam, że taka będzie również dla czytelników - rozczarowanych uprasza się o kontakt ;-)). W skrócie, temat sprowadza się do takiego uruchomienia aplikacji webowej, aby jej cechy aplikacji webowej były respektowane w ramach platformy OSGi i odwrotnie, tj. cechy pakunku OSGi (i zasady oraz obowiązki z tego wynikające) będą respektowane przez kontener aplikacji webowej. Nie oznacza to jednak, że samo tworzenie aplikacji webowej powinno zmienić się z punktu widzenia jej twórcy, aczkolwiek uruchomienie na platformie OSGi daje tą zaletę, że jej zależności to zależności pakunku OSGi, więc za pomocą mechanizmów udostępnianych przez platformę OSGi istnieje możliwość zarządzania nimi dynamicznie, w trakcie działania aplilkacji oraz ich automatyczne wersjonowanie. Weźmy na tapetę biblioteki aplikacji webowej umieszczane w katalogu WEB-INF/lib (podobnie będzie z klasami w WEB-INF/classes). Specyfikacja Java Servlet gwarantuje, że oba katalogi WEB-INF/{lib,classes} są dostępne dla zarządcy klas aplikacji webowej. W przypadku pakunku OSGi tak nie jest. Tutaj sprawa jest obwarowana zasadami, które mówią, że pakunek ma dostęp wyłącznie do klas/interfejsów w ramach pakunku oraz tych zależności zewnętrznych zadeklarowanych przez nagłówki Import-Package czy Bundle-Classpath w manifeście. Katalog WEB-INF/lib nie jest niczym specjalnym z punktu widzenia pakunku OSGi, chyba że jest jawnie zadeklarowany we wspomnianym Bundle-Classpath. Podobnie sprawa się tyczy bibliotek zapewnianych przez kontener serwletów, np. javax.servlet. Tylko tyle pakunek OSGi "zobaczy", ile zadeklaruje w Bundle-Classpath i/lub Import-Package. Całe te zawiłości, początkowo mogące być postrzegane jako utrudnienia, dają tę zaletę, że udostępniamy pakunkowi OSGi, który jest aplikacji webową, biblioteki dynamicznie i ich zarządzanie staje się dynamiczne. Jedną z wiodących cech takiego podejścia, jest spowodowanie, że biblioteki, które zazwyczaj znajdowały się w WEB-INF/lib, teraz również instaluje się w środowisku OSGi jako pakunki OSGi, a aplikacje webowe, które jej wymagają deklarują odpowiednią zależność w manifeście (dla już przestraszonych skomplikowaniem sprawy tworzenia aplikacji webowej jako pakunku OSGi, a jeszcze nie dostrzegających zalet takiego podejścia, nadmienię tylko, że Spring-DM poprzez mechanizm wtyczek mavenowych wykrywa zależności i samodzielnie tworzy odpowiedni plik manifestu w trakcie budowania aplikacji). Podsumowując zarządzanie klasami zlecone jest platformie OSGi, więc prawa rządzące aplikacją webową, w kontekście widoczności klas z pakietu javax.servlet i podobnych oraz WEB-INF/{classes,lib} muszą być zadeklarowane w pakunku OSGi explicite (opisane jest to w części 8.3.2. Servlets). Obowiązkowo należy dodać Import-Package: javax.servlet,javax.servlet.http,javax.servlet.resources, które zgodnie ze specyfikacją Java Servlet są automatycznie udostępniane przez kontener serwletów, a w przypadku OSGi muszą być jawnie zadeklarowane (bo nie są niczym szczególnym z punktu widzenia środowiska OSGi).

Podkreślając znaczenie uwspólniania zależności aplikacji webowych przytoczę wycinek dokumentacji Spring-DM - rozdział 8.3.2 Servlets:

Before creating entries for embedded libraries, consider whether the library cannot be installed as an OSGi bundle - doing so will allow it to be shared with other WARs if needed and since OSGi allows versioning, it is perfectly okay to have multiple versions of the library inside the same VM.

Dzięki uaktywnieniu bibliotek wykorzystywanych przez pojedyńczą aplikację webową jako pakunków OSGi (jedynie dodanie odpowiednich nagłówków do manifestu aplikacji) oraz ich instalacja poza tą jedną aplikacją webową mamy możliwość udostępnienia jej dla innych pakunków OSGi, niekoniecznie będących aplikacjami webowymi, oraz najistotniejsze, mamy możliwość dynamicznego uaktualniania jej w trakcie pracy systemu zrzucając na barki platformy OSGi zarządzanie zależnościami - dostępnością klas, niszczeniem dostawców klas czy utrzymaniem wielu wersji w trakcie korzystania z klasy w pakunku, który właśnie jest uaktualniany. Za pomocą Spring-DM mamy możliwość połączenia dwóch światów OSGi oraz aplikacji webowych w jeden, co wymaga od nas postrzegania tworzenia aplikacji webowych, a zmiana wpływa na większą modularyzację aplikacji. Tym razem widzę same zalety stosowania Springa (niejednokrotnie podkreślałem, że Spring nie jest lekarstwem na wszystko i mogłobyć to odebrane jako niechęć w jego wykorzystaniu, co już w przypadku Spring-DM można odczuć nie jest prawdą). O takim podejściu do tworzenia aplikacji webowych pisał chociażby Daniel w komentarzu do wpisu Nawigacja w Wicket:

Jak widać po Twoich wpisach OSGi nie jest Ci obce, dlatego zdziwiło mnie zdanie: "to nie wiem jaki miałby zysk wdrożenie OSGi mając na pokładzie Spring + Wicket." :) Przecież korzyści jakie płyną z wykorzystania OSGi są w miarę jasne, chociażby podstawowa: dobre wsparcie dla modularyzacji aplikacji.

Naczytałem się o Spring DM i jego zaletach aktywacji aplikacji webowych jako pakunków OSGi, albo raczej ich uruchomienia na platformie OSGi z Jetty (domyślnie jest już pakunkiem OSGi i Spring-DM dodaje jedynie aktywator pakunku) lub Tomcat (Spring-DM tworzy z niego pakunek OSGi - wpisy w manifeście - oraz dodaje aktywator), ale nie znalazłem chociażby przykładu jakby to ugryźć. Niestety, ale o jakości dokumentacji również nie mogę napisać w samych superlatywach i liczba błędów w dokumentacji Spring DM oraz oraz brak potrzebnych zależności w dystrybucji Spring-DM świadczą o zbytnim pośpiechu (co ma tą zaletę, że wypuszcza się na rynek produkt do ewaluacji i daje możliwość zrozumienia problemu, który rozwiązuje, a błędy poprawia się w kolejnych iteracjach).

Popróbuję się więc z tymi atrakcjami Spring-DM w kontekście uruchamiania aplikacji webowych jako pakunków OSGi. Zacznę od stworzenia projektu. I tu pytanie numer 1. - jak stworzyć projekt, aby skorzystać z spring-osgi-bundle-archetype tworząc projekt aplikacji webowej? Na stronie Spring Dynamic Modules Demos znajdują się dwie prezentacje tworzenia przykładowych aplikacji z użyciem Spring-DM i do utworzenia projektu wystarczył jedynie domyślny archetyp. Tyle tylko, że tam nie było aplikacji webowej (!) Nie zakładałem, że będzie łatwo, ale już na początku takie problemy?! Nie wygląda to zachęcająco.

Rozpocznę od archetypu maven-archetype-webapp, gdyż chcę uniknąć zawiłości związanych z bardziej zaawansowanymi szkieletami webowymi, np. korzystających z JSF, kiedy przyjdzie mi je uruchamiać na OSGi po raz pierwszy z pomocą Spring-DM, a bez dostępnych bibliotek jako pakunków OSGi (parametr -U dodałem wyłącznie dla celów ochronnych opisanych w Niuanse Apache Maven 2 - opcja -U).
 jlaskowski@work /cygdrive/c/projs/osgi
$ mvn -U archetype:create -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=pl.jaceklaskowski.osgi -DartifactId=spring-osgi-webapp
...
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating OldArchetype: maven-archetype-webapp:RELEASE
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: pl.jaceklaskowski.osgi
[INFO] Parameter: packageName, Value: pl.jaceklaskowski.osgi
[INFO] Parameter: basedir, Value: c:\projs\osgi
[INFO] Parameter: package, Value: pl.jaceklaskowski.osgi
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: artifactId, Value: spring-osgi-webapp
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] OldArchetype created in dir: c:\projs\osgi\spring-osgi-webapp
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
Temat obsługi manifestu sprowadzę do konfiguracji wtyczki maven-war-plugin. Dodaję odpowiednią sekcję do pom.xml i konfiguruję obowiązkowy nagłówek Bundle-SymbolicName (korzystam z pomocy Eclipse i jego wtyczki Maven Integration for Eclipse - m2eclipse).
 <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<archive>
<manifestEntries>
<Bundle-SymbolicName>${groupId}.${artifactId}</Bundle-SymbolicName>
</manifestEntries>
</archive>
</configuration>
</plugin>
Uruchomienie aplikacji webowej jako pakunku OSGi spróbuję z Apache Felix 1.0.4 jako platformie OSGi. W 5.3. Required Spring Framework and Spring Dynamic Modules Bundles opisano wszystkie niezbędne pakunki, które należy uruchomić dla poprawnego uruchomienia pakunku dowolnej aplikacji webowej. Nie trwało długo, zanim okazało się, że Spring-DM 1.1.0 M2 dostarcza jedynie część z wymaganych bibliotek, a w tym brakuje najważniejszej - spring-osgi-web-extender (!)

Buduję aplikację webową i podchodzę do jej instalacji na Apache Felix 1.0.4 z Spring-DM.
 jlaskowski@work /cygdrive/c/projs/osgi/spring-osgi-webapp
$ mvn package
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building spring-osgi-webapp Maven Webapp
[INFO] task-segment: [package]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] No sources to compile
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] No sources to compile
[INFO] [surefire:test]
[INFO] No tests to run.
[INFO] [war:war]
[INFO] Exploding webapp...
[INFO] Assembling webapp spring-osgi-webapp in c:\projs\osgi\spring-osgi-webapp\target\spring-osgi-webapp
[INFO] Copy webapp webResources to c:\projs\osgi\spring-osgi-webapp\target\spring-osgi-webapp
[INFO] Generating war c:\projs\osgi\spring-osgi-webapp\target\spring-osgi-webapp.war
[INFO] Building war: c:\projs\osgi\spring-osgi-webapp\target\spring-osgi-webapp.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
Sposób w jaki działa Spring-DM to dostarczenie pakunku spring-osgi-web-extender, który monitoruje zainstalowane pakunki i rozpoznaje, które są aplikacjami webowymi. Jeśli trafi się jedna, to następuje zainstalowanie jej na kontenerze servletów (Tomcat lub Jetty), który jest również pakunkiem OSGi dostarczanym przez Spring-DM.

Rozpoczynam instalację pakunków OSGi dostarczanych przez Spring-DM - dostępne w katalogach dist oraz lib. Instaluję:
  • dist/spring-osgi-web-1.1.0-m2.jar
  • lib/servlet-api.osgi-2.5-SNAPSHOT.jar
  • lib/slf4j-log4j12-1.4.3.jar
  • lib/jcl104-over-slf4j-1.4.3.jar
  • lib/slf4j-api-1.4.3.jar
  • lib/log4j.osgi-1.2.15-SNAPSHOT.jar
Do rozpoznania pozostaje sposób instalacji wielu pakunków automatycznie bez konieczności ich ręcznej instalacji (gdzieś o tym czytałem, ale teraz nie pamiętam - może ktoś się zlituje i napisze koledze Jackowi?!).

Na zakończenie przychodzi mi instalować moją aplikację webową i wierzyć, że zostanie rozpoznana i poprawnie zainstalowana w kontenerze servletów przez Spring-DM.

W trakcie uaktualniania pakunku na Felix 1.0.4 natrafiłem na błąd, gdzie pakunek musiał koniecznie mieć rozszerzenie pliku jar, aby został zaakceptowany przez polecenie update - zgłosiłem jako FELIX-544 update command should accept other file extensions than jar only.
 -> update 10 file:/C:/projs/osgi/spring-osgi-webapp/target/spring-osgi-webapp.war
Unable to open input stream: java.io.FileNotFoundException:
C:\projs\osgi\spring-osgi-webapp\target\spring-osgi-webapp.war.jar (The system cannot find the file specified)
-> uninstall 10
-> install file:/C:/projs/osgi/spring-osgi-webapp/target/spring-osgi-webapp.war
Bundle ID: 11
-> start 11
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.4)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.1)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.1)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.3)
[ 4] [Active ] [ 1] spring-osgi-web (1.1.0.m2)
[ 5] [Active ] [ 1] servlet-api.osgi (2.5.0.SNAPSHOT)
[ 6] [Active ] [ 1] slf4j-log4j12 (1.4.3)
[ 7] [Active ] [ 1] jcl104-over-slf4j (1.4.3)
[ 8] [Active ] [ 1] slf4j-api (1.4.3)
[ 9] [Active ] [ 1] log4j.osgi (1.2.15.SNAPSHOT)
[ 11] [Active ] [ 1] pl.jaceklaskowski.osgi.spring-osgi-webapp
-> headers 11

Bundle 11
---------
Built-By = jlaskowski
Archiver-Version = Plexus Archiver
Created-By = Apache Maven
Build-Jdk = 1.5.0_14
Manifest-Version = 1.0
Bundle-SymbolicName = pl.jaceklaskowski.osgi.spring-osgi-webapp
Niestety żadnych komunikatów, aby jakakolwiek akcja została wykonana po instalacji aplikacji webowej. Coś nie gra. Pytanie jak to sprawdzić? Jeszcze raz zaglądam do dokumentacji, gdzie znalazłem, że aplikacja webowa zostanie rozpoznana przez Spring DM jedynie, jeśli spełnia jeden z poniższych warunków: zawiera katalog META-INF/spring bądź nagłówek Spring-Context (więcej w 5.1. Bundle format and Manifest headers).

Dodaję do pom.xml:
 <Spring-Context>*;publish-context:=true</Spring-Context>
buduję aplikację i ponownie podchodzę do instalacji (jak można zauważyć zainstalowałem w międzyczasie inne pakunki OSGi dostarczane przez Spring-DM - nazwy pakunków odpowiadają nazwom plików z katalogów dist lub lib).
 -> install file:/C:/projs/osgi/spring-osgi-webapp/target/spring-osgi-webapp.war
Bundle ID: 21
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.4)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.1)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.1)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.3)
[ 4] [Active ] [ 1] spring-osgi-web (1.1.0.m2)
[ 5] [Active ] [ 1] servlet-api.osgi (2.5.0.SNAPSHOT)
[ 6] [Active ] [ 1] slf4j-log4j12 (1.4.3)
[ 7] [Active ] [ 1] jcl104-over-slf4j (1.4.3)
[ 8] [Active ] [ 1] slf4j-api (1.4.3)
[ 9] [Active ] [ 1] log4j.osgi (1.2.15.SNAPSHOT)
[ 12] [Active ] [ 1] spring-osgi-extender (1.1.0.m2)
[ 13] [Active ] [ 1] spring-context (2.5.4)
[ 14] [Active ] [ 1] spring-beans (2.5.4)
[ 15] [Active ] [ 1] spring-osgi-core (1.1.0.m2)
[ 16] [Active ] [ 1] spring-core (2.5.4)
[ 17] [Active ] [ 1] aopalliance.osgi (1.0.0.SNAPSHOT)
[ 18] [Active ] [ 1] spring-osgi-io (1.1.0.m2)
[ 19] [Active ] [ 1] spring-aop (2.5.4)
[ 21] [Installed ] [ 1] pl.jaceklaskowski.osgi.spring-osgi-webapp
-> start 21
-> WARNING: *** Class 'org.springframework.beans.factory.xml.NamespaceHandlerResolver' was not found
because bundle 21 does not import 'org.springframework.beans.factory.xml' even though bundle 14 does export it.
To resolve this issue, add an import for 'org.springframework.beans.factory.xml' to bundle 21.
*** (java.lang.ClassNotFoundException: *** Class 'org.springframework.beans.factory.xml.NamespaceHandlerResolver'
was not found because bundle 21 does not import 'org.springframework.beans.factory.xml' even though bundle 14 does export it.
To resolve this issue, add an import for 'org.springframework.beans.factory.xml' to bundle 21. ***)
...i jeszcze kilka innych WARNINGów. Rozwiązanie w samym komunikacie i zgodnie z 8.3.2. Servlets dodaję odpowiednie zależności w pom.xml w sekcji konfiguracji wtyczki maven-war-plugin:
 <Import-Package>javax.servlet,javax.servlet.http,javax.servlet.resources</Import-Package>
<Bundle-Classpath>.,WEB-INF/classes</Bundle-Classpath>
Ponownie buduję aplikację i podchodzę do jej instalacji.
 -> install file:/C:/projs/osgi/spring-osgi-webapp/target/spring-osgi-webapp.war
INFO: Class path entry not found: WEB-INF/classes
Bundle ID: 23
-> start 23
DEBUG: WIRE: 23.0 -> javax.servlet.http -> 5.0
DEBUG: WIRE: 23.0 -> javax.servlet -> 5.0
DEBUG: WIRE: 23.0 -> javax.servlet.resources -> 5.0
-> WARNING: *** Class 'org.springframework.beans.factory.xml.NamespaceHandlerResolver' was not found
because bundle 23 does not import 'org.springframework.beans.factory.xml' even though bundle 14 does export it.
To resolve this issue, add an import for 'org.springframework.beans.factory.xml' to bundle 23. ***
(java.lang.ClassNotFoundException: *** Class 'org.springframework.beans.factory.xml.NamespaceHandlerResolver'
was not found because bundle 23 does not import 'org.springframework.beans.factory.xml' even though bundle 14 does export it.
To resolve this issue, add an import for 'org.springframework.beans.factory.xml' to bundle 23. ***)
...i ponownie WARNINGi, czyli dokumentacja Spring-DM nie opisuje dokładnie wymaganych importów (!) Dodaję do pom.xml kolejne:
 <Import-Package>javax.servlet,javax.servlet.http,javax.servlet.resources,
org.springframework.beans.factory.xml,org.springframework.aop,
org.springframework.aop.framework,org.aopalliance.aop,org.xml.sax,
org.apache.jasper.servlet,org.apache.commons.el,org.apache.catalina.servlets</Import-Package>
I znowu budowanie aplikacji i jej instalacja (z nadzieją, że to może już).
 -> uninstall 23
-> install file:/C:/projs/osgi/spring-osgi-webapp/target/spring-osgi-webapp.war
INFO: Class path entry not found: WEB-INF/classes
Bundle ID: 24
-> start 24
DEBUG: WIRE: 24.0 -> javax.servlet.http -> 5.0
DEBUG: WIRE: 24.0 -> javax.servlet -> 5.0
DEBUG: WIRE: 24.0 -> org.xml.sax -> 0
DEBUG: WIRE: 24.0 -> org.springframework.beans.factory.xml -> 14.0
DEBUG: WIRE: 24.0 -> javax.servlet.resources -> 5.0
DEBUG: WIRE: 24.0 -> org.springframework.aop.framework -> 19.0
DEBUG: WIRE: 24.0 -> org.aopalliance.aop -> 17.0
DEBUG: WIRE: 24.0 -> org.springframework.aop -> 19.0
Niestety, jeszcze nie koniec zmagań, ale tym razem już czysto. Wciąż żadnych komunikatów związanych z uruchomieniem aplikacji. Dokumentacja wspomina o cglib-nodep jako bibliotece do tworzenia proxy, ale niestety nie ma jej w dystrybucji (!) Dodatkowo informacja o konieczności instalacji pakunku cglib-nodep znajduje się w readme.txt w katalogu lib w dystrybucji Spring DM.

I dalej, rozdział 2. Requirements opisuje wymaganie Bundles deployed for use with Spring Dynamic Modules should specify "Bundle-ManifestVersion: 2" in their manifest (OSGi R4). Koniecznie należy jawnie określić wersję pakunku, gdyż domyślna wartość to 1, która najwyraźniej nie jest rozpoznawana przez Spring-DM.

Za specyfikacją OSGi 4.1 strona 27:

3.2.1.10 Bundle-ManifestVersion: 2
The Bundle-ManifestVersion header defines that the bundle follows the rules of this specification. The Bundle-ManifestVersion header determines whether the bundle follows the rules of this specification. It is 1 (the default) for Release 3 Bundles, 2 for Release 4 and later. Future version of the OSGi Service Platform can define higher numbers for this header.


Dodaję
 <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
do pom.xml.

W 8.2. Web support usage napisano:

To use Spring-DM Web, install:
* spring-osgi-web.jar - Spring-DM web support
* spring-osgi-web-extender.jar - Spring-DM web extender

Niestety, ale już na starcie trudno spełnić to wymaganie, bo nie istnieje spring-osgi-web-extender.jar w katalogu lib lub dist w dystrybucji Spring-DM 1.1.0 M2, a jedynie spring-osgi-extender.jar (!) Nie obejdzie się bez pobrania jej z repozytorium S3 jako spring-osgi-web-extender-1.1.0-m2-20080425.012916-2.jar.

W końcu trochę zniecierpliwiony tymi niespodziankami zabrałem się za przeglądanie kodów źródłowych z repozytorium Spring-DM. Niestety próba zbudowania ich za pomocą polecenia mvn -P equinox,it clean install zakończyła się komunikatem błędu o niedostępności biblioteki do obsługi repozytorium S3 - net.java.dev.jets3t:jets3t:jar:0.5.1-20080115. Ech, jakby nie chciano, abym skosztował tych wszystkich nowości, którymi szczyci się Spring-DM 1.1.0-m2.
 Downloading: http://repo1.maven.org/eclipse//net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.pom
Downloading: http://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext//net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.pom
Downloading: http://maven.springframework.org/release/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.pom
Downloading: http://maven.springframework.org/external/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.pom
Downloading: http://maven.springframework.org/milestone/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.pom
Downloading: http://m2.safehaus.org/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.pom
Downloading: http://maven.springframework.org/osgi/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.pom
Downloading: http://jets3t.s3.amazonaws.com/maven2/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.pom
Downloading: http://repo1.maven.org/maven2/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.pom
Downloading: http://repo1.maven.org/eclipse//net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.jar
Downloading: http://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext//net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.jar
Downloading: http://maven.springframework.org/release/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.jar
Downloading: http://maven.springframework.org/external/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.jar
Downloading: http://maven.springframework.org/milestone/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.jar
Downloading: http://m2.safehaus.org/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.jar
Downloading: http://maven.springframework.org/osgi/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.jar
Downloading: http://jets3t.s3.amazonaws.com/maven2/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.jar
Downloading: http://repo1.maven.org/maven2/net/java/dev/jets3t/jets3t/0.5.1-20080115/jets3t-0.5.1-20080115.jar
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Failed to resolve artifact.

Missing:
----------
1) net.java.dev.jets3t:jets3t:jar:0.5.1-20080115

Try downloading the file manually from the project website.

Then, install it using the command:
mvn install:install-file -DgroupId=net.java.dev.jets3t -DartifactId=jets3t
-Dversion=0.5.1-20080115 -Dpackaging=jar -Dfile=/path/to/file

Alternatively, if you host your own repository you can deploy the file there:
mvn deploy:deploy-file -DgroupId=net.java.dev.jets3t -DartifactId=jets3t -Dversion=0.5.1-20080115
-Dpackaging=jar -Dfile=/path/to/file -Durl=[url] -DrepositoryId=[id]

Path to dependency:
1) org.springframework.osgi:spring-osgi:pom:1.1.0-rc1-SNAPSHOT
2) net.java.dev.jets3t:jets3t:jar:0.5.1-20080115

----------
1 required artifact is missing.

for artifact:
org.springframework.osgi:spring-osgi:pom:1.1.0-rc1-SNAPSHOT

from the specified remote repositories:
ibiblio.org (http://repo1.maven.org/maven2),
spring-release (http://maven.springframework.org/release),
spring-ext (http://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext/),
safehaus-repository (http://m2.safehaus.org),
spring-external (http://maven.springframework.org/external),
spring-milestone (http://maven.springframework.org/milestone),
springsource-s3-osgi-repo (http://maven.springframework.org/osgi),
jets3t (http://jets3t.s3.amazonaws.com/maven2),
eclipse-repository (http://repo1.maven.org/eclipse/)


[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
W przykładowej aplikacji dostępnej w katalogu samples/simple-web-app znalazłem klasę testu jednostkowego - integration-test/src/test/java/org/springframework/osgi/samples/simplewebapp/OsgiHttpIntegrationTest.java, gdzie wymieniono wszystkie konieczne zależności do poprawnego jej uruchomienia. Kolejny raz pojawiła się wzmianka o spring-osgi-web-extender.
 col.add(SPRING_OSGI_GROUP + ", servlet-api.osgi, 2.5-SNAPSHOT");
col.add(SPRING_OSGI_GROUP + ", jsp-api.osgi, 2.0-SNAPSHOT");

// JSP compiler
col.add(SPRING_OSGI_GROUP + ", jasper.osgi, 5.5.23-SNAPSHOT");
col.add(SPRING_OSGI_GROUP + ", commons-el.osgi, 1.0-SNAPSHOT");

// standard tag library
col.add("org.springframework.osgi, jstl.osgi, 1.1.2-SNAPSHOT");

// add MX4J for 1.4
// if < jdk 1.5, add an JMX implementation
if (!JdkVersion.isAtLeastJava15())
col.add(SPRING_OSGI_GROUP + ", mx4j.osgi, 3.0.2-SNAPSHOT");

col.add(SPRING_OSGI_GROUP + ", catalina.osgi, 5.5.23-SNAPSHOT");
col.add(SPRING_OSGI_GROUP + ", catalina.start.osgi, 1.0-SNAPSHOT");

// Spring DM web extender
col.add(SPRING_OSGI_GROUP + ", spring-osgi-web," + getSpringDMVersion());
col.add(SPRING_OSGI_GROUP + ", spring-osgi-web-extender," + getSpringDMVersion());
col.add(SPRING_OSGI_GROUP + ", cglib-nodep.osgi, 2.1.3-SNAPSHOT");
Pobieram brakujące pakunki z repozytorium http://jets3t.s3.amazonaws.com/maven2:
Podsumowując zainstalowałem następujące pakunki dostarczane z dystrybucją Spring-DM 1.1.0 M2:
  • install file:/C:/apps/spring-osgi/dist/spring-osgi-web-1.1.0-m2.jar
  • install file:/C:/apps/spring-osgi/lib/servlet-api.osgi-2.5-SNAPSHOT.jar
  • install file:/C:/apps/spring-osgi/lib/jcl104-over-slf4j-1.4.3.jar
  • install file:/C:/apps/spring-osgi/lib/slf4j-api-1.4.3.jar
  • install file:/C:/apps/spring-osgi/lib/slf4j-log4j12-1.4.3.jar
  • install file:/C:/apps/spring-osgi/lib/log4j.osgi-1.2.15-SNAPSHOT.jar
  • install file:/C:/apps/spring-osgi/dist/spring-osgi-extender-1.1.0-m2.jar
  • install file:/C:/apps/spring-osgi/lib/spring-context-2.5.4.jar
  • install file:/C:/apps/spring-osgi/lib/spring-beans-2.5.4.jar
  • install file:/C:/apps/spring-osgi/lib/spring-core-2.5.4.jar
  • install file:/C:/apps/spring-osgi/lib/aopalliance.osgi-1.0-SNAPSHOT.jar
  • install file:/C:/apps/spring-osgi/dist/spring-osgi-io-1.1.0-m2.jar
  • install file:/C:/apps/spring-osgi/lib/spring-aop-2.5.4.jar
oraz pobrane z repozytorium S3:
  • install file:/C:/spring-dm-libs/jsp-api.osgi-2.0-20080103.183925-4.jar
  • install file:/C:/spring-dm-libs/jasper.osgi-5.5.23-20080305.122359-4.jar
  • install file:/C:/spring-dm-libs/commons-el.osgi-1.0-20080303.111409-4.jar
  • install file:/C:/spring-dm-libs/jstl.osgi-1.1.2-20080103.183925-4.jar
  • install file:/C:/spring-dm-libs/catalina.osgi-5.5.23-20080425.154256-4.jar
  • install file:/C:/spring-dm-libs/catalina.start.osgi-1.0-20080425.161832-4.jar
  • install file:/C:/spring-dm-libs/cglib-nodep.osgi-2.1.3-20080103.183925-4.jar
  • install file:/C:/spring-dm-libs/spring-osgi-web-extender-1.1.0-m2-SNAPSHOT.jar
Po instalacji pakunków oraz samej aplikacji webowej pozostało dowiedzieć się jaki jest kontekst aplikacji - adres, pod którym dostępna jest aplikacja. Po przeczytaniu całej dostępnej dokumentacji Spring DM na stronie http://www.springframework.org/osgi w poszukiwaniu odpowiedzi przeniosłem się na forum, gdzie po kolei przeglądałem kolejne pytania dotyczące wsparcia aplikacji webowych w Spring DM. Szczęśliwie natrafiłem na wątek WAR deploy in 1.1.0-m1 vs. 1.1.0-m2?, gdzie zauważyłem nagłówek Bundle-Name, który pasował do nazwy kontekstu (adresu aplikacji).

Dodałem do pom.xml następujący wpis:
 <Bundle-Name>${artifactId}</Bundle-Name>
ponownie zbudowałem aplikację - mvn clean package - i zaktualizowałem pakunek.
 -> uninstall 26
-> install file:/C:/projs/osgi/spring-osgi-webapp/target/spring-osgi-webapp.war
Bundle ID: 27
-> start 27
DEBUG: WIRE: 27.0 -> javax.servlet.http -> 5.0
DEBUG: WIRE: 27.0 -> javax.servlet -> 5.0
DEBUG: WIRE: 27.0 -> org.xml.sax -> 0
DEBUG: WIRE: 27.0 -> org.springframework.beans.factory.xml -> 14.0
DEBUG: WIRE: 27.0 -> javax.servlet.resources -> 5.0
DEBUG: WIRE: 27.0 -> org.springframework.aop.framework -> 19.0
DEBUG: WIRE: 27.0 -> org.aopalliance.aop -> 17.0
DEBUG: WIRE: 27.0 -> org.springframework.aop -> 19.0
-> headers 27

spring-osgi-webapp (27)
-----------------------
Built-By = jlaskowski
Created-By = Apache Maven
Build-Jdk = 1.5.0_14
Manifest-Version = 1.0
Bundle-ManifestVersion = 2
Spring-Context = *;publish-context:=true
Archiver-Version = Plexus Archiver
Import-Package = javax.servlet,javax.servlet.http,javax.servlet.resources,
org.springframework.beans.factory.xml,org.springframework.aop,org.springframework.aop.framework,
org.aopalliance.aop,org.xml.sax
Bundle-Name = spring-osgi-webapp
Bundle-Classpath = .,WEB-INF/classes
Bundle-SymbolicName = pl.jaceklaskowski.osgi.spring-osgi-webapp
W końcu upragnione uruchomienie aplikacji webowej spring-osgi-webapp jako pakunku OSGi, jednakże adres http://localhost:8080/spring-osgi-webapp/ kończył się pustą stroną lub wyjątkiem o niedostępności klas (niestety komunikat nie wskazywał jakiej). Coś zaczęło działać, ale niepoprawnie. Wykluczam mój błąd ;-) Po zmianie strony JSP na statyczną stronę HTML pojawia się wreszcie strona aplikacji!


Wciąż jednak strony JSP nie są poprawnie kompilowane. Stan platformy OSGi (Felix 1.0.4) prezentuje się następująco:
 -> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.4)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.1)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.1)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.3)
[ 5] [Active ] [ 1] servlet-api.osgi (2.5.0.SNAPSHOT)
[ 6] [Active ] [ 1] slf4j-log4j12 (1.4.3)
[ 7] [Active ] [ 1] jcl104-over-slf4j (1.4.3)
[ 8] [Active ] [ 1] slf4j-api (1.4.3)
[ 9] [Active ] [ 1] log4j.osgi (1.2.15.SNAPSHOT)
[ 13] [Active ] [ 1] spring-context (2.5.4)
[ 14] [Active ] [ 1] spring-beans (2.5.4)
[ 15] [Active ] [ 1] spring-osgi-core (1.1.0.m2)
[ 16] [Active ] [ 1] spring-core (2.5.4)
[ 17] [Active ] [ 1] aopalliance.osgi (1.0.0.SNAPSHOT)
[ 18] [Active ] [ 1] spring-osgi-io (1.1.0.m2)
[ 19] [Active ] [ 1] spring-aop (2.5.4)
[ 32] [Active ] [ 1] spring-webmvc (2.5.4)
[ 33] [Active ] [ 1] spring-web (2.5.4)
[ 37] [Active ] [ 1] spring-osgi-web (1.1.0.m2)
[ 38] [Active ] [ 1] spring-osgi-extender (1.1.0.m2)
[ 50] [Active ] [ 1] jsp-api.osgi (2.0.0.SNAPSHOT)
[ 51] [Active ] [ 1] jasper.osgi (5.5.23.SNAPSHOT)
[ 52] [Active ] [ 1] commons-el.osgi (1.0.0.SNAPSHOT)
[ 56] [Active ] [ 1] cglib-nodep.osgi (2.1.3.SNAPSHOT)
[ 61] [Active ] [ 1] spring-osgi-web-extender (1.1.0.m2-SNAPSHOT)
[ 64] [Active ] [ 1] catalina.start.osgi (1.0.0.SNAPSHOT)
[ 65] [Active ] [ 1] catalina.osgi (5.5.23.SNAPSHOT)
[ 68] [Active ] [ 1] spring-osgi-webapp Maven Webapp (1.0)
[ 69] [Installed ] [ 1] jstl.osgi (1.1.2.SNAPSHOT)
Zauważyłem jednak, że wiele (wszystkie?) z dyskusji na forum Spring-DM dotyczyły uruchomienia aplikacji webowej na Equinox. Equinox jest projektem platformy OSGi rozwijanym w organizacji Eclipse, która jest podstawą technologiczną dla Eclipse IDE i mechanizmu wtyczek, które de facto są pakunkami OSGi. Zniechęcony wynikami moich doświadczeń z Apache Felix, postanowiłem na koniec sprawdzić uruchomienie aplikacji z Spring-DM na Equinoksie.
 jlaskowski@work ~
$ java -jar c:/apps/eclipse/plugins/org.eclipse.osgi_3.4.0.v20080326.jar -console

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080326
Instaluje wszystkie pakunki Spring-DM konieczne do uruchomienia mojej aplikacji webowej, jak to miało miejsce wcześniej z Apache Felix.
 osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080326
1 ACTIVE org.springframework.bundle.osgi.web_1.1.0.m2
2 ACTIVE org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
3 ACTIVE jcl104.over.slf4j_1.4.3
4 ACTIVE slf4j.api_1.4.3
5 ACTIVE slf4j.log4j12_1.4.3
6 ACTIVE org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
7 ACTIVE org.springframework.bundle.osgi.extender_1.1.0.m2
8 ACTIVE org.springframework.bundle.spring.context_2.5.4
9 ACTIVE org.springframework.bundle.spring.beans_2.5.4
10 ACTIVE org.springframework.bundle.spring.core_2.5.4
11 ACTIVE org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
12 ACTIVE org.springframework.bundle.osgi.io_1.1.0.m2
13 ACTIVE org.springframework.bundle.spring.aop_2.5.4
14 ACTIVE org.springframework.osgi.jsp-api.osgi_2.0.0.SNAPSHOT
15 ACTIVE org.springframework.osgi.jasper.osgi_5.5.23.SNAPSHOT
16 ACTIVE org.springframework.osgi.commons-el.osgi_1.0.0.SNAPSHOT
17 INSTALLED org.springframework.osgi.jstl.osgi_1.1.2.SNAPSHOT
18 RESOLVED org.springframework.osgi.catalina.osgi_6.0.16.SNAPSHOT
19 RESOLVED org.springframework.osgi.catalina.start.osgi_6.0.16.SNAPSHOT
20 RESOLVED org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
21 RESOLVED org.springframework.bundle.osgi.web.extender_1.1.0.m2-SNAPSHOT
22 RESOLVED org.springframework.osgi.catalina.start.osgi_1.0.0.SNAPSHOT
23 RESOLVED org.springframework.osgi.catalina.osgi_5.5.23.SNAPSHOT
24 ACTIVE pl.jaceklaskowski.osgi.spring-osgi-webapp_1.0.0
25 ACTIVE org.springframework.bundle.osgi.core_1.1.0.m2
a następnie próbuję je wszystkie uruchomić poleceniem start 1 2 ... 25 (zamień ... na odpowiednie liczby - identyfikatory pakunków). Niestety pakunek o identyfikatorze 17 odmawia współpracy wyjątkiem:
 org.osgi.framework.BundleException: The bundle could not be resolved. Reason: Missing Constraint: 
Import-Package: org.apache.taglibs.standard.tag.common.fmt; version="0.0.0"
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:305)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:265)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:257)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:257)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:150)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:303)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:288)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:224)
at java.lang.Thread.run(Thread.java:595)
Nie wygląda to dobrze. Spróbowałem odszukać biblioteki znaczników standard JSTL jako pakunek OSGi, ale niestety nic nie znalazłem w repozytorium Spring-DM na S3. Skoro nie było, to znaczy, że najprawdopodobniej nie jest potrzebne. Podchodzę jeszcze raz do tematu, tyle że postanawiam uruchomić jedynie moją aplikację webową bez uruchamiania pozostałych pakunków (platforma OSGi zmieni ich stan na RESOLVED, ale nie na ACTIVE, w którym otrzymywałem wyjątek brakującego pakietu. Zawiłości platformy OSGi, w które nie ma co na razie wchodzić). Zmieniam również wersję Equinoksa na starszą - 3.3.2.R33x_v20080105.
 jlaskowski@work ~
$ java -jar c:/.m2/eclipse/eclipse/plugins/org.eclipse.osgi_3.3.2.R33x_v20080105.jar -console

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
Skąd ja wziąłem akurat tą wersję Equinoksa jest poza moją wiedzą - jakoś się kiedyś przy czymś ściągnęło. Najlepiej pobrać aktualną produkcyjną wersję ze strony domowej projektu Equinox. Wersja 3.3.2 powinna spełniać nasze wymagania.
 jlaskowski@work ~
$ java -jar c:/.m2/eclipse/eclipse/plugins/org.eclipse.osgi_3.3.2.R33x_v20080105.jar -console

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105

osgi> install file:/C:/apps/spring-osgi/dist/spring-osgi-core-1.1.0-m2.jar
Bundle id is 1

osgi> install file:/C:/apps/spring-osgi/dist/spring-osgi-web-1.1.0-m2.jar
Bundle id is 2

osgi> install file:/C:/apps/spring-osgi/dist/spring-osgi-io-1.1.0-m2.jar
Bundle id is 3

osgi> install file:/C:/apps/spring-osgi/lib/servlet-api.osgi-2.5-SNAPSHOT.jar
Bundle id is 4

osgi> install file:/C:/apps/spring-osgi/lib/jcl104-over-slf4j-1.4.3.jar
Bundle id is 5

osgi> install file:/C:/apps/spring-osgi/lib/slf4j-api-1.4.3.jar
Bundle id is 6

osgi> install file:/C:/apps/spring-osgi/lib/slf4j-log4j12-1.4.3.jar
Bundle id is 7

osgi> install file:/C:/apps/spring-osgi/lib/log4j.osgi-1.2.15-SNAPSHOT.jar
Bundle id is 8

osgi> install file:/C:/apps/spring-osgi/dist/spring-osgi-extender-1.1.0-m2.jar
Bundle id is 9

osgi> install file:/C:/apps/spring-osgi/lib/spring-context-2.5.4.jar
Bundle id is 10

osgi> install file:/C:/apps/spring-osgi/lib/spring-beans-2.5.4.jar
Bundle id is 11

osgi> install file:/C:/apps/spring-osgi/lib/spring-core-2.5.4.jar
Bundle id is 12

osgi> install file:/C:/apps/spring-osgi/lib/aopalliance.osgi-1.0-SNAPSHOT.jar
Bundle id is 13

osgi> install file:/C:/apps/spring-osgi/lib/spring-aop-2.5.4.jar
Bundle id is 14

osgi> install file:/C:/spring-dm-libs/jsp-api.osgi-2.0-SNAPSHOT.jar
Bundle id is 15

osgi> install file:/C:/spring-dm-libs/jasper.osgi-5.5.23-SNAPSHOT.jar
Bundle id is 16

osgi> install file:/C:/spring-dm-libs/commons-el.osgi-1.0-SNAPSHOT.jar
Bundle id is 17

osgi> install file:/C:/spring-dm-libs/jstl.osgi-1.1.2-SNAPSHOT.jar
Bundle id is 18

osgi> install file:/C:/spring-dm-libs/catalina.osgi-6.0.16-SNAPSHOT.jar
Bundle id is 19

osgi> install file:/C:/spring-dm-libs/catalina.start.osgi-6.0.16-SNAPSHOT.jar
Bundle id is 20

osgi> install file:/C:/spring-dm-libs/cglib-nodep.osgi-2.1.3-SNAPSHOT.jar
Bundle id is 21

osgi> install file:/C:/spring-dm-libs/spring-osgi-web-extender-1.1.0-m2-SNAPSHOT.jar
Bundle id is 22

osgi> install file:/C:/spring-dm-libs/catalina.start.osgi-1.0-20080425.161832-4.jar
Bundle id is 23

osgi> install file:/C:/spring-dm-libs/catalina.osgi-5.5.23-20080425.154256-4.jar
Bundle id is 24

osgi> install file:/C:/projs/osgi/spring-osgi-webapp/target/spring-osgi-webapp.war
Bundle id is 25

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
1 INSTALLED org.springframework.bundle.osgi.core_1.1.0.m2
2 INSTALLED org.springframework.bundle.osgi.web_1.1.0.m2
3 INSTALLED org.springframework.bundle.osgi.io_1.1.0.m2
4 INSTALLED org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
5 INSTALLED jcl104.over.slf4j_1.4.3
6 INSTALLED slf4j.api_1.4.3
7 INSTALLED slf4j.log4j12_1.4.3
8 INSTALLED org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
9 INSTALLED org.springframework.bundle.osgi.extender_1.1.0.m2
10 INSTALLED org.springframework.bundle.spring.context_2.5.4
11 INSTALLED org.springframework.bundle.spring.beans_2.5.4
12 INSTALLED org.springframework.bundle.spring.core_2.5.4
13 INSTALLED org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
14 INSTALLED org.springframework.bundle.spring.aop_2.5.4
15 INSTALLED org.springframework.osgi.jsp-api.osgi_2.0.0.SNAPSHOT
16 INSTALLED org.springframework.osgi.jasper.osgi_5.5.23.SNAPSHOT
17 INSTALLED org.springframework.osgi.commons-el.osgi_1.0.0.SNAPSHOT
18 INSTALLED org.springframework.osgi.jstl.osgi_1.1.2.SNAPSHOT
19 INSTALLED org.springframework.osgi.catalina.osgi_6.0.16.SNAPSHOT
20 INSTALLED org.springframework.osgi.catalina.start.osgi_6.0.16.SNAPSHOT
21 INSTALLED org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
22 INSTALLED org.springframework.bundle.osgi.web.extender_1.1.0.m2-SNAPSHOT
23 INSTALLED org.springframework.osgi.catalina.start.osgi_1.0.0.SNAPSHOT
24 INSTALLED org.springframework.osgi.catalina.osgi_5.5.23.SNAPSHOT
25 INSTALLED pl.jaceklaskowski.osgi.spring-osgi-webapp_1.0.0

osgi> start 24

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
1 RESOLVED org.springframework.bundle.osgi.core_1.1.0.m2
2 RESOLVED org.springframework.bundle.osgi.web_1.1.0.m2
3 RESOLVED org.springframework.bundle.osgi.io_1.1.0.m2
4 RESOLVED org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
5 RESOLVED jcl104.over.slf4j_1.4.3
6 RESOLVED slf4j.api_1.4.3
7 RESOLVED slf4j.log4j12_1.4.3
8 RESOLVED org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
9 RESOLVED org.springframework.bundle.osgi.extender_1.1.0.m2
10 RESOLVED org.springframework.bundle.spring.context_2.5.4
11 RESOLVED org.springframework.bundle.spring.beans_2.5.4
12 RESOLVED org.springframework.bundle.spring.core_2.5.4
13 RESOLVED org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
14 RESOLVED org.springframework.bundle.spring.aop_2.5.4
15 RESOLVED org.springframework.osgi.jsp-api.osgi_2.0.0.SNAPSHOT
16 RESOLVED org.springframework.osgi.jasper.osgi_5.5.23.SNAPSHOT
17 RESOLVED org.springframework.osgi.commons-el.osgi_1.0.0.SNAPSHOT
18 INSTALLED org.springframework.osgi.jstl.osgi_1.1.2.SNAPSHOT
19 RESOLVED org.springframework.osgi.catalina.osgi_6.0.16.SNAPSHOT
20 RESOLVED org.springframework.osgi.catalina.start.osgi_6.0.16.SNAPSHOT
21 RESOLVED org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
22 RESOLVED org.springframework.bundle.osgi.web.extender_1.1.0.m2-SNAPSHOT
23 RESOLVED org.springframework.osgi.catalina.start.osgi_1.0.0.SNAPSHOT
24 ACTIVE org.springframework.osgi.catalina.osgi_5.5.23.SNAPSHOT
25 RESOLVED pl.jaceklaskowski.osgi.spring-osgi-webapp_1.0.0

osgi> start 1

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
1 ACTIVE org.springframework.bundle.osgi.core_1.1.0.m2
2 RESOLVED org.springframework.bundle.osgi.web_1.1.0.m2
3 RESOLVED org.springframework.bundle.osgi.io_1.1.0.m2
4 RESOLVED org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
5 RESOLVED jcl104.over.slf4j_1.4.3
6 RESOLVED slf4j.api_1.4.3
7 RESOLVED slf4j.log4j12_1.4.3
8 RESOLVED org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
9 RESOLVED org.springframework.bundle.osgi.extender_1.1.0.m2
10 RESOLVED org.springframework.bundle.spring.context_2.5.4
11 RESOLVED org.springframework.bundle.spring.beans_2.5.4
12 RESOLVED org.springframework.bundle.spring.core_2.5.4
13 RESOLVED org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
14 RESOLVED org.springframework.bundle.spring.aop_2.5.4
15 RESOLVED org.springframework.osgi.jsp-api.osgi_2.0.0.SNAPSHOT
16 RESOLVED org.springframework.osgi.jasper.osgi_5.5.23.SNAPSHOT
17 RESOLVED org.springframework.osgi.commons-el.osgi_1.0.0.SNAPSHOT
18 INSTALLED org.springframework.osgi.jstl.osgi_1.1.2.SNAPSHOT
19 RESOLVED org.springframework.osgi.catalina.osgi_6.0.16.SNAPSHOT
20 RESOLVED org.springframework.osgi.catalina.start.osgi_6.0.16.SNAPSHOT
21 RESOLVED org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
22 RESOLVED org.springframework.bundle.osgi.web.extender_1.1.0.m2-SNAPSHOT
23 RESOLVED org.springframework.osgi.catalina.start.osgi_1.0.0.SNAPSHOT
24 ACTIVE org.springframework.osgi.catalina.osgi_5.5.23.SNAPSHOT
25 RESOLVED pl.jaceklaskowski.osgi.spring-osgi-webapp_1.0.0

osgi> start 22
log4j:WARN No appenders could be found for logger (org.springframework.util.ClassUtils).
log4j:WARN Please initialize the log4j system properly.
org.osgi.framework.BundleException: Exception in org.springframework.osgi.web.extender.internal.activator.WarLoaderListener.start()
of bundle org.springframe...
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:1018)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:974)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:346)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:260)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:252)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:260)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:150)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:300)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:285)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:221)
at java.lang.Thread.run(Thread.java:595)
Caused by: org.springframework.osgi.OsgiException: Cannot create Tomcat deployer
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:156)
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.<init>(WarListenerConfiguration.java:84)
at org.springframework.osgi.web.extender.internal.activator.WarLoaderListener.start(WarLoaderListener.java:243)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl$2.run(BundleContextImpl.java:999)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:993)
... 14 more
Caused by: org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=org.apache.catalina.Service)] unavailable
at org.springframework.osgi.service.importer.internal.aop.ServiceDynamicInterceptor.getTarget(ServiceDynamicInterceptor.java:330)
at org.springframework.osgi.service.importer.internal.aop.ServiceDynamicInterceptor.afterPropertiesSet(ServiceDynamicInterceptor.java:366)
at org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean.createProxy(OsgiServiceProxyFactoryBean.java:118)
at org.springframework.osgi.service.importer.support.AbstractOsgiServiceImportFactoryBean.getObject(AbstractOsgiServiceImportFactoryBean.java:94)
at org.springframework.osgi.web.deployer.internal.util.Utils.createServerServiceProxy(Utils.java:117)
at org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer.afterPropertiesSet(TomcatWarDeployer.java:90)
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:152)
... 19 more
Nested Exception:
org.springframework.osgi.OsgiException: Cannot create Tomcat deployer
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:156)
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.<init>(WarListenerConfiguration.java:84)
at org.springframework.osgi.web.extender.internal.activator.WarLoaderListener.start(WarLoaderListener.java:243)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl$2.run(BundleContextImpl.java:999)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:993)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:974)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:346)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:260)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:252)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:260)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:150)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:300)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:285)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:221)
at java.lang.Thread.run(Thread.java:595)
Caused by: org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=org.apache.catalina.Service)] unavailable
at org.springframework.osgi.service.importer.internal.aop.ServiceDynamicInterceptor.getTarget(ServiceDynamicInterceptor.java:330)
at org.springframework.osgi.service.importer.internal.aop.ServiceDynamicInterceptor.afterPropertiesSet(ServiceDynamicInterceptor.java:366)
at org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean.createProxy(OsgiServiceProxyFactoryBean.java:118)
at org.springframework.osgi.service.importer.support.AbstractOsgiServiceImportFactoryBean.getObject(AbstractOsgiServiceImportFactoryBean.java:94)
at org.springframework.osgi.web.deployer.internal.util.Utils.createServerServiceProxy(Utils.java:117)
at org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer.afterPropertiesSet(TomcatWarDeployer.java:90)
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:152)
... 19 more
Nested Exception:
org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=org.apache.catalina.Service)] unavailable
at org.springframework.osgi.service.importer.internal.aop.ServiceDynamicInterceptor.getTarget(ServiceDynamicInterceptor.java:330)
at org.springframework.osgi.service.importer.internal.aop.ServiceDynamicInterceptor.afterPropertiesSet(ServiceDynamicInterceptor.java:366)
at org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean.createProxy(OsgiServiceProxyFactoryBean.java:118)
at org.springframework.osgi.service.importer.support.AbstractOsgiServiceImportFactoryBean.getObject(AbstractOsgiServiceImportFactoryBean.java:94)
at org.springframework.osgi.web.deployer.internal.util.Utils.createServerServiceProxy(Utils.java:117)
at org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer.afterPropertiesSet(TomcatWarDeployer.java:90)
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:152)
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.<init>(WarListenerConfiguration.java:84)
at org.springframework.osgi.web.extender.internal.activator.WarLoaderListener.start(WarLoaderListener.java:243)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl$2.run(BundleContextImpl.java:999)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:993)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:974)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:346)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:260)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:252)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:260)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:150)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:300)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:285)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:221)
at java.lang.Thread.run(Thread.java:595)
Nested Exception:
org.springframework.osgi.OsgiException: Cannot create Tomcat deployer
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:156)
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.<init>(WarListenerConfiguration.java:84)
at org.springframework.osgi.web.extender.internal.activator.WarLoaderListener.start(WarLoaderListener.java:243)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl$2.run(BundleContextImpl.java:999)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:993)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:974)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:346)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:260)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:252)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:260)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:150)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:300)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:285)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:221)
at java.lang.Thread.run(Thread.java:595)
Caused by: org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=org.apache.catalina.Service)] unavailable
at org.springframework.osgi.service.importer.internal.aop.ServiceDynamicInterceptor.getTarget(ServiceDynamicInterceptor.java:330)
at org.springframework.osgi.service.importer.internal.aop.ServiceDynamicInterceptor.afterPropertiesSet(ServiceDynamicInterceptor.java:366)
at org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean.createProxy(OsgiServiceProxyFactoryBean.java:118)
at org.springframework.osgi.service.importer.support.AbstractOsgiServiceImportFactoryBean.getObject(AbstractOsgiServiceImportFactoryBean.java:94)
at org.springframework.osgi.web.deployer.internal.util.Utils.createServerServiceProxy(Utils.java:117)
at org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer.afterPropertiesSet(TomcatWarDeployer.java:90)
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:152)
... 19 more
Nested Exception:
org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=org.apache.catalina.Service)] unavailable
at org.springframework.osgi.service.importer.internal.aop.ServiceDynamicInterceptor.getTarget(ServiceDynamicInterceptor.java:330)
at org.springframework.osgi.service.importer.internal.aop.ServiceDynamicInterceptor.afterPropertiesSet(ServiceDynamicInterceptor.java:366)
at org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean.createProxy(OsgiServiceProxyFactoryBean.java:118)
at org.springframework.osgi.service.importer.support.AbstractOsgiServiceImportFactoryBean.getObject(AbstractOsgiServiceImportFactoryBean.java:94)
at org.springframework.osgi.web.deployer.internal.util.Utils.createServerServiceProxy(Utils.java:117)
at org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer.afterPropertiesSet(TomcatWarDeployer.java:90)
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:152)
at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.<init>(WarListenerConfiguration.java:84)
at org.springframework.osgi.web.extender.internal.activator.WarLoaderListener.start(WarLoaderListener.java:243)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl$2.run(BundleContextImpl.java:999)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:993)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:974)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:346)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:260)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:252)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:260)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:150)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:300)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:285)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:221)
at java.lang.Thread.run(Thread.java:595)

osgi>

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
1 ACTIVE org.springframework.bundle.osgi.core_1.1.0.m2
2 RESOLVED org.springframework.bundle.osgi.web_1.1.0.m2
3 RESOLVED org.springframework.bundle.osgi.io_1.1.0.m2
4 RESOLVED org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
5 RESOLVED jcl104.over.slf4j_1.4.3
6 RESOLVED slf4j.api_1.4.3
7 RESOLVED slf4j.log4j12_1.4.3
8 RESOLVED org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
9 RESOLVED org.springframework.bundle.osgi.extender_1.1.0.m2
10 RESOLVED org.springframework.bundle.spring.context_2.5.4
11 RESOLVED org.springframework.bundle.spring.beans_2.5.4
12 RESOLVED org.springframework.bundle.spring.core_2.5.4
13 RESOLVED org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
14 RESOLVED org.springframework.bundle.spring.aop_2.5.4
15 RESOLVED org.springframework.osgi.jsp-api.osgi_2.0.0.SNAPSHOT
16 RESOLVED org.springframework.osgi.jasper.osgi_5.5.23.SNAPSHOT
17 RESOLVED org.springframework.osgi.commons-el.osgi_1.0.0.SNAPSHOT
18 INSTALLED org.springframework.osgi.jstl.osgi_1.1.2.SNAPSHOT
19 RESOLVED org.springframework.osgi.catalina.osgi_6.0.16.SNAPSHOT
20 RESOLVED org.springframework.osgi.catalina.start.osgi_6.0.16.SNAPSHOT
21 RESOLVED org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
22 RESOLVED org.springframework.bundle.osgi.web.extender_1.1.0.m2-SNAPSHOT
23 RESOLVED org.springframework.osgi.catalina.start.osgi_1.0.0.SNAPSHOT
24 ACTIVE org.springframework.osgi.catalina.osgi_5.5.23.SNAPSHOT
25 RESOLVED pl.jaceklaskowski.osgi.spring-osgi-webapp_1.0.0

osgi> start 23

osgi> 2008-04-30 23:43:48 org.apache.catalina.startup.ClusterRuleSetFactory getClusterRuleSet
INFO: Unable to find a cluster rule set in the classpath. Will load the default rule set.
2008-04-30 23:43:48 org.apache.catalina.startup.ClusterRuleSetFactory getClusterRuleSet
INFO: Unable to find a cluster rule set in the classpath. Will load the default rule set.
2008-04-30 23:43:48 org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-8080
2008-04-30 23:43:48 org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 262 ms
2008-04-30 23:43:48 org.apache.catalina.core.StandardService start
INFO: Starting service Catalina
2008-04-30 23:43:48 org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/6.0.16
2008-04-30 23:43:48 org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on http-8080

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
1 ACTIVE org.springframework.bundle.osgi.core_1.1.0.m2
2 RESOLVED org.springframework.bundle.osgi.web_1.1.0.m2
3 RESOLVED org.springframework.bundle.osgi.io_1.1.0.m2
4 RESOLVED org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
5 RESOLVED jcl104.over.slf4j_1.4.3
6 RESOLVED slf4j.api_1.4.3
7 RESOLVED slf4j.log4j12_1.4.3
8 RESOLVED org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
9 RESOLVED org.springframework.bundle.osgi.extender_1.1.0.m2
10 RESOLVED org.springframework.bundle.spring.context_2.5.4
11 RESOLVED org.springframework.bundle.spring.beans_2.5.4
12 RESOLVED org.springframework.bundle.spring.core_2.5.4
13 RESOLVED org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
14 RESOLVED org.springframework.bundle.spring.aop_2.5.4
15 RESOLVED org.springframework.osgi.jsp-api.osgi_2.0.0.SNAPSHOT
16 RESOLVED org.springframework.osgi.jasper.osgi_5.5.23.SNAPSHOT
17 RESOLVED org.springframework.osgi.commons-el.osgi_1.0.0.SNAPSHOT
18 INSTALLED org.springframework.osgi.jstl.osgi_1.1.2.SNAPSHOT
19 RESOLVED org.springframework.osgi.catalina.osgi_6.0.16.SNAPSHOT
20 RESOLVED org.springframework.osgi.catalina.start.osgi_6.0.16.SNAPSHOT
21 RESOLVED org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
22 RESOLVED org.springframework.bundle.osgi.web.extender_1.1.0.m2-SNAPSHOT
23 ACTIVE org.springframework.osgi.catalina.start.osgi_1.0.0.SNAPSHOT
24 ACTIVE org.springframework.osgi.catalina.osgi_5.5.23.SNAPSHOT
25 RESOLVED pl.jaceklaskowski.osgi.spring-osgi-webapp_1.0.0

osgi> start 22

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
1 ACTIVE org.springframework.bundle.osgi.core_1.1.0.m2
2 RESOLVED org.springframework.bundle.osgi.web_1.1.0.m2
3 RESOLVED org.springframework.bundle.osgi.io_1.1.0.m2
4 RESOLVED org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
5 RESOLVED jcl104.over.slf4j_1.4.3
6 RESOLVED slf4j.api_1.4.3
7 RESOLVED slf4j.log4j12_1.4.3
8 RESOLVED org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
9 RESOLVED org.springframework.bundle.osgi.extender_1.1.0.m2
10 RESOLVED org.springframework.bundle.spring.context_2.5.4
11 RESOLVED org.springframework.bundle.spring.beans_2.5.4
12 RESOLVED org.springframework.bundle.spring.core_2.5.4
13 RESOLVED org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
14 RESOLVED org.springframework.bundle.spring.aop_2.5.4
15 RESOLVED org.springframework.osgi.jsp-api.osgi_2.0.0.SNAPSHOT
16 RESOLVED org.springframework.osgi.jasper.osgi_5.5.23.SNAPSHOT
17 RESOLVED org.springframework.osgi.commons-el.osgi_1.0.0.SNAPSHOT
18 INSTALLED org.springframework.osgi.jstl.osgi_1.1.2.SNAPSHOT
19 RESOLVED org.springframework.osgi.catalina.osgi_6.0.16.SNAPSHOT
20 RESOLVED org.springframework.osgi.catalina.start.osgi_6.0.16.SNAPSHOT
21 RESOLVED org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
22 ACTIVE org.springframework.bundle.osgi.web.extender_1.1.0.m2-SNAPSHOT
23 ACTIVE org.springframework.osgi.catalina.start.osgi_1.0.0.SNAPSHOT
24 ACTIVE org.springframework.osgi.catalina.osgi_5.5.23.SNAPSHOT
25 RESOLVED pl.jaceklaskowski.osgi.spring-osgi-webapp_1.0.0

osgi> start 25

osgi> 2008-04-30 23:44:21 org.apache.catalina.startup.DigesterFactory register
WARNING: Could not get url for /javax/servlet/jsp/resources/jsp_2_1.xsd
2008-04-30 23:44:21 org.apache.catalina.startup.DigesterFactory register
WARNING: Could not get url for /javax/servlet/jsp/resources/web-jsptaglibrary_2_1.xsd
2008-04-30 23:44:21 org.apache.catalina.startup.DigesterFactory register
WARNING: Could not get url for /javax/servlet/resources/j2ee_web_services_1_1.xsd


osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
1 ACTIVE org.springframework.bundle.osgi.core_1.1.0.m2
2 RESOLVED org.springframework.bundle.osgi.web_1.1.0.m2
3 RESOLVED org.springframework.bundle.osgi.io_1.1.0.m2
4 RESOLVED org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
5 RESOLVED jcl104.over.slf4j_1.4.3
6 RESOLVED slf4j.api_1.4.3
7 RESOLVED slf4j.log4j12_1.4.3
8 RESOLVED org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
9 RESOLVED org.springframework.bundle.osgi.extender_1.1.0.m2
10 RESOLVED org.springframework.bundle.spring.context_2.5.4
11 RESOLVED org.springframework.bundle.spring.beans_2.5.4
12 RESOLVED org.springframework.bundle.spring.core_2.5.4
13 RESOLVED org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
14 RESOLVED org.springframework.bundle.spring.aop_2.5.4
15 RESOLVED org.springframework.osgi.jsp-api.osgi_2.0.0.SNAPSHOT
16 RESOLVED org.springframework.osgi.jasper.osgi_5.5.23.SNAPSHOT
17 RESOLVED org.springframework.osgi.commons-el.osgi_1.0.0.SNAPSHOT
18 INSTALLED org.springframework.osgi.jstl.osgi_1.1.2.SNAPSHOT
19 RESOLVED org.springframework.osgi.catalina.osgi_6.0.16.SNAPSHOT
20 RESOLVED org.springframework.osgi.catalina.start.osgi_6.0.16.SNAPSHOT
21 RESOLVED org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
22 ACTIVE org.springframework.bundle.osgi.web.extender_1.1.0.m2-SNAPSHOT
23 ACTIVE org.springframework.osgi.catalina.start.osgi_1.0.0.SNAPSHOT
24 ACTIVE org.springframework.osgi.catalina.osgi_5.5.23.SNAPSHOT
25 ACTIVE pl.jaceklaskowski.osgi.spring-osgi-webapp_1.0.0

osgi> uninstall 25

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
1 ACTIVE org.springframework.bundle.osgi.core_1.1.0.m2
2 RESOLVED org.springframework.bundle.osgi.web_1.1.0.m2
3 RESOLVED org.springframework.bundle.osgi.io_1.1.0.m2
4 RESOLVED org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
5 RESOLVED jcl104.over.slf4j_1.4.3
6 RESOLVED slf4j.api_1.4.3
7 RESOLVED slf4j.log4j12_1.4.3
8 RESOLVED org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
9 RESOLVED org.springframework.bundle.osgi.extender_1.1.0.m2
10 RESOLVED org.springframework.bundle.spring.context_2.5.4
11 RESOLVED org.springframework.bundle.spring.beans_2.5.4
12 RESOLVED org.springframework.bundle.spring.core_2.5.4
13 RESOLVED org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
14 RESOLVED org.springframework.bundle.spring.aop_2.5.4
15 RESOLVED org.springframework.osgi.jsp-api.osgi_2.0.0.SNAPSHOT
16 RESOLVED org.springframework.osgi.jasper.osgi_5.5.23.SNAPSHOT
17 RESOLVED org.springframework.osgi.commons-el.osgi_1.0.0.SNAPSHOT
18 INSTALLED org.springframework.osgi.jstl.osgi_1.1.2.SNAPSHOT
19 RESOLVED org.springframework.osgi.catalina.osgi_6.0.16.SNAPSHOT
20 RESOLVED org.springframework.osgi.catalina.start.osgi_6.0.16.SNAPSHOT
21 RESOLVED org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
22 ACTIVE org.springframework.bundle.osgi.web.extender_1.1.0.m2-SNAPSHOT
23 ACTIVE org.springframework.osgi.catalina.start.osgi_1.0.0.SNAPSHOT
24 ACTIVE org.springframework.osgi.catalina.osgi_5.5.23.SNAPSHOT

osgi> install file:/C:/projs/osgi/spring-osgi-webapp/target/spring-osgi-webapp.war
Bundle id is 26

osgi> start 26

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105
1 ACTIVE org.springframework.bundle.osgi.core_1.1.0.m2
2 RESOLVED org.springframework.bundle.osgi.web_1.1.0.m2
3 RESOLVED org.springframework.bundle.osgi.io_1.1.0.m2
4 RESOLVED org.springframework.osgi.servlet-api.osgi_2.5.0.SNAPSHOT
5 RESOLVED jcl104.over.slf4j_1.4.3
6 RESOLVED slf4j.api_1.4.3
7 RESOLVED slf4j.log4j12_1.4.3
8 RESOLVED org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
9 RESOLVED org.springframework.bundle.osgi.extender_1.1.0.m2
10 RESOLVED org.springframework.bundle.spring.context_2.5.4
11 RESOLVED org.springframework.bundle.spring.beans_2.5.4
12 RESOLVED org.springframework.bundle.spring.core_2.5.4
13 RESOLVED org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
14 RESOLVED org.springframework.bundle.spring.aop_2.5.4
15 RESOLVED org.springframework.osgi.jsp-api.osgi_2.0.0.SNAPSHOT
16 RESOLVED org.springframework.osgi.jasper.osgi_5.5.23.SNAPSHOT
17 RESOLVED org.springframework.osgi.commons-el.osgi_1.0.0.SNAPSHOT
18 INSTALLED org.springframework.osgi.jstl.osgi_1.1.2.SNAPSHOT
19 RESOLVED org.springframework.osgi.catalina.osgi_6.0.16.SNAPSHOT
20 RESOLVED org.springframework.osgi.catalina.start.osgi_6.0.16.SNAPSHOT
21 RESOLVED org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
22 ACTIVE org.springframework.bundle.osgi.web.extender_1.1.0.m2-SNAPSHOT
23 ACTIVE org.springframework.osgi.catalina.start.osgi_1.0.0.SNAPSHOT
24 ACTIVE org.springframework.osgi.catalina.osgi_5.5.23.SNAPSHOT
26 ACTIVE pl.jaceklaskowski.osgi.spring-osgi-webapp_1.0.0
Na razie czysto, a aplikacja jako pakunek 26 wydaje się być uruchomiona (stan ACTIVE) wraz z pakunkami Tomcata (pakunki 22, 23 i 24). Uruchomienie przeglądarki i skierowanie na adres http://localhost:8080/spring-osgi-webapp/index.jsp kończy się....


Aplikacja webowa spring-osgi-webapp działa!

Na koniec jeszcze raz próba z z Apache Felix 1.0.4 (coś mnie tknęło, że to jednak moja wcześniejsza niewiedza niż niedoskonałości Feliksa były przyczyną niepowodzenia).
 jlaskowski@work /cygdrive/c/apps/felix
$ rm -rf c\:/Documents\ and\ Settings/jlaskowski/.felix

jlaskowski@work /cygdrive/c/apps/felix
$ java -Dfelix.cache.profile=spring-osgi-webapp -jar bin/felix.jar

Welcome to Felix.
=================

DEBUG: WIRE: 1.0 -> org.osgi.service.packageadmin -> 0
DEBUG: WIRE: 1.0 -> org.osgi.service.startlevel -> 0
DEBUG: WIRE: 1.0 -> org.ungoverned.osgi.service.shell -> 1.0
DEBUG: WIRE: 1.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 1.0 -> org.apache.felix.shell -> 1.0
DEBUG: WIRE: 2.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 2.0 -> org.apache.felix.shell -> 1.0
DEBUG: WIRE: 3.0 -> org.osgi.service.obr -> 3.0
DEBUG: WIRE: 3.0 -> org.osgi.framework -> 0
-> DEBUG: WIRE: 3.0 -> org.apache.felix.shell -> 1.0

...tutaj instaluję pakunki i niektóre uruchamiam (w stanie ACTIVE)

-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.4)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.1)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.1)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.3)
[ 4] [Active ] [ 1] spring-osgi-core (1.1.0.m2)
[ 5] [Resolved ] [ 1] spring-osgi-web (1.1.0.m2)
[ 6] [Resolved ] [ 1] spring-osgi-io (1.1.0.m2)
[ 7] [Resolved ] [ 1] servlet-api.osgi (2.5.0.SNAPSHOT)
[ 8] [Resolved ] [ 1] jcl104-over-slf4j (1.4.3)
[ 9] [Resolved ] [ 1] slf4j-api (1.4.3)
[ 10] [Resolved ] [ 1] slf4j-log4j12 (1.4.3)
[ 11] [Resolved ] [ 1] log4j.osgi (1.2.15.SNAPSHOT)
[ 12] [Installed ] [ 1] spring-osgi-extender (1.1.0.m2)
[ 13] [Resolved ] [ 1] spring-context (2.5.4)
[ 14] [Resolved ] [ 1] spring-beans (2.5.4)
[ 15] [Resolved ] [ 1] spring-core (2.5.4)
[ 16] [Resolved ] [ 1] aopalliance.osgi (1.0.0.SNAPSHOT)
[ 17] [Resolved ] [ 1] spring-aop (2.5.4)
[ 18] [Resolved ] [ 1] jsp-api.osgi (2.0.0.SNAPSHOT)
[ 19] [Resolved ] [ 1] jasper.osgi (5.5.23.SNAPSHOT)
[ 20] [Resolved ] [ 1] commons-el.osgi (1.0.0.SNAPSHOT)
[ 21] [Installed ] [ 1] jstl.osgi (1.1.2.SNAPSHOT)
[ 22] [Resolved ] [ 1] cglib-nodep.osgi (2.1.3.SNAPSHOT)
[ 23] [Active ] [ 1] spring-osgi-web-extender (1.1.0.m2-SNAPSHOT)
[ 24] [Active ] [ 1] catalina.start.osgi (1.0.0.SNAPSHOT)
[ 25] [Active ] [ 1] catalina.osgi (5.5.23.SNAPSHOT)
[ 26] [Active ] [ 1] spring-osgi-webapp Maven Webapp (1.0)
Kieruję przeglądarkę na adres aplikacji i okazuje się, że z Felix też działa! Wersje pakunków i ich stan (uruchomiony - ACTIVE bądź jedynie rozpoznany - RESOLVED) ma najwyraźniej znacznie.

A na koniec ciekawa lektura SpringSource Launches New Application Server without Java EE. I co?! Kto już o tym wspominał rok temu, że aspiracjami i21 czy teraz SpringSource to właśnie rynek serwerów aplikacyjnych, gdzie możnaby potraktować Spring Framework z dodatkami jako właśnie serwer aplikacyjny?! Ciekawe, co teraz oponenci takiego spojrzenia mają do powiedzenia?! Zamieniam się w słuch...

Pytanie konkursowe: Jaki nagłówek manifestu służy Spring-DM do rozpoznania pakunku aplikacji webowej do uruchomienia? Nagród nie przewiduje się.