JAX-WS Hello World !

4 04 2012

Potrzebny będzie Eclipse (instalacja), JDK 6 (instalacja), Apache Tomcat 7.0.26 (instalacja), Apache Ant 1.8.3 (instalacja), JAX-WS 2.2.6 (instalacja).

W Eclipse tworzymy nowy Java project o nazwie HelloWorldWS.
W pakiecie (np. com.wordpress.rpodhajny.ws.server) tworzymy klasę HelloWorld.java która będzie bazą dla nowego webserwisu:

package com.wordpress.rpodhajny.ws.server;
import javax.jws.WebService;

@WebService
public class HelloWorld {
    public String sayHelloWorld() {
        return "Hello World !";
    }
}

Anotacja @WebService powoduje, że podczas budowania aplikacji klasa HelloWorld stanie się webservisem opisanym za pomocą standardu WSDL.

Wspomniany proces budowania aplikacji wykonany zostanie za pomocą skryptu ant:

<project name="helloWorldWS" default="generate_compile_war">
    <property name="javac_srcdir" location="src" />
    <property name="javac_destdir" location="classes" />
    <property name="hello_world_ws_class" value="com.wordpress.rpodhajny.ws.server.HelloWorld" />
    <property name="war_file" value="your_apache_tomcat_installation_directory/webapps/helloWorldWS.war" />
    <property name="war_basedir" value="web" />
    <property name="lib_location" location="web/WEB-INF/lib" />

    <target name="generate_ws_files">
        <exec executable="wsgen">
            <arg line="-cp ./bin -keep -s ./src -d ./bin ${hello_world_ws_class}" />
        </exec>
    </target>

    <target name="compile">
        <mkdir dir="${javac_destdir}" />
        <javac srcdir="${javac_srcdir}" compiler="modern" destdir="${javac_destdir}" listfiles="true">
            <include name="**/*.java" />
        </javac>
    </target>

    <target name="war">
        <war destfile="${war_file}" basedir="${war_basedir}">
            <lib dir="${lib_location}" />
            <classes dir="${javac_destdir}" />
        </war>
    </target>

    <target name="generate_compile_war" depends="generate_ws_files, compile, war" />
</project>

Uwaga. Aby program wsgen oraz wsimport (wykorzystany podczas generowania klienta) były możliwe do uruchomienia, zmienna środowiskowa JAVA_HOME musi być przypisana ścieżka do głównego katalogu wykorzystywanego JDK.

Powyższy skrypt umożliwia generowanie i kompilację wszystkich klas webserwisu oraz spakowanie całości do archiwum WAR.

Wszystkie biblioteki Ant są domyślnie wbudowane w Eclipse, tak więc aby uruchomić skrypt który przed chwilą został napisany należy kliknąć na nim prawym klawiszem myszy i wybrać polecenie Run As -> Ant Build …, lub wcisnąć kombinację kalwiszy Shift + Alt + X, a gdy pojawi się menu z dostępnymi pleceniami wcisnąć Q. Trzecim sposobem na uruchamie skryptów Anta jest wykorzystanie jednego z wielu dostępnych w Eclipse widoków (lub jeżeli ktoś woli okien czy paneli). Widok skryptów Ant można dodać klikając lewym klawiszem myszki na ikonę + znajdującą się w lewym dolnym rogu środowiska Eclipse, a następnie Others. W nowym oknie nalezy odszukać widok o nazwie Ant (najłatwiej będzie skorzystać z filtra w górnej części okna). W widoku który zostanie dodany do aktualnie używanej perspektywy klikamy w ikonę mrówki z plusem i wybieramy skrypt Ant który ma zostać dodany do listy. Target z dodanego skryptu uruchamiany jest po dwukrotnym kliknięciu na niego lewym klawiszem myszy.

Uruchamiamy target generate_compile_war aby upewnić się że proces przebiega bezbłędnie. Wyniki działania skryptu pojawią się w konsoli. Komunikat „BUILD SUCCESSFUL” informuje o poprawnym zakończeniu budowania webservisu.

W wyniku działania skryptu w projekcie powstały dwie nowe klasy (F5 na projekcie odświerzy jego zawartość) SayHelloWorld.java oraz SayHelloWorldResponse.java.

Opublikowanie webservisu sprowadza się do umieszczenia WARa w katalogu webapps serwera Apache Tomcat (w tym przypadku wersja 7.0.26) i odczekanie chwili aż wszystko zostanie rozpakowane i uruchomione. Aby sprawdzić czy webservice działa, należy wpisać w przeglądarce internetowej adres http://localhost:8080/helloWorldWS/helloWorld. Na ekranie powinna pokazać się tabela z informacjami na temat webserwisu.

Aby sprawdzić czy webservice rzeczywiście mówi „Hello World !”, trzeba napisać klienta, który będzie się z nim komunikował.
Tworzenie klienta rozpoczynamy od utworzenia nowego Java Project o nazwie HelloWorldWSClient do którego dodajemy plik build.xml, który będzie generował klasy klienta na podstawie wcześniej wspomnianego WSDLa:

<project name="helloWorldWSClient" default="webserviceImport">
    <property name="package_name_for_generated_files" value="com.wordpress.rpodhajny.ws.client" />
    <property name="wsdl_url" value="http://localhost:8080/helloWorldWS/helloWorld?wsdl"/>

    <target name="webserviceImport">
        <exec executable="wsimport">
            <arg line="-keep -s ./src -p ${package_name_for_generated_files} -d ./bin ${wsdl_url}" />
        </exec>
    </target>
</project>

Przed uruchomieniem targetu webserviceImport należy opublikować utworzony wcześniej webserwice. Komunikat „BUILD SUCCESSFUL” informuje o poprawnym zakończeniu generowania klas klienta.

W projekcie, po jego odświerzeniu, powinien pojawić sie pakiet com.wordpress.rpodhajny.ws.client wraz zawartością:

  • HelloWorld.java
  • HelloWorldService.java
  • OjectFactory.java
  • package-info.java
  • SayHelloWorld.java
  • SayHelloWorldResponse.java

Powyższy zestaw klas posłuży do komunikacji z helloWorldWS. Aby tego dokonać tworzymy nową klasę:

package com.wordpress.rpodhajny.ws.client;
public class HelloWorldWSClient {
    public static void main(String[] args) {
        HelloWorldService helloWorldService = new HelloWorldService();
        HelloWorld helloWorld = helloWorldService.getHelloWorldPort();
        String message = helloWorld.sayHelloWorld();
        System.out.println(message);
    }
}

Klasa HelloWorldService służy do nawiązania połączenia z webserwisem. Udostępnia ona również interfejs wygenerowany dla napisanej na początku tego tutoriala klasy HelloWorld. Dzięki temu wiemy jakie metody są dostępne do wykorzystania po stronie klienta. Uruchamiając powyższy kod na ekranie konsoli powininiśmy zobaczyć napis: „Hello World !”.





Porządki w HTML przy użyciu HTMLCleaner

28 02 2010

Czasami zachodzi potrzeba sięgnięcia do źródła danych jakim są strony internetowe. Niestety często zdarza się tak, że kod HTML nie jest dobrze sformatowany i nie da się go przejrzeć za pomocą narzędzi do XML.

import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class Moja {
    public static void main(String[] args) {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);

        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse("index.htm"); // błąd podczas konwersji dokumentu na XML

        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Wykonanie tego kodu może spowodować pojawienie się w zależności od zawartości dokumentu index.htm różnych błędów np.:

  • Server returned HTTP response code: 503
  • The entity „nbsp” was referenced, but not declared.

Powodami najczęściej są brakujące domknięcia tagów, czy niezrozumiałe dla parsera XML znaki twardych spacji „&nbsp;”. Można taki kod poprawić ręcznie, można napisać własne narzędzie do normalizacji dokumentów HTML, ale na pewno szybciej i prościej będzie skorzystać z gotowego rozwiązania.

W tym artykule postaram się przybliżyć bibliotekę HTMLCleaner która udostępniana jest na licencji BSD, a więc każdy może ją bez przeszkód używać, modyfikować i rozpowszechniać.

Instalacja

Biblioteka jest dostępna do pobrania pod adresem http://htmlcleaner.sourceforge.net/download.php. Po pobraniu rozpakowujemy archiwum i dodajemy plik htmlcleaner.jar do naszego projektu.

Aby dodać do projektu bibliotekę HTMLCleaner w środowisku Eclipse Galileo należy:

  • Kliknąć prawym klawiszem myszy na projekcie i wybrać Build Path -> Configure Build Path…
  • W nowym oknie przechodzimy do zakładki Libraries i klikamy na przycisk Add External JARs…
  • Wybieramy plik htmlcleaner.jar i zatwierdzamy zmiany klikając na przycisk OK.

Podstawy

import java.io.File;
import java.io.IOException;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;

public class Moja {
    public static void main(String[] args) {
        HtmlCleaner cleaner = new HtmlCleaner();
        try {
            TagNode rodzic = cleaner.clean(new File("index.htm"));
            TagNode[] dzieci = rodzic.getAllElements(false); // true/false - wywołanie rekurencyjne/bez rekurencji  			

            for (TagNode dz : dzieci) {
                System.out.println(dz);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Linia 9:
Wykorzystanie obiektu stworzonego w linii 7 do wyczyszczenia pliku index.html (plik nie jest modyfikowany, jego zawartość przechowywana jest w pamięci). Czyszczenie kodu to nic innego jak poprawienie istniejącej struktury tak aby spełniała standardy XML (w przypadku braku tagu zamykającego, jest on dopisywany, itp.). Metoda clear() ma wiele wersji przystosowanych między innymi do pozyskiwania kody HTML z plików (File), serwerów (URL) czy w postaci łańcuchów tekstowych (String), my chcemy wczytać dane z pliku zapisanego w katalogu z którego uruchamiana jest aplikacja więc wykorzystaliśmy wersję pobierającą w parametrze obiekt typu File. Klasa TagNode wykorzystywana jest do przechowywania informacji o pojedynczym węźe, umożliwia dostęp do atrybutów węzła oraz jego dzieci za pośrednictwem między innymi takich metod:

  • TagNode[] getAllElements(boolean arg), List getAllElementsList(boolean arg)
Zwracają wszystkie dzieci danego węzła, arg true włącza wywołanie rekurencyjne (zostaną pobrane nie tylko dzieci danego węzła, ale także dzieci dzieci itd. innymi słowy wszystkie węzły które leżą niżej w hierarhii od rodzica), arg false spowoduje wykonanie nie rekurencyjnej wersji tej metody (zwróci wszystkie dzieci danego elementu ale tylko te które leżą o 1 stopień niżej w hierarchii niż rodzic).
  • String getAttributeByName(String name)

Zwraca wartość atrybutu danego węzła.

  • Map getAttributes()

Zwraca mapę atrybutów gdzie kluczem jest nazwa atrybutu.

  • TagNode getParent()

Zwraca węzeł który jest rodzicem danego węzła.

  • String getText()

Zwraca tekst jaki zawiera dany element, trzeba jednak zaznaczyć że funkcja ta działa rekurencyjnie i zwraca również tekst który umieszczony został w wewnętrznych węzłach (dzieciach).

Linia 10:
Pobranie wszystkich elementów z głównego węzła (html). Ustawienie parametru na false zwróci dwa węzły: head i body (bo są dziećmi węzła głównego: html). Ustawienie parametru na true rekurencyjnie zwróci węzły z całego dokumentu HTML.

Linia 12:
Wypisanie nazw wszystkich elementów pozyskanych w linii 10. Klasa TagNode ma przeciążoną metodą toString() w taki sposób aby zwracała nazwę węzła.





Java vs. PHP

11 02 2010
Java PHP
Skalowalność (1-10) 7 10
Szybkość budowania
aplikacji (1-10)
5 7
Wsparcie
narzędziowe (1-10)
10 5
Późniejsza pielęgnacja
kodu (1-10)
7 5
Typowane zmienne tak nie
Łatwość nauki trudniejsza niż PHP łatwiejsza niż Javy
Popularność:
prywatne strony WWW
mniejsza niż PHP większa niż Javy
Popularność: zastosowania
komercyjne
większa niż PHP mniejsza niż Java
Debugowanie kompilator, podłączenie pod
serwer lub ręcznie
dodatkowa biblioteka lub
ręcznie
Hosting aplikacji
(ilość serwerów)
mała ilość duża ilość
Cena hostingu aplikacji większa niż hosting PHP mniejsza niż hosting Java

O wiele łatwiej jest dokonać zmian w kodzie aplikacji napisanej w PHP niż tej napisanej w Javie. PHP jest w końcu językiem skryptowym w całości interpretowanym. Java choć interpretowana to jest jednak wcześniej kompilowana do byte kodu, który nie nadaje się już do obróbki, jakiekolwiek zmiany w kodzie muszą być zatem poprzedzone przebudowaniem projektu.

Java ze względu na mnogość narzędzi deweloperskich oraz łatwość późniejszej pielęgnacji kodu jest lepszym wyborem dla dużych internetowych aplikacji. PHP wygrywa jednak jeżeli chodzi o strony prywatne lub mniejsze projekty.

Ponadto wśród programistów krąży opinia że kod pisany w PHP znacznie łatwiej jest zepsuć (bałagan w kodzie) niż kod Javy.





JBoss Seam zmienne kontekstowe

18 11 2009

W zasisęgu kontekst możemy przechowywać dowolne wartości, jednak zazwyczaj
przechowywane są tam stany (wartości) różnego rodzaju komponentów.

Stany obiektów dostępne w ramach danego kontekstu przechowywane są jako
para [klucz, wartość].

Możemy w łatwy sposób uzyskać wartość obiektu z kontekstu:

User user = (User) Contexts.getSessionContext().get("user");

Równie łatwo można zmienić wartość obiektu:

Contexts.getSessionContext().set("user", user);

Zazwyczaj jednak do uzyskiwania dostępu do zmiennych kontekstowych
wykorzystywany jest mechanizm wstrzykiwania (injection) a do zapisu
zmiennej w ramach kontekstu outjection.

Aby pobrać lub zapisać wartość do zmiennej kontekstowej, musi być ona
uprzednio odszukana. Wyszukiwanie odbywa się według określonej
hierarchii (od najbardziej szczegółowego kontekstu do najbardziej ogólnego):

  1. kontekst zdarzenia (request)
  2. kontekst strony
  3. kontekst konwersacji
  4. kontekst sesji
  5. kontekst biznesowy
  6. kontekst aplikacji

Więcej o kontekstach na stronie: https://rpodhajny.wordpress.com/2009/11/18/jboss-seam-kontekst-komponentu/





JBOSS Seam kontekst komponentu

18 11 2009

Konteks bezstanowy

W kontekście tym umieszczane są komponenty bezstanowe. Cechą tych komponentów jest to że są one tworzone od nowa przy każdym użyciu.

Konteks zdarzeniowy (request[żądanie])

Jest to najbardziej szczegółowy kontekst. Komponenty powiązane z nim
są niszczone tuż po zakończeniu zdarzenia (requestu), ale ich stan
dostępny jest conajmniej tak długo jak trwa żądanie.

Konteks strony

Stany obiektów (mozna w skrócie powiedzieć obiekty) umieszczone w ramach
kontekstu strony są powiązanie z konkretną instancją(egzemplarzem)
tejże strony. Obiekty zachowane w kontekście danej strony, mogą być
wykorzystane w eventach z niej wygenerowanych. Jest on szczególnie
przydatny przy funkcjonalnościach np. typu aktywnych list, gdzie
każde klikniecie na wiersz z listy powoduje zaczynanie danych z serwera
np. szczegółów zamówienia. Stan jest przypisywany do konkretnej instancji
strony u użytkownika, umożliwia swobodne korzystanie z przycisku wstecz,
strona załadowana z historii, będzie posiadać swój oryginalny stan
(stan w jakim zostala opuszczona).

Konteks konwersacji

Jest głównym kontekstem Seamowym. Może obejmować kilka interakcji
z użytkownikiem, requestów oraz kilka odwołań do bazy danych, wszystko
co jest konieczne aby wykonać daną funkcjonalność.

Użytkownik może mieć rozpoczętych kilka konwersacji, zazwyczaj w osobnych oknach,żadna z nich
nie ma jednak możliwości narobienia bałaganu w drugiej
(konwersacje nie widzą siebie nawzajem).

Konwersacje mogą być zagnieżdżane.

Konwersacje mogą wygasać (timeout), jest to parametr który można
konfirugować, zabezpiecza przed niebezpecznym rozrostem konwersacji
np. gdy użytkownik opuści stronę bez wylogowania się.

Można tak skonfigurować Seama, aby konwersacje trzymane były
w przeglądarce użytkownika.

Kontekst sesji

Kontekst sesji przechowuje obiekty powiązane z użytkownikiem po
jego zalogowaniu się. Zazwyczaj nie jest stosowany do przechowywania
globalnych informacji, wyjątkiem może być przechowywanie informacji
wykorzystywanych przez kilka różnych konwersacji w ramach danej sesji.

Kontekst procesu biznesowego

Kontekst biznesowy przechowuje obiekty powiązane z długo trwającym procesem
biznesowym. Jest zarządzamy poprzez JBoss jBPM. Proces biznesowy obejmuje
interakcje pomiędzy wieloma użytkownikami.

Kontekst aplikacji

Kontekst aplikacji jest najszerszym kontekstem i jest wykorzystywany
przeważnie do przechowywania statycznych danych np. dotyczących
konfiguracji.

Artykuł napisany w oparciu o dokument Seam reference.