Some of posts from this blog has been moved to dywicki.pl. You will be automatically redirected to new blog if you would submit comment.
New posts are published on dywicki.pl, this blog contains old content and it is not continued.

Niektóre posty z tego bloga zostały przeniesione do dywicki.pl. Zostaniesz automatycznie przekierowany jeśli bedzięsz chciał dodać komentarz.
Nowe posty sa publikowane na dywicki.pl, ten blog zawiera stare treści i nie jest kontynuowany.

Przeprowadzka

10 Cze
2011

Po dłuższym czasie braku aktywności na tym blogu – postanowiłem przenieść część z postów, które zostały na nim opublikowane do nowego blogu, który tyczy się kwestii java, middleware itd. pod adresem dywicki.pl. Być może zawitają tam inne języki. :) Wszystkie pozostałe posty są i nadal będą dostępne. Zmiany są podyktowane tym, że jest mi trudno prowadzić blog jednoosobowej firmy i developerski zarazem. Stąd też decyzja o stworzeniu tylko jednego – developerskiego.

Dla osób, które jeszcze subskrybują kanały RSS z tego bloga – proszę o przepięcie na nowy adres – dywicki.pl, na pewno nie pożałujecie. Nowy blog z pewnością będzie aktualizowany częściej niż ten. :)

I work with Apache Karaf almost every day. There is a lot of commands provided by default and most of them are a bit anonymous. In this post I would like introduce these commands.

List bundles

Common command executed in Karaf shell is list. There is few switches which makes this command more usable. First of them is -l which shows bundle locations, second is -t. Second switch is available from Karaf 2.1.

Below example output of these commands:


        __ __                  ____
       / //_/____ __________ _/ __/
      / ,<  / __ `/ ___/ __ `/ /_
     / /| |/ /_/ / /  / /_/ / __/
    /_/ |_|\__,_/_/   \__,_/_/


  Apache Karaf (2.1.2)

Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or 'osgi:shutdown' to shutdown Karaf.

karaf@root> list -l
START LEVEL 100 , List Threshold: 50
   ID   State         Blueprint      Spring    Level  Location
[  34] [Resolved   ] [            ] [       ] [   60] mvn:org.apache.geronimo.specs/geronimo-servlet_2.5_spec/1.1.2
[  39] [Active     ] [            ] [       ] [   60] mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jetty/6.1.25_1

karaf@root> list -l -t 0
START LEVEL 100 , List Threshold: 0
   ID   State         Blueprint      Spring    Level  Location
[   0] [Active     ] [            ] [       ] [    0] System Bundle
[   1] [Active     ] [            ] [       ] [    5] mvn:org.ops4j.pax.url/pax-url-mvn/1.2.1
[   2] [Active     ] [            ] [       ] [    5] mvn:org.ops4j.pax.url/pax-url-wrap/1.2.1
[   3] [Active     ] [            ] [       ] [    8] mvn:org.ops4j.pax.logging/pax-logging-api/1.5.3
[   4] [Active     ] [            ] [       ] [    8] mvn:org.ops4j.pax.logging/pax-logging-service/1.5.3
[   5] [Active     ] [            ] [       ] [   10] mvn:org.apache.felix/org.apache.felix.configadmin/1.2.4
[   6] [Active     ] [            ] [       ] [   11] mvn:org.apache.felix/org.apache.felix.fileinstall/3.0.2
[   7] [Active     ] [Created     ] [       ] [   20] mvn:org.apache.aries.blueprint/org.apache.aries.blueprint/0.2-incubating
[   8] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.jaas/org.apache.karaf.jaas.config/2.1.2
[   9] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.admin/org.apache.karaf.admin.command/2.1.2
[  10] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf/org.apache.karaf.management/2.1.2
[  11] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.spring/2.1.2
[  12] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.features/org.apache.karaf.features.core/2.1.2
[  13] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.shell/org.apache.karaf.shell.packages/2.1.2
[  14] [Active     ] [            ] [       ] [   30] mvn:org.apache.aries.jmx/org.apache.aries.jmx.blueprint/0.2-incubating
[  15] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.jaas/org.apache.karaf.jaas.modules/2.1.2
[  16] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.shell/org.apache.karaf.shell.ssh/2.1.2
[  17] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.features/org.apache.karaf.features.management/2.1.2
[  18] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.features/org.apache.karaf.features.command/2.1.2
[  19] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.shell/org.apache.karaf.shell.log/2.1.2
[  20] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.admin/org.apache.karaf.admin.core/2.1.2
[  21] [Active     ] [            ] [       ] [   30] mvn:org.apache.aries.jmx/org.apache.aries.jmx/0.2-incubating
[  22] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.blueprint/2.1.2
[  23] [Active     ] [            ] [       ] [   30] mvn:org.apache.mina/mina-core/2.0.0-RC1
[  24] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.shell/org.apache.karaf.shell.dev/2.1.2
[  25] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.shell/org.apache.karaf.shell.osgi/2.1.2
[  26] [Active     ] [            ] [       ] [   30] mvn:org.apache.sshd/sshd-core/0.4.0
[  27] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.shell/org.apache.karaf.shell.commands/2.1.2
[  28] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.features/2.1.2
[  29] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.shell/org.apache.karaf.shell.console/2.1.2
[  30] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.admin/org.apache.karaf.admin.management/2.1.2
[  32] [Active     ] [Created     ] [       ] [   30] mvn:org.apache.karaf.shell/org.apache.karaf.shell.config/2.1.2
[  34] [Resolved   ] [            ] [       ] [   60] mvn:org.apache.geronimo.specs/geronimo-servlet_2.5_spec/1.1.2
[  39] [Active     ] [            ] [       ] [   60] mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jetty/6.1.25_1

As you see first comman returns short list which contains bundles installed by me (servlet api and jetty). Second list contains bundles default installed also by Karaf. Another usefull switch is -s which shows symbolic names:

karaf@root> list -s
START LEVEL 100 , List Threshold: 50
   ID   State         Blueprint      Spring    Level  Symbolic name
[  34] [Resolved   ] [            ] [       ] [   60] org.apache.geronimo.specs.geronimo-servlet_2.5_spec (1.1.2)
[  39] [Active     ] [            ] [       ] [   60] org.apache.servicemix.bundles.jetty (6.1.25.1)

Switch -t may be mixed with both -s and -l.

List services

After first impression with OSGi and bundles as modules most of us moving to using OSGi services. That’s really cool stuff and gives a lot of fun, but without helper commands we may stuck. Karaf provides command named ls which shows services exported by given bundle.

karaf@root> ls 132

Apache Karaf :: Web Console :: Admin Plugin (132) provides:
-----------------------------------------------------------
osgi.service.blueprint.compname = adminPlugin
felix.webconsole.label = admin
objectClass = javax.servlet.Servlet
service.id = 176
----
osgi.blueprint.container.version = 2.1.2
osgi.blueprint.container.symbolicname = org.apache.karaf.webconsole.admin
objectClass = org.osgi.service.blueprint.container.BlueprintContainer
service.id = 178

If we would check which services are in use by our bundle we have very usefull switch -u.

karaf@root> ls -u 21
Apache Aries JMX Bundle (21) uses:
----------------------------------
service.vendor = Apache Software Foundation
service.pid = org.apache.felix.cm.ConfigurationAdmin
service.description = Configuration Admin Service Specification 1.2 Implementation
objectClass = org.osgi.service.cm.ConfigurationAdmin
service.id = 33

List packages

When you work under OSGi it’s important which packages you import and export. The two commands packages:imports and packages:exports will simply show what’s comes and goes from your bundle. I’ll not show how these commands work but I have little trick for you. When you’re unable to resolve bundle because you have missing import packages and you have ClassNotFoundException type dev:dynamic-import bundleid command. This command add DynamicImport-Package: * entry to bundle manifest. After that, when you’ll resolve bundle type packages:imports and check complete list of import you missed in your headers.

Features

All commands shown above are strictly related to OSGi. But Karaf is a little bigger and allow you do more than OSGi execution environment. One of tools which Karaf build on top of OSGi framework is features mechanism. You may define list of things to install and add dependencies between them instead typing install command line after line. But sometimes you would like to check what given feature contains. To do that type features:info command. This command requires feature name as argument.

karaf@root> features:info webconsole

Description of webconsole 2.1.2 feature
----------------------------------------------------------------
Feature has no configuration
Feature depends on:
  webconsole-base 2.1.2
Feature contains followed bundles:
  mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.admin/2.1.2
  mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.features/2.1.2
  mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.gogo/2.1.2

You may use few additional switches: -b, -d, -c. First shows bundles in feature, second bundle dependencies and last feature configuration. We have also another switch -t which shows all these informations plus tree of features and it’s bundles.

karaf@root> features:info -t webconsole
Description of webconsole 2.1.99-SNAPSHOT feature
----------------------------------------------------------------
Feature has no configuration
Feature depends on:
  webconsole-base 2.1.99-SNAPSHOT
Feature contains followed bundles:
  mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.admin/2.1.99-SNAPSHOT
  mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.features/2.1.99-SNAPSHOT
  mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.gogo/2.1.99-SNAPSHOT

Feature tree
 webconsole 2.1.99-SNAPSHOT
 + mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.admin/2.1.99-SNAPSHOT
 + mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.features/2.1.99-SNAPSHOT
 \ mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.gogo/2.1.99-SNAPSHOT
    webconsole-base 2.1.99-SNAPSHOT
    + mvn:org.apache.felix/org.apache.felix.metatype/1.0.4
    + mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.branding/2.1.99-SNAPSHOT
    \ mvn:org.apache.felix/org.apache.felix.webconsole/3.1.6
       http 2.1.99-SNAPSHOT
       + mvn:org.ops4j.pax.web/pax-web-api/0.8.1
       + mvn:org.ops4j.pax.web/pax-web-spi/0.8.1
       + mvn:org.ops4j.pax.web/pax-web-runtime/0.8.1
       \ mvn:org.ops4j.pax.web/pax-web-jetty/0.8.1
          jetty [7.0,8.0) *
Tree contains 1 unresolved dependencies
 * means that node declares dependency but the dependant feature is not available.

Development commands

I mentioned dev:dynamic-import command before. But Karaf have few more commands which makes development easier. First of all is dev:show-tree which shows bundles tree, for example:

karaf@root> dev:show-tree 39
Bundle dump [39] is currently ACTIVE

dump [39]
+- org.apache.karaf.diagnostic.core [16]
+- org.apache.aries.blueprint [7]
   +- org.apache.felix.configadmin [5]
   |  +- org.ops4j.pax.logging.pax-logging-api [3]
   +- org.ops4j.pax.logging.pax-logging-api [3]

Another command you may use is dev:framework which allows you change OSGi framework used by Karaf. I don’t use this command to often.

Last command I would introduce is dev:create-dump commited to Karaf trunk by me. This command creates zip archive which contains diagnostic stuff you may attach to JIRA or send to developers in your company to check what was wrong. By default dumps contains log entries from $KARAF_BASE/data/log, list of installed bundles and features. You may also create new diagnostic providers. Sample code is available in SVN: demo, provider class, blueprint config.

Complete

Remember that every Karaf command can be executed with –help switch which shows all arguments and switches. In this post you was introduced to following commands:

  • list and switches -t -l -s
  • ls and -u switch
  • packages:imports, packages:exports, dev:dynamic-import
  • features:info and switches -b -d -c and -t
  • dev:show-tree, dev:framework, dev:create-dump

UiBinder jest rozszerzeniem, które pozwala nam szybko i bardzo przyjemnie stworzyć widok okienka w XML’u.  Dzięki niemu możemy oddzielić widok aplikacji webowej od logiki ją obsługującej w czystym GWT. Sposób używania UiBinder’a jest bardzo dobrze opisany. Jednak już po chwili korzystania  z tego narzędzia wraz z klasami dostępnymi standardowo w GWT odczujemy mały niedosyt, że potrzebujemy tworzyć własne widgety, które będą kompatybilne z UiBinder’em albo będziemy musieli zrezygnować całkowicie z tego narzędzia.

W tym wpisie właśnie chciałbym przedstawić sposób stworzenia własnego prostego widget’u. Będzie nim pole do wpisywania hasła, wraz z labelk’ą opisującą do czego to pole służy. Naszym celem jest stworzenie widget’u który wygląda mniej więcej tak:

1

Przejdźmy do kodu, stwórzmy klasę reprezentującą widget:

// Copyright (C) 2010 Code-House
// All rights reserved
package com.hqcargo.shippingstock.client.application.view.widget.form;

import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.hqcargo.shippingstock.client.application.view.widget.LabelWidget;

/**
 * Widget zawierający pole label opisu, wpisywania hasła, a także label
 * w którym będzie wyświetlany błąd. Klasa musi dziedziczyć po Widgecie,
 * w naszym wypadku dziedziczy pośrednio po FlowPanel'u, na którym są
 * rysowane elementy naszego Widget'u.
 *
 * @author Michał Rowicki
 */
public class PasswordWidget extends FlowPanel {

    //Wszystkie pola
    /**
     * Etykieta pola.
     */
    LabelWidget label;

    /**
     * Etykieta błędu.
     */
    Label errorLabel;

    /**
     * Pole, które musi rozszerzać klasę Widget.
     * {@link com.google.gwt.user.client.ui.Widget}
     */
    PasswordTextBox pass;

    /**
     * Wartość id używana we wszelkich formularzach, by można było
     * cache'ować dane wpisane do ramki i później by były wczytywane
     * w przeglądarce.
     */
    String id;

    /**
     * Należy stworzyć konstruktor bezargumentowy tak by framework
     * UiBinder'a mógł stworzyć instancję naszego obiektu i później
     * sam ją wypełnił. Jeżeli chcemy móc tworzyć widgety z danymi
     * ładowanymi w Javie musimy skorzystać z @UiFactory
     * {@link http://code.google.com/intl/pl/webtoolkit/doc/latest/DevGuideUiBinder.html#Share_resource_instances}
     * W konstruktorze musimy zainicjować wszystkie pola by móc
     * później z nich korzystać.
     */
    public PasswordWidget() {
        pass = new PasswordTextBox();

        label = new LabelWidget();
        //ustawienie atrybutu class, by móc podpiąć własne CSS'y
        label.setStyleName("item_label");

        errorLabel = new Label();
        errorLabel.setStyleName("error_label");

        add(label);
        add(pass);
        add(errorLabel);

        //ustawienie atrybutu class dla całego FlowPanelu
        setStylePrimaryName("item");
    }

    //by nasza aplikacja działała poprawnie musimy utworzyć gettersy i settersy dla pól klasy

    /**
     * Pobranie wartości pola label.
     *
     * @return Wartość label
     */
    public final LabelWidget getLabel() {
        return label;
    }

    /**
     * Ustawienie wartości pola label.
     *
     * @param Podaj label
     */
    public final void setLabel(LabelWidget label) {
        this.label = label;
    }

    /**
     * Pobranie wartości pola errorLabel.
     *
     * @return Wartość errorLabel
     */
    public final Label getErrorLabel() {
        return errorLabel;
    }

    /**
     * Ustawienie wartości pola errorLabel.
     *
     * @param Podaj errorLabel
     */
    public final void setErrorLabel(Label errorLabel) {
        this.errorLabel = errorLabel;
    }

    /**
     * Pobranie wartości pola pass.
     *
     * @return Wartość pass
     */
    public final PasswordTextBox getPass() {
        return pass;
    }

    /**
     * Ustawienie wartości pola pass.
     *
     * @param Podaj pass
     */
    public final void setPass(PasswordTextBox pass) {
        this.pass = pass;
    }

    /**
     * Pobranie wartości pola id.
     *
     * @return Wartość id
     */
    public final String getId() {
        return id;
    }

    //ustawia wartość pola id dla obiektu pass

    /**
     * Ustawienie Id pola.
     *
     * @param id Identyfikator pola.
     */
    public final void setFieldId(final String id) {
        this.id = id;

        pass.getElement().setId(id);
        label.setFor(id);
    }
}

 

Użycie naszego widget’u w pliku *.ui.xml jest bardzo proste należy jedynie dodać w polu UiBinder xmlns:dowolna_nazwa="urn:import:nazwa_paczki". A następnie w ciele HTMLPanela dodać pole dowolna_nazwa:PasswordWidget i uzupełnić odpowiednio pola, np:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder
    xmlns:ui="urn:ui:com.google.gwt.uibinder"
    xmlns:widget="urn:import:client.application.view.widget"
    xmlns:gwt="urn:import:com.google.gwt.user.client.ui">

    <gwt:HTMLPanel tag="div">

        <widget:PasswordWidget fieldId="password" fieldLabel="Hasło" ui:field="password" />

    </gwt:HTMLPanel>
</ui:UiBinder>

W ten oto sposób stworzyliśmy najprostszy własny widget który możemy użyć w UiBinderze.

Tutaj załączam cały działający projekt Eclipse’a z naszym własnym widgetem (należy przed uruchomieniem zainstalować SDK GWT).

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.

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.

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! :)

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. :-)

Enterprise OSGi

23 Mar
2010

Do opublikowania tego postu zachęcił mnie Jacek Laskowski swym postem pod tytułem W piątek 4Developers ze mną z Enterprise OSGi i in.

Bardzo się cieszę że na 4Developers (na którym niestety mnie nie będzie) temat Enterprise OSGi będzie poruszony, ponieważ jak się zdaje jest to nieuchronny kierunek rozwoju Javy. Pod wpływem słów Jacka zacząłem się zastanawiać nad długofalowymi efektami jakie OSGi ma wnieść do developmentu.

Hałas który obecnie jest wokół OSGi w przybiera konkretne kształty w postaci projektów takich jak Aries czy Gemini. Obydwa projekty skupiają się nad ostatnimi draftami OSGi R4 V4.2 i mają na celu udostępnienie technologii takich jak JNDI, JPA i JMX wewnątrz kontenerów OSGi. Zacznijmy jednak od początku..

OSGi a jarhell

JAR-hell occurs when software is deployed into a runtime environment which is unsuitable, but nothing other than full integration testing would detect this. Having multiple software packages dependent upon the same piece of software, with unpredictable incompatibilities, is pure hell. Ensuring the compatibility of a variety of dependent packages is duanting, doing it amongst the variety supported by a hierarchy of complex class loaders, is inhuman.

Źródło Apache Depot

Czyli w skrócie – piekło zaczyna się robić gdy pojawiają się niekompatybilności między bibliotekami w poprawnym środowisku. Co więcej owe niekompatybilności można wykryć dopiero po dogłębnych testach we wszystkich środowiskach w których ma działać aplikacja.

W przypadku OSGi wszystkie zależności są przewidywalne, co więcej nie uda się nam uruchomić paczki bez jej zależności – stąd teoretycznie nigdy nie powinniśmy widzieć ClassNotFoundException. Nie uda nam się również uruchomić naszego bundle jeśli powstanie konflikt w używanych zależnościach. Przykład z życia wzięty – mamy bundle zależące od camel-activemq oraz activemq-core. Pierwszy z nich pozwala na import spring-jms w wersji < 4.0, natomiast drugi w wersji < 2.6. Jeśli do tego mamy dwie wersje bundle spring-jms: 2.5.6 oraz 3.0.0 to mamy klapę. Naszej paczki nie da się wystartować ponieważ otrzymamy "Packages usage conflict". Zostaliśmy ochronieni przed JAR Hellem kosztem zablokowania kodu nawet jeśli zależność była opcjonalna.

Rozwiązanie zagadki packages usage.

Problem wydaje się trywialny – teoretycznie to są dwie różne wersje Springa, nie da się zaprzeczyć że 2.5.6 != 3.0.0. W praktyce jednak zmiany w spring-jms były tak niewielkie że można bez problemu uruchomić activemq-core z nową wersją. W takim wypadku jesteśmy zmuszeni do czekania na nową wersję ActiveMQ, która będzie pozwalała na korzystanie ze Springa 3.0 bądź samodzielnie zmodyfikować manifesty. Obydwa rozwiązania są równie złe – jedno to czekanie, drugie to tworzenie nowej dystrybucji ActiveMQ.

Co w takim wypadku możemy zrobić? Możemy użyć serwisów OSGi, które pozwalają na oddzielenie implementacji od interfejsu, dzięki czemu możemy połączyć dwie wersje bibliotek za fasadą w postaci ServiceReference. Tutaj jednak może pojawić się inny problem – mianowicie część bibliotek które lubią dostęp do ClassLoaderów może skutecznie protestować – na przykład Hibernate czy Open JPA. Dla przykładu diagram obrazujący kolejny z życia wzięty przypadek:

Diagram bundli

W tym przypadku usiłowałem stworzyć działającą usługę która zapisywała by przychodzące komunikaty w bazie danych. Może parę słów o tym, który bundle co robi:

  • datasource otwiera połączenie do bazy danych, tworzy ConnectionFactory dla JMS a także EntityManagera.
  • binding bramka do przyjmowania komunikatów – w tym przypadku był to web service.
  • engine definicja routingu z użyciem Camela.
  • persistence użycie EntityManagera do zapisywania komunikatów
  • domain POJO, klasy domenowe

Całość komunikacji odbywała się z JMS w trybie request-reply dzięki Apache Camel. Po bardzo długich „walkach” poniższą strukturę udało się uruchomić pod OSGi. Ostatecznie całość działa z Hibernate w układzie takim jak poniżej.

Działająca struktura

Z diagramu wyrzuciłem paczki które nie są istotne takie jak driver JDBC czy Commons ConnectionPool. Jedyny mankament na jaki trafiłem wiąże się z DAO Service, mianowicie bundle który go eksportuje poprzez Spring-DM musi zadeklarować widoczność wszystkich swoich klas dla paczki która będzie korzystać z usługi co jak by nie patrzeć jest drobnym wypaczeniem fasady jaką ma być ServiceReference. Niestety po 2 tygodniach poświęconych na uruchomienie JPA w OSGi nie siliłem się na elegancję.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:osgi="http://www.springframework.org/schema/osgi"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/osgi
        http://www.springframework.org/schema/osgi/spring-osgi.xsd
    ">

    <osgi:reference id="dataSource" interface="javax.sql.DataSource" />

    <osgi:service id="exchangeDAOExporter" ref="exchangeDAO"
        context-class-loader="service-provider"
        interface="org.code_house.dataaccess.ExchangeDAO" />
</beans>

Kolejne metadane

Problem jaki powstaje z OSGi to metadane. Do tej pory – jeśli zarządzałem zależnościami do bibliotek robiłem to przez dependencyManagement Mavena, dzięki któremu rozwiązywałem wszystkie konflikty. OSGi jednak nie wiąże się z Mavenem ponieważ są to dwa różne obszary o zgoła innym funkcjonowaniu – OSGi to runtime, Maven to build time. Dopóki nie zapanuje harmonia pomiędzy tymi dwoma uruchamianie czegokolwiek w OSGi będzie katorgą. Należy do tego dodać jeszcze jeden element związany z OSGi – mianowicie OSGi Bundle Repository (OBR), a OBR nijak się ma do repozytoriów Mavena przez co rozbieżności tylko się nasilają.
Aby temu zapobiegać najpopularniejsze istniejące repozytoria SpringSource Enterprise Bundle Repository oraz ServiceMix 4 Bundles repository – publikują artefakty w repozytoriach Mavena. Problem w tym, że część artefaktów jest powielona. Tak jak kiedyś były 3 wersje Java Persistence API tak teraz dochodzą kolejne dwie – z manifestami OSGi. Czy ktoś wspominał o piekle?
Należy również dodać że nie każdy JAR który ląduje w OSGi jest traktowany tak samo – oprócz standardowych bundli są również fragmenty, które są świetnym rozwiązaniem, jednakże początkowo potrafią przysporzyć wielu problemów. Cały trik sprowadza się do tego, że fragmenty mają wspólny class loader z paczką do której są przypięte.

Korzyści z Enterprise OSGi

Vendor Lock Po całych tych wywodach na temat problemów z OSGi pora na to co ma ono nam dać – przenośność. Podobnie jak Java EE 6 z profilami tak OSGi ma zapewnić większą przenośność klocków pomiędzy środowiskami. Wyobrażacie sobie, że można przenieść aplikację z kontenera servletów na serwer aplikacyjny bez modyfikacji? Albo usługę z szyny integracyjnej na kontener servletów? Niedorzeczne, ale z OSGi możliwe do wykonania. Wystarczy zainstalować wszystkie wymagane bundle i całość będzie działać.

Oczywiście naiwna była by wiara w to, że tak będzie. Każda specyfikacja która powstaje dla Javy ma standaryzować i ujednolicać środowiska. W praktyce jednak każda z nich staje się punktem wyjściowym do rozwoju nowych produktów. Każdy dostawca oferuje zgodność ze specyfikacją plus coś. Nie twierdzę, że to złe, ponieważ polaryzacja rynku oprogramowania jest tak samo potrzebna jak wolny rynek, należy się jednak wystrzegać monopolistów a w przypadku oprogramowania również vendor locków.

Patrzę na OSGi z nadzieją ponieważ w moim mniemaniu jest to przedłużenie idei jaką niosła specyfikacja Java Business Integration. Propozycja JBI 2.0 spotkała się z krytyką ze strony IBM oraz BEA Systems zasłaniających się tym, że istnieje SCA. Problem w tym, że obydwie specyfikacje traktują o innych warstwach integracji – SCA gwarantuje przenośność usług, podczas gdy JBI miało zapewnić przenośność komponentów i komunikacji między nimi. SCA i JBI mogą a nawet powinny iść w parze. Teoria JBI mówiła o możliwości uruchomienia servicemix-cxf-bc (zgodnego z JBI) na Open ESB, w praktyce okazywało się to jednak bardzo trudne. Dzięki OSGi/Enterprise OSGi stanie się to łatwiejsze.

W ciągu kilku najbliższych lat trend OSGi dzięki zainteresowaniu wielkich korporacji będzie rósł w siłę. Trzymam kciuki by Enterprise OSGI przyniosło więcej korzyści niż tylko zbawienie od JAR Hella.

Dnia 23 lutego w ramach Warszawa JUG miałem przyjemność wraz z Tomkiem Nurkiewiczem prezentować narzędzia integracyjne z otwartym kodem źródłowym. Tomek przedstawił Mule ESB, podczas gdy ja zająłem się Apache ServiceMix i Apache Camel. Ze względu na objętość przykładu ten wpis będzie jedynie wprowadzeniem do konsoli.

Niestety podczas prezentacji nie udało mi się uruchomić przykładu na „szynie” – ponieważ uniemożliwiły to zależności do bibliotek których nie miałem zapisanych lokalnie. Drugim mym przeciwnikiem był czas – nie było wielu chętnych by słuchać po 2h tłumaczeń dlaczego się nie udało :-) Na problem z zależnościami stworzyłem rozwiązanie i zgłosiłem je do Karaf-a (FELIX 2141). W przyszłej wersji – 1.6 – wszyscy będą mogli skorzystać z polecenia features:info -t które wyświetli całe drzewko zależności potrzebnych do zainstalowania nowych funkcjonalności.

Przygotowanie środowiska

Do uruchomienia przykładów potrzebować będziemy dwóch paczek – pierwsza to Apache ServiceMix, druga to Apache Maven. Ze swojej strony polecam pobranie FUSE ESB 4.2, produktu który jest oparty o ServiceMix. Zgodnie z moimi zapowiedziami od tej wersji FUSE Source wprowadza pełne wsparcie produkcyjne dla ServiceMix 4. Jeśli nie masz zainstalowanego Mavena i nigdy tego nie robiłeś zajrzyj na wiki Code-House: Wprowadzenie do Maven 2

Po pobraniu odpowiedniej wersji należy ją rozpakować do wybranego folderu. Szynę uruchamiamy skryptem servicemix.bat. Jeśli wszystko przebiegło poprawnie naszym oczom powinien ukazać się obrazek jak poniżej:

E:\tools\progress\fuse-esb\4.2.0-b\bin>servicemix.bat
 ____                  _          __  __ _
/ ___|  ___ _ ____   _(_) ___ ___|  \/  (_)_  __
\___ \ / _ \ '__\ \ / / |/ __/ _ \ |\/| | \ \/ /
 ___) |  __/ |   \ V /| | (_|  __/ |  | | |>  <
|____/ \___|_|    \_/ |_|\___\___|_|  |_|_/_/\_\

  Apache ServiceMix (4.2.0-fuse-01-00)

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

Od tej chwili mamy do dyspozycji konsolę administracyjną. Aby zwiększyć jej użyteczność wykonujemy następujące polecenia:

karaf@root> osgi:install wrap:mvn:http://download.java.net/maven/2!net.java.dev.jna/jna/3.1.0
Bundle ID: 134

Spowoduje to pobranie z repozytorium Mavena (http://download.java.net/maven/2) biblioteki JNA w wersji 3.1.0. Nie musimy oczywiście określać za każdym razem adresu repozytoriów, ale o tym nieco później. Prefix wrap: spowoduje wygenerowanie manifestu OSGi na podstawie zawartości pobranej biblioteki. Jest on konieczny ponieważ JNA nie dostarcza potrzebnych danych do uruchomienia w środowisku OSGi.

karaf@root> osgi:install mvn:http://jansi.fusesource.org/repo/release!org.fusesource.jansi/jansi/1.2
Bundle ID: 135

Druga biblioteka wykorzystuje JNA i umożliwia kolorowanie tekstu w konsoli windowsowej zgodnie z ANSI, czyli tak jak w standardowych terminalach Unix-a. Po zainstalowaniu tych dwóch rzeczy pora zaprząc je do pracy. Poniżej filtrujemy listę zainstalowanych rzeczy po słowie Console.

karaf@root> list|grep Console
[  10] [Active     ] [Created     ] [       ] [   30] Apache Felix Karaf :: Shell Console (1.4.0.fuse-01-00)
karaf@root> refresh 10

Numer 10 oznacza id paczki OSGi, tekst Active to stan paczki a tekst Created informuje o stanie kontekstu blueprint, czwarty bloczek to stan kontekstu Springa, numer 30 to start level paczki a w ostatnim nawiasie mamy wersję. Sporo informacji jak na jedną linijkę, nieprawdaż? Blueprint to standaryzowanie tego czym jest Spring i Spring DM, także można korzystać zamiennie bądź z jednego bądź z drugiego rozwiązania. Ciekawe porównanie funkcjonalności obu rozwiązań – Spring DM oraz Blueprint opublikował kilka dni temu Guillaume Nodet na swoim blogu we wpisie Spring-DM, Aries Blueprint and custom namespaces. Apache Camel od wersji 2.3 będzie wspierał Blueprint (CAMEL-2022).


 ____                  _          __  __ _
/ ___|  ___ _ ____   _(_) ___ ___|  \/  (_)_  __
\___ \ / _ \ '__\ \ / / |/ __/ _ \ |\/| | \ \/ /
 ___) |  __/ |   \ 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>

Teraz wykonanie poleceń z filtrem grep spowoduje podświetlenie szukanej frazy:

karaf@root> list |grep Console
[  10] [Active     ] [Created     ] [       ] [   30] Apache Felix Karaf :: Shell Console (1.4.0.fuse-01-00)

Takie małe udogodnienie przy przeglądaniu dłuższych rezultatów jest nieocenione.

Podstawowe polecenia konsoli

Polecenia w Apache Karaf są podzielone na kilka grup, które ułatwiają zarządzanie. Pierwszą grupą jest OSGi.

Polecenia OSGi

Polecenie Przeznaczenie
list Wyświetlenie listy zainstalowanych paczek
ls [bundle id] Wyświetlenie usług eksportowanych przez paczkę.
ls -u [bundle id] Wyświetlenie usług używanych przez paczkę.
headers [bundle id] Wyświetlenie manifestu paczki.
start [bundle id] Uruchomienie paczki o danym ID.
stop [bundle id] Zatrzymanie paczki o danym ID.
restart [bundle id] Zatrzymanie i wystartowanie paczki o danym ID.
update [bundle id] Aktualizacja paczki.
refresh [bundle id] Odświeżenie importów paczki a także przeładowanie kontekstu Spring-DM.
install [url] Zainstalowanie nowej paczki.
uninstall [bundle id] Odinstalowanie paczki.
shutdown Zatrzymanie kontenera.
bundle-level [bundle id] [startLevel] Ustawienie start levelu dla paczki.
start-level

Kiedy znamy już listę poleceń nie pozostaje nic innego jak je wypróbować. :-) Checkout przykładowego kodu z SVN pozwoli wykonać nam kilka ćwiczeń. Po wykonaniu polecenia mvn clean install w repozytorium Mavena są JARy które, naturalnie, chcemy zainstalować. Rezultat jaki powinniśmy zobaczyć w konsoli to:

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] Money transfer ServiceMix example ..................... SUCCESS [2.028s]
[INFO] Money transfer :: API ................................. SUCCESS [3.182s]
[INFO] Money transfer :: POI bundle .......................... SUCCESS [7.504s]
[INFO] Money transfer :: Internal ............................ SUCCESS [0.093s]
[INFO] Money transfer :: Internal :: CSV ..................... SUCCESS [4.399s]
[INFO] Money transfer :: Internal :: XLS ..................... SUCCESS [2.823s]
[INFO] Money transfer :: Internal :: Mail .................... SUCCESS [2.309s]
[INFO] Money transfer :: Internal :: Splitter ................ SUCCESS [0.936s]
[INFO] Money transfer :: Internal :: Routes .................. SUCCESS [1:21.241s]
[INFO] Money transfer :: External ............................ SUCCESS [0.016s]
[INFO] Money transfer :: External :: Customer ................ SUCCESS [3.182s]
[INFO] Money transfer :: External :: Bank .................... SUCCESS [2.059s]
[INFO] Money transfer :: External :: Validator ............... SUCCESS [2.918s]
[INFO] Money transfer :: Features ............................ SUCCESS [0.171s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1 minute 54 seconds
[INFO] Finished at: Mon Mar 22 10:47:27 CET 2010
[INFO] Final Memory: 127M/341M
[INFO] ------------------------------------------------------------------------

Może teraz kilka słów o modułach które są widoczne w Mavenie.

Moduł Ścieżka
Maven ID
Przeznaczenie
Money transfer :: API api
org.code-house.samples/api
API systemów zewnętrznych oraz POJO wykorzystywane do komunikacji.
Money transfer :: POI bundle poi
org.code-house.samples/poi
Bundle zawierający bibliotekę Apache POI oraz wszystkie jej zależności z Manifestem OSGi pozwalającym na jej uruchomienie na szynie.
Money transfer :: Internal :: CSV internal/csv
org.code-house.samples.internal/csv
Paczka zawierająca implementację procesora przetwarzającego pliki CSV na POJO MoneyTransfer.
Money transfer :: Internal :: XLS internal/xls
org.code-house.samples.internal/xls
Paczka zawierająca implementację procesora przetwarzającego pliki XLS na POJO MoneyTransfer.
Money transfer :: Internal :: Mail internal/mail
org.code-house.samples.internal/mail
Paczka z kodem dzielącym przychodzący mail z załącznikami na pojedyncze wiadomości które można przetworzyć jako XLS bądź CSV.
Money transfer :: Internal :: Splitter internal/splitter
org.code-house.samples.internal/splitter
Paczka z kodem odpowiedzialnym za rozdzielanie listy obiektów MoneyTransfer na listę wiadomości.
Money transfer :: Internal :: Routes internal/routes
org.code-house.samples.internal/routes
Główna paczka z definicjami routingu
Money transfer :: External :: Customer external/customer
org.code-house.samples.external/customer
Implementacja WebService odpowiedzialnego za pobieranie danych klienta na podstawie numeru rachunku bankowego.
Money transfer :: External :: Bank external/bank
org.code-house.samples.external/bank
Implementacja usługi zwracającej informację o nazwie banku na podstawie numeru rachunku.
Money transfer :: External :: Validator external/validator
org.code-house.samples.external/validator
Usługa weryfikująca czy MoneyTransfer jest poprawny.
Money transfer :: Features features
org.code-house.samples/features
Moduł zawierający opcjonalny deskryptor do uruchomienia modułów.

Jak widać dwa główne obszary projektu są skupione w katalogach internal oraz external. Ten pierwszy zawiera implementacje ściśle związaną z usługami natomiast drugi to „zatyczki” emulujące działanie systemów zewnętrznych. Drobna nota – kolumna Maven ID nie zawiera informacji o wersji – w każdym module jest to 1.0.0.SNAPSHOT.

Aby zainstalować któryś bundle przechodzimy do konsoli ServiceMix’a i wykonujemy takie polecenia:

karaf@root> install -s mvn:org.code-house.samples/api/1.0.0.SNAPSHOT
Bundle ID: 210
karaf@root> features:install camel-jetty
karaf@root> install -s mvn:org.code-house.samples.external/customer/1.0.0.SNAPSHOT
Bundle ID: 211

Po wykonaniu tych poleceń powinien być uruchomiony Web Service którego WSDL znajduje się pod adresem http://localhost:9001/CustomerWs?wsdl. Przełącznik -s w przypadku polecenia install powoduje że po zainstalowaniu bundle zostanie wystartowany. Polecenie features-install camel-jetty jest potrzebne nie ze względu na to że kolejna paczka korzysta z Camela, dzięki jego wykonaniu zostanie zainstalowane Jetty, z którego korzysta CXF.

Kolejne paczki instalujemy analogicznie:

karaf@root> features:install camel-activemq
karaf@root> install -s mvn:org.code-house.samples.external/validator/1.0.0.SNAPSHOT
Bundle ID: 215
karaf@root> install -s mvn:org.code-house.samples.external/bank/1.0.0.SNAPSHOT
Bundle ID: 217

Rozszerzenie camel-activemq jest rozszerzeniem modułu camel-jms które pozwala na nieco wydajniejszą pracę z ActiveMQ.

Integracja ServiceMix z repozytoriami Mavena

Maven jako narzędzie do budowania korzysta z określonego schematu składowania bibliotek które następnie są automatycznie pobierane. Karaf, który jak wspomniałem podczas prezentacji, wyłonił się z projektu ServiceMix Kernel korzysta z biblioteki Pax URL. Dzięki temu z marszu mamy dostęp do standardowych repozytoriów Mavena, co jednak gdy mamy swoje repozytorium, które zawiera tylko nasze artefakty?
Otwieramy plik etc/org.ops4j.pax.url.mvn.cfg i dodajemy w nim co trzeba. Moja standardowa konfiguracja wygląda następująco:

org.ops4j.pax.url.mvn.settings=E:/tools/maven-2.2.1/conf/settings.xml
org.ops4j.pax.url.mvn.localRepository=E:/repository
org.ops4j.pax.url.mvn.defaultRepositories=file:${karaf.home}/${karaf.default.repository}@snapshots
org.ops4j.pax.url.mvn.repositories= \
    http://repo1.maven.org/maven2, \
    http://repo.fusesource.com/maven2, \
    http://repo.fusesource.com/maven2-snapshot@snapshots@noreleases, \
    http://repository.apache.org/content/groups/snapshots-group@snapshots@noreleases, \
    http://repository.ops4j.org/maven2, \
    http://svn.apache.org/repos/asf/servicemix/m2-repo, \
    http://repository.springsource.com/maven/bundles/release, \
    http://repository.springsource.com/maven/bundles/external, \
    http://repository.code-house.org/content/groups/release, \
    http://repository.code-house.org/content/groups/snapshot@snapshots@noreleases, \
    http://jansi.fusesource.org/repo/release

Dzięki temu PAX w pierwszej kolejności będzie skanował katalog E:/repository zamiast standardowego ~/.m2/repository. Jeśli któreś z repozytoriów wymaga autoryzacji adres powinien wyglądać następująco:

http://user:pass@jansi.fusesource.org/repo/release

Podsumowanie

Mam nadzieję że wpis ten przybliży chociaż w niewielkim stopniu ServiceMix 4 oraz Karafa. W przyszłym wpisie, którego daty publikacji nie sposób przewidzieć zostaną dokładniej omówione polecenia z grupy features. Póki co życzę miłej zabawy z konsolą. :-) W razie pytań, niejasności i problemów – proszę o komentarze.

jrebel_biggerOd dłuższego czasu pracuję z następującymi narzędziami: Maven, Eclipse, Jetty. Nigdy nie starałem się na to by moje projekty dobrze współgrały z Eclipse ponieważ wszystko i tak uruchamiam przez Mavena. Korzyścią jest przenośność tego rozwiązania, wadą brak klikalnej wygody, tj zakładki serwerów w Eclipse i aplikacji które są na nich uruchomione.
Read the rest of this entry »

top