Brakujący element Javarsovii

Tegoroczna edycja konferencji Javarsovia jest bez wątpienia największym wydarzeniem tej kategorii w Polsce. Grono sponsorów i partnerów nafaszerowane gigantycznymi korporacjami (IBM, Oracle, Microsoft, Adobe, Google). Pomyśleć, że Code-House w zeszłym roku był sponsorem tej niewielkiej konferencji a w tym roku nie przyszło by nam rywalizować nawet o ławkę sponsorów rezerwowych. :-) Moje gratulacje dla kapituły przekazuję już teraz.

Wróćmy jednak do meritum – a mianowicie czego brakuje na Javarsovii. :-) Brakuje prelekcji o OSGi, które mimo porządnego wieku (10 lat) jest w dalszym ciągu nowością dla wielu developerów. Szczęśliwie, w ramach spotkań Warszawa JUG było zaplanowane spotkanie pt. Springowe spojrzenie na OSGi – Spring Dynamic Modules for OSGi(tm) – które miał poprowadzić Jacek Lis. Niestety, Jacek w planowanym terminie (15 czerwiec) będzie poza Warszawą. Aby obronić OSGi i załatać deficyt tego tematu na Javarsovii (zgłaszałem się z OSGi R4.2 Enterprise do C4P), postanowiłem zastąpić Jacka i poprowadzić prezentację – której temat również ewoluował na Spring Dynamic Modules, Blueprint, OSGi – deklaratywne.

Podsumowując, czego można się spodziewać w najbliższy wtorek:

  • Celem prezentacji jest przedstawienie OSGi.
  • Tradycyjne podejście do usług w OSGi.
  • Co dodaje Spring DM/Blueprint.
  • Jak to uruchomić na Karafie1

Serdecznie zapraszam wszystkich zainteresowanych jak i zawiedzionych tym, że OSGi nie będzie na Javarsovii. :-)


1 Trwają negocjacje, czy Apache Felix Karaf zostanie projektem wysokiego poziomu (TLP) fundacji Apache.

Latest comments across all posts

Najnowsze wpisy

post Praktyki studenckie w Code-House

Jakiś czas temu Jacek Laskowski wspominał o praktykach studenckich w IBM.

Celem praktyk w IBM jest zapoznanie się z produktami korporacji, praktyki w Code-House mają nieco inny wymiar – przede wszystkim technologiczny. Część z dziedzin, w których obracamy się obecnie przy okazji ServiceMixa jest nieco futurystyczna (OSGi R 4.2, dOSGi), część za to bardzo praktyczna np. szerokie użycie Mavena oraz Spring Framework czy JAX-WS/JAX-RS. Cały kod, który powstanie w wyniku praktyk będzie opublikowany na licencji Apache 2.0 i udostępniony publicznie.

Pula tematów dla praktykantów obejmuje następujące projekty fundacji Apache:

  • Apache ServiceMix 4
  • Apache Ode
  • Apache ActiveMQ
  • Apache Camel
  • Apache Karaf

Organizacja praktyk

Praktyki są bezpłatne (niestety), odbywają się w trybie zdalnym i trwają do 3 miesięcy – termin rozpoczęcia praktyk jest uzgadniany ze studentem. Nie ma konieczności przebywania w naszym biurze – cały proces naboru aplikacji odbywa się przy pomocy Internetu i dopiero ostatni etap – rozmowa kwalifikacyjna – wymaga spotkania. Na co dzień konsultacje będziemy odbywać przy pomocy komunikatorów – Skype, GTalk, w skrajnym wypadku Gadu-Gadu. ;-) Co dwa tygodnie podsumowujemy minioną “iterację” oraz planujemy następną.
Po wprowadzeniu do narzędzi i projektów studenci będą wypływać na głębokie wody – czyli samodzielną realizację zadań.

Do zarządzania, koordynacji zadań oraz dokumentacji i weryfikacji kodu wykorzystujemy narzędzia Atlassian:

  • JIRA
  • Green Hopper
  • Confluence
  • Bamboo
  • Fisheye

Jak aplikować?

Jedyną wymaganą cechą jest znajomość języka Java – która zostanie zweryfikowana testem. Jeśli dysponujesz tą cechą i jesteś miłośnikiem technologii zapraszamy do wysyłania aplikacji pod adres rekrutacja@code-house.org z tematem Praktyki 2010. Nabór aplikacji na najbliższą edycję praktyk prowadzimy do 20 kwietnia. W zależności od rezultatów będą organizowane kolejne.
Nieco więcej informacji znajdziecie na stronie Praktyki Studenckie 2010.


post OSGi-fikacja oraz nowe bundle w repozytorium ServiceMix

W tym wpisie zostanie omówiony proces OSGi-fikacji artefaktów, który przechodziłem gdy uruchamiałem prostą usługę na ServiceMix, która miała śledzić zewnętrzny RSS i pobierać z niego wpisy. Postanowiłem skorzystać z camel-rss. Przykłady które były do niego załączone są wystarczające by stworzyć odpowiedniego konsumenta…

Problem zaczął się gdy usiłowałem uruchomić endpoint camela w OSGi. Mimo poprawnej konfiguracji, rozwiązanych zależności otrzymywałem wyjątek:

java.lang.NoClassDefFoundError: Could not initialize class com.sun.syndication.feed.synd.SyndFeedImpl
        at com.sun.syndication.io.SyndFeedInput.build(SyndFeedInput.java:123)
        at org.apache.camel.component.rss.RssUtils.createFeed(RssUtils.java:34)
        at org.apache.camel.component.rss.RssEntryPollingConsumer.createFeed(RssEntryPollingConsumer.java:54)
        at org.apache.camel.component.feed.FeedEntryPollingConsumer.poll(FeedEntryPollingConsumer.java:42)
        at org.apache.camel.impl.ScheduledPollConsumer.run(ScheduledPollConsumer.java:106)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
        at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:317)
        at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:150)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$101(ScheduledThreadPoolExecutor.java:98)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.runPeriodic(ScheduledThreadPoolExecutor.java:181)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:205)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:619)

Naturalnie, strasznie zirytowany, wziąłem się za dochodzenie – początkowo byłem przekonany że brakuje importów w camel-rss jednakże krótkie googlowanie wskazało rozwiązanie. Winne było kilka linii w klasie PluginManager:

    private Class[] getClasses() throws ClassNotFoundException {
        // Ten ClassLoader wskazuje na bundle w którym jest zdefiniowany endpoint!
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        List classes = new ArrayList();
        boolean useLoadClass = Boolean.valueOf(System.getProperty("rome.pluginmanager.useloadclass", "false")).booleanValue();
        for (int i = 0; i <_propertyValues.length; i++) {
            // Naturalnie tutaj leciał ClassNotFoundException
            Class mClass = (useLoadClass ?  classLoader.loadClass(_propertyValues[i]) :
                Class.forName(_propertyValues[i], true, classLoader));
            classes.add(mClass);
        }
        Class[] array = new Class[classes.size()];
        classes.toArray(array);
        return array;
    }

Po przeróbce metoda wygląda następująco.

    private Class[] getClasses() throws ClassNotFoundException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        List classes = new ArrayList();
        boolean useLoadClass = Boolean.valueOf(System.getProperty("rome.pluginmanager.useloadclass", "false")).booleanValue();
        for (int i = 0; i <_propertyValues.length; i++) {
            Class mClass = null;
            try {
                if (useLoadClass) {
                    mClass = classLoader.loadClass(_propertyValues[i]);
                } else {
                    mClass = Class.forName(_propertyValues[i], true, classLoader);
                }
            } catch (ClassNotFoundException e) {
                // Jeśli zewnętrzny class loader zgłosi wyjątek usiłujemy załadować klasę
                // z bieżącej paczki
                mClass = getClass().getClassLoader().loadClass(_propertyValues[i]);
            }
            classes.add(mClass);
        }
        Class[] array = new Class[classes.size()];
        classes.toArray(array);
        return array;
    }

Naturalnie, można się zastanawiać po co bibliotece do obsługi RSS zabiegi z ClassLoaderami. Otóż ROME wykorzystuje plik .properties do konfiguracji “pluginów” (poors man DI). W określonych miejscach możemy dodać własne klasy które obsłużą jakiś niestandardowy format. Problem w tym, że “patent” z plikiem properties świetnie sprawdza się przy płaskim classloaderze, niestety zawodzi w OSGi. Należy pamiętać o tym, że w OSGi nasz class loader ma dostęp do tego, do czego mu pozwalają importy i nie wszystko to, co widzi nasze oko w archiwum musi być dostępne dla naszego programu.

Przy okazji stworzenia bundle z ROME 1.0 postanowiłem również stworzyć bundle dla Apache POI 3.6. Bibliotekę tą wykorzystywałem wspólnie z Tomkiem Nurkiewiczem podczas prezentacji “Mule ESB vs ServiceMix”.

OSGi-fikacja

Bardzo swobodna definicja:

OSGi-fikacja to proces mający na celu stworzenie działającego bundle z istniejącej już biblioteki. Wiele z projektów nie dostarczają poprawnych z punktu widzenia frameworku OSGi manifestów, czasami są budowane poprzez Ant bądź w ogóle są dostępne tylko ich binarne wersje co utrudnia analizę. Proces ten w większości przypadków sprowadza się do analizy zależności klas (importów) oraz zadeklarowanych klas (eksportów). W skrajnych wypadkach konieczna jest dodanie kodu bądź podmiana jego fragmentów tak by nie powodowały problemów po uruchomieniu. W celu zachowania porządku w publicznych repozytoriach Mavena stworzone w ten sposób archiwa są zapisywane z innym ArtifactId bądź GroupId, natomiast z zachowaniem oryginalnej wersji.

Nie aspiruję do miana człowieka który tworzy nowe pojęcia. Ten nieco przydługi wywód ma na celu jedynie przybliżenie działań które czasami są konieczne do uzyskania biblioteki działającej w OSGi.

Na potrzeby przykładu OSGi-fikacji wybrałem log4j, jako popularną bibliotekę z małym zbiorem zależności. Wersja 1.2.12 nie zawiera poprawnego manifestu OSGi przez co nie można jej użyć pod Equinoxem czy Felixem.
Zależności które ma log4j takie jak:

  • javax.mail
  • javax.swing
  • javax.naming
  • javax.activation
  • javax.management
  • com.sun.jdmk.comm

Są opcjonalne, co znaczy że biblioteka bez problemów uruchomi się jeśli nie uda się zaimportować wyżej wymienionych paczek. Określimy zatem ich resolution na optional.

Pozostałe zależności wymienione poniżej są wymagane by móc odczytać plik log4j.xml:

  • org.w3c.dom
  • javax.xml.parsers
  • org.xml.sax

Poniżej znajduje się pom.xml który przygotowałem po to by zaprezentować użycie wcześniej wspomnianych pluginów. Pom ten ma skutkować stworzeniem artefaktu OSGi gotowego do uruchomienia pod Kara-fem.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
      <groupId>org.apache.servicemix.bundles</groupId>
      <artifactId>bundles-pom</artifactId>
      <version>4</version>
    </parent>

    <groupId>org.apache.servicemix.bundles</groupId>
    <artifactId>org.apache.servicemix.bundles.log4j</artifactId>
    <version>1.2.12-SNAPSHOT</version>
    <packaging>bundle</packaging>
    <name>Apache ServiceMix Bundles: ${pkgArtifactId}-${pkgVersion}</name>
    <description>
        This bundle simply wraps ${pkgArtifactId}-${pkgVersion}.jar.
    </description>

    <properties>
        <!-- Zmienne dla maven-bundle-plugin -->
        <servicemix.osgi.export.pkg>
            org.apache.log4j*;version=${pkgVersion}
        </servicemix.osgi.export.pkg>
        <servicemix.osgi.import.pkg>
            <!-- Zależności opcjonalne -->
            com.sun.jdmk.comm;resolution:=optional,
            javax.jms;resolution:=optional,
            javax.mail*;resolution:=optional,
            javax.management;resolution:=optional,
            javax.naming;resolution:=optional,
            javax.swing*;resolution:=optional,
            * <!-- Wszystkie inne zależności jakie doda analizator -->
        </servicemix.osgi.import.pkg>
        <!-- Zmienne artefaktu -->
        <pkgGroupId>log4j</pkgGroupId>
        <pkgArtifactId>log4j</pkgArtifactId>
        <pkgVersion>1.2.12</pkgVersion>
    </properties>

    <dependencies>
        <!-- zależność do log4j -->
        <dependency>
            <groupId>${pkgGroupId}</groupId>
            <artifactId>${pkgArtifactId}</artifactId>
            <version>${pkgVersion}</version>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Kopiowanie plików .class -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <includes>
                                    <include>${pkgGroupId}:${pkgArtifactId}</include>
                                </includes>
                            </artifactSet>
                            <filters>
                                <filter>
                                    <artifact>${pkgGroupId}:${pkgArtifactId}</artifact>
                                    <excludes>
                                        <exclude>**</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
                            <createDependencyReducedPom>true</createDependencyReducedPom>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Teraz pora na instalację bundla na szynie:


 ____                  _          __  __ _
/ ___|  ___ _ ____   _(_) ___ ___|  \/  (_)_  __
\___ \ / _ \ '__\ \ / / |/ __/ _ \ |\/| | \ \/ /
 ___) |  __/ |   \ V /| | (_|  __/ |  | | |>  <
|____/ \___|_|    \_/ |_|\___\___|_|  |_|_/_/\_\

  Apache ServiceMix (4.2.0-fuse-01-00)

Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
karaf@root>

karaf@root> install mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.log4j/1.2.12-SNAPSHOT
Bundle ID: 186
karaf@root> list|grep log4j
[ 186] [Resolved   ] [            ] [       ] [   60] Apache ServiceMix Bundles: log4j-1.2.12 (1.2.12.SNAPSHOT)
karaf@root> start 186
karaf@root> list|grep log4j
[ 186] [Active     ] [            ] [       ] [   60] Apache ServiceMix Bundles: log4j-1.2.12 (1.2.12.SNAPSHOT)
karaf@root> packages:imports 186
OSGi System Bundle (0): javax.management; version="0.0.0"
OSGi System Bundle (0): javax.naming; version="0.0.0"
OSGi System Bundle (0): javax.swing; version="0.0.0"
OSGi System Bundle (0): javax.swing.border; version="0.0.0"
OSGi System Bundle (0): javax.swing.event; version="0.0.0"
OSGi System Bundle (0): javax.swing.table; version="0.0.0"
OSGi System Bundle (0): javax.swing.text; version="0.0.0"
OSGi System Bundle (0): javax.swing.tree; version="0.0.0"
OSGi System Bundle (0): javax.xml.parsers; version="0.0.0"
OSGi System Bundle (0): org.w3c.dom; version="0.0.0"
OSGi System Bundle (0): org.xml.sax; version="0.0.0"
OSGi System Bundle (0): org.xml.sax.helpers; version="0.0.0"
OPS4J Pax Logging - API (3): org.apache.log4j.spi; version="1.2.15"
OPS4J Pax Logging - API (3): org.apache.log4j.xml; version="1.2.15"
OPS4J Pax Logging - API (3): org.apache.log4j; version="1.2.15"
geronimo-jms_1.1_spec (64): javax.jms; version="1.1.0"
Apache ServiceMix Bundles: mail-1.4.1 (86): javax.mail.internet; version="1.4.1"
Apache ServiceMix Bundles: mail-1.4.1 (86): javax.mail; version="1.4.1"
karaf@root> packages:exports 186
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.lf5.util; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.net; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.lf5.viewer; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.jmx; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.jdbc; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.config; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.helpers; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.lf5.config; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.or.jms; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.nt; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.or.sax; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.lf5.viewer.categoryexplorer; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.lf5; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.or; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.chainsaw; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.varia; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.lf5.viewer.configure; version="1.2.12"
Apache ServiceMix Bundles: log4j-1.2.12 (186): org.apache.log4j.lf5.viewer.images; version="1.2.12"
karaf@root>

Jak widać wszystko działa i ma się dobrze. :) Jedyna uwaga jaką mam to by nie używać tak spreparowanego log4j. W specyfikacji OSGi R4 V4.2 Enterprise jest opisana usługa logująca. Oprócz niej jest jeszcze Pax Logging (używany przez Karafa) wspierająca kilka różnych bibliotek od log4j poprzez slf4j po wspomnianą usługę OSGi.

Pozdrawiam i życzę miłej OSGi-fikacji! :)


post Specyfikacja Enterprise OSGi została opublikowana

Dnia dzisiejszego została wydana finalna wersja specyfikacji OSGi R4 V4.2 Enterprise. Wersja jest gotowa do pobrania pod adresem http://www.osgi.org/Download/Release4V42. Dokument ma 483 strony. :-)

Older Posts

Enterprise OSGi

Wprowadzenie do Apache ServiceMix 4 cz. 1

Wygodne środowisko dla programisty z JRebel

Spring 3.0 RC3 + Maven

GWT oraz implementacje MVC

Instalowanie oprogramowania Atlassian cz. 1