ksoap2 v prefixo to any other prefix - java

Hi i have a code for generating a simple request to a example soap server where i need to build a request like:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://10.1.5.80:8080/">
<soapenv:Header/>
<soapenv:Body>
<ns:GETSERVERTIME/>
</soapenv:Body>
</soapenv:Envelope>
but i get
<v:Envelope
xmlns:i="http://www.w3.org/1999/XMLSchema-instance"
xmlns:d="http://www.w3.org/1999/XMLSchema"
xmlns:c="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
<v:Header />
<v:Body>
<n0:GETSERVERTIME xmlns:n0="http://localhost:8080/" />
</v:Body>
</v:Envelope>
i Only need to change "v:" to "soapenv:"
my code:
/**
* Created by Vinicius Gati on 30/12/14.
*
*/
public class ServerSOAP {
private static final String METHOD_NAME = "GETSERVERTIME";
private static final String NAMESPACE = "http://localhost:8080/";
private static final String SOAP_ACTION = "";
private static final String URL = "http://10.1.5.80:8080/ws/SERVERTIME.apw?WSDL";
public static String getServerTime() {
String retorno = "";
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER10);
envelope.implicitTypes = false;
envelope.setAddAdornments(false);
envelope.setOutputSoapObject(request);
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
androidHttpTransport.debug = true;
try {
androidHttpTransport.call( NAMESPACE + METHOD_NAME, envelope );
SoapObject response = (SoapObject) envelope.getResponse();
} catch (Exception e) {
e.printStackTrace();
}
return retorno;
}
}
but i'm crazy trying this, no success.

As You may see in:
https://github.com/mosabua/ksoap2-android/blob/master/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java
writing method defines prefixes as string constants (code copy from class SoapEnvelope, see provided link):
public void write(XmlSerializer writer) throws IOException {
writer.setPrefix("i", xsi);
writer.setPrefix("d", xsd);
writer.setPrefix("c", enc);
writer.setPrefix("v", env);
writer.startTag(env, "Envelope");
writer.startTag(env, "Header");
writeHeader(writer);
writer.endTag(env, "Header");
writer.startTag(env, "Body");
writeBody(writer);
writer.endTag(env, "Body");
writer.endTag(env, "Envelope");
}
So, You may try to define own class, inheriting from SoapSerializationEnvelope and experiment with redefining this method to use "soapenv" prefix.
BTW: if WS cant read prefix of any name, its a poor code on this service side. "soapenv" or "v" should be interpreted as identical in both xml's You included.
Marcin

Dude,
I've solved this issue. Actually I've extend the SoapSerializationEnvelope class and overridden the SoapSerializationEnvelope's write() method as follows
#Override
public void write(XmlSerializer writer) throws IOException {
Log.d("DEBUG", "XML create process started");
env = "http://schemas.xmlsoap.org/soap/envelope/";
String tem = "http://tempuri.org/";
String ner = "http://schemas.datacontract.org/2004/07/MYSERVICE";
//writer.text("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
// Don't try like above, it will cause errors, for instead of this, you should use below code
writer.startDocument("UTF-8", true);
writer.setPrefix("soapenv", env); //these are propably same for you
writer.setPrefix("tem", tem); //these are propably same for you
writer.setPrefix("ner", ner); // but this must change with your namespace writer.setPrefix("myTag", ner);
writer.startTag(env, "Envelope");
writer.startTag(env, "Header");
writeHeader(writer);
writer.endTag(env, "Header");
writer.startTag(env, "Body");
writer.startTag(tem, "Method");
writer.startTag(tem, "inputObject");
writer.startTag(ner, "property");
writer.text("property value");
writer.endTag(ner, "property");
writeBody(writer);
writer.endTag(tem, "inputObject");
writer.endTag(tem, "Method");
writer.endTag(env, "Body");
writer.endTag(env, "Envelope");
writer.endDocument();
Log.d("DEBUG","XML created");
}

Related

wss4j:1.6.5 - Add BinarySecurityToken to SOAP request

We have an old application written in Java that uses and old version of wss4j to generate the SOAP requests and xFire to transmit those requests.
This project uses a few dependencies that I believe are relevant:
wss4j (1.6.5)
xfire-all (1.2.6)
xfire-jsr181-api (1.0-M1)
Frankly, I have little experience with SOAP and zero experience with this wss4j library.
From looking online, I was unable to find a lot of helpful resources for this older version of wss4j.
With all of the developers who worked on this project having left the company before I joined, I'm hoping to find some help from the vast knowledge found on SO.
A change in the SOAP service has been made by an external team for which we need to modify our client.
We need to be able to add a BinarySecurityToken to the Security section of the header as well as an arbitrary element to the Header.
From what I've read online, the BinarySecurityToken is supposed to be used to encode certificate data, so I'm not sure if we're trying to use it correctly, but frankly we don't have a choice as the change has been made to the service and we have to adapt the client to meet it's new requirements :(
Below is an example or what our application currently sends; it does not include a BinarySecurityToken in the Security element or an authMethod element in the Header element:
<soap:Header>
<wsse:Security soap:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-b92109ac-4a13-444d-b8e5-19c400f5910f">
<wsse:Username>some_username</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">some_password</wsse:Password>
<wsse:Nonce>Daaaaa1bBbbbbBb2cccccc==</wsse:Nonce>
<wsu:Created>2022-09-19T09:10:31Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
<wsa:Action>some_text</wsa:Action>
<wsa:MessageID>some_text_value</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>some_text_value</wsa:Address>
</wsa:ReplyTo>
<wsa:To endpointID="some_text_value">http://172.31.9.216:443/P/V3_20090316L/XML/FICR_AR062004CA</wsa:To>
<wsa:From endpointID="some_text_value">
<wsa:Address>some_text_value</wsa:Address>
</wsa:From>
<language>en</language>
</soap:Header>
The expected SOAP request must contain a header that looks like the below, it must include a BinarySecurityToken in the Security element and an authMethod element in the Header element:
<soap:Header>
<wsse:Security soap:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-b92109ac-4a13-444d-b8e5-19c400f5910f">
<wsse:Username>some_username</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"> </wsse:Password>
<wsse:Nonce>Daaaaa1bBbbbbBb2cccccc==</wsse:Nonce>
<wsu:Created>2022-09-19T09:10:31Z</wsu:Created>
</wsse:UsernameToken>
<wsse:BinarySecurityToken ValueType="authToken" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-c0c6c469-235a-42e5-869c-cec77b1c2dbd">some_token_which_will_be_base64_encoded</wsse:BinarySecurityToken>
</wsse:Security>
<wsa:Action>some_text</wsa:Action>
<wsa:MessageID>some_text_value</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>some_text_value</wsa:Address>
</wsa:ReplyTo>
<wsa:To endpointID="some_text_value">http://172.31.9.216:443/P/V3_20090316L/XML/FICR_AR062004CA</wsa:To>
<wsa:From endpointID="some_text_value">
<wsa:Address>some_text_value</wsa:Address>
</wsa:From>
<language>en</language>
<ehr:authMethod xmlns:ehr="some_text_value">authToken</ehr:authMethod>
</soap:Header>
Below an a sample of the current code:
I cleaned-up the code a little to make it legible and added full class-declarations where I thought important.
Transmitter.java
public class Transmitter
{
public org.w3c.dom.Document sendRequest(
Document reqMsg,
String serverUrlAndLocation,
String action,
String msgId,
String receiverNetAddr,
String senderNetAddr,
String toEndpointID,
String fromEndpointID,
String username,
String authToken,
String testCase
)
final EHIP_routingServiceClient prclient = new EHIP_routingServiceClient(serverUrlAndLocation);
final EHIP_routingPortType prpt = prclient.getEHIP_routingPort0();
final org.codehaus.xfire.client.Client client = org.codehaus.xfire.client.Client.getInstance(prpt);
String interactionId = stripVersion(action);
action = "urn:hl7-org:v3:" + action;
final RxDOMInHandler rxHandler = new RxDOMInHandler(testCase);
client.addInHandler (rxHandler); // creates the DOM for the SOAP msg;
client.addOutHandler (new org.codehaus.xfire.util.dom.DOMOutHandler.DOMOutHandler());
client.addFaultHandler(new org.codehaus.xfire.util.dom.DOMOutHandler.DOMOutHandler());
client.addInHandler (new org.codehaus.xfire.util.LoggingHandler());
client.addOutHandler (new RxLoggingHandler(testCase, interactionId));
client.addFaultHandler(new RxLoggingHandler(testCase, interactionId));
this.enableUsernameTokenEClaims(
client,
username,
authToken
);
this.enableWSAddressing(
client,
action,
msgId,
receiverNetAddr,
senderNetAddr,
toEndpointID,
fromEndpointID
);
client.addOutHandler(new RxSoapActionOutHandler(action, false));
client.addOutHandler(new RxAdjustBodyOutHandler(m_api.getCreationTimeFormatFlag()));
final Object[] params = new Object[] {reqMsg};
Object[] ret;
try
{
ret = client.invoke("processRequest", params);
}
catch(XFireFault fault)
{
if (fault.getCause() instanceof WstxEOFException)
{
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder documentBuilder = factory.newDocumentBuilder();
ret = new Document[1];
ret[0] = documentBuilder.newDocument();
}
else
{
throw fault;
}
}
return rxHandler.getResponse();
}
private void enableUsernameTokenEClaims(
Client client,
String username,
String authToken
)
{
final java.util.Map<String, Object> usernameTokenConfig = new HashMap<String, Object>();
usernameTokenConfig.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
usernameTokenConfig.put(WSHandlerConstants.USER, username);
usernameTokenConfig.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
final RxPasswordHandler pwdHandler = new RxPasswordHandler();
pwdHandler.setPassword(authToken, true);
usernameTokenConfig.put(WSHandlerConstants.PW_CALLBACK_REF, pwdHandler);
client.addOutHandler(new WSS4JOutHandler(usernameTokenConfig));
}
private void enableWSAddressing(
org.codehaus.xfire.client.Client client,
String action,
String msgId,
String receiverNetAddr,
String senderNetAddr,
String toEndpointID,
String fromEndpointID
)
{
final RxAddressingHeadersFactory200508 factory = new RxAddressingHeadersFactory200508(
m_api.getLanguage(),
toEndpointID,
fromEndpointID
);
final AddressingHeaders ah = new AddressingHeaders();
if (msgId == null || msgId.trim().equals(""))
{
ah.setMessageID(new RandomGUID(true).toString());
}
else
{
ah.setMessageID(msgId);
}
final EndpointReference epr1 = factory.createEClaimsDefaultEPR(senderNetAddr);
ah.setFrom(epr1);
ah.setReplyTo(factory.createDefaultEPR());
ah.setTo(receiverNetAddr);
ah.setAction(action);
final java.util.Map<String, Object> properties = new HashMap<String, Object> ();
properties.put(AddressingInHandler.ADRESSING_HEADERS.toString(), ah);
properties.put(AddressingInHandler.ADRESSING_FACTORY.toString(), factory);
client.addOutHandler(new RxAddressingOutHandler(properties));
}
}
RxDOMInHandler
public class RxDOMInHandler extends org.codehaus.xfire.util.dom.DOMInHandler
{
public static final String DOM_MESSAGE = "dom.message";
private String interactionId = null;
private String testCase = null;
private org.w3c.dom.Document response = null;
public RxDOMInHandler()
{
super();
setPhase(org.codehaus.xfire.handler.Phase.PARSE);
before(org.codehaus.xfire.soap.handler.ReadHeadersHandler.class.getName());
}
public RxDOMInHandler(String testCase)
{
super();
setPhase(org.codehaus.xfire.handler.Phase.PARSE);
before(org.codehaus.xfire.soap.handler.ReadHeadersHandler.class.getName());
this.testCase = testCase;
}
public void invoke(org.codehaus.xfire.MessageContext context)
throws Exception
{
org.w3c.dom.Document doc = null;
javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory.newInstance();
dbf.setValidating(false);
dbf.setIgnoringComments(false);
dbf.setIgnoringElementContentWhitespace(false);
dbf.setNamespaceAware(true);
dbf.setCoalescing(false);
org.codehaus.stax2.XMLStreamReader2 reader2 = (org.codehaus.stax2.XMLStreamReader2) context.getInMessage().getXMLStreamReader();
doc = org.codehaus.xfire.util.STAXUtils.read(dbf.newDocumentBuilder(), reader2, false);
this.response = doc;
context.getInMessage().setProperty(DOM_MESSAGE, doc);
context.getInMessage().setXMLStreamReader(new org.codehaus.xfire.util.stax.W3CDOMStreamReader(doc.getDocumentElement()));
this.interactionId = RxDOMUtil.getInteractionIdFromDoc(doc);
RxDOMUtil.writeXmlToFile("xmls/SOAP_Response_" + this.interactionId + "_" + this.testCase + ".xml", doc);
}
public org.w3c.dom.Document getResponse()
{
return response;
}
}
I tried modifying the enableUsernameTokenEClaims method to add a BinarySecurityToken, but so far I have not been able to get that to work.
I have already successfully make this same change to another client of this service, but that client was written in VB and the libraries being used there were either more flexible or less strict.
Based on speaking with coworkers with more experience with these libraries and SOAP in general, it seems that this version of the wss4j library just doesn't support BinarySecurityToken :(
As such we're going to go a different route to transmit the tokens.

How to send a SOAP request using WebServiceTemplate?

I am trying to send a request to a SOAP webservice. I read this tutorial and prepared the following code. However, I am going to send different requests to multiple SOAP webservices, whereas the tutorial focused on one request. How can I send SOAP request using WebserviceTemplate?
WebServiceTemplate
SoapMessage soapMsg = new SoapMessage();
soapMsg.setUsername("Requester");
soapMsg.setPassword("Pass");
soapMsg.setLanguageCode("EN");
Request request = new Request();
request.setDeparture("FDH");
request.setDestination("HAM");
Date date = new Date();
SimpleDateFormat frm2 = new SimpleDateFormat("yyyy-MM-dd");
request.setDepartureDate(frm2.parse(frm2.format(date)));
request.setNumADT(1);
request.setNumCHD(0);
request.setNumInf(0);
request.setCurrencyCode("EUR");
request.setWaitForResult(true);
request.setNearByDepartures(true);
request.setNearByDestinations(true);
request.setRronly(false);
request.setMetaSearch(false);
soapMsg.setRequest(request);
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(). //how to create object and send request!
Object response = webServiceTemplate.marshalSendAndReceive(
"https://aaa5.elsyarres.net", soapMsg);
Response msg = (Response) response;
System.err.println("size of results of wogolo:"
+ msg.getFlights().getFlight().size());
You can use following code, you do not need to define anything in xml file.
try {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(
MessageFactory.newInstance());
messageFactory.afterPropertiesSet();
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(
messageFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("PACKAGE");
marshaller.afterPropertiesSet();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
Response response = (Response) webServiceTemplate
.marshalSendAndReceive(
"address",
searchFlights);
Response msg = (Response) response;
} catch (Exception s) {
s.printStackTrace();
}
To send different SOAP requests to different SOAP services, you just need to make your WebServiceTemplate aware of all requests and responses it will have to process.
Create a Java class for each request and response like so:
package models;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
#XmlRootElement
public class FlyRequest implements Serializable {
private boolean nearByDeparture;
public FlyRequest() {}
public boolean isNearByDeparture() {
return nearByDeparture;
}
public void setNearByDeparture(boolean nearByDeparture) {
this.nearByDeparture = nearByDeparture;
}
}
(The #XmlRootElement is because we use JAXB marshaller below; see Jaxb reference for more info).
The setup of the template is done for example like so:
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
messageFactory.afterPropertiesSet();
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(messageFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("models");
marshaller.afterPropertiesSet();
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
"models" is the name of the package where the Request/Responses classes are, so that jaxb can find them.
Then you just instantiate the request of the class you want to perform the call, like so:
// call fly service:
FlyRequest flyRequest = new FlyRequest();
flyRequest.setNearByDeparture(false);
Object flyResponse = webServiceTemplate.marshalSendAndReceive("https://example.net/fly", flyRequest);
// call purchase service:
PurchaseRequest purchaseRequest = new PurchaseRequest();
purchaseRequest.setPrice(100);
Object purchaseResponse = webServiceTemplate.marshalSendAndReceive("https://example.net/purchase", purchaseRequest);
Similarly, you can cast the response objects into your JAXB classes defined above.
Here is an Example what you should be looking for
Soap has a lot of restriction unlike REST, It follows some standards which have to be meet before you get Network call to work,
But unlike Rest, in Soap if you have WSDL URL you can get all the information needed to call the Soap call
private final String NAMESPACE = "http://www.w3schools.com/webservices/";
private final String URL = "http://www.w3schools.com/webservices/tempconvert.asmx?WSDL";
private final String SOAP_ACTION = "http://www.w3schools.com/webservices/CelsiusToFahrenheit";
private final String METHOD_NAME = "CelsiusToFahrenheit";
this code was written in Android so you can ignore some part of it but I still kept it in answer so someone from android background can put a good use to it
Open [WSDL][1] in browser and check for the things which matter to call a remote method on server.
1
you will see an attribute targetNamespace whose value would be Namespace which you will use in this case Namespace is http://www.w3schools.com/webservices/
2
Now you require the name of the method this WSDL has four method each of the are int attribute s:element with the value is the name of the Method in this case four methods are FahrenheitToCelsius, FahrenheitToCelsiusResponse, CelsiusToFahrenheit, CelsiusToFahrenheitResponse
3
Now you have to fure out the SOAP Action which is NAMESPACE+METHOD
but WSDL also gives information about that as well, look for the tag soap:operation and it's soapAction attribute havs the Soap action as it's value in this case which we want to call is http://www.w3schools.com/webservices/CelsiusToFahrenheit
private class MyTask extends AsyncTask<Void, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog.show();
}
#Override
protected String doInBackground(Void... params) {
try {
SoapObject soapObject = new SoapObject(NAMESPACE, METHOD_NAME);
soapObject.addProperty("Celsius","12");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(soapObject);
HttpTransportSE httpTransportSE = new HttpTransportSE(URL);
httpTransportSE.call(SOAP_ACTION, envelope);
SoapPrimitive soapPrimitive = (SoapPrimitive)envelope.getResponse();
Log.d("TAG", "doInBackground: "+soapPrimitive.toString());
return soapObject.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String aVoid) {
super.onPostExecute(aVoid);
progressDialog.dismiss();
textView.setText(""+aVoid);
}
}
Assuming that your SoapMessage is marhsallable
To send the same message to multiple endpoints you only need to loop on the sending code and the request handler.
Something like this:
{
String endpoint = "https://aaa5.elsyarres.net"
WebServiceTemplate webServiceTemplate = new WebServiceTemplate().
webServiceTemplate.setDefaultUri(endpoint);
Object response = webServiceTemplate.marshalSendAndReceive(soapMsg);
// handle you are response as you are currently doing.
// Loop changing the endpoint as you need.
}
This code uses the Spring WebServiceTemplate
I tried many options and finally below one worked for me if you have to send soap header with authentication(Provided authentication object created by wsimport) and also need to set soapaction.
public Response callWebService(String url, Object request)
{
Response res = null;
log.info("The request object is " + request.toString());
try {
res = (Response) getWebServiceTemplate().marshalSendAndReceive(url, request,new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) {
try {
// get the header from the SOAP message
SoapHeader soapHeader = ((SoapMessage) message).getSoapHeader();
// create the header element
ObjectFactory factory = new ObjectFactory();
Authentication auth =
factory.createAuthentication();
auth.setUser("****");
auth.setPassword("******");
((SoapMessage) message).setSoapAction(
"soapAction");
JAXBElement<Authentication> headers =
factory.createAuthentication(auth);
// create a marshaller
JAXBContext context = JAXBContext.newInstance(Authentication.class);
Marshaller marshaller = context.createMarshaller();
// marshal the headers into the specified result
marshaller.marshal(headers, soapHeader.getResult());
} catch (Exception e) {
log.error("error during marshalling of the SOAP headers", e);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
return res;
}

How can convert local xml file to org.ksoap2.serialization.SoapObject?

I am developing android web application which needs to connect web - service for response.
I am using kSOAP for web service invocation process.
[kSOAP is a SOAP web service client library for constrained Java environments such as Applets or J2ME applications.]
If I am saving that responded xml into the local directory, eg. /mnt/sdcard/appData/config.xml and then when ever I ask request for web service, first it will checks if local file is there then consider that file as responded file otherwise connect to the server.
This process reduce response time and increase efficiency of application.
Is it possible to convert it ('config.xml') to SOAP object? And How?
Consider my xml local file is as below:
config.xml
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<Response xmlns="http://testuser.com/webservices/response">
<Result>
<SName>Test User</SName>
<UnitDesc>SAMPLE Test </UnitDesc> <RefreshRate>60</RefreshRate>
<Out>
<Definition>
<Code>ABC</Code>
<Description>(Specific)</Description>
<Action>true</Action>
<Despatch>false</Despatch>
</Definition>
<Definition>
<Code>CDE</Code><Description>(Specific)</Description>
<ActionDate>true</ActionDate>
</Definition>
</Out>
<SampleText>
<string>Test XML Parsing</string>
<string>Check how to convert it to SOAP response</string>
<string>Try if you know</string>
</SampleText>
<GeneralData>
<Pair>
<Name>AllowRefresh</Name>
<Value>Y</Value>
</Pair>
<Pair>
<Name>ListOrder</Name>
<Value>ACCENDING</Value>
</Pair>
</GeneralData>
</Result>
</Response>
</soap:Body>
</soap:Envelope>
Current code is shown below:
final String CONFIGURATION_FILE="config.xml";
File demoDataFile = new File("/mnt/sdcard/appData");
boolean fileAvailable=false;
File[] dataFiles=demoDataFile.listFiles(new FilenameFilter() {
#Override
public boolean accept(File dir, String filename) {
return filename.endsWith(".xml");
}
});
for (File file : dataFiles) {
if(file.getName().equals(CONFIGURATION_FILE))
{
fileAvailable=true;
}
}
if(fileAvailable)
{
//**What to do?**
}
else
{
//Create the envelope
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
//Put request object into the envelope
envelope.setOutputSoapObject(request);
//Set other properties
envelope.encodingStyle = SoapSerializationEnvelope.XSD;
envelope.dotNet = true;
String method="test";
synchronized (transportLockObject)
{
String soapAction = "http://testuser.com/webservices/response/"+method;
try {
transport.call(soapAction, envelope);
} catch (SSLHandshakeException she) {
she.printStackTrace();
SecurityService.initSSLSocketFactory(ctx);
transport.call(soapAction, envelope);
}
}
//Get the response
Object response = envelope.getResponse();
//Check if response is available... if yes parse the response
if (response != null)
{
if (sampleResponse != null)
{
sampleResponse.parse(response);
}
}
else
{
// Throw no response exception
throw new NoResponseException("No response received for " + method + " operation");
}
}
You can extend HttpTransportSE class and override method call like this:
public void call(String soapAction, SoapEnvelope envelope) throws IOException, XmlPullParserException
{
if(localFileAvailable)
{
InputStream is = new FileInputStream(fileWithXml);
parseResponse(envelope, is);
is.close();
}
else
{
super.call(soapAction, envelope);
}
}
The question was how to convert an xml file to a SoapObject. So how to get your input xml envelope into a ksoap2 call.
The way to do this is actually available within the HttpTransportSE class even though this was not its intended use!
There is a method "parseResponse" that takes in the envelope and an input stream(your xml file) and updates the envelope input header and body. But the clever thing is that you can copy these into the outHeader and outBody fields and then all the hard work of mapping fields goes away.
#Override
public void call(String soapAction, SoapEnvelope envelope) throws IOException, XmlPullParserException {
if ( getFileInputStream() != null ){
parseResponse(envelope, getFileInputStream());
envelope.bodyOut = envelope.bodyIn;
envelope.headerOut = envelope.headerIn;
getFileInputStream().close();
}
super.call(soapAction,envelope);
}

Android - WSDL / Ksoap2

I've two questions about Ksoap2.
First at all, below an example of my webservice request in XML.
REQUEST:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://my-webservice.com/">
<soapenv:Header/>
<soapenv:Body>
<web:getBoard>
<!--Optional:-->
<language></language>
<identification login="" pwd=""/>
</web:getBoard>
</soapenv:Body>
</soapenv:Envelope>
RESPONSE:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:getBoardResponse xmlns:ns2="http://my-webservice.com/">
<board code="NONE">WhatIWant</board>
<board code="HALF">blabla</board>
<board code="FULL">blablah</board>
<board code="RONL">blablalblba</board>
<board code="BRKF">blabla</board>
<board code="ALLI">blablablah</board>
</ns2:getBoardResponse>
</soap:Body>
</soap:Envelope>
I have to set datas for the fields: "language", "login" and "pwd" but i'm not sure to use the good method... (addProperty or setProperty for the field "language" ?)
public class CallRefservices {
private static final String NAMESPACE = "http://api.myapi.com/test/ws/test";
private static final String URL = "http://api.myapi.com/test/ws/test?wsdl";
private static final String SOAP_ACTION = "getBoard";
private static final String METHOD_NAME = "getBoard";
Boolean getConnection(String login, String pwd) {
Boolean checkBoardType = false;
try {
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("login", login);
request.addProperty("pwd", pwd);
request.addProperty("language", "FR");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.setOutputSoapObject(request);
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
androidHttpTransport.call(SOAP_ACTION, envelope);
SoapObject objetSOAP = (SoapObject)envelope.getResponse();
checkBoardType = this.parserObjet(objetSOAP);
} catch (Exception e) {
Log.e("getConnection", "", e);
}
return checkBoardType;
}
private boolean parserObjet(SoapObject objet) {
SoapObject boardObjet = (SoapObject)objet.getProperty("board");
String board = boardObjet.getProperty("NONE").toString();
if (board == "WhatIWant")
return true;
else
return false;
}
}
I also need help about the parser.
what kind of parser is use with Ksoap2? (DOM, SAX)
Am I using the good method?
any help is appreciated!
merry christmas btw ;).
EDIT:
Well, my problem is that the xml sent is the following:
[...] <login i:type="d:string">mylogin</login><pwd i:type="d:string">mypassword</pwd><language i:type="d:string">FR</language></n0:getBoardTypes></v:Body></v:Envelope>
Whereas I need:
[...] <language></language>
<identification login="" pwd=""/>
:|
EDIT 2:
Hi Tomislav, thanks for your time and your answer !
I tried your solution but it can't works.
I'm suppose to send something like:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://my.api.com/">
<soapenv:Header/>
<soapenv:Body>
<web:getBoard>
<language>FR</language>
<identification login="username" pwd="mypwd"/>
</web:getBoard>
</soapenv:Body>
</soapenv:Envelope>
With your solution i've something like:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://my.api.com/">
<soapenv:Header/>
<soapenv:Body>
<web:getBoard>
<language>FR</language>
<identification>
<login>username</login>
<pwd>mypwd</pwd>
</identification>
</web:getBoard>
</soapenv:Body>
</soapenv:Envelope>
I think that I gonna create a XML template... :/ !
So I assume your service method signature is something like
getBoard(String language, identification ident) // parameter names here must match the param names in the code below
where identification is a class with two string properties login and pwd. If that's the case you can create a new class and implement KvmSerializable like below:
public class identification implements KvmSerializable {
public String login;
public String pwd;
public identification (String login, String pwd) {
this.login = login;
this.pwd = pwd;
}
public Object getProperty(int i) {
switch (i) {
case 0:
return login;
case 1:
return pwd;
}
return null;
}
public int getPropertyCount() {
return 2;
}
public void setProperty(int i, Object o) {
switch (i) {
case 0:
login = o.toString();
break;
case 1:
pwd = o.toString();
break;
}
}
public void getPropertyInfo(int i, Hashtable hashtable, PropertyInfo propertyInfo) {
switch (i) {
case 0:
propertyInfo.type = PropertyInfo.STRING_CLASS;
propertyInfo.name = "login";
break;
case 1:
propertyInfo.type = PropertyInfo.STRING_CLASS;
propertyInfo.name = "pwd";
break;
}
}
}
And then add this property like the following:
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("language", "FR");
PropertyInfo info = new PropertyInfo();
info.setName("identification"); // you have to make sure here that the parameter name matches the one in the WSDL, which you havent posted
info.setType(identification.class);
info.setValue(new identification("username", "password"));
request.addProperty(info);

How to properly format a SOAP message envelope using a custom SOAPHandler

I have a class that implements the SOAPHandler interface. The handleMessage is defined as:
public boolean handleMessage(SOAPMessageContext context) {
SOAPMessage msg = context.getMessage();
SOAPPart part = msg.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
// add namespaces
SOAPElement envelope.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
envelope.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-
// add the header with additional elements
Name qname = envelope.createName("Security", "sse", "http://example.com/security.xsd");
element = envelope.addHeader().addChildElement(qname);
qname = envelope.createName("mustUnderstand");
element.addAttribute(qname, "1");
qname = envelope.createName("UsernameToken", "sse", "http://example.com/user.xsd");
element = envelope.getHeader().addHeaderElement(qname);
element.addTextNode("user1");
qname = envelope.createName("Password");
element = envelope.getHeader().addHeaderElement(qname);
element.addTextNode("1234");
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
This generates the following message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<S:Header>
<sse:Security xmlns:sse="http://example.com/security.xsd" mustUnderstand="1"/>
<sse:UsernameToken xmlns:sse="http://example.com/user.xsd">user1</sse:UsernameToken>
</S:Header>
<S:Body>
....The rest of the transaction
</S:Body>
</S:Envelope>
The problem is I need to generate a message with the following format:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<sse:Security soapenv:mustUnderstand="1" xmlns:sse="http://example.com/security.xsd">
<sse:UsernameToken wsu:Id="UsernameToken-9993341" xmlns:wsu="http://example.com/user.xsd">
<sse:Username>user1</sse:Username>
<sse:Password Type="http://example.com/password#PasswordText">1234</sse:Password>
</sse:UsernameToken>
</sse:Security>
</soapenv:Header>
<soapenv:Body>
....The rest of the transaction
</soapenv:Body>
</soapenv:Envelope>
The "mustUnderstand" attribute doesn't have the soapenv prefix, the sse:Security tag is closed right away instead of having the other tags as children, and the UserName isn't properly formatted as
<sse:Username>user1</sse:Username>
. How can I format the message properly using the SOAPElement methods? The biggest thing I need to know is how to properly next the tags inside of the Security tag and how to have the username/password tags properly formatted.
I've tried different combinations of the addHeaderElement and addChildElement methods, but I can't get it formatted properly and the javadocs don't give enough detail about what they will generate.
This is taken from my working handler. Hope it works for you.
public static final String WSSE_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
public static final String PASSWORD_TEXT_TYPE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
public static final String WSSE_SECURITY_LNAME = "Security";
public static final String WSSE_NS_PREFIX = "wsse";
private String username;
private String password;
private boolean mustUnderstand = false;
public boolean handleMessage(SOAPMessageContext messageContext) {
Object bOutbound = messageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (bOutbound == Boolean.TRUE) {
try {
if (username != null && username.length() != 0) {
addSecurityHeader(messageContext);
LOG.debug("Added security header");
} else {
LOG.debug("No username configured thus not adding a security header");
}
} catch (Exception e) {
LOG.error("Exception in handleMessage", e);
return false;
}
}
return true;
}
private void addSecurityHeader(SOAPMessageContext messageContext) throws SOAPException {
SOAPFactory sf = SOAPFactory.newInstance();
SOAPHeader header = messageContext.getMessage().getSOAPPart().getEnvelope().getHeader();
if (header == null) {
header = messageContext.getMessage().getSOAPPart().getEnvelope().addHeader();
}
Name securityName = sf.createName(WSSE_SECURITY_LNAME, WSSE_NS_PREFIX, WSSE_NS);
SOAPHeaderElement securityElem = header.addHeaderElement(securityName);
securityElem.setMustUnderstand(mustUnderstand);
Name usernameTokenName = sf.createName("UsernameToken", WSSE_NS_PREFIX, WSSE_NS);
SOAPElement usernameTokenMsgElem = sf.createElement(usernameTokenName);
Name usernameName = sf.createName("Username", WSSE_NS_PREFIX, WSSE_NS);
SOAPElement usernameMsgElem = sf.createElement(usernameName);
usernameMsgElem.addTextNode(username);
usernameTokenMsgElem.addChildElement(usernameMsgElem);
Name passwordName = sf.createName("Type", WSSE_NS_PREFIX, WSSE_NS);
SOAPElement passwordMsgElem = sf.createElement("Password", WSSE_NS_PREFIX, WSSE_NS);
passwordMsgElem.addAttribute(passwordName, PASSWORD_TEXT_TYPE);
passwordMsgElem.addTextNode(password);
usernameTokenMsgElem.addChildElement(passwordMsgElem);
securityElem.addChildElement(usernameTokenMsgElem);
}
Just posting my solution if someone is still wondering --
Name name = soapenv.createName("Security", "sse", "URL");
SOAPHeaderElement security = soapenv.getHeader().addHeaderElement(name);
security.setMustUnderstand(true);
SOAPElement usernameToken = security.addChildElement("UsernameToken", "sse");
SOAPElement username = usernameToken.addChildElement("Username", "sse");
username.addTextNode("TestUser");
SOAPElement password = usernameToken.addChildElement("Password", "sse");
password.addTextNode("TestPassword");
There's enough problems in this code that I'm thinking it's a troll, but heres a start:
The line :
element = envelope.addHeader().addChildElement(qname);
should read:
SOAPHeaderElement secHdrElement = envelope.addHeader().addHeaderElement(qname);
next, instead of:
qname = envelope.createName("mustUnderstand");
element.addAttribute(qname, "1");
probably:
secHdrElement.setMustUnderstand(true);
and
qname = envelope.createName("UsernameToken", "sse", "http://example.com/user.xsd");
element = envelope.getHeader().addHeaderElement(qname);
element.addTextNode("user1");
should be something like:
qname = envelope.createName("UsernameToken", "sse", "http://example.com/user.xsd");
element = secHdrElement.addHeaderElement(
envelope.createName("UsernameToken", "sse", "http://example.com/user.xsd"));
and so on...

Categories

Resources