Tanulság: Ha linuxon fut az appserver/webserver akkor a bug keresése közben sose felejtsük ki a jogosultságokból adódó problémákat.

A sztori:

Résztvevők: Ubuntu Linux, Weblogic AppServer, Apache Weblogic plugin, és a csodálatos alkalmazásunk egy generált Web Service interface-szel.

A Probléma: Application maintanance és partial upgrade után az ügyfél Web Service Kliense a createProduct(Product product) WS methoddal nem működött. A findAllProduct() -tal viszont igen. A hibaüzenet a következő volt:

The remote server returned an unexpected response:
     (400) Bad Request. --->  System.Net.WebException:
     The remote server returned an error: (400) Bad Request..

1. Tesztelés saját klienssel: Kipróbáltam saját C# WS klienssel, és minden WS method tökéletesen működött. A saját és a kliens  environmentjén egyaránt. Megírtam nekik, hogy frissítsék a WSDL file-t, generálják újra a WS Kliens Stub-ot, és próbálják újra.

2. Újrapróbálták. Nem ment.

3. Csináltam nekik példaprojectet. Ők visszaküldték az ő példájukat, ami tényleg nem működött. De csak a kliens environmentjén nem működött.

Az én C# kliensem a régi módszerrel Web Referenciaként importálta a WSDL file-t, míg az övéké Service Reference-ként. Egy ideig azt hittem, hogy a Service reference megvalósítás a hibás. De a mi env-ünkön ment…

4. Hozzáfogtam összehasonlítani a mi rendszerünket az ügyfélével. Minden tökéletesnek tűnt. A generált WSDL file megegyezett. A domain logig nem jutott el a kérés… Közben kiderült, hogy csak a paraméteres függvényhívások buknak el. Az Apache access logjában a következőt találtam:

Invalid URI in request (- SOAP message -)
                     GET /bridge_error.html HTTP/1.1

Ez még ok, mert nincs bridge_error.html bekonfigurálva. De mitől jön bridge error?

5. Az Apache Weblogic pluginon bekonfigoltuk a logozást. Ebből az derült ki, hogy “/tmp/_wl_proxy/”-ban lévő temp file-t nem tudja írni az apache mert nincs hozzá jogosultsága, ezért bukik el a request.

- Going to get the post data of size=2419 clength=0
- getWLFilePath: Complete File name =
               [/tmp/_wl_proxy/_post_9929_0]
- Cannot open TEMP post file '/tmp/_wl_proxy/_post_9929_0'
               for POST of 2419 bytes
- Redirecting the error response to the errorPage =
               [/bridge_error.html]
- IO error reading client POST data; sys err#:
               [13] sys err msg [Permission denied]

Az ok: Amikor az ügyfél rendszergazdája karbantarotta a servert, létrehozott egy új usert/groupot az Apache server futtatásához. Mivel az Apache ezzel az új userrel futott, az előzőleg létrehozott “_wl_proxy” könyvtárhoz nem volt hozzáférése. A POST HTTP requestek esetén az Apache proxy temp fileba menti a  paramétereket, amiket jelen esetben nem tudott lementeni.

- Az ügyfél C# kliensével (Service Reference based) a paraméter nélküli függvényhívások azért működtek, mert nem kellett az Apache-nak semmit lementenie.

- Az én C#  kliensemmel (Web Reference based) azért működött, mert az GET HTTP requesteket használt…

Megoldás: Rendszergazdaként töröltük a “_wl_proxy” mappát. A következő paraméteres HTTP POST requestnél az Apache újrakreálta ezt a mappát, és ezek után minden tökéletesen működött.

Az előző post-hoz kapcsolódik a következő kis probléma:

Amikor a Maven JAX-WS Plugin-jának wsimport goal-jával legeneráltam a Kliens stub-ot, a Service URL-jét szerettem volna konfigurálhatóvá tenni. Fontos komment, hogy a WSDL file és a Service methodusai is csak egy basic HTTP authentikáció után érhetőek el.

Tehát elsőre fogtam magam, és a következőket követtem el:

URL url = new URL("http://localhost/ServiceUrl");
QName qName = new QName("ServiceNamespace", "ServiceName");

AuthWService authWService = new AuthWService(url, qName);
AuthWServicePort port = authWService.getAuthWServicePort();

És itt az történt, amire egyáltalán nem számítottam:

javax.xml.ws.WebServiceException Response: '401: 
                       Unauthorized' for url: 'http://localhost/ServiceUrl'

Az AuthWService példányosításakor elszált a futtatás, mivel nem tudott hozzáférni a megadott URL-en az adott Service-hez. Merthogy nincs meg a username/password amire szükség lenne… De miért is akarja ő azt most még elérni? Ott van a WSDL file a resources mappában. Az URL valid. A Service class példányosításhoz miért kellene nekem rögtön elérnem magát a Service-t a net-en? Nem elég csak akkor amikor meg akarom hívni az egyik methodusát? Mindegy.

Rövid keresgélés után (thx google) a következőt találtam: megoldás

1. Authenticator:

Kell nekünk egy saját authenticator class, ami elintézi a username/password authentikációt, amikor ez a szemét váratlanul csatlakozni akar a net-en levő Service-hez. Ez a következőképpen nézhet ki:

public class HttpAuthenticator extends Authenticator {
    private String userName;
    private String password;

    public HttpAuthenticator(String username, String password) {
        this.userName = username;
        this.password = password;
    }

    protected PasswordAuthentication getPasswordAuthentication() {
        char[] pwdChar = password.toCharArray();
        return new PasswordAuthentication(userName, pwdChar);
    }
}

2. Beállítjuk a Default Authentikátort:

Mielőtt példányosítanánk az AuthWService classt, be kell állítanunk a Default Authenticatort a következőképpen:

Authenticator.setDefault(new HttpAuthenticator("UserName", "Password"));

Itt már majdnem készen vagyunk. Az AuthWService példányosodik, eddig minden rendben.

3. UserName és Password beállítása az AuthWServicePort objecten:

A port object-en meghívott Remote WS  Method-oknak szintén szükségük van authentikációra. Ezt a port objecten tudjuk beállítani az alábbi példa alapján:

AuthWServicePort port = authWService.getAuthWServicePort();
((BindingProvider) port).getRequestContext().put(
                           BindingProvider.USERNAME_PROPERTY, "UserName");
((BindingProvider) port).getRequestContext().put(
                           BindingProvider.PASSWORD_PROPERTY, "Password");

Ezzel készen is vagyunk.

Megjegyzés: Ha a WSDL file-unk nincs HTTP Authentikációval védve, akkor az 1. és 2. pontra nincs szükségünk….

Feladat: Csináljunk egy WS Clientet Eclipse-ben Maven 2-vel. (Megjegyzés: Gyűlölöm a Maven-t. Ez a post az “ismerd meg ellenséged” jegyében született.)

I. Maven plugin Eclipse-hez: Én az m2eclipse plugint installáltam (http://m2eclipse.sonatype.org/)

II. Új Project: Hozzunk létre új Maven Projectet eclipse-ben:

Fontos, hogy bejelöljük a Simple Project opciót (nem akarunk Maven Project templateket használni):
A következő lépésben be kell állítanunk a groupId-t és az artifactId-t. A groupId egy egyedi azonosítója a company-nak vagy projectnek. Kb olyan mint a Java-ban a package. Az artifactId a konkrét Eclipse-es Project (modul) neve (kb a jar file neve). Egy Project aktuális buildjét a következőkkel tudjuk egyértelműen azonosítani:[groupId:artifactId:version]
Mindezek után a következő Projectet láthatjuk az Eclipseben:

Itt látható, hogy a Maven-es projecteknek más a hierarchiája mint a standard Eclipse-es projecteknek. Pl.: nincs bin mappa a Projecten belül. Ha lehetőség van rá használjuk a maven-es hierarchiát, így megspóroljuk a pom file szerkesztgetését. Indulásnak a következő könyvtárszerkezetünk van:
  • src/main/java: Csak a forráskódnak amit írunk.
  • src/main/resources: Minden más resource (Nálunk pl.: a WSDL file és a konfig fileok is).
  • src/test/java: JUnit tesztek
  • src/resources: a tesztekhez szükséges resource-ok, amik nem lesznek deployolva…
  • target: a fordítás eredménye kerül ide. (+ a resource fileok is)

III: Project Object Model: A pom.xml file irja le a teljes projectet. Szemben az ant-tal, ahol a build.xml file leírja a definiált projectünk elemein végzendő műveleteket (dependent lib-eket tesz a classpathra, fordít, könyvtárat hoz létre, jar/war file-t készít, deployol, stb.), a maven pom.xml-je a project gerincét adja. Itt határozzuk meg a project függőségeit, a pluginokat amelyeket a build sorén használni szeretnénk, a repository-kat amelyekből a különböző lib-eket letölti a Maven.

A Maven Project életciklusai:

  • generate sources: pl.: A mi esetünkben a wsdl fileból generál Java forráskódot.
  • compile: A forráskód fordítása.
  • test-compile: A JUnit tesztek fordítása.
  • test: A JUnit tesztek futtatása.
  • package: jar, war stb.
  • integration-test:deployol az staging environmentre ha szükséges.
  • install:belepakolja a local repository-ba az elkészült jar/war filet, ha más projectek ezen dependálnak.
  • deploy: production environmentre másolja fel a végleges verziót.

A mi pom xml fileunk a következőképpen néz ki:

<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>
    <groupId>GAAuthWSTest</groupId>
    <artifactId>GAAuthWSTest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</project>
Hogy teszteljük a Maven Pluginunkat az Eclipse-ben, futtassunk egy Maven installt:
Az Maven install végeredményének egy “BUILD SUCCESSFUL” üzenetnek kell lennie. (Ha nem ez, akkor valami van az installált pluginnal.)
IV. WSDL File: Másoljuk be a WSDL file-unkat az src/main/resources dir-be.
V. Client stub generálása a WSDL file-ból: Legalább 3 különböző módszerrel lehetne Java kódot generálni a wsdl file-ból Maven-nal. Egyrészt szóba kerülhet a maven “antrun” plugin-ja, ahol használhatnánk a jól ismert “axis-wsdl2java” task-ot. Másrészt ha axis2-t szeretnénk használni, akkor a maven saját “wsdl2code” plugin-ja is megfelelő lehet. A probléma az axis-szal, hogy rengeteg és eléggé átláthatatlan kódot generál. Az ultimate solution wiszont a “jaxws” maven plugin használata. Gyönyörű, tiszta és egyszerű kódot generál, része a Java 1.6-nak, stb. Ami probléma lehet, hogy a wsimport (jaxws code generáló parancsa) nem támogatja az RPC/Encoded tipusokat.
VI. JAX-WS Plugin: A hivatalos leírás a következő webhelyen található: https://jax-ws-commons.dev.java.net/jaxws-maven-plugin/
Ahhoz hogy büzemeljük a “jaxws” plugint, a következő elemeket kell hozzáadnunk a pom.xml filehoz:
Először is adjuk hozzá a sun jaxws-rt lib-jét a dependenciák közé. A többi függőséggel nem kell foglalkoznunk, a maven autómatikusan kideríti, hogy ennek mire van szüksége, és letölti azokat is.
<dependencies>
    <dependency>
        <groupId>com.sun.xml.ws</groupId>
        <artifactId>jaxws-rt</artifactId>
        <version>2.1.3</version>
    </dependency>
</dependencies>
Pluginok:
<plugin>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
  <source>1.6</source>
  <target>1.6</target>
  </configuration>
</plugin>
A fenti plugin azért szükséges, hogy a Maven a 6-os java-val fordítsa a Projectet. (Egyébként valami 1.3-assal lenne kompatibilis talán… :( )

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>add-source</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>add-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>src/main/java</source>
                    <source>src/main/generated</source>
                    <source>src/main/resources</source>
                    <source>src/test/java</source>
                </sources>
            </configuration>
        </execution>
    </executions>
</plugin>
A build helper pluginra akkor lehet szükségünk, ha eltérünk a standard maven directory struktúrától (mint például mi a generated sources esetében).

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>wsimport</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <wsdlUrls>
            <wsdlUrl>src/main/resources/AuthWService.wsdl</wsdlUrl>
        </wsdlUrls>
        <sourceDestDir>src/main/generated</sourceDestDir>
        <packageName>com.wk.auth.ws</packageName>
    </configuration>
</plugin>
És végül a “jaxws-maven-plugin” melynek a wsdlimport goal-ja generálja nekünk a Java forrást a wsdl fileból. A configuration sectionban megadhatjuk a wsdl file helyét, a package nevét és a directory-t ahová a forráskódot generálja a goal.

VII. Hibák, amikbe belefutottam:

  1. Java verzió:A Java 1.6 update 4 előtti változatai a JAX-WS 2.0-át használják. Én a mavennel viszont a 2.1.3-as verziót töltöttem le. Probléma nem is látszott egészen addíg, amíg az Eclipse-ből nem akartam futtatni közvetlenül egy tesztet. Mivel ekkor az eclipse-ben beállított sun-os rt.jar-t kezdte használni a program, a maven által letöltött jaxws-rt.jar helyett. Java update-vel megoldódott.
  2. RPC/Encoded wsdl file: Nincs rá megoldás. A JAX-WS nem támogatja…
Follow

Get every new post delivered to your Inbox.