I have to change Mime header for SOAP request, especially the 'content type' header, my code is already working for most of headers, but for some reason I get back the old value for the content type header:
Here is the simplified code with comments:
public static synchronized SOAPMessage sendSoapRequest(String endpointUrl, SOAPMessage request) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
SOAPMessage response = null;
HashMap<String,String> headers = ConfigManager.GetInstance().GetHeaders();
int number_of_retries = ConfigManager.GetInstance().GetNumberOfAttempts();
if (number_of_retries==0)
{
number_of_retries = 10;
}
for (int i = 0; i <=number_of_retries ; i++) {
try {
// Send HTTP SOAP request and get response
SOAPConnection soapConnection
= SOAPConnectionFactory.newInstance().createConnection();
***// here I have mime headers with "Content-Type: text/xml; charset=utf-8"***
if (headers != null)
{
Iterator it = headers.entrySet().iterator();
while (it.hasNext()) {
HashMap.Entry pair = (HashMap.Entry)it.next();
// request.getMimeHeaders().addHeader(pair.getKey().toString(), pair.getValue().toString());
request.getMimeHeaders().setHeader(pair.getKey().toString(), pair.getValue().toString()); ***// I set here: "Content-Type => application/soap+xml"***
}
request.saveChanges(); ***// I got back mime headers with "Content-Type: text/xml;*** charset=utf-8"
}
response = soapConnection.call(request, endpointUrl); ***// If I don't use request.saveChanges(), I also get back mime headers with "Content-Type: text/xml; charset=utf-8"***
// Close connection
soapConnection.close();
return response;
}
}
return null;
}
I don't understand why Content-Type is set back to 'text/xml; charset=utf-8' and how to fix it.
From the code you have and the comment, it seems to me that you are using SAAJ to make a SOAP call to a SOAP 1.2 endpoint and your attempt to set the correct content type is conflicting with whatever SAAJ is doing behind the scenes.
SAAJ allows you to work with an object tree while handling the SOAP details for you. By default it knows to work with SOAP 1.1. The main thing to note here is that for SOAP 1.1. the content type is text/xml and the SOAP XML namespace is http://schemas.xmlsoap.org/soap/envelope/.
You want to send application/soap+xml which is the content type for SOAP 1.2. The SOAP XML namespace will in this case be http://www.w3.org/2003/05/soap-envelope.
So even if you manage to set the desired content type, your XML message created by SAAJ will still be wrong because it will have the namespace of SOAP 1.1. because that's the default.
If you want to call a SOAP 1.2. endpoint you need to tell SAAJ that. Your SAAJ object tree is probably built by using a MessageFactory which has a default instantiation of SOAP 1.1. with:
MessageFactory mf = MessageFactory.newInstance();
If you need SOAP 1.2. you need to tell SAAJ that with this instead:
MessageFactory mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
If you do that you will still have the issue with the content type, but this time it will be SAAJ setting application/soap+xml (which is what you want) and also set the proper XML SOAP namespaces within the message.
Related
I want to read Soap response - how can i convert soap response to pojos(Class objects)
Also i want to send soap post request - how can i convert class objects to soap request.
I need both ways, can you please help here
If you want to marshal and unmarshal a SOAP message to POJOs, you can use xjc to generate JAXB bindings from the schema https://www.w3.org/2003/05/soap-envelope/.
However SOAP is not usually unmarshaled using JAXB, but through a specialized framework: SOAP with Attachments API for Java. This framework can also unmarshal SOAPs with attachments: it's a multipart/related MIME document with an XML SOAP message and a series of attachments.
To deserialize using SAAJ you use a MessageFactory and provide the MIME headers of the message and an input stream:
final InputStream is = ...;
final MimeHeaders mimeHeaders = ...;
final SOAPMessage soap = MessageFactory.newInstance().createMessage(mimeHeaders, is);
If you have SOAPMessage, you can access the parts you are interested in through getSOAPPart, getSOAPBody, getSOAPHeader which return you DOM interfaces, which you can transform to POJOs using JAXB.
To serialize you just have to create a new SOAPMessage, fill it and call the writeTo method:
final SOAPMessage soap = MessageFactory.newInstance().createMessage();
... // fill the message
final OutputStream os = ...;
soap.writeTo(os);
I'm currently working on a custom SOAP call to a specific domain beyond my control. I know the SOAP call fails but I cannot seem to grab the returned (wrong)value.
Right now I'm using the code below:
Document document = convertStringToDocument(this.MeldingString);
// System.out.println(document);
SOAPConnectionFactory myFct = SOAPConnectionFactory.newInstance();
MessageFactory myMsgFct = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
SOAPMessage message = myMsgFct.createMessage();
SOAPConnection myCon = myFct.createConnection();
// Adding parts
SOAPPart mySPart = message.getSOAPPart();
SOAPEnvelope myEnvp = mySPart.getEnvelope();
// Escape the password for usage in header
String escpwd = StringEscapeUtils.escapeJava(this.Password);
// Header
MimeHeaders headers = message.getMimeHeaders();
headers.setHeader("Content-Type", "application/soap+xml;charset=UTF-8");
headers.setHeader("Authorization", this.Username + ":" + escpwd);
// Body
SOAPBody body = myEnvp.getBody();
body.addDocument(document);
// Sending
Core.getLogger("GetResultSOAPmsg").trace("Started");
URL endPt = new URL(
"URL-TO-MY-SERVICE");
System.setProperty("java.net.useSystemProxies", "true");
try {
SOAPMessage reply = myCon.call(message, endPt); "UTF-8");
}
catch(Exception e)
{
e.printStackTrace();
}
This yields the following error which is very common al throughout SO:
SEVERE: SAAJ0537: Invalid Content-Type. Could be an error message instead of a SOAP message
com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Invalid Content-Type:text/html. Is this an error message instead of a SOAP response?
Now I have read most of these topics already and they explain how this problem is usually solved (namespaces, escaping URL, etc.) but I cannot seem to figure out what is wrong in my case. This is a private service and the other side is unfortunately unable to assist me in this case. The error could be anything from wrong certificates to misspelling the URL.
Therefore I would like to actually SEE onscreen what the actual reply is that was received when making the call. This is going to help me (assuming it's something like a 503, 404 or other page). Regardless of what I do and where I set my breakpoints, there is no information on Reply. It makes somewhat sense since it was unable to create said object but the entire message seems to be discarded.
In what way will I be able to see what the actual reply was to my call before it is discarded?
I think there's a problem with your header
headers.setHeader("Content-Type", "application/soap+xml;charset=UTF-8");
try something like
headers.setHeader("Content-Type", "application/xml;charset=UTF-8");
or
headers.setHeader("Content-Type", "application/json;charset=UTF-8");
depending on the content type that the content type accepted by the service
Scenario: I have to call a WSDL based webService and parse its response.
Problem: I am having problem to update the SOAPMessage SOAP Header & SOAP Body OR SOAP Enevelope, which I can create using the classes generated from the WSimport tool.
I have generated java classes for the provided wsdl using WSimport tool. There were many classess generated and among them I found one class which has objects to create SOAP Header, Body and other details as shown in the picture. My problem is that when I generate my soapbody and header from the provided class, I am not able to add it to the SOAP Message which i want to post to the webservice in order to get the response. Is there any method that i need to create to generate SOAP message ?
{
Body mySoapBody = new Body();
Header mySoapHeader = new Header();
Envelope mySoapEnevelope = new Envelope();
//Generate SOAP Body
mySoapBody.getAny().add(objofIdentifyCustomerClass);
//Generate SOAP Header
mySoapHeader.getAny().add(emfHeader);
//Generate SOAP Enevelope
mySoapEnevelope.setBody(mySoapBody);
mySoapEnevelope.setHeader(mySoapHeader);
//This is an Issue, Cant add generated Header to the SOAP Message
SOAPMessage soapMsg= MessageFactory.newInstance().createMessage( );
//MessageFactory factory = MessageFactory.newInstance();
//SOAPMessage message = factory.createMessage();
soapMsg.getSOAPBody().addDocument(getRE02XMLBodyMsg(ciscoRE02Request));
// How to add/update SOAP Header in the SOAPMessage ????
}
I guess I was not understanding the process correctly. I read blogs of this location which helped me resolve the problem and made the code efficient:
http://www.javadb.com/using-a-message-handler-to-alter-the-soap-header-in-a-web-service-client/
If you have problem writing to a WebService I highly encourage you to read this article.
Regards,
Nevin
How do I remove a
<soapenv:Header xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
node from a response message using Java (or web service configuration)?
I don't want to send back user name and password that is displayed in the header. Do I create a class to extend AbstractSoapInterceptor?
I am using `cxf bus to configure my web service.
I donĀ“t know how are you receiving the SOAP message, but If you can use (or instantiate) a javax.xml.soap.SOAPMessage you remove the header element or attribute that is bothering you:
// Removes the attribute "key" from the message header
message.getSOAPHeader().removeAttribute("key");
Anyway, reusing the same message sounds weird to me, perhaps you should consider creating a new fresh response message:
This statement create a brand new message:
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage message = mf.createMessage(headers, in)
This one, from Mime headers and an inputstream:
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage message = mf.createMessage(headers, in)
How can I get the HTTP status from the result of the SOAPConnection.call()?
Taken from W3C note on SOAP (Section 6.2)
SOAP HTTP follows the semantics of the HTTP Status codes for
communicating status information in HTTP. For example, a 2xx status
code indicates that the client's request including the SOAP component
was successfully received, understood, and accepted etc.
In case of a SOAP error while processing the request, the SOAP HTTP
server MUST issue an HTTP 500 "Internal Server Error" response and
include a SOAP message in the response containing a SOAP Fault element
(see section 4.4) indicating the SOAP processing error.
And from documentation on SOAPFault in the API
An element in the SOAPBody object that contains error and/or status
information. This information may relate to errors in the SOAPMessage
object or to problems that are not related to the content in the
message itself.
So, a possible answer could be
SoapMessage soapMessage = null;
soapMessage = MySOAPConnection.call(...);
soapMessage.getSOAPPart().getEnvelope().getBody().getFault().getFaultCode();
Some references which helped me create this answer are:
http://forums.devshed.com/java-help-9/java-httpstatus-code-59166.html
Apache Axis2 SAAP SoapConnectionImpl
The simple answer is you can't. Burrowing into the HttpSOAPConnection code, a local instance of an HttpURLConnection object is used to do the actual communication with the target service. This does get the httpResponse code but it more or less completely hides it from the caller. All you conclude is that if you don't get an exception but the returned SOAPMessage contains a SOAPFault, then the return code was HttpURLConnection.HTTP_INTERNAL_ERROR (i.e. 500). No exception and no SOAPFault means the return code was 200 to 206, all of which are "SUCCESS" - unfortunately the status entry from the HTTP headers in the HttpURLConnection object is explicitly not copied to the MIMEHeaders in the returned SOAPMessage ...
// Header field 0 is the status line so we skip it.
Anything else will raise an exception and the code will start after the open bracket in the message field of the exception and is probably three digits, it's hard to be precise because someone forgot the close bracket or any other separator before the message...
throw new SOAPExceptionImpl(
"Bad response: ("
+ responseCode
+ httpConnection.getResponseMessage());
For example:
com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Bad response: (502internal error - server connection terminated
It's horrible relying on the formatting of a text message in an exception, but the response code isn't exposed anywhere else.
You can get access to the HTTP headers through the MessageContext interface.
http://docs.oracle.com/javaee/5/api/javax/xml/ws/handler/MessageContext.html
The most straight forward way is probably to implement a SOAPHandler which will give you access to the MessageContext:
http://docs.oracle.com/cd/E15051_01/wls/docs103/webserv_adv/handlers.html#wp222394
However, SOAP applications are generally not supposed to build the interaction on the HTTP status codes as those are transport specific.
Another alternative (java 8) :
public class HttpResponseHandler implements SOAPHandler<SOAPMessageContext> {
private Logger log = Logger.create(HttpResponseHandler.class);
#Override
public boolean handleMessage(SOAPMessageContext context) {
boolean outboundProperty = (boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); // Response
if(!outboundProperty) {
int status = (int)context.get(MessageContext.HTTP_RESPONSE_CODE);
log.debug("HTTP status code = " + status);
}
return true;
}
}
// Usage : building your service
List<Handler> soapHandlers = new ArrayList();
soapHandlers.add(new HttpResponseHandler());
URL wsdlDocumentLocation = this.getClass().getResource("some_url");
Service service = Service.create(wsdlDocumentLocation, new QName("namespace", "servicename"));
service.setHandlerResolver(new HandlerResolver() {
public List<Handler> getHandlerChain(PortInfo portInfo) {
return soapHandlers;
}
});
BindingProvider provider = (BindingProvider)service.getPort(new QName("namespace", "portname"), serviceInterface);
provider.getRequestContext().put("javax.xml.ws.service.endpoint.address", this.endpointAddress);
provider.getRequestContext().put("com.sun.xml.ws.connect.timeout", connectTimeout);
provider.getRequestContext().put("com.sun.xml.ws.request.timeout", requestTimeout);
return provider;
I do have similar requirement stated as this question, business analyst want to log every http response code related to every inbound and outbound soap calls.. My answer is valid for Apache CXF 2.7.5.
You can access http status code via MessageContext interface by the below code fragment in an implementation of javax.xml.ws.handler.soap.SoapHandler interface.
int status = (( javax.servlet.http.HttpServletResponse)messageContext.get("HTTP.RESPONSE")).getStatus();