niedenzu

Hints and tricks for developers

Martins Amazon Wunschliste

SOAPClient – Programmiert – nicht generiert 9. August 2010

In unserem Projekt hatten wir folgendes Problem.
Der von uns generierte SOAP-Client lief nur in unserer Entwicklungsumgbung.
Auf der Testumgeung ist der Code jedoch mit einer NullPointerException abgestürzt.
Der Absturz erfolgte in der Axis-Runtime und nicht im generierten Code.
Der Fehler war für uns weder reproduzierbar noch behebbar.
Also haben wir uns entschlossen einen eigenen Client zu programmieren der SOAP macht ohne generiert worden zu sein. Mit dem Netzwerkanalysetool Wireshark (früher Etherreal)konnten wird den Netzwerkverkehr mitsniffen um zu ermitteln wie die SOAP-Anfrage aussehen muß, und wie die Antwort zu interpretieren ist.
Dies ist kein Plaidoyer für gegen SOAP oder Axis sondern die letzte Möglichkeit das Problem zu beheben. Natürlich können man auch einen andere SOAP-Implementierung verwenden. Doch haben die anderen Implementierungen (z.B. von IBM) nicht mit portablität geglenzt.

Ziel:

Ersetzung eines mit Apache Axis 1.4 generierter SOAP-Client durch einen
Hangestrickten SOAP-Client mit java std-mitteln (keine Third party libs).

Tip:

Wir haben bewust den in Java eingebauten HTTP-Client entschieden (er heißt dort HttpUrlConnection). Zwar wird der HTTP-Client von Apache von vielen preferiert der eingabaute ist jedoch in jedem Java ab Version 1.4 enthalten. So benötigen wir keine externen libs. Telnet ist noch immer ein super Tool um TCP Anfragen zu verschicken (HTTP gehört ja zur TCP-Familie).

Schwierigkeiten

  • SOAP-Request erstellen (Aufbau des HTTP-Headers)
  • Parsen des SOAP-Replys (XML-Dokumentes mit namespaces). Kenntnisse über XPATH ausdrücke kann man unter http://www.w3schools.com/xpath auffrischen.

Header der SOAP-Anfrage


POST /Med_PartnerWeb/sca/PartnerAsExport1 HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.4
Host: myserver.mydomain.de
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 641

Analyse des Headers

Erste wichtig Erkenntnis – Es muß ein HTTP POST ausgeführt werden (kein GET).
Der Aufrufe mit einem Browser ist somit nicht ohne weiteres möglich.

Inhalt der SOAP-Antwort

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header />
<soapenv:Body>
...
</soapenv:Body>
</soapenv:Envelope>

Das Parsen des Dokuments stellte sich als äußerst kompliziertes Unterfangen raus.
Das Parsen von XML mit Java ist eine einfaches Sache, so lange kein namespaces verwendet werden. http://blog.davber.com/2006/09/17/xpath-with-namespaces-in-java beschreibt dieses Problem in der Tiefe, dort habe ich die Infos gefunden die mir weitergeholfen haben.

URL url;

// Create connection
url = new URL("http://myserver.mydomain.de/Med_PartnerWeb/sca/PartnerAsExport1");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type","text/xml; charset=utf-8");
connection.setRequestProperty("Accept", "application/soap+xml, application/dime, multipart/related, text/*");
connection.setRequestProperty("Content-Type","text/xml; charset=utf-8");
connection.setRequestProperty("User-Agent", "Axis/1.4");
connection.setRequestProperty("Host", "esb-itu.ww-intern.de");
connection.setRequestProperty("Cache-Control", "no-cache");
connection.setRequestProperty("Pragma", "no-cache");
connection.setRequestProperty("SOAPAction", """");
connection.setRequestProperty("Content-Length","" + Integer.toString(auftrag.getBytes().length));
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
OutputStreamWriter wr = new OutputStreamWriter(
connection.getOutputStream());
wr.write(auftrag);
wr.flush(); // Get the response

// Get the response
BufferedReader rd = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
StringBuilder sb = new StringBuilder();

while ((line = rd.readLine()) != null) {
// Process line...
sb.append(line);
}
wr.close();
rd.close();

NamespaceContext ctx = new NamespaceContext() {
public String getNamespaceURI(String prefix) {
String uri;
if (prefix.equals("soapenv"))
uri = "http://schemas.xmlsoap.org/soap/envelope/";
else if (prefix.equals("as"))
uri = "http://Lib_Partner/de/wwag/bsw/partner/service/PartnerAs/";
else if (prefix.equals("soapenc"))
uri = "http://schemas.xmlsoap.org/soap/encoding/";
else if (prefix.equals("xsd"))
uri = "http://www.w3.org/2001/XMLSchema/";
else if (prefix.equals("xsi"))
uri = "http://www.w3.org/2001/XMLSchema-instance/";
else
uri = null;
return uri;
}

// Dummy implementation - not used!
// Wird nicht aufgerufen, muß jedoch implementiert werden
// Damit die Schnittstelle erfüllt ist.
public Iterator getPrefixes(String val) {
return null;
}

// Dummy implemenation - not used!
// Wird nicht aufgerufen, muß jedoch implementiert werden
// Damit die Schnittstelle erfüllt ist.
public String getPrefix(String uri) {
return null;
}
};


/soapenv:Envelope/soapenv:Body/*/lieferung/header/status/text()

Der rote Teil des Xpath ausdrucks ist dem SOAP-Protokoll geschuldet. Wichtig hierbei ist die angabe des Namespaces vor dem eigentlichen Knoten (org.w3c.dom.Node). Der Blaue Teil ist der Pfad zu den Nutzdaten. Mit Hilfe der text() Location Path Expression kann man auf den Textinhalt eines Knotens

No Comments on SOAPClient – Programmiert – nicht generiert
Categories: Uncategorized

Dynamische AJAX-Tabelle 5. August 2010

Basis-Tabelle

Javascriptcode

]]>

Anmerkung.

Dieses Basiskonstrukt läuft im sowohl im Internetexplorer (7) als auch im neuesten Chrome.

No Comments on Dynamische AJAX-Tabelle
Categories: Uncategorized

JSF ComboBoxen mit Autopostback 4. August 2010

Um Dynamische GUIs zu bauen gibt es verschieden Optionen die GUI zu verändern.
In den meisten JSF-Einführungenwird zwar beschrieben wie man ein SelectOne verwendet, jedoch nicht wie man eine Bestimmte logik im Backing bean anspricht.

<h:selectOneMenu id="selectCar" value="#{carBean.currentCar}">
<f:selectItems value="#{carBean.carList}" />
</h:selectOneMenu>

Der Entscheidende Punkt ist, dass 2 Attribute gesetut werden müssen.
das onchange="this.form.submit()" bewirkt, dass die Werte des Formulars ins Model übernommen werden (durch Aufruf der setter-Methode).
Die Business-Logik sollte man aber nicht in den Setter reinbauen. Sondern sollte mittels eines valueChangeListener eingebunden werden.

<h:selectOneMenu id="selectCar" value="#{carBean.currentCar}"
valueChangeListener="#{BackingBean.processValueChange}"
onchange="this.form.submit()"
>
<f:selectItems value="#{carBean.carList}" />
</h:selectOneMenu>

public void  proessValueChange(ValueChangeEvent event) {
//some logic
}
No Comments on JSF ComboBoxen mit Autopostback
Categories: Uncategorized
Martin Rocks