Jak "włamać się" do obcego bundla w OSGi

Przeglądając swojego bloga zauważyłem że od dłuższego czasu nie pojawiały się na nim tematy stricte techniczne. Spowodowane to było między innymi małą ilością czasu. Dobrze byłoby to zmienić by blog zachował swój pierwotny charakter. Ponieważ ostatnie kilka lat spędziłem pracując nad rozwojem technologii Eclipse postanowiłem zacząć od tej tematyki.

Temat dzisiejszy to integracja oprogramowania w modelu OSGi. Oczywiście nie będę tutaj opisywał mechanizmu ponieważ jest to bezcelowe. Istnieje setka tutoriali która wyjaśnia co i jak działa. Tutoriale te jednak zazwyczaj kończą się na prostych przykładach a z perspektywy pracy nad projektami modularnymi wiem że to zdecydowanie za mało. To co w 90% jest błogosławieństwem, to w 10% jest problemem. Jednym z takich tematów jest mechanizm ukrywania API.

Ukrywanie klas jest kluczowym aspektem opracowania dobrego API które będzie współpracowało przez lata z produktami rozwijanymi przez inne firmy. Dostawcy bibliotek określają które elementy kodu są publiczne a które prywatne. Co jednak gdy tworzymy zaawansowaną integrację systemów wykraczającą poza publiczne API? To ryzykowna sprawa jednak w realnych projektach czasami nieunikniona. W OSGi klasy z pakietów prywatnych są dla nas niedostępne... do czasu :)

Przykładowym scenariuszem może być próba integracji własnych narzędzi z edytorem JBoss Drools IDE którą robiłem kilka lat temu. Biblioteka ta praktycznie wszystkie pakiety miała prywatne. Między innymi pakiet w którym znajduje się klasa edytora którą byłem zainteresowany jako potencjalnym komponentem w multi-edytorze. Jak się do niej dostać? Istnieją dwie metody: "studencka" i poprawna.

Studencka polega na modyfikacji biblioteki i jak się każdy domyśla nie jest to najlepsze rozwiązanie (nawet jeżeli pozwala na to licencja). Zmodyfikowana biblioteka trafia na nasze serwery więc na nas spoczywa odpowiedzialność za jej dalsze uaktualnienia, zgłoszenia błędów etc. Znacznie podnosi to koszty utrzymania takiego rozwiązania. Dodatkowo coraz więcej bibliotek jest podpisanych certyfikatami by wykluczyć nieautoryzowane modyfikacje.

Jak więc poprawnie zaimplementować modyfikacje pliku Manifest.MF tak by nie modyfikować samego bundla? Odpowiedzią jest mechanizm 'fragments' platformy OSGi. Zazwyczaj służy on do dodawania plików z tłumaczeniami, bibliotek dla różnych systemów itp. Tym razem użyjemy go do naszego "włamania" i upublicznienia prywatnych pakietów.

By całość zademonstrować przygotowałem następujący przykład. Firma 'Private Library Company' dostarczyła nam bundla w którym między innymi znajduje się klasa potrafiąca zwrócić nam cały świat :) Niestety klasa znajduje się w pakiecie wewnętrznym który nie jest udostępniony w manifeście.


Nasza firma 'Client Plugin Company' przygotowuje aplikację dostaczającą usługę której zadaniem jest pozdrowienie całego świata. Musimy więc dostać się do klasy która potrafi zwrócić nam 'świat'. Posiadamy oczywiście zależność na bibliotekę jednak nie jest to wystarczające. Kod nie będzie działał ponieważ klasa 'World' nie jest widoczna.


By to zmienić musimy przygotować odpowiednie rozszerzenie dla posiadanej biblioteki. Wykorzystamy w tym przypadku mechanizm fragmentów bundli. Z założenia mechanizm ten nie może modyfikować elementów, dostarczać nowego API jednak do naszego "włamania" nadaje się jak najbardziej. Tworzymy projekt fragmentu rozszerzający bundle biblioteki, możemy go nazwać 'privatelibrary.customization'. Następnie tworzymy w nim ścieżkę identyczną z tą którą chcemy upublicznić i zakładamy w niej pustą klasę np. o nazwie 'PackagePublisher'. Po utworzeniu klasy pojawia nam się możliwość upublicznienia jej pakietu w manifeście fragmentu. Platforma OSGi by obsłużyć naszą publiczną klasę dodaje 'regułę publiczności' dla wybranego pakietu dla nadrzędnej biblioteki.

Tym samym w projekcie klienckim uzyskujemy dostęp do interesującej nas klasy prywatnej 'World'. By całość działała nasz dodatek musi być dostępny dla platformy. Jeżeli go usuniemy całość ponownie przestanie się kompilować a jeżeli element będzie niedostępny w ramach runtime pojawi się wyjątek ClassNotFound przy próbie odwołania się do klasy świata. Oczywiście nasza klasa 'PackagePublisher' którą upubliczniliśmy w tym pakiecie nie jest publicznie dostępna. Nie zezwala na to kontrakt mechanizmu fragmentów. Poniżej zrzut z poprawnie zdefiniowanego manifestu.



Mam nadzieję że całość nie jest zbyt zagmatwana. Starałem się zrobić na tyle długi wstęp by całość wyjaśnić łącznie ze scenariuszem kiedy takie obejścia stosujemy. Oczywiście nie nakłaniam nikogo do odwoływania się do klas prywatnych w zewnętrznych bibliotekach. Takie sytuacje nigdy nie powinny być rutyną. Jeżeli jednak jest to koniczne z punktu widzenia biznesu warto wiedzieć jak to zrobić poprawnie. Gotowy projekt dostępny jest pod adresem:

http://sites.google.com/site/deepdiveinto/Home/PrivatePackage.zip?attredirects=0&d=1

Obrazek z wróżką pochodzi z prezentacji: OSGi Best and Worst Practices
Jak zawsze zachęcam także każdego do wypróbowania dodatku Facebook Like Plus

0 komentarze: