# SOAP-JAVA



## gast (18. Okt 2006)

Guten Morgen,

und zwar hab ich ein Problem und hoffe das mri ejamnd helfen kann,

Ich muss eienn SOAP Client basteln der auf einem SOAP SERVER eine methode aufruft und denn wert dann ausgibt.

Ok soweit so gut, hab schon einiges aus Tutorials und Hilfen herausgepickt da Ich auf diesem Gebiet noch neu bin.

Leider bekomme ich nun immer einen FEHLER:

Application failed during request deserialization: Unresolved prefix 'soapenv' for attribute 'soapenv:actor'


was ich nun herausgefunden hab is das im SOAP-ENVELOP ein ACTOR ist den man auch mit HEADER.setActor();
beinflussen können sollte, aber egal was ich da reinschreib es tut sich nix.


Auch schreibe Ich eigentlich nichts besonderes in denn HEADER rein so das das ACTOR OBJECT 
geändert werden könnte.. hier mal ein auszug  vom code...



```
.
.
.

            Service service = new Service();
            Call call = (Call) service.createCall();
            SOAPHeaderElement Header = new SOAPHeaderElement(new
javax.xml.namespace.QName("http:SOAP-SERVICE", "user_id"));
            
            Header.setActor("SOAPConstants.URI_SOAP_ACTOR_NEXT"); //<-- egal was ich reinschreib es komt immer der FEHLER
    
            call.addHeader(new SOAPHeaderElement(endpoint,"user_id","1"));
            call.addHeader(new SOAPHeaderElement(endpoint,"transaction_id","1"));
  
.
.
.
```


die user_id und transaction_id benötigt der header.. wenn ich se weglasse meckert er.. auch wenn ich den Header
komplett weglasse meckert  er... die methode muss richtig sein denn wenn ich sei anders nenn dann findet er Sie nicht...

muss ich vielleicht ein actor Element auch machen so wie das user_id element..?


wäre um jeden Tipp dankbar


Danke im Vorraus greetz


----------



## crumble (18. Okt 2006)

Ich würde eine SOAP-Anfrage anders aufbauen:

```
try {

SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
SOAPConnection conn = scf.createConnection();

MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage() //->erzeuge generische SOAP-Nachricht und verändere diesen in den folgenden Zeilen

//--jetzt der SOAP-Part--
SOAPPart sp = msg.getSOAPPart();

//Envelope
SOAPEnvelope env = sp.getEnvelope();

//namespaces hinzufügen (s.w3-spezifikationen)
env.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
env.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
env.addNamespaceDeclaration("soap", "http://schemas.xmlsoap.org/soap/envelope");

//SOAP-Header
//es wird keine extra header angefügt, nur ein "<SOAP-ENV:Header />"
SOAPHeader header = msg.getSOAPHeader();
header.detachNode();

//jetzt der body
SOAPBody bd = env.getBody();

//erzeuge einträge für funktionsnamen und dessen namespace {}-Werte ersetzen!
QName bodyName = new QName({namespace}, {methodenname});
SOAPBodyElement be = bd.addBodyElement(bodyName);

//wenn du willst, jetzt noch parameter für den funktionsaufruf:
be.addChildElement(({parametername}.addTextNode({parameterwert}).setAttribute({namespacename},{namespacewert});

//jetzt die veränderungen an der nachricht speichern
msg.saveChanges();

//url festlegen
URL endpoint = new URL({url});

//debug: soap-nachricht ausgeben
System.out.print("Call-Message: ");
msg.writeTo(System.out);
System.out.println("");

//Antwort-instanz erzeugen aus dem SOAP-Funktionsaufruf mit call und dabei übergabe der SOAP-Nachricht und der url als adresse
SOAPMessage response = conn.call(msg, endpoint);

//connection schliessen
conn.close();

//Antwort "speichern"
SOAPBody soapBody = response.getSOAPBody();

//debug: soap-antwort ansehen
System.out.println("Return-Message: ");
response.writeTo(System.out);
System.out.println("");

}
catch (Exception e) {}
```

Das ergebnis steht jetzt in soapBody und kann jetzt mit XML-Anweisungen ausgelesen werden:

```
Iterator it = soapBody.getChildElements();
while (it.hasNext()) {
 SOAPBodyElement bodyElement = (SOAPBodyElement)it.next();
 NodeList nl = bodyElement.getChildNodes();
 ...
}
```

Man muss aber eventuell bei einer anderen Kodierung (hier am Beispiel rpc/literal) die nachricht anders aufbauen. Bei rpc/encoded müssen beispielsweise die Datentypen noch mit rein etc.

Hoffe geholfen zu haben.

crumble


----------



## Gast (18. Okt 2006)

super VIELEN DANK für die SUPER ausführliche ANTWORT werde es sofort mal ausprobieren...


eine frage noch benötige ich für dieses Beispiel eine wdsl datei ?
weil bei manchen braucht man glaub eine und bei manchen nicht.. oder?


Danke nochmals!!!!


greetz


----------



## crumble (18. Okt 2006)

Java und wsdl-datei-richtig-nutzen ist so eine sache.

es gibt wsdl2java, das erzeugt aus einer wsdl-datei methodenstubs (das sind methodenköpfe mit rückgabewert und parameter). braucht man aber nicht unbedingt.
für die anfrage an sich brauchst du keine wsdl-datei, die "einstellungen" werden ja im beispiel oben von hand vorgenommen.
Soweit ich weiss kann java auch nicht sonderlich gut mit WSDL-Daten umgehen. Beispielsweise in c# stellt man die wsdl-datei als webverweis ein und kann auf alle funktionen lässig zugreifen (sogar per intelli-liste) - also alles während der Entwicklung schon bekannt. bei java stochert man eher im dunkeln rum. Allerdings kann man natürlich mit der WSDL und funktionen wie __getFunctions (kenn die nur von PHP, weiss nicht, ob die in WSDL, PHP, SOAP oder sonstwo spezifiziert ist?!?) alle verfügbaren Funktionen (Rückgabewerte, parameter) ermitteln und bei bedarf die Anfragen anpassen. Das ist aber ziemlich aufwendig, also dynamisch zu reagieren, da WSDL-Dateien ohnehin meist nur selten verändert werden.

Also als Antwort: Nein, du musst nicht 

Jedoch vereinfacht sie eine Menge (vor allem serverseitig, was die angaben von der codierung, namespaces etc. betrifft)

crumble

(Hoffe, das stimmt soweit, ich beschäftige mich auch erst ca 1 jahr damit bzw. mit wsdl+java seit 2 monaten  )


----------



## Gast (18. Okt 2006)

super ok mega THANKs

hab leider noch eine Frage und zwar kennt der die Methoen nicht du kannst mia nich zufällig die Libaries sagen die ich dazu einbinden muss 

hab google gefragt aber naja... 

wäre super falls du es wüsstest


----------



## Gast (18. Okt 2006)

OK hat sich erledigt  
hab se mir zusammen gesucht 

sauber es läuft.. aber.. i weis nicht wie i des jetzt ausgeben kann also die return message...kannst mir des vielleicht en bissel erklären wie du des meinst mit dem xml code.. wäre wirklich supi 


greetz


----------



## crumble (18. Okt 2006)

sehen wir und dazu mal eine typische, mit der kodierung "rpc/literal" empfangene Nachricht an:

```
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope 
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
 <SOAP-ENV:Body>
  <SOAP-ENV:getManufacturerNameResponse>
   <Result xsi:type="xsd:string">ABC
   </Result>
  </SOAP-ENV:getManufacturerNameResponse>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
```
Es handelt sich also um den einfachen String "ABC" als reine Antwort (das getManufacturerNameResponse wurde übrigens in der WSDL-Datei definiert. ;-) )
Mit der oben stehenden Funktion

```
SOAPBody soapBody = response.getSOAPBody();
```
lesen wir aus der Nachricht den SOAPBody:

```
<SOAP-ENV:Body>
  <SOAP-ENV:getManufacturerNameResponse>
   <Result xsi:type="xsd:string">ABC
   </Result>
  </SOAP-ENV:getManufacturerNameResponse>
 </SOAP-ENV:Body>
```
Um an den Wert zu kommen, benötigen wir XML-Abfragen:

```
//zuerst holen wir die "child"-elemente:
Iterator it = soapBody.getChildElements();

//dann durchlaufen wir den Iterator - wohlwissen, dass eigentlich nur ein! Child-Element gefunden wurde (nämlich der gesamte Ausdruck)
while(it.hasNext()) {

//der Ausdruck ist ein SOAPBodyElement
SOAPBodyElement bodyElement = (SOAPBodyElement)it.next();

//Nun holen wir erneut die Child-nodes. Es existiert wieder nur eins, nämlich getManufacturerNameResponse, von diesem wollen
//wir wiederum die Childnodes, nämlich das eine "Result", bekommen
NodeList nl = bodyElement.getChildNodes().item(0).getChildNodes();

System.out.println("Value: " + nl.item(0).getNodeValue());
}
```
Könnte man natürlich auch gleich schreiben als

```
System.out.println( ((SOAPBodyElement)response.getSOAPBody().getChildElements().next()).getChildNodes().item(0).getChildNodes().item(0).getNodeValue() );
```


Wichtig ist nur zu wissen, was passiert, also warum item(0) reicht, was getChildNodes etc bewirkt.
Ich weiss gerade nicht, ob java xpath-anfragen kennt, dann könnte man das auch darüber machen (halt gleich das resultfeld rausangeln mit //Result zum beispiel. aber oben stehendes sollte auf jeden fall funktionieren (für strings als antwort). Arrays kann man so natürlich auch bearbeiten, dann steigt man einfach noch eine Nodeliste tiefer... (dann mit for über getLength())

crumble


----------



## crumble (18. Okt 2006)

achso, NodeList, Node und das ganze XML-Zeugs ist in "org.w3c.dom" enthalten.


----------



## crumbleIStheBEST (19. Okt 2006)

Guten Morgen,

Vielen herzlichen Dank!

Mega starkes DANKESCHÖN, mein Respekt das du mir das so genau erklärst 
find ich echt mega super  , ok werd das jetzt mal ausprobieren!

nochmals herzlichen Dank für deine preziesen und super Antworten 

greetz


----------



## crumbleIStheBEST (19. Okt 2006)

Hallo hab es jetzt mal so eingebaut .. nur leider sagt er das er  ne transaction_id nich findet..

"no transaction_id found in SOAP message at..."

da hab ich es mal manuel hinzugefügt geht aber irgendwie nicht... hab dir heir mal meinen code.. weist du vielleicht was  wäre wirklich super...



```
public class HelloWorldClient
{
  public static void main( String[] args )
  {
  try {

String a="asd";
String af="text";
String type="test";
String text="123asd";
String test="geht nicht";


SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
SOAPConnection conn = scf.createConnection();

MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage(); //->erzeuge generische SOAP-Nachricht und verändere diesen in den folgenden Zeilen

//--jetzt der SOAP-Part--
SOAPPart sp = msg.getSOAPPart();

//Envelope
SOAPEnvelope env = sp.getEnvelope();

//namespaces hinzufügen (s.w3-spezifikationen)
env.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
env.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
env.addNamespaceDeclaration("soap", "http://schemas.xmlsoap.org/soap/envelope");

//SOAP-Header
//es wird keine extra header angefügt, nur ein "<SOAP-ENV:Header />"
SOAPHeader header = msg.getSOAPHeader();
header.addChildElement("transaction_id");
header.addChildElement("user_id");
System.out.println("------------------------------"+msg.getSOAPHeader());
header= msg.getSOAPHeader();


//msg.createAttachmentPart("transaction_id");

//jetzt der body
SOAPBody bd = env.getBody();

//erzeuge einträge für funktionsnamen und dessen namespace {}-Werte ersetzen!
QName bodyName = new QName("http://semo.esk.fraunhofer.de/ECoachService#sendMessage ", "sendmessage");
 Name bn = env.createName("sendMessage", "", "http://semo.esk.fraunhofer.de/ECoachService#sendMessage ");
javax.xml.soap.SOAPBodyElement be= bd.addBodyElement(bn);
//wenn du willst, jetzt noch parameter für den funktionsaufruf:

be.addChildElement(env.createName("transaction_id")).addTextNode("1");
be.addChildElement(env.createName("user_id")).addTextNode("1");

be.addChildElement(env.createName("type")).addTextNode("lala");
be.addChildElement(env.createName("text")).addTextNode("lala");
be.addChildElement(env.createName("test")).addTextNode("lala");
be.addChildElement(env.createName("a")).addTextNode("lala");
                 
/*
be.addChildElement(type.addTextNode("text").setAttribute("urn:MessageHandler","namespacewert"));
be.addChildElement(text.addTextNode("test").setAttribute("urn:MessageHandler","namespacewert"));
be.addChildElement(test.addTextNode("test").setAttribute("urn:MessageHandler","namespacewert"));
be.addChildElement(a.addTextNode("text").setAttribute("urn:MessageHandler","namespacewert"));
*/
//jetzt die veränderungen an der nachricht speichern
msg.saveChanges();

//url festlegen
URL endpoint = new URL( "http://semo.esk.fraunhofer.de:11011/ECoachService");

//debug: soap-nachricht ausgeben
System.out.print("Call-Message: ");
msg.writeTo(System.out);
System.out.println("");

//Antwort-instanz erzeugen aus dem SOAP-Funktionsaufruf mit call und dabei übergabe der SOAP-Nachricht und der url als adresse
SOAPMessage response = conn.call(msg, endpoint);

//connection schliessen
conn.close();

//Antwort "speichern"
javax.xml.soap.SOAPBody soapBody = response.getSOAPBody();

//debug: soap-antwort ansehen
System.out.println("Return-Message: ");
response.writeTo(System.out);
System.out.println("");

}
catch (Exception e) {} 







}
```

nochmals danke für deine super HILFE 

greetz


----------



## Gast (20. Okt 2006)

es geht danke danke 
hab denn fehler gefunde 

hatte im header ne falsceh xmlns seite angegeben jetzt gehts 
danke für die HILFE


----------



## Guest (7. Jan 2007)

gast hat gesagt.:
			
		

> Guten Morgen,
> 
> und zwar hab ich ein Problem und hoffe das mri ejamnd helfen kann,
> 
> ...


----------



## jotobi (30. Mrz 2007)

Ja, das ist wirklich eine gute Erklärung. Aber eine große Frage hätt ich dazu noch. 

Wenn du deinem Funktionsaufruf Parameter hinzufügst, machst du das ja mit Strings. Was ist aber, wenn die Operation, die ich aufrufe, einen komplexen Datentyp als Parameter haben will? (als complexType in der WSDL definiert) ?


----------



## Flipperdream (6. Aug 2007)

Hallo zusammen!

ich habe folgenden Code:

```
public static OMElement createAnfrage()
	{	
		OMFactory fac = OMAbstractFactory.getOMFactory();
		OMNamespace ns = fac.createOMNamespace("http://www.firma_abc.de/","abc");
		OMElement SuchePersonAnfrage= fac.createOMElement(
				"SuchePersonAnfrage", ns);
		OMElement such1 = fac.createOMElement("nachname", ns, SuchePersonAnfrage);
		such1.setText("Meier");
		OMElement such2 = fac.createOMElement("vorname", ns, SuchePersonAnfrage);
		such2.setText("otto");
		OMElement such3 = fac.createOMElement("alter",ns,SuchePersonAnfrage);
		such3.setText("50");
		
		
		return SuchePersonAnfrage;


	}
	
public static void main(String[] args) throws Exception
	{		
		OMElement anfrage = createAnfrage();
		// ... 
	}
```

dieser soll folgende SOAP Nachricht erzeugen:

```
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
	<soapenv:Body>
		<abc:SuchePersonAnfrage xmlns:abc="http://www.firma_abc.de/">
			<abc:nachname>Meier</abc:nachname>
			<abc:vorname>otto</abc:vorname>
			<abc:alter>50</abc:alter>
		</abc:SuchePersonAnfrage>
	</soapenv:Body>
</soapenv:Envelope>
```

dabei soll die Nachricht die folgendene Struktur /Abhängigkeit haben (soll):

```
anfrage:
firstchild:
	lokalname=nachname
	value=Meier
	
lastchild:
	localname=alter
	value=50
```

jedoch ist es leider so (ist):


```
anfrage:
firstchild:
	lokalname=nachname
	firstchild: 
		localname=include
		value=Meier
	lastchlid:
		localname=include
		value=Meier
lastchild:
	localname=alter
	firstchild: 
		localname=include
		value=50
	lastchlid:
		localname=include
		value=50
```

Was habe ich im oben aufgelisteten Code falsch gemacht, damit meine gewünschte "Soll" Struktur/Abhängigkeit erzeugt wird.
Wenn ich diese Struktur nicht einhalte habe ich Probleme mit dem Verarbeiten der Nachricht, da ich die entsprechenden Werte nicht abgreifen kann. 

Ich habe diesen Code in Anlehnung an das Buch "Java Web Services mit Apache Axis2" geschrieben. Wenn meine Art nicht funktioniert würde ich gern die hier genutzte Weise versuchen, kann mir da jemand ein passendes Codebeispiel schicken/posten.

Vielen Dank schon mal im voraus.


----------

