Spring WS endpoint add multiple child elements to soap body - soap

I'm creating a soap webservice with Spring WS. I need to add multiple child elements to my soap body. At the moment I only get this working with an additional wrapper element.
Example of a response I should return:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope>
<soap:Body>
<Response>
<ResponseResult href="#id1"/>
</Response>
<Result id="id1">
...
</Result>
</soap:Body>
</soap:Envelope>
But I only get this working:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope>
<soap:Body>
<Wrapper>
<Response>
<ResponseResult href="#id1"/>
</Response>
<Result id="id1">
...
</Result>
</Wrapper>
</soap:Body>
</soap:Envelope>
Has somebody an idea how to solve this? Do you need additional information?

I finally solved it. I basically did the same as in this post: https://stackoverflow.com/a/15741795/6931969 but I'll provide some more informations.
I extended the EndpointInterceptorAdapter and changed the method handleResponse(MessageContext messageContext, Object endpoint).
import org.springframework.stereotype.Component;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.endpoint.interceptor.EndpointInterceptorAdapter;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import java.util.LinkedList;
import java.util.List;
#Component
public class CustomEndpointInterceptorAdapter extends EndpointInterceptorAdapter {
/**
* Removes wrapper element.
*/
#Override
public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
System.out.println("--------------------- Interceptor ---------------------");
SOAPMessage soapMsg = ((SaajSoapMessage)messageContext.getResponse()).getSaajMessage();
SOAPBody soapBody = soapMsg.getSOAPBody();
// Get all child nodes of wrapper
Node wrapper = soapBody.getFirstChild();
List<Node> children = getChildren(wrapper);
soapBody.removeContents();
// Add children to SOAPBody
children.forEach(node -> addNode(soapBody, node));
soapMsg.saveChanges();
return true;
}
private List<Node> getChildren(Node wrapper) {
List<Node> result = new LinkedList<>();
NodeList children = wrapper.getChildNodes();
for(int i = 0; i < children.getLength(); i++) {
result.add(children.item(i));
}
return result;
}
private void addNode(SOAPBody soapBody, Node node) {
try {
soapBody.addChildElement((SOAPElement)node);
} catch (SOAPException e) {
System.out.println("Doesn't work for Node: " + node.getNodeName());
e.printStackTrace();
}
}
}

Related

javafx8 FXMLLoader returns null

I believe the only thing different with this is the version of SceneBuilder is 8.3 and I am using TreeTableView and TreeTableColumn with Oracle Java 8
Here is the fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TreeTableColumn?>
<?import javafx.scene.control.TreeTableView?>
<TreeTableView maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="1" minWidth="1" prefHeight="400.0" prefWidth="600.0" showRoot="false" stylesheets="#org/cornova/AudioExplorer.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.cornova.AudioExplorerController">
<columns>
<TreeTableColumn fx:id="mixerName" editable="false" maxWidth="6000.0" minWidth="100.0" prefWidth="-1.0" text="Mixers" />
<TreeTableColumn fx:id="lineNames" maxWidth="49.0" minWidth="100.0" prefWidth="-1.0" text="Lines" />
<TreeTableColumn fx:id="controlNames" minWidth="100.0" prefWidth="-1.0" text="Controls" />
<TreeTableColumn fx:id="formatsNames" minWidth="100.0" prefWidth="-1.0" text="Formats" />
</columns>
</TreeTableView>
Here is the controller class
/**
* Sample Skeleton for 'AudioExplorer.fxml' Controller Class
*/
package org.cornova;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.scene.control.TreeTableColumn;
public class AudioExplorerController {
#FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
#FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
#FXML // fx:id="mixerName"
private TreeTableColumn<?, ?> mixerName; // Value injected by FXMLLoader
#FXML // fx:id="lineNames"
private TreeTableColumn<?, ?> lineNames; // Value injected by FXMLLoader
#FXML // fx:id="controlNames"
private TreeTableColumn<?, ?> controlNames; // Value injected by FXMLLoader
#FXML // fx:id="formatsNames"
private TreeTableColumn<?, ?> formatsNames; // Value injected by FXMLLoader
#FXML // This method is called by the FXMLLoader when initialization is complete
void initialize() {
assert mixerName != null : "fx:id=\"mixerName\" was not injected: check your FXML file 'AudioExplorer.fxml'.";
assert lineNames != null : "fx:id=\"lineNames\" was not injected: check your FXML file 'AudioExplorer.fxml'.";
assert controlNames != null : "fx:id=\"controlNames\" was not injected: check your FXML file 'AudioExplorer.fxml'.";
assert formatsNames != null : "fx:id=\"formatsNames\" was not injected: check your FXML file 'AudioExplorer.fxml'.";
}
}
Here is the main class
package org.cornova;
import java.net.URL;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.Mixer.Info;
/**
*
* #author walt
*/
public class AudioExplorer extends Application {
private URL url;
private FXMLLoader explorerLoader;
private AudioExplorerController audioController;
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("Audio Explorer");
final Scene scene = new Scene(new Group(), 200, 400);
Group sceneRoot = (Group)scene.getRoot();
try {
url = getClass().getResource("/AudioExplorer.fxml");
explorerLoader = new FXMLLoader(url);
explorerLoader.setLocation(url);
audioController = explorerLoader.getController();
} catch (Exception e) {
System.out.println(e);
}
//Creating the root element
TreeItem rootNode = new TreeItem();
rootNode.setExpanded(true);
Info mixers[] = AudioSystem.getMixerInfo();
rootNode = new TreeItem<>(mixers[0]);
//Creating tree items
for (int i = 0; i < mixers.length; i++) {
rootNode.getChildren().add(new TreeItem<>(mixers[i]));
}
//Creating a column
TreeTableColumn<Info,String> mixerInfo = new TreeTableColumn<>("Mixers");
mixerInfo.setMaxWidth(1500);
//Defining cell content
mixerInfo.setCellValueFactory((CellDataFeatures<Info, String> p) ->
new ReadOnlyStringWrapper(p.getValue().getValue().getName()));
//Creating a tree table view
final TreeTableView<Info> treeTableView = new TreeTableView<>(rootNode);
treeTableView.getColumns().add(mixerInfo);
treeTableView.setMaxWidth(1500);
treeTableView.setShowRoot(false);
treeTableView.autosize();
sceneRoot.getChildren().add(treeTableView);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
This is all done under netbeans 8.2. I've done, perhaps a couple dozen of these scene builder based graphic apps perhaps 18 months ago. One thing I discovered back then was to insure the location of the css and controller source were adjusted for the name space they'd be in, which is reflected in the fxml.
The css file is empty as I only defined it in SceneBuilder at this point, which is why I did not include it (only comments).
One think I do recall from when I was starting with SceneBuilder is it's proclivity to fail silently.
Tnanks!
You actually need to load the fxml for the controller to be created based on the fxml. Also passing the location twice is not necessary:
url = getClass().getResource("/AudioExplorer.fxml");
explorerLoader = new FXMLLoader(url);
try {
TreeTableView<?> audioExplorer = explorerLoader.load();
audioController = explorerLoader.getController();
...
} catch ...
Furthermore there is little reason to keep a reference to the FXMLLoader instance in a field. Once you're done loading simply extract the information you need and "drop" the loader reference. Calling load a second time should be avoided anyways...
In addition to fabian's answer, please note that I've written some utility methods, which can reduce FXML-loading to a single statement, such as:
TreeTableView<?> audioExplorer = FXMLLoaders.load(AudioExplorer.class);
Please note, this is only possible though, if you stick to the following naming convention:
If a class "mypackage.<name>" loads a FXML file, then the FXML file
should be in the same package and be named "<name>.fxml".
The library is Open Source. Maybe you find it useful:
<dependency>
<groupId>org.drombler.commons</groupId>
<artifactId>drombler-commons-fx-core</artifactId>
<version>0.10</version>
</dependency>
You can read more about it here:
http://puces-blog.blogspot.ch/2015/03/drombler-commons-conventions-to.html

namespace prefix for generated jersey rest response xml

I'm generating rest responses (using Jersey) from jaxb models. And for some of the responses, the generated XML has namespace prefix (ns2) added to the namespace attribute although they all exists in the same namespace. But for others, it is perfectly fine.
With my analysis, I think it happens when there is a complex element (another jaxb model) is being used inside one. But all these models are declared in same namespace in package-info.java.
Here is the code.
XYZModel.class
package int.xyxp.model;
#XmlType(name="xyztype")
#XmlRootElement(name="xyz")
#XmlSeeAlso({XModel.class, YModel.class, Z.class})
#XmlAccessorType(XmlAccessType.FIELD)
public class XYZModel extends VModel {
#XmlElement(name="code")
private String code;
#XmlElementWrapper(name="refs", namespace="http://reference.com/ref")
#XmlElementRef
private List<XModel> refs = new ArrayList<XModel>(0);
//continues
package-info.java
#javax.xml.bind.annotation.XmlSchema(
namespace = "http://reference.com/ref",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package int.xyxp.model;
generated XML
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<ns2:xyz version="1.0" xmlns:ns2="http://reference.com/ref">
<ns2:code>15</ns2:code>
<ns2:refs/>
</ns2:xyz>
expected XML (without prefix, by assuming default namespace).
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<xyz version="1.0" xmlns="http://reference.com/ref">
<code>15</code>
<refs/>
</xyz>
any thoughts. Thanks.
[EDIT]
After I tried to insert my preferred namespace prefix and it doesn't work even. so probably the package-info.java is used only for namespace and not for selecting the namespace prefix.
package-info.java
#javax.xml.bind.annotation.XmlSchema(
namespace = "http://reference.com/ref",
xmlns = {
#javax.xml.bind.annotation.XmlNs(prefix = "ref", namespaceURI = "http://reference.com/ref"),
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package int.xyxp.model;
NOTE: I have overridden MessageBodyWriter to provide my own namespace ("my"). Even though I have returned empty "", it takes ns2 by default when its empty. So this answers works if you want to have your own namespace instead of default "ns2".
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper;
#Produces(value=MediaType.APPLICATION_XML)
public class WSNamespaceWriter implements MessageBodyWriter<Object>{
#Context
protected Providers providers;
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
System.out.println("Calling MessageWriter writetable--> " + type.getName());
return true;
}
public void writeTo(Object object, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException,
WebApplicationException {
try {
System.out.println("Calling MessageWriter-->");
ContextResolver<JAXBContext> resolver
= providers.getContextResolver(JAXBContext.class, mediaType);
JAXBContext jaxbContext;
if(null == resolver || null == (jaxbContext = resolver.getContext(type))) {
jaxbContext = JAXBContext.newInstance(type);
}
Marshaller m = jaxbContext.createMarshaller();
NamespacePrefixMapper mapper = new NamespacePrefixMapper() {
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
System.out.println ("Called NAMESPACE----------" + namespaceUri);
if ("http://www.example.com".equals(namespaceUri)
|| ("").equals(namespaceUri)) {
System.out.println ("Called NAMESPACE return --------");
return "my"; // my own namespace
}
System.out.println ("Called NAMESPACE return ns--------");
return "";
}
};
m.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", mapper);
m.marshal(object, entityStream);
} catch(JAXBException jaxbException) {
throw new WebApplicationException(jaxbException);
}
}
public long getSize(Object t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
}

Multiple markers at this line - String cannot be resolved to a variable - Syntax error on token "food", delete

I am having this errors:
1- Multiple markers at this line - String cannot be resolved to a variable - Syntax error on token "food", delete
2- Multiple markers at this line
- Syntax error on token "detail", delete
this token
- String cannot be resolved to a variable
I marked these lines with ** in NewPortlet.java
. Below are my two related files
NewPortlet.java
package com.test;
import java.io.IOException;
import java.util.Date;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import com.liferay.counter.service.CounterLocalServiceUtil;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.servlet.SessionMessages;
import com.liferay.portal.kernel.util.Constants;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.model.User;
import com.liferay.portal.service.UserLocalServiceUtil;
import com.liferay.util.bridges.mvc.MVCPortlet;
public class NewPortlet extends MVCPortlet {
public static String VIEW_JSP ="/html/new/view.jsp";
#Override
public void doView(RenderRequest renderRequest, RenderResponse renderResponse ) throws IOException, PortletException {
//renderRequest
super.doView(renderRequest, renderResponse);
}
public void processAction(ActionRequest actionRequest, ActionResponse actionResponse) throws IOException, PortletException {
String cmd = ParamUtil.getString(actionRequest, Constants.CMD);
try {
if (cmd.equals(Constants.ADD)){
String food = ParamUtil.getString(actionRequest, "food");
String detail = ParamUtil.getString(actionRequest, "detail");
long userId = CounterLocalServiceUtil.increment();
User user_= UserLocalServiceUtil.createUser(userId);
** user_.setFood(food);
** user_.setDetail(detail);
user_.setCreateDate(new Date());
UserLocalServiceUtil.updateUser(user_ , false);
SessionMessages.add(actionRequest,"food-added-succesfuly");
}
}catch (SystemException e) {
//TODO Auto-generated Catch block
e.printStackTrace();
}
super.processAction(actionRequest, actionResponse);
}
}
}
and my service builder
<entity name="Food" local-service="true" remote-service="true">
<!-- PK fields -->
<column name="foodId" type="long" primary="true" />
<!-- Group instance -->
<column name="food" type="string" />
<column name="detail" type="string" />
<column name="createDate" type="new Date()" />
...
You are trying to add Food entity but you have used user entity instead.
User entity does not have any attribute/column as food or detail.
To add Food entity you have to use code as below.
long userId = CounterLocalServiceUtil.increment(Food.class);
Food foodEntry_=FoodLocalServiceUtil.createFood(userId);
foodEntry_.setFood(food);
foodEntry_.setDetail(detail);
foodEntry_ .setCreateDate(new Date());
FoodLocalServiceUtil.updateUser(foodEntry_ , false);
SessionMessages.add(actionRequest,"food-added-succesfuly");

How to configure Jsonp and CXF with Interceptors

I am developing a restful WS and I want to give the option to the users to take data back from my WS in the form of XML or Json and also to be able to choose a callback function if they want Jsonp. This is what I have until now and the part with the Interceptors is based on CXF - JAX-RS : Data Bindings.
My Rest Service:
#GET
#Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
#Produces({ "application/x-javascript", MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response getServers(#Context ServletContext context,
#Context Request request,
#QueryParam("format") String format,
#QueryParam("callback") String callback) {
some code where server object is created....
if(format.equals("json"){
if(callback!= null){
response = Response.status(Status.OK).type("application/x-javascript")
.entity(server).build();
}else{
response = Response.status(Status.OK).type("application/json")
.entity(server).build();
}
} else {
response = Response.status(Status.OK).type("application/xml")
.entity(server).build();
}
return response;
}
My Server object:
#XmlRootElement (name="Server")
public class Server implements Serializable {
private String name=null;
private String hardware = null;
public Server(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHardware() {
return hardware;
}
public void setHardware(String hardware) {
this.hardware = hardware;
}
}
My beans.xml in the WEB-INF:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<context:property-placeholder/>
<context:annotation-config/>
<bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer"/>
<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer"/>
<jaxrs:server id="services" address="/">
<jaxrs:serviceBeans>
<bean class="com.ba.serversws_cxf.resources.MyResource" />
</jaxrs:serviceBeans>
<jaxrs:inInterceptors>
<bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpInInterceptor">
<property name="callbackParam" value="callback"/>
</bean>
</jaxrs:inInterceptors>
<jaxrs:outInterceptors>
<bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpPreStreamInterceptor">
<property name="mediaType" value="application/x+javascript"/>
</bean>
<bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpPostStreamInterceptor"/>
</jaxrs:outInterceptors>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>
</jaxrs:providers>
</jaxrs:server>
</beans>
The error that I get when I set the query parameter "callback" is:
org.apache.cxf.jaxrs.interceptor.JAXRSOutInterceptor writeResponseErrorMessage
WARNING: No message body writer has been found for response class Server.
It works fine for other other two cases.
I have searched to find a solution but still nothing.
Any ideas?
Thanks
Here is my class that #Overrides the wrtieTo() method of the org.apache.cxf.jaxrs.provider.json.JSONProvider;
First of all in my beans.xml file I have used the <jaxrs:extensionMappings> and I have declared a jsonp extention.
Here is the code
<jaxrs:server id="services" address="/">
<jaxrs:extensionMappings>
<entry key="xml" value="application/xml" />
<entry key="json" value="application/json" />
<entry key="jsonp" value="application/javascript"/>
</jaxrs:extensionMappings>
</jaxrs:server>
And below is my code of the writeTo() method that I have #Override
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import org.apache.cxf.jaxrs.provider.json.JSONProvider;
import org.apache.cxf.jaxrs.utils.HttpUtils;
#SuppressWarnings("rawtypes")
#Produces("application/javascript")
public class JsonpProvider extends JSONProvider {
#SuppressWarnings("unchecked")
#Override
public void writeTo(Object obj, Class cls, Type genericType,
Annotation[] anns, MediaType m, MultivaluedMap headers,
OutputStream os) throws IOException {
String requestURI = getContext().getHttpServletRequest()
.getRequestURI();
if (requestURI.contains(".jsonp")) {
String prefix = getContext().getHttpServletRequest().getParameter(
"_jsonp");
if (prefix != null && !prefix.isEmpty()) {
os.write(prefix.getBytes(HttpUtils.getSetEncoding(m, headers,
"UTF-8")));
} else {
os.write("callback".getBytes(HttpUtils.getSetEncoding(m,
headers, "UTF-8")));
}
os.write('(');
super.writeTo(obj, cls, genericType, anns, m, headers, os);
os.write(')');
} else {
super.writeTo(obj, cls, genericType, anns, m, headers, os);
}
}
}
So what I am doing in the code above is I am checking if the .jsonp extension has been given to the URL. If yes then I know that I have to return the jsopn with a callback function. The last thing is to set the name of the callback function. If the user has set the _jsonp query parameter to the URL then the value of this parameter will be the name of the callback function. If the _jsonp pquery parameter is null then I put a defult name callback.
And off course in the beans.xml file as a json provider you put the JsonpProvider above:
<jaxrs:providers>
<bean class="org.apache.cxf.jaxrs.ext.search.SearchContextProvider"/>
<bean class="com.ba.serversws_cxf.utils.JsonpProvider">
<property name="ignoreNamespaces" value="true" />
<property name="dropRootElement" value="false" />
<property name="supportUnwrapped" value="true" />
</bean>
</jaxrs:providers>
Hope that helps #bhuvan !

jax-ws change soap response [duplicate]

How can I modify the namespace of the response like this:
old response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:GetAmountResponse xmlns:ns2="http://ws.dsi.otn.com/dab">
<etat>0</etat>
<montant>500.0</montant>
</ns2:GetAmountResponse>
</soap:Body>
</soap:Envelope>
new response wanted :
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetAmountResponse xmlns="http://ws.dsi.otn.com/dab">
<etat>0</etat>
<montant>500.0</montant>
</GetAmountResponse>
</soap:Body>
</soap:Envelope>
I want to remove the ns2 namespce prefix.
In the first case, the GetAmountResponse is in namespace http://ws.dsi.otn.com/dab while etat and montant are in a default (empty) namespace.
In the new message you want, GetAmountResponse, etat and montant are all in namespace http://ws.dsi.otn.com/dab.
The namespaces can be controlled from the namespaces of your classes. Use the same namespace in all and you will have them in the same namespace, leave classes with defaults and they default to empty namespace.
For example, if you were to have something like this in your web service class:
#WebMethod
public
#WebResult(name = "getAmountResponse", targetNamespace = "http://ws.dsi.otn.com/dab")
AmountResponse getAmount(
#WebParam(name = "getAmountRequest", targetNamespace = "http://ws.dsi.otn.com/dab") AmountRequest request) {
AmountResponse response = new AmountResponse();
response.setEtat(0);
response.setMontant(500.0);
return response;
}
with a response class like this:
#XmlRootElement
public class AmountResponse {
private int etat;
private double montant;
// getter and setters omitted
}
you will end up with the first type of soap message.
But if you change the response class to look like this instead:
#XmlRootElement(namespace = "http://ws.dsi.otn.com/dab")
#XmlAccessorType(XmlAccessType.NONE)
public class AmountResponse {
#XmlElement(namespace = "http://ws.dsi.otn.com/dab")
private int etat;
#XmlElement(namespace = "http://ws.dsi.otn.com/dab")
private double montant;
// getters and setter omitted
}
you will bring all tags in the same namespace and you get something equivalent to the new type of message you want. I said equivalent because I don't think you will get exactly this:
<GetAmountResponse xmlns="http://ws.dsi.otn.com/dab">
<etat>0</etat>
<montant>500.0</montant>
</GetAmountResponse>
It's more likely to get something like this instead:
<ns2:getAmountResponse xmlns:ns2="http://ws.dsi.otn.com/dab">
<ns2:etat>0</ns2:etat>
<ns2:montant>500.0</ns2:montant>
</ns2:getAmountResponse>
It's the same "XML meaning" for both messages although they don't look the same.
If you absolutely want it to look like that, I think you will have to go "low level" and use something like a SOAP handler to intercept the response and modify it. But be aware that it won't be a trivial task to change the message before it goes on the wire.
logical handler are enough to transform to the message as expected :
package com.ouertani.slim;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.LogicalMessage;
import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.LogicalMessageContext;
import javax.xml.ws.handler.MessageContext;
/**
*
* #author ouertani
*/
public class MyLogicalHandler implements LogicalHandler<LogicalMessageContext> {
#Override
public boolean handleMessage(LogicalMessageContext messageContext) {
/// extract state and amount
int state = 0;
double amount = 200.0;
transform(messageContext, state, amount);
return false;
}
public boolean handleFault(LogicalMessageContext messageContext) {
return true;
}
public void close(MessageContext context) {
}
private void transform( LogicalMessageContext messageContext, int etat, double montant){
LogicalMessage msg = messageContext.getMessage();
String htom = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"+
"<soap:Body>"+
"<GetAmountResponse xmlns=\"http://ws.dsi.otn.com/dab\">"+
"<etat>"+etat+"</etat>"+
"<montant>"+montant+"</montant>"+
"</GetAmountResponse>"+
"</soap:Body>"+
"</soap:Envelope>";
InputStream is = new ByteArrayInputStream(htom.getBytes());
Source ht = new StreamSource(is);
msg.setPayload(ht);
}
}
This is a very old question, still it is yet to be effectively answered. This week I faced a very similar problem. My application is invoking a Soap web-service provided by a legacy system whose XML is response syntactically wrong with some empty characters (line break, or tabs or white spaces) before XML declaration. In my scenario I could not change the legacy system to fix its response so changing the response before parsing was the only alternative I was left with.
Here is my solution:
I have added the following maven dependencies to my application:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>2.3.0</version>
</dependency>
Then I have registered a Java SPI custom implementation of “com.oracle.webservices.impl.internalspi.encoding.StreamDecoder”. This class is invoked immediately before the XML parse with the corresponding response InputStream, so at this point you can read the response InputStream or wrap/proxy it and make any change to jax-ws response before parsing. In my case I just remove some invisible characters before first visible character.
My StreamDecoder SPI implementation:
package sample.streamdecoder;
import com.oracle.webservices.impl.encoding.StreamDecoderImpl;
import com.oracle.webservices.impl.internalspi.encoding.StreamDecoder;
import com.sun.xml.ws.api.SOAPVersion;
import com.sun.xml.ws.api.message.AttachmentSet;
import com.sun.xml.ws.api.message.Message;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
public class MyStreamDecoder implements StreamDecoder {
//JAX-WS default implementation
private static final StreamDecoderImpl streamDecoder = new StreamDecoderImpl();
#Override
public Message decode(InputStream inputStream, String charset, AttachmentSet attachmentSet, SOAPVersion soapVersion) throws IOException {
//Wrapping inputStream
InputStream wrapped = wrapInputStreamStrippingBlankCharactersBeforeXML(inputStream, charset);
//Delegating further processing to default StreamDecoder
return streamDecoder.decode(wrapped, charset, attachmentSet, soapVersion);
}
private InputStream wrapInputStreamStrippingBlankCharactersBeforeXML(InputStream inputStream, String charset) throws IOException {
int WHITESPACE = (int) Charset.forName(charset).encode(" ").get();
int LINE_BREAK = (int) Charset.forName(charset).encode("\n").get();
int TAB = (int) Charset.forName(charset).encode("\t").get();
return new InputStream() {
private boolean xmlBegin = true;
#Override
public int read() throws IOException {
int read = inputStream.read();
if (!xmlBegin) {
return read;
} else {
while (WHITESPACE == read
|| LINE_BREAK == read
|| TAB == read) {
read = inputStream.read();
}
xmlBegin = false;
}
return read;
}
};
}
}
In order to register it, just create a file “META-INF/services/ com.oracle.webservices.impl.internalspi.encoding.StreamDecoder” named “” and write the fully qualified name of your SPI implementation on the first line like that:
Content of file META-INF/services/ com.oracle.webservices.impl.internalspi.encoding.StreamDecoder :
sample.streamdecoder.MyStreamDecoder
Now every response will be passed to you implementation before parse.