sobota, 26 grudnia 2015

Technologia Java Web Start - co i jak?

TL;DR Jeśli nie korzystałeś nigdy z technologi Java Web Start (pliki JNLP) i jeśli nie musisz tego robić, to spokojnie możesz nie czytać dalej - nie obrażę się. Jest to zamierająca technologia i niedługo nie będzie wspierana w nowszych przeglądarkach (zobacz: The Final Countdown for NPAPI). Jeśli jednak korzystałeś / musisz skorzystać z tej technologi, to pewnie [mam nadzieję] dowiesz się czegoś ciekawego z tego i kolejnych artykułów.

Czym jest Java Web Start? Popatrzmy na dokumentację Javy SE.


Powinniście kojarzyć powyższy widok. Zazwyczaj korzystamy z Java SE API, natomiast Web Start jest zdefiniowany trochę wyżej po lewej, zaraz koło Applet'ów - a to nie wróży nic dobrego.


Java Web Start pozwala nam na uruchamianie javowych aplikacji standalone, bezpośrednio po kliknięciu na link’a na stronie www. Nie jest potrzebna żadna instalacja. Jest to wygodny sposób na dystrybuowanie wśród użytkowników najnowszej wersji naszego oprogramowania standalone - wejdźcie na stronę X, kliknijcie link Y i uruchomi się najnowsza wersja oprogramowania z którego [niestety] musicie korzystać. Jest to coś innego niż aplety, bo aplikacja uruchamia się po za przeglądarką.

Jak to działa? Po kliknięciu na link ściągany jest plik JNLP, który jest on w formacie XML’owym. Generalnie plik ten zawiera informację,  co ma uruchomić, skąd pobrać JAR’y, parametry uruchomieniowe itd. Aby całość zadziałała, musimy mieć jakąś Javę zainstalowaną na docelowej maszynie klienckiej.

Tyle teorii. Moim zdaniem, ta technologia świetnie sprawdza się z tutorialem do Swinga, gdzie za pomocą jednokliku możemy odpalić przykład ze strony i zobaczyć efekt. Wystarczy kliknąć na poniższą ikonkę:

https://docs.oracle.com/javase/tutorialJWS/samples/uiswing/CelsiusConverterProject/CelsiusConverter.jnlp



I to by było na tyle. Jeśli chodzi o jakieś bardziej skomplikowane aplikacje, to już może nie być tak różowo. Oracle nigdy jakoś szczególnie nie dbał o jakość tej technologii, czasem wprowadzał niekompatybilne zmiany, zaostrzał security itd.

Największą jednak bolączką programistów zmuszonych do korzystania z tej technologi jest niepewność, na której wersji wirtualnej maszyny Javy będzie odpalona aplikacja. Póki jest to proste demo (tutorial do Swinga), to żadnych problemów nie powinno być. Gdy jednak stworzyliśmy kolosa, który z jakiś powodów działa tylko na konkretnej wersji JVM’a, to zaczynają się schody.

Gdy zdefiniujemy w naszym pliku JNLP, że aplikacja ma się odpalić na Javie 1.7, a user będzie miał zainstalowaną Javę 1.8, to może zobaczyć poniższy komunikat:


Ten i inne screeny otrzymałem grzebiąc w plikach JNLP edytora UML’a: ArgoUML, udostępniającego link do uruchomienia za pomocą Web Start'a.

Czyli aplikacja w tym wypadku uruchomi się na Javie 1.8. W przypadku, gdy mamy w systemie zainstalowane kilka wersji Javy, to użytkownik będzie miał możliwość wyboru, na której się uruchomi.

Jeszcze innym problemem, może być posiadanie przez użytkownika starszej wersji Javy. Jeśli zdefiniujemy, że aplikacja ma się tylko odpalać na wersji 1.8.0_50, a będziemy mieć zainstalowaną starszą (1.8.0_28), to dalej nie mamy pewności, na której się uruchomi - patrz komunikat poniżej.


Jak dobrze pamiętam, to jeszcze jest możliwość, że Web Start będzie próbował ściągnąć nowszą wersję JRE i zainstalować. Jest to spory problem w dużych firmach, gdzie korpo ludki mogą nie mieć (albo raczej nie powinni mieć) uprawnień do instalowania nowych rzeczy na swojej maszynie.

Inną niedogodnością jest jeszcze konieczność podpisywania swoich aplikacji. Od którejś wersji Javy, własne certyfikaty (self signed) są z domysłu blokowane. Trzeba więc albo zdobyć porządny certyfikat od zaufanego wystawcy (czytaj zapłacić), albo skonfigurować userom maszyny tak, aby mogli otwierać aplikacje z zaufanych źródeł.

Jak widać jest sporo tej babraniny, aby całość doprowadzić do ładu i składu. Spójrzmy zatem na kolejne fragmenty przykładowego pliku JNLP. Pozwoliłem sobie bazować na pliku z ArgoUML, ale dorzuciłem też parę ciekawych rzeczy, których tam nie ma zdefiniowanych.
<?xml version="1.0" encoding="utf-8"?>
<!-- JNLP File for launching ArgoUML with WebStart -->
<jnlp
  spec="1.0+"
  codebase="http://argouml-downloads.tigris.org/maven2"
  href="http://argouml-downloads.tigris.org/jws/argouml-latest-stable.jnlp">

Całość będzie zawierać się w tagu jnlp. Możemy w nim zdefiniować atrybut codebase, który będzie naszym bazowym URL’em przy pobieraniu JAR’ów.

Bardzo ciekawy jest atrybut href. Jeśli jest on zdefiniowany, to po uruchomieniu pobranego pliku JNLP (czy to w przeglądarce, czy bezpośrednio z dysku), to Web Start pobierze ten plik jeszcze raz ze zdefiniowanego tutaj adresu i jego będzie interpretował! Jeśli wprowadzimy jakieś zmiany lokalnie w pliku JNLP, ale będzie istniał atrybut href, to nic nam to nie da! Dla celów produkcyjnych warto ustawić ten parametr, na pewno utrudni on życie początkujących hackerom.
<information>
    <title>ArgoUML Latest Stable Release 0.34</title>
    <vendor>Tigris.org (Open Source)</vendor>
    <homepage href="http://argouml.tigris.org/"/>
    <description>ArgoUML application.
                 This is the latest stable release.
    </description>
    <description kind="short">ArgoUML 0.34</description>
    <icon href="http://argouml.tigris.org/images/argologo16x16.gif" width="16" height="16" />
    <icon href="http://argouml.tigris.org/images/argologo32x32.gif" width="32" height="32" />
    <icon href="http://argouml.tigris.org/images/argologo64x64.gif" width="64" height="64" />
    <offline-allowed/>
  </information>

Dalej mamy definicje, które są gdzieś tam wyświetlane podczas uruchamiania aplikacji, czyli tytuł, wydawca, strona domowa, opis, ikonka itp. Ostanie ustawienie (offline-allowed) definiuje nam, że docelowa aplikacja może działać, gdy nie mamy połączenia z netem. Jest jeszcze możliwość tworzenia skrótu na pulpicie, menu start i inne pierdoły.
  <security>
    <all-permissions/>
  </security>
Tutaj definiujemy, co nasza aplikacja może, a co nie. Jeśli jest to okienko swingowe z tutoriala, to nic nie potrzebujemy. Gdy chcemy zapisywać pliki na dysku itp, to potrzebujemy dostęp, najlepiej do wszystkiego. Dokładnie takie same seciurity (ale w trochę innej formie zapisu), musi być zdefiniowane w Manifeście w JAR’ze zawierającym klasę, którą będziemy uruchamiać (z metodą main()).
  <resources>
    <j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se” 
             initial-heap-size=„64m" 
             max-heap-size=„512m" 
             java-vm-args="-ea -Xincgc"/>

Następnie mamy definicję na jakiej wersji Javy ma być odpalona docelowa aplikacja. W specyfikacji 6.0 JNLP zamiast „j2se" może pojawić się „java".

Wersja może być zdefiniowana na wiele sposobów. Możemy podać dokładnie (np.: 1.7, 1.4.2, 1.4.2_04, 1.5.0-beta2), albo większe równie niż (np.: 1.5+, 1.4.2+). Można również podać kilka wersji kolejno wg. preferencji.

Gdy chcemy jakiejś egzotycznej wersji, musimy podać atrybut href.

Na koniec możemy jeszcze zdefiniować ustawienia pamięci i przekazać jakieś flagi do JVM’a.
    <jar href="http://argouml-downloads.tigris.org/maven2/antlr/antlr/2.7.7-3/antlr-2.7.7-3.jar"/>
    <jar href="http://argouml-downloads.tigris.org/maven2/org/argouml/argouml-euml/0.34/argouml-euml-0.34.jar"/>

Dalej definiujemy nasze zależności, które Web Start ma ściągnąć. Zależności są cache’owane, więc jak podbijamy wersję biblioteki, to warto zmienić nazwę pliku. Gdy nie podamy pełnej ścieżki do pliku, to będzie ona budowana na podstawie codebase (w tag’u jnlp).
    <property name=„argouml.modules" value=";org.argouml….."    />
</resources>

W ten sposób definiujemy sobie właściwości wspólne dla wszystkich platform. Muszą one jednak się zaczynać od "javaws." lub "jnlp.”. Ta powyższa definicja z ArgoUML wg. mnie nie zadziała. Jeszcze niektóre predefiniowane propertisy (np.: http.agenthttp.keepAlive) można zdefiniować w ten sposób.

To były właściwości wspólne dla wszystkich systemów operacyjnych. Mamy jeszcze możliwość zdefiniowania właściwości specyficznych dla konkretnej platformy, np.:
  <resources os="Windows" arch="amd64">
     <property name="jnlp.abc" value=„xyz"/>
  </resources>

  <resources os="Mac OS X" arch="x86_64">
     <property name="jnlp.abc" value=„123"/>
  </resources>

Jeszcze na koniec pozostaje nam punkt wejścia do aplikacji:
<application-desc main-class=„com.mycompany.myapp.MainApp">
   <argument>arg1</argument>
   <argument>arg2</argument>
</application-desc>

Tutaj możemy dodatkowo przekazać argumenty do aplikacji. Pełen opis tego co możemy zrobić w pliku JNLP można znaleść tutaj: JNLP File Syntax.

I to już wszystko jeśli chodzi o sam plik JNLP. Przygotowujemy taki plik, wrzucamy na serwer, umieszczamy do niego link, podpisujemy nasze JAR’y, robimy je możliwe do ściągnięcia i już możemy cieszyć się Web Startem. Brzmi prosto, ale na pewno takie nie jest. W kolejnym artykule opiszę, jak można sobie trochę ułatwić życie.

1 komentarz: