Getting raw XML response from Java web service client - java

I am trying to get the raw XML response from a web service, instead of the usual set of POJOs.
I am using a webservice client that I generated (so I have access to the client's code) from a WSDL and some schemas. The client is generated in RAD 7.5, I think using JAX-WS. I've been looking at the client code itself, but I'm not even sure if the client code ever handles raw XML or if it passes it off to other libraries.

You can do it using
javax.xml.ws.handler.soap.SOAPHandler<javax.xml.ws.handler.soap.SOAPMessageContext>
you can simply get message using SOAPMessageContext#getMessage() and convert message to String using method
public static String getXmlMessage(SOAPMessage message) throws Exception
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
message.writeTo(os);
final String encoding = (String) message.getProperty(SOAPMessage.CHARACTER_SET_ENCODING);
if (encoding == null)
{
return new String(os.toByteArray());
}
else
{
return new String(os.toByteArray(), encoding);
}
}
Also you can read here about SOAP handler on client side
Article

It's not widely documented, but you can use the Dispatch interface to implement JAXWS clients which work directly w/ the XML. Here and here are some articles for getting started.

Related

Apache CXF: sending a byte array

We are sending a byte array to a REST API we do not control.
The REST API requires the byte array to be sent as the body.
The code is as follows:
String path = "/dokumente/angebote/{angebotsId}/unterlagen/{dokumentId}";
WebTarget target = createWebTarget().path(path).resolveTemplate("angebotsId", angebotsId).resolveTemplate("dokumentId", documentType);
try (ResponseHandler handler = new ResponseHandler(
target.request(document.getMimeType()).header("Content-Type", document.getMimeType())
.post(Entity.entity(MY_BYTE_ARRAY))) {
Response response = handler.getResponse();
...
}
This has worked well so far, we have been using JBoss and RestEasy. We have now migrated our application to OpenLiberty, which means that Apache CXF will be used as JAX-RS implementation.
And since the migration, the implementation does not work anymore. We found out that if we use a wrapper class like that:
MyWrapper myByteArrayWrapper = new MyWrapper();
myByteArrayWrapper.setData(MY_BYTE_ARRAY));
.post(Entity.entity(myByteArrayWrapper))
then Apache CXF will successfully transfer the byte array, but this does not comply to the API definition of the service we are calling.
Has anybody succeeded in getting a byte[] upload running with Apache CXF?
The problem was that OLP (in contrast to JBoss) used chunked transfer. The server that we called did not support that. Chunked transfer was now enabled on the other server, and now everything works.

UnsupportedMediaException -> how do you get the actual response?

I'm calling a remote web service and am occasionally getting the following error:-
Error caught: com.sun.xml.internal.ws.server.UnsupportedMediaException: Unsupported Content-Type: text/plain;charset=ISO-8859-1 Supported ones are: [text/xml]
Does anyone know how to get the actual message that was returned by the server? It sounds like it might be text or a web page but I'm unable to get it.
I can catch the UnsupportedMediaException but I don't know what to do to extract the actual response. Here's the code:-
val selectedDate = exchange.`in`.getHeader("selectedDate").toString()
val accountNumberMinor = exchange.`in`.getHeader("accountNumberMinor").toString()
val accountNumberMajor = exchange.`in`.getHeader("accountNumberMajor").toString()
val accountIdentifier = if (accountNumberMinor.trim() != "") accountNumberMinor else accountNumberMajor
val effectiveDate = SimpleDateFormat("yyyy-MM-dd").parse(selectedDate)
val response = webRequest.getResponse(accountIdentifier, selectedDate)
val result = response.result as FixedIncomeCurrencyForwardAccountV10Result
Thanks,
Adam
An HTML page is usually a server error yes. Probably a static service page (like 404 or 5xx). It could even be an error in your request that should be returned as a SOAPFault, but is not implemented as such by the specific server.
Sometimes the server does communicate a valid SOAP (Fault) message, but the content type header is just wrong. In that case you're better off rewriting the Content-Type from the response with a proxy server. See for references on this subject:
SOAP unsupported media exception text/plain Supported ones are: [text/xml]
So, what can you do to view the HTML content?
With JAX-WS you can enable all HTTP web service traffic to be logged to System.out with the following vm options:
-Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=TRUE
-Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=TRUE
-Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=TRUE
-Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=TRUE
-Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dumpTreshold=999999
See for references:
https://www.rgagnon.com/javadetails/java-logging-with-jax-ws.html
https://www.javatips.net/api/com.sun.xml.ws.transport.http.client.httptransportpipe
Now, this will dump all http requests and responses, but you might only be interested in the ones where you don't get soap/xml.
So, what else can you do?
You could set these options programmatically and re-send the request when you catch the UnsupportedMediaException. But in the time this takes the error might have disappeared. Note that these properties are cached, so setting them needs to go through com.sun.xml.ws.transport.http.client.HttpTransportPipe#setDump(Boolean)
If you're willing to switch to the JAX-WS runtime, you could also create your own com.sun.xml.ws.api.pipe.TransportTubeFactory since jaxws-rt can load custom instances of this factory. I have successfully created my own TransportTubeFactory that uses a custom com.sun.xml.ws.transport.http.client.HttpTransportPipe (by extending it and overriding processRequest) that reads the http response from the com.sun.xml.ws.api.pipe.Codec upon catching the UnsupportedMediaException. By wrapping the Codec you can store the input stream on the decode method call.
This runtime is nearly identical to the internal runtime, and should be fully compatible.
This may also work with the internal classes from the Java runtime, but since those are located in RT.jar it's difficult to depend on it and build your project. So i would advice switching to the external JAX-WS runtime.
What you then do with the input stream is up to you (which is the body of the HTTP response at the moment of catching the UnsupportedMediaException).
Note that you can also rewrite most* content type headers in code with this codec wrapper.
See for reference how to add your own implementation of this factory via META-INF/services here:
https://www.javadoc.io/doc/com.sun.xml.ws/jaxws-rt/latest/com.sun.xml.ws/com/sun/xml/ws/api/pipe/TransportTubeFactory.html
In short:
Create a file in META-INF/services called com.sun.xml.ws.api.pipe.TransportTubeFactory
The contents of this file should be a single line with the full class name of your custom factory, for example my.soap.transport.MyTransportTubeFactory
Note; if you're using the classes from the Java runtime instead of jaxws-rt, use com.sun.xml.internal.ws as the package for everything in this post that references com.sun.xml.ws.
*Note: newer versions of this runtime (jaxws-rt-2.3.x or jre 8+ internal) throw a different exception on text/html responses. Sadly before calling Codec.decode. So in that case you would have to copy more code into your custom HttpTransportPipe. text/plain currently still works though.
Some snippets of my code:
public class TransportTubeFactoryImpl extends TransportTubeFactory {
#Override
public Tube doCreate(ClientTubeAssemblerContext context) {
String scheme = context.getAddress().getURI().getScheme();
if (scheme != null) {
if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) {
CodecWrapper codec = new CodecWrapper(context.getCodec());
return new HttpTransportPipeImpl(codec, context.getBinding());
}
}
throw new WebServiceException("Unsupported endpoint address: " + context.getAddress());
}
}
public class CodecWrapper implements Codec {
private Codec wrapped;
public CodecWrapper(Codec wrapped) {
this.wrapped = wrapped;
}
#Override
public void decode(InputStream in, String contentType, Packet response) throws IOException {
copyInputStream(in); // todo: implement this
wrapped.decode(in, contentType, response);
}
}
public class HttpTransportPipeImpl extends HttpTransportPipe {
private CodecWrapper codec;
public HttpTransportPipeImpl(CodecWrapper codec, WSBinding binding) {
super(codec, binding);
this.codec = codec;
}
#Override
public NextAction processRequest(Packet request) {
try {
return super.processRequest(request);
} catch (UnsupportedMediaException ex) {
// todo: here you can access the stored data from the codec wrapper
}
}
}
I have also created a complete working demonstration of this principle on my github: https://github.com/s-lindenau/SoapContentTypeDemo
If you still have the option to switch to a completely different client library, you could also check Apache CXF:
How can I make jaxws parse response without checking Content-Type header

Access to http request body raw data

I am trying to implement a web application using play framework as a replacement for old http server implementation that is interfacing the old non-browser legacy http client.
That client is written in Delphi and it is posting data directly in the body of a request with some header information about it.
I thought I would get something in
request.body /* In the play controller */
but nothing is there.
See the code below:
public static void uploadPicture() {
InputStream data = request.body;
String fx = Play.getFile("").getAbsolutePath()+File.separator+"uploads"+File.separator+"test.jpg";
File f = new File(fx);
FileOutputStream moveTo = new FileOutputStream(fx);
try {
byte[] b = new byte[4096];
for (int x = 0; (data.read(b)) != -1;){
moveTo.write(b, 0, x);
}
} finally{
moveTo.close();
}
}
EDIT:
To clarify my point : I went and I created a simple Dynamic Web Project in eclipse HttpServlet and in doPost() method when I get the request.getInputStream() it contains the file that is sent from the legacy client.
Play is doing something to the body of the request!?
What are my options?
Thanks.
Irfan
Ok, it was a bug in Play 1.2.4. I installed latest version 1.2.5 and everything works out of the box.
You can access raw body of a request in request.body in the controller.

how to write a file object on server response and without saving file on server?

I am using Spring with DWR . I want to return a file object as response , however I save the file (to be sent) at server temporary location and then send its location as href for anchor tag on client side , however I wonder if there could be a way to throw the file directly to browser on response object without saving it temporarily on server.
I expected if there could be a way to send file as a response via DWR.
public ModelAndView writeFileContentInResponse(HttpServletRequest request, HttpServletResponse response) throws IOException {
FileInputStream inputStream = new FileInputStream("FileInputStreamDemo.java"); //read the file
response.setHeader("Content-Disposition","attachment; filename=test.txt");
try {
int c;
while ((c = inputStream.read()) != -1) {
response.getWriter().write(c);
}
} finally {
if (inputStream != null)
inputStream.close();
response.getWriter().close();
}
}
It has been years since I've used Spring, and I'm unfamiliar with DWR, but the essence of your question is basic to the web.
The answer is yes, you can. In effect, you need to set the HTTP header Content-Disposition: attachment, then stream down the contents. All of this will be in the response to the original request (as opposed to sending back a link).
The actual code to achieve this will depend on your circumstances, but this should get you started.
you call the method from Java Script, right? I didn't really understand how Spring is related in this flow, but as far as I know DWR allows you to produce Java Script Stubs and call the Java methods of the exposed bean directly on server right from your java script client code.
You can read the file byte-by-byte and return it from your java method as long as it really returns a byte array.
However what would you do with this byte array on client?
I just think in this specific flow you shouldn't use the DWR but rather issue an ordinar AJAX request (if DWR can wrap it somehow for convenience - great). This request shouldn't come to DWRServlet, but rather be proceeded by a regular servlet/some web-based framework, like Spring MVC :)
Once the request comes to the servlet, use
response.setHeader("Content-Disposition","attachment; filename=test.txt");
as was already stated.
Hope this helps,
Good luck!
Mark
An example which return a excel to download from client:
//Java side:
public FileTransfer getExcel(Parametros param){
byte[] result = <here get data>;
InputStream myInputStream = new ByteArrayInputStream(result);
String excelFormat = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
FileTransfer dwrExcelFile = new FileTransfer("excel.xlsx", excelFormat, myInputStream);
return dwrExcelFile;
}
//Javascript side:
function downloadExcelFile() {
dwr.engine.setTimeout(59000);
var params = <params_to_send>;
<Java_class>.getExcel(params, {callback:function(dataFromServer) {
downloadExcelCallback(dataFromServer);
}});
}
function downloadExcelCallback(data) {
dwr.engine.openInDownload(data);
}

Using a JSON web service from a Java client application

I am developing a client-side Java application that has a bit of functionality that requires getting data from some web services that transmit in JSON (some RESTful, some not). No JavaScript, no web browser, just a plain JAR file that will run locally with Swing for the GUI.
This is not a new or unique problem; surely there must be some open source libraries out there that will handle the JSON data transmission over HTTP. I've already found some that will parse JSON, but I'm having trouble finding any that will handle the HTTP communication to consume the JSON web service.
So far I've found Apache Axis2 apparently which might have at least part of the solution, but I don't see enough documentation for it to know if it will do what I need, or how to use it. Maybe part of the problem is that I don't have experience with web services so I'm not able to know a solution when I see it. I hope some of you can point me in the right direction. Examples would be helpful.
Apache HttpClient 4.0
is the best in the business and is moderately easy to learn.
If you want easier you could use HtmlUnit which imitates the behaviour of browsers so you could easily get the content (and parse it into Html, javascript and css, you could also execute javascript code on content so you could probably parse JSON files to using JSON.parse or any other equivalent functions) of any page on the web.
so for HtmlUnit here is a sample code:
WebClient wc = new WebClient(BrowserVersion.FIREFOX_3_6);
HtmlPage page = wc.getPage("http://urlhere");
page.executeJavaScript("JS code here");
but it maybe rather heavy for your requirements so a highly recommend the use of HttpClient library.
I'm sure you could find many JSON libraries for java but here is one for you json-lib
I did it using a simple Java JSON libary. Use the Google library..
URL url = new URL("http://www.siteconsortium.com/services/hello.php");
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
JSONParser parser=new JSONParser();
Object object = parser.parse(in);
JSONArray array = (JSONArray) object;
JSONObject object2 = (JSONObject)array.get(0);
System.out.println(object2.get("hello"));
If the webservice uses OAuth and an access token you can't use the above example though.
Its great to see that your web services are RESTful. RESTful web services are pretty easy to develop and to consume.Well... you do not need to take any extra care to tranmit JSON data over the network... Data whether is in JSON on in XML format are embedded into the HTTP header..Following code snippet will help you understand the idea :
httpConnection = new HTTPConnectionManager(request);
HttpURLConnection httpURLConnection = httpConnection.connect();
int responseCode = httpURLConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
in = httpURLConnection.getInputStream();
int x;
StringBuilder stringBuilder = new StringBuilder();
while ((x = in.read()) != -1) {
stringBuilder.append((char) x);
}
XMLParser xmParser = new XMLParser();
....
....
}
In this code i am receiving data in XML format from web services.After receiving the data into a StringBuilder object,i am parsing the XML. In the same way you can call your web services using this code and can receive your JSON data. you can use javaJSON APIs,available Here, to extract the data from JSON notation.
Hope code will help you...
PS: HTTPConnectionManager,XMLParser and Request(request object) classes are not any standard APIs. they are written by my own account to handle multiple web service calls. This code snippet is just to give you my idea.

Categories

Resources