I've been through the same trouble. Unfortunately (or not) WebServiceTemplate with implementation of SOAPMessageFactory (such as SaajSoapMessageFactory) will do everything possible to assure you are sending a well-formed XML as a request by tying you to the Transformers from Source to Result, including never let you repeat 'xmlns' in children when you already did in parent. You have a couple of elegant options to try - what doesn't mean they're the simplest ones. You can work at XML level by using javax.xml.ws.Service and Dispatch interface, which is quite easy if you don't need SSL authentication. Check these links out (first one is written in Pt-BR):
http://www.guj.com.br/t/nfe-v2-00-veja-como-consumir-o-ws/297304
https://alesaudate.wordpress.com/2010/08/09/how-to-dynamically-select-a-certificate-alias-when-invoking-web-services/
Also you can try another message factory, such as DomPoxMessageFactory. This link might be useful:
http://forum.spring.io/forum/spring-projects/web-services/128221-webservicetemplate-get-it-to-stop-adding-soap-envelope
However, if changing the structure of your project isn't an option (which was my case), I have a workaround for you. Yes, a workaround, but once the target webservice IS EXPECTING a malformed XML, I absolve myself :D
I just created abstractions of HttpComponentsMessageSender and HttpComponentsConnection classes, the second one is instantiated through the first one's method createConnection(URI uri). So I can create my WebServiceTemplate like this:
WebServiceTemplate wst = new WebServiceTemplate(new SaajSoapMessageFactory());
wst.setMessageSender(new CustomHttpComponentsMessageSender());
Sadly you'll need to reply the createConnecion method to the new abstraction just to instantiate the custom connection. As I said, it's a workaround!
@Override
public WebServiceConnection createConnection(URI uri) throws IOException {
HttpPost httpPost = new HttpPost(uri);
if (isAcceptGzipEncoding()) {
httpPost.addHeader(HttpTransportConstants.HEADER_ACCEPT_ENCODING,
HttpTransportConstants.CONTENT_ENCODING_GZIP);
}
HttpContext httpContext = createContext(uri);
return new CustomHttpComponentsConnection(getHttpClient(), httpPost, httpContext);
}
The message is effectively sent inside the method onSendAfterWrite(WebServiceMessage message) of the HttpComponentsConnection class I'm abstracting from. Surprisingly, the 'message' parameter isn't used inside the method. It's there only for inheritance rules. And the good news: It's a protected method. The downside, again, is that I need to copy almost the entire class in order to change only this method, once the fields has no public visibility, and framework will need them in response handling. So, I'll post my entire class down:
public class CustomHttpComponentsConnection extends HttpComponentsConnection {
private final HttpClient httpClient;
private final HttpPost httpPost;
private final HttpContext httpContext;
private HttpResponse httpResponse;
private ByteArrayOutputStream requestBuffer;
protected CustomHttpComponentsConnection(HttpClient httpClient, HttpPost httpPost, HttpContext httpContext) {
super(httpClient, httpPost, httpContext);
Assert.notNull(httpClient, "httpClient must not be null");
Assert.notNull(httpPost, "httpPost must not be null");
this.httpClient = httpClient;
this.httpPost = httpPost;
this.httpContext = httpContext;
}
public HttpResponse getHttpResponse() {
return httpResponse;
}
public HttpPost getHttpPost() {
return httpPost;
}
@Override
protected OutputStream getRequestOutputStream() throws IOException {
return requestBuffer;
}
@Override
protected void onSendBeforeWrite(WebServiceMessage message) throws IOException {
requestBuffer = new ByteArrayOutputStream();
}
@Override
protected void onSendAfterWrite(WebServiceMessage message) throws IOException {
OutputStream out = getRequestOutputStream();
String str = out.toString();
str = str.replaceAll("<NFe>", "<NFe xmlns=\"http://www.portalfiscal.inf.br/nfe\">");
ByteArrayOutputStream bs = new ByteArrayOutputStream();
bs.write(str.getBytes());
getHttpPost().setEntity(new ByteArrayEntity(bs.toByteArray()));
requestBuffer = null;
if (httpContext != null) {
httpResponse = httpClient.execute(httpPost, httpContext);
}
else {
httpResponse = httpClient.execute(httpPost);
}
}
@Override
protected int getResponseCode() throws IOException {
return httpResponse.getStatusLine().getStatusCode();
}
@Override
protected String getResponseMessage() throws IOException {
return httpResponse.getStatusLine().getReasonPhrase();
}
@Override
protected long getResponseContentLength() throws IOException {
HttpEntity entity = httpResponse.getEntity();
if (entity != null) {
return entity.getContentLength();
}
return 0;
}
@Override
protected InputStream getRawResponseInputStream() throws IOException {
HttpEntity entity = httpResponse.getEntity();
if (entity != null) {
return entity.getContent();
}
throw new IllegalStateException("Response has no enclosing response entity, cannot create input stream");
}
@Override
public Iterator<String> getResponseHeaderNames() throws IOException {
Header[] headers = httpResponse.getAllHeaders();
String[] names = new String[headers.length];
for (int i = 0; i < headers.length; i++) {
names[i] = headers[i].getName();
}
return Arrays.asList(names).iterator();
}
@Override
public Iterator<String> getResponseHeaders(String name) throws IOException {
Header[] headers = httpResponse.getHeaders(name);
String[] values = new String[headers.length];
for (int i = 0; i < headers.length; i++) {
values[i] = headers[i].getValue();
}
return Arrays.asList(values).iterator();
}
Again, this is the easiest way I found when changing project structure is not an option. Hope this helps.