Attribute “xmlns:ns2” was already specified for element …

Hi, all:

It is surely cold here in northeast right now. January does have its own teeth! ūüė¶

I am posting something I found during a SOAP Webservice invocation, which I hope can help you save some time in your development.

There was an error message when a SOAP client gets and parses a response from a SOAP Webservice and it started to complain about something like:

Attribute “xmlns:ns2” was already specified for element …..

It puzzled me at the first glance because in the actual captured SOAP response, the element the error is complaining about has no prefix “ns2” at all. After a couple days of not knowing what is wrong, I finally noticed that the element in the SOAP response has an empty seemingly benign namespace attribute:

xmlns=””

This one is actually the culprit! The error is actually indicating that the element SHOULD have a namespace based on the WSDL/XSD (in this case it was designed to have namespace of http://www.w3.org/2005/08/addressing). ¬†The empty namespace xmlns=”” triggered the error during parsing! ¬†After fixing at the server side to ensure the SOAP response has the correct namespace designated for the element as¬†http://www.w3.org/2005/08/addressing, the problem went away!

Now, the question remains why the error is complaining about the xmlns:ns2? i.e. where does the ns2 prefix come from? It is actually quite obvious when I look at the whole SOAP response. The ns2 prefix is mapped to the URI http://www.w3.org/2005/08/addressing at the root element of the SOAP response, thus, the error is referring to that prefix throughout the whole document.

Next time when we see similar error message to this, we can easily find out what namespace the element of concern should have and enforce it to be assigned correctly.

Stay warm!

-Tony

Datapower changes Content-Type in HTTP header

Hi, There:

I am having this issue with IBM WebSphere Datapower routing function, which I still haven’t solved it yet after a week or so. I suspect this is a bug in the Datapower for WS proxy. Anyway, the issue is as following.

I am trying to do some incoming URL based routing using XSLT, pretty straightforward like this core line in the stylesheet.

<dp:set-variable name=”‘var://service/routing-url'” value=”‘http://newhost:7001/newURL'&#8221; />

This routing itself actually works correctly as expected. However, when the incoming SOAP WS call is a multipart request such as in MTOM/XOP, the Datapower will strip off the Content-Type in the HTTP header (I could not prove this until I used Wireshark to capture the network packets).

The original HTTP Content-Type going into the WS proxy was:

multipart/related; boundary=”uuid:d50f031f-5eda-46cd-aee3-29d1d0e3f026″; start-info=”application/soap+xml”; type=”application/xop+xml”; start=”<rootpart*d50f031f-5eda-46cd-aee3-29d1d0e3f026@example.jaxws.sun.com>”; action=””

And after Datapower XSLT routing action, it becomes simply this:

application/xop+xml;charset=utf-8;type=”application/soap+xml”

This caused a SOAP fault on my Web Service server side with this error:

javax.xml.ws.soap.SOAPFaultException: Couldn’t create SOAP message due to exception: XML reader error: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character ‘-‘ (code 45) in prolog; expected ‘<‘ at [row,col {unknown-source}]: [3,1] at com.sun.xml.ws.fault.SOAP12Fault.getProtocolException(SOAP12Fault.java:204) at com.sun.xml.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:122) at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119) at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:89) at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:118)

 

The error simply means that the server ignores the multipart (due to the new Content-Type) and attempts to parse the whole HTTP body as a single SOAP envelope. Naturally¬†it would fail¬†because it expects the first char from the SOAP XML as ‘<‘ instead of the actual first char of boundary ‘‘.

To date, I still haven’t solved this problem yet, although I truly believe Datapower altered the HTTP content-type header. ¬†I have tried a few things such as <dp:freeze-headers/> and set http request header’s content-type to var://service/original-content-type, in vain.

In comparison, without the XSLT routing, the content-type preserves correctly and the web services with multipart runs as expected without any issues. So the Content-Type loss is indeed related to the routing-url change.

I will update this issue when I found a solution by myself or from other resources.

-TY

 

SOAP Webservice Streaming – Memory and CPU usage pattern

Hi, There:

I have recently implemented SOAP Streaming with CXF 3.14 and datahandler. The streaming works really well with huge payload in MTOM. Without detailing on the code in this post, let me show the memory/CPU usage in the application on the server side. The pattern on the client side is very similar too.

See the memory usage when streaming for 1 GB (first small oscillation) and 8 GB (second big oscillation) of payload. The heap memory is constantly using and releasing throughout the streaming process.  Meanwhile the CPU usage is heavy also due to the constant heavy IO, since streaming utilizes heavy temp file storage.

Not showing here, but for non-streaming SOAP MTOM, memory usage is pretty much a big lump without oscillation and its CPU usage is minimum due to limited processing and IO. During non-streaming, peak memory usage is much much higher than that of streaming since the whole payload is processed and contained in memory.

Cheers!

-TY

CPU_Mem_Usage

 

SSL Handshake under the hood …

Happy Friday!

We always hear about¬†SSL handshake and routinely use it, but never really want/need to drill down to see what really is going on there. This is a nice handshake sequence description.¬†During my development recently, I used CXF to call SOAP webservices via HTTPS over TLS. I ran into a few “handshake walls” which I eventually resolved. In the process, I found enabling tracer for SSL handshake is very useful. For Java, adding this to the VM params will enable it

-Djavax.net.debug=SSL,handshake

I captured the SSL communication as below. I won’t go through them in details because they are very self descriptive.

Be good and do excellent coding!

===========================================================

adding as trusted cert:
Subject: CN=…….
Issuer: CN=…….
Algorithm: RSA; Serial number: ……
Valid from Tue Nov 07 19:00:00 EST 2006 until Wed Jul 16 19:59:59 EDT 2036
…….
…….

Starting to Invoke Webservice –> https://xxx.xxx/mywebservice Fri Jun 26 09:54:27 EDT 2015
keyStore is :
keyStore type is : jks
keyStore provider is :
init keystore
init keymanager of type SunX509
trustStore is: C:\Program Files\Java\jdk1.8.0_45\jre\lib\security\cacerts
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
Subject: CN=Equifax Secure Global eBusiness CA-1, O=Equifax Secure Inc., C=US
Issuer: CN=Equifax Secure Global eBusiness CA-1, O=Equifax Secure Inc., C=US
Algorithm: RSA; Serial number: 0xc3517
Valid from Mon Jun 21 00:00:00 EDT 1999 until Mon Jun 22 00:00:00 EDT 2020

…….
…….
trigger seeding of SecureRandom
done seeding SecureRandom
trigger seeding of SecureRandom
done seeding SecureRandom
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
…….
…….

Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
main, setSoTimeout(0) called
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 for TLSv1
…….
…….

%% No cached client session
*** ClientHello, TLSv1.2
RandomCookie: GMT: 1418549396 bytes = { 224, 43, 120, 102, 139, 75, 239, 187, 229, 175, 114, 139, 145, 9, 221, 183, 138, 2, 112, 134, 123, 94, 7, 68, 36, 208, 182, 123 }
Session ID: {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, ……., TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, ……, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, ……, SHA1withDSA, MD5withRSA
***
main, WRITE: TLSv1.2 Handshake, length = 207
main, READ: TLSv1.2 Handshake, length = 81
*** ServerHello, TLSv1.2
RandomCookie: GMT: 1418549396 bytes = { 142, 100, 141, 6, 255, 138, 243, 120, 33, 45, 208, 118, 224, 24, 6, 151, 36, 161, 154, 87, 240, 174, 80, 69, 194, 85, 209, 122 }
Session ID: {46, 112, 146, 68, 191, 63, 204, 28, 124, 103, 103, 9, 165, 180, 85, 127, 114, 199, 154, 56, 103, 219, 197, 48, 77, 75, 154, 11, 54, 168, 7, 157}
Cipher Suite: SSL_RSA_WITH_RC4_128_SHA
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
%% Initialized: [Session-1, SSL_RSA_WITH_RC4_128_SHA]
** SSL_RSA_WITH_RC4_128_SHA
main, READ: TLSv1.2 Handshake, length = 2990
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: CN= …….
…….
…….
]
main, READ: TLSv1.2 Handshake, length = 4
*** ServerHelloDone
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1.2
main, WRITE: TLSv1.2 Handshake, length = 262
SESSION KEYGEN:
PreMaster Secret:
…….
…….
CONNECTION KEYGEN:
Client Nonce:
…….
…….
Server Nonce:
…….
…….
Master Secret:
…….
…….
Client MAC write Secret:
…….
…….
Server MAC write Secret:
…….
…….
Client write key:
…….
…….
Server write key:
…….
…….
main, WRITE: TLSv1.2 Change Cipher Spec, length = 1
*** Finished
verify_data: { 5, 13, 28, 40, 35, 90, 186, 31, 217, 111, 119, 83 }
***
main, WRITE: TLSv1.2 Handshake, length = 36
main, READ: TLSv1.2 Change Cipher Spec, length = 1
main, READ: TLSv1.2 Handshake, length = 36
*** Finished
verify_data: { 48, 164, 101, 77, 74, 77, 125, 12, 151, 111, 77, 111 }
***
%% Cached client session: [Session-1, SSL_RSA_WITH_RC4_128_SHA]
main, WRITE: TLSv1.2 Application Data, length = 450
main, WRITE: TLSv1.2 Application Data, length = 4116
…….
…….
main, READ: TLSv1.2 Application Data, length = 25

Finished Invoking Webservice.1435326869036

===========================================================

MTOM disappeared when using SOAPHandler ??!!

Hi, There:

I am working on Streaming Attachment with SOAP webservices these days, because I need to send GBs of data in one SOAP invocation. Naturally MTOM/XOP is required so base64Binary can become MIME attachment. Everything works well with both client and server. Streaming GB of binary via the SOAP call was a breeze and memory usage on both Client and Server are kept in minimum. The problem arose when I had to add WSSE with UserNameToken/Password into the SOAP header. The way you would add WSSE header is via Handler. The handler itself works fine and user/password are in the WSSE header and taken nicely by the server. The problem is the XOP/MTOM that had been working just disappeared!  The base64Binary went back into SOAP as an inline data inside the XML as base64 strings. As a result, the Streaming functionality is now out of the question. A couple of hundred MB of payload will yield out of memory exception!

The reason of this happening is basically this. The Handler that adds WSSE header is doing its job AFTER the MTOM/XOP is being processed. When the Handler is processing the SOAP message, it will de-XOP it and push binary payload back to the SOAP before adding the WSSE header. As a result, MTOM is gone and if the payload is too big, out of memory exception happens.

Googling shows there are many others having the same problem. In particular, To shreds explained similar thing and offers a work around solution. The work around basically manually implements the XOP process and pushes binary from SOAP inline content back to XOP attachment. I actually tried to implement that work around. It works but it still has a problem. The processing of move the binary back to XOP requires to load the complete payload content in memory first. That requires again a lot of memory. It is true that the transporting from client to server is back to MTOM. But it kind of defeat the purpose for me because I intended to stream GBs of data without loading whole payload into memory.

Finally, my attention diverted from JAX_WS default implementation to CXF. CXF’s factory model allows the client to add WSSE header first before invoking service. Hence, XOP/MTOM will be intact since that is the last step¬†before leaving for the server’s endpoint.

Here is my working code:

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

//MTOM
Map<String,Object> props = new HashMap<String, Object>();
props.put(“mtom-enabled”, Boolean.TRUE);
factory.setProperties(props);

// ensure SOAP 1.2
SoapBindingConfiguration configuration = new SoapBindingConfiguration();
configuration.setVersion(Soap12.getInstance());
factory.setBindingConfig(configuration);

// WSSE
Map <String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER, ClientPasswordCallback.testUserID);
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName());
outProps.put(WSHandlerConstants.MUST_UNDERSTAND, “0”); // false/0¬†or true/1
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
wssOut.setAllowMTOM(Boolean.TRUE);
factory.getOutInterceptors().add(wssOut);

// Monitors both direction
factory.getOutInterceptors().add(new LoggingOutInterceptor());
factory.getInInterceptors().add(new LoggingInInterceptor());

// Custom Service Class XYZ
factory.setServiceClass(XYZ.class);
factory.setAddress(“http://localhost:7001/ws/endpoint/location&#8221;);
XYZ client = (XYZ) factory.create();

MyFileUploader f = new MyFileUploader();

DataHandler dh = new DataHandler(new FileDataSource(“c:\10GB.zip”));
f.setDataHandler(dh);
XYZResponse nr = client.streamingUpload(f);

With the above code, you will have both WSSE UserNameToken and Streaming MTOM in SOAP calls. The Streaming works exceptionally well with almost a flat liner in memory usage.

I haven’t tried other WSSE functions such as signing/encryption. Hope I can get to them later this week if needed.

Be good and write good code!

-Tony

SSL issues by Soap Client from within Weblogic Server

Hi,

Happy Chinese New Year!

Every problem that took me more than 2 days to find the solution deserves a writeup! This time it was a known issue when starts a SOAP request from a Webservice client from within Weblogic server, over HTTPS.  The problem I had was that we use a wildcard certificate for the receiving host, something like *.tonyyan.com, but the hostname is something like server1.tonyyan.com. WLS by default will fail the hostname verification process, hence fails the SSL handshake. There is a very nice link  http://jandrewthompson.blogspot.com/2010/04/weblogic-and-wildcard-ssl-certificates.html where it is described how to impl. and use a custom hostname verifier. However, I liked to use it just for my own client instead of changing verifier at the WLS server level. Thus, I used this code to by pass hostname verification all together (warning, this should not be used in PROD env, where some better and strict logic should be used to control wild card certificate against its hostname. Or, you could be vulnerable to man-in-middle attack).

//BindingProvider
BindingProvider bp = (BindingProvider)port;

// hostname issue – allow any hostname vs. its certificate
Map<String, Object> ctxt = bp.getRequestContext();

HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName,SSLSession session) {
logger.info(“HostnameVerifier without validation.¬† urlHostName is ” + urlHostName);
return true;
}
};
ctxt.put(“com.sun.xml.ws.transport.http.client.hostname.verifier”, hv);
ctxt.put(“com.sun.xml.ws.transport.https.client.hostname.verifier”, hv);
ctxt.put(“com.sun.xml.internal.ws.client.hostname.verifier”, hv);

With this code, it still didn’t work for me. It turns out that I forgot to put the server certificate in WLS’ truststore, based on this order of usage of certificates.

http://docs.oracle.com/cd/E11035_01/wls100/secmanage/identity_trust.html#wp1183754

After I put in the server certificate in DemoTrust.jks, things start to work great.

-TY