Monday, January 16, 2012

Have you ever tried to invoke secure WebLogic web services with javaws client? - part 2

OK... where were we?

Oh yes... everything seemed to be working fine (with few exceptions which constitute the norm in programming). So, we have a secure communication between server and client (tested by JUnit). Now, it's time to make the client really stand-alone, which means that it can run on a different machine without any WebLogic installation. Don't forget that the client classpath has a reference to the weblogic.jar found in directory <WL_HOME>/server/lib.

The first naive attempt was to simply move weblogic.jar along with all the other dependencies in a separate directory and set the runtime classpath. No luck... but I admit it... it was my fault since I haven't read carefully the manual on weblogic clients. This is a classic problem with us... we never EVER read the documentation!

Our dear Oracle offers an overview of the all types of weblogic clients here. According to this document, I should have used wseeclient.jar or perhaps wlfullclient.jar. The latter is supposed to cover all cases (at least the vast majority) but it turned out be insufficient as far as web services are concerned. No problem... wseeclient.jar seemed to be suitable for that.
Invoking web services.
Oracle has an extensive documentation on that, here. There is a section on invoking web services by truly-standalone clients, that is, clients that run on workstations with no WebLogic installation (see here). Have I told you that most developers (including me) do not actually read manuals? Or at least we never make a proper reading / studying, just a diagonal scan, keeping only those phrases that hit on us (there is a scientific explanation for this... I'm sure)! It is explicitly stated that:
the stand-alone client JAR file does not, however, support invoking Web Services that use the following advanced feature: Message-level security (WS-Security)
Disappointment... If I was more careful and have seen that, it would save me lots of time... But in my defense, I would expect that Oracle should provide a stand-alone client that does what it is meant to do! How is it ever possible that there is no actual client for this?!?!?!

In any case all candidates (weblogic.jar, wseeclient.jar and wlfullclient.jar) are not designed to be used as is. If you take a look at their manifest files (just jar-xvf them and look into the META-INF) there is an extensive classpath reference. Since only weblogic seemed to be the final winner then I had to concentrate on that.

Gathering all the weblogic dependencies and resetting the client classpath wouldn't work as well (dependency graph)! There were two choices:
  1. Continue the same procedure until I could finally include all resources in the client classpath.
  2. Be a bit smarter (no more than a chimp - see here and here) and monitor the classes which are loaded when the WL_HOME/server/lib/weblogic.jar is used in the client classpath.
The obvious choice was the second one. If you specify the -verbose flag in java command, then you get the list of classes and the corresponding jars. With the help of some regular expressions, I had finally gathered the list of necessary jars.
  • bcprov-jdk14-138.jar
  • com.bea.core.antlr.runtime_2.7.7.jar
  • com.bea.core.common.security.api_1.0.0.0_6-1-0-0.jar
  • com.bea.core.common.security.utils_1.0.0.0_6-1-0-0.jar
  • com.bea.core.datasource6_1.9.0.0.jar
  • com.bea.core.descriptor_1.9.0.0.jar
  • com.bea.core.diagnostics.core_2.5.0.0.jar
  • com.bea.core.i18n_1.8.0.0.jar
  • com.bea.core.logging_1.8.0.0.jar
  • com.bea.core.management.core_2.8.0.0.jar
  • com.bea.core.stax2_1.0.0.0_3-0-1.jar
  • com.bea.core.timers_1.7.0.0.jar
  • com.bea.core.utils_1.9.0.0.jar
  • com.bea.core.utils.classloaders_1.8.0.0.jar
  • com.bea.core.weblogic.rmi.client_1.8.0.0.jar
  • com.bea.core.weblogic.saaj_1.6.0.0.jar
  • com.bea.core.weblogic.security_1.0.0.0_6-1-0-0.jar
  • com.bea.core.weblogic.security.identity_1.1.2.1.jar
  • com.bea.core.weblogic.security.wls_1.0.0.0_6-1-0-0.jar
  • com.bea.core.weblogic.socket.api_1.2.0.0.jar
  • com.bea.core.weblogic.stax_1.8.0.0.jar
  • com.bea.core.weblogic.workmanager_1.9.0.0.jar
  • com.bea.core.woodstox_1.0.0.0_4-0-5.jar
  • com.bea.core.workarea_1.7.0.0.jar
  • com.bea.core.xml.beaxmlbeans_2.2.0.0_2-5-1.jar
  • com.bea.core.xml.weblogic.xpath_1.4.0.0.jar
  • cryptoj.jar
  • glassfish.jaxb_1.0.0.0_2-1-12.jar
  • glassfish.jaxws.fastinfoset_1.0.0.0_2-1-5.jar
  • glassfish.jaxws.rt_1.1.0.0_2-1-5.jar
  • glassfish.stax.ex_1.0.0.0_1-2.jar
  • glassfish.xmlstreambuffer_1.0.0.0_0-5-257.jar
  • javax.jms_1.1.1.jar
  • javax.servlet_1.0.0.0_2-5.jar
  • javax.xml.rpc_1.2.1.jar
  • swing-worker-1.1.jar
  • weblogic.jar
  • webservices.jar
  • ws.api_1.1.0.0.jar
These jars are found either in WL_HOME/server/lib or WL_HOME/../modules/.(WL_HOME is basically wlserver_10.3 in the WebLogic installation directory). So... I was almost there, right? WRONG!!!
"A solved problem creates two new problems, and the best prescription for happy living is not to solve any more problems than you have to" - Russell Baker
This is where java web start comes in to play... Remember that the client should run as a java web start program (the web service client is a part of PDF handling program). If you run the client from command line then everything worked just fine. If you do the exact same thing but using java web start instead (javaws <local_JNLP_file>) then you would get the following error:

java.lang.InternalError: error initializing kernel caused by: java.lang.AssertionError: Duplicate initialization of WorkManager
at weblogic.work.WorkManagerFactory.set(WorkManagerFactory.java:107)
at weblogic.work.ExecuteQueueFactory.initialize(ExecuteQueueFactory.java:23)
at weblogic.kernel.Kernel.initialize(Kernel.java:103)
at weblogic.kernel.Kernel.ensureInitialized(Kernel.java:64)
at weblogic.rjvm.wls.WLSRJVMEnvironment.ensureInitialized(WLSRJVMEnvironment.java:50)
at weblogic.protocol.ProtocolManager$DefaultAdminProtocolMaker.<clinit>(ProtocolManager.java:53)
at weblogic.protocol.ProtocolManager.getDefaultAdminProtocol(ProtocolManager.java:218)
at weblogic.protocol.ProtocolHandlerAdmin.<clinit>(ProtocolHandlerAdmin.java:23)
at weblogic.rjvm.wls.WLSRJVMEnvironment.registerRJVMProtocols(WLSRJVMEnvironment.java:120)
at weblogic.rjvm.RJVMManager.ensureInitialized(RJVMManager.java:87)
at weblogic.rjvm.RJVMManager.<clinit>(RJVMManager.java:46)
at weblogic.rjvm.LocalRJVM.<init>(LocalRJVM.java:97)
at weblogic.rjvm.LocalRJVM.<init>(LocalRJVM.java:28)
at weblogic.rjvm.LocalRJVM$LocalRJVMMaker.<clinit>(LocalRJVM.java:31)
at weblogic.rjvm.LocalRJVM.getLocalRJVM(LocalRJVM.java:72)
at weblogic.xml.crypto.utils.DOMUtils.generateId(DOMUtils.java:403)
at weblogic.xml.crypto.utils.DOMUtils.generateId(DOMUtils.java:395)
at weblogic.xml.crypto.utils.DOMUtils.assignId(DOMUtils.java:374)
at weblogic.xml.crypto.wss.SecurityBuilderImpl.assignUri(SecurityBuilderImpl.java:148)
at weblogic.wsee.security.policy.SigningReferencesFactory.getSigningReferences(SigningReferencesFactory.java:100)
at weblogic.wsee.security.wss.policy.wssp.SigningPolicyBlueprintImpl.addSignatureNodeListToReference(SigningPolicyBlueprintImpl.java:446)
at weblogic.wsee.security.wss.policy.wssp.SigningPolicyBlueprintImpl.addSignatureNodeListToReference(SigningPolicyBlueprintImpl.java:335)
at weblogic.wsee.security.wss.plan.SecurityMessageArchitect.resolveSignatureList(SecurityMessageArchitect.java:574)
at weblogic.wsee.security.wss.plan.SecurityMessageArchitect.resolveSignatureList(SecurityMessageArchitect.java:428)
at weblogic.wsee.security.wss.plan.SecurityMessageArchitect.constructMessage(SecurityMessageArchitect.java:304)
at weblogic.wsee.security.wss.plan.SecurityMessageArchitect.buildWssMessage(SecurityMessageArchitect.java:138)
at weblogic.wsee.security.wss.plan.SecurityMessageArchitect.buildWssMessage(SecurityMessageArchitect.java:121)
at weblogic.wsee.security.wss.SecurityPolicyArchitect.processOutbound(SecurityPolicyArchitect.java:225)
at weblogic.wsee.security.wss.SecurityPolicyArchitect.processMessagePolicy(SecurityPolicyArchitect.java:123)
at weblogic.wsee.security.wss.SecurityPolicyConductor.processRequestOutbound(SecurityPolicyConductor.java:119)
at weblogic.wsee.security.wss.SecurityPolicyConductor.processRequestOutbound(SecurityPolicyConductor.java:91)
at weblogic.wsee.security.wssp.handlers.WssClientHandler.processOutbound(WssClientHandler.java:117)
at weblogic.wsee.security.wssp.handlers.WssClientHandler.processRequest(WssClientHandler.java:69)
at weblogic.wsee.security.wssp.handlers.WssHandler.handleRequest(WssHandler.java:112)
at weblogic.wsee.jaxws.framework.jaxrpc.TubeFactory$JAXRPCTube.processRequest(TubeFactory.java:222)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:866)
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:815)
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:778)
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:680)
at com.sun.xml.ws.client.Stub.process(Stub.java:272)
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:153)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:115)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:95)
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:136)
at $Proxy29.fetchCSD(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at weblogic.wsee.jaxws.spi.ClientInstanceInvocationHandler.invoke(ClientInstanceInvocationHandler.java:84)
at $Proxy30.fetchCSD(Unknown Source)
at exchangecsdclient.CSDExchangeClientImpl.fetchCSD(CSDExchangeClientImpl.java:151)
at pdfutil.PDFUtilApp.initialize(PDFUtilApp.java:55)
at org.jdesktop.application.Application$1.run(Application.java:170)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

at weblogic.kernel.Kernel.ensureInitialized(Kernel.java:66)
at weblogic.rjvm.wls.WLSRJVMEnvironment.ensureInitialized(WLSRJVMEnvironment.java:50)
at weblogic.protocol.ProtocolManager$DefaultAdminProtocolMaker.<clinit>(ProtocolManager.java:53)
at weblogic.protocol.ProtocolManager.getDefaultAdminProtocol(ProtocolManager.java:218)
at weblogic.protocol.ProtocolHandlerAdmin.<clinit>(ProtocolHandlerAdmin.java:23)
at weblogic.rjvm.wls.WLSRJVMEnvironment.registerRJVMProtocols(WLSRJVMEnvironment.java:120)
at weblogic.rjvm.RJVMManager.ensureInitialized(RJVMManager.java:87)
at weblogic.rjvm.RJVMManager.<clinit>(RJVMManager.java:46)
at weblogic.rjvm.LocalRJVM.<init>(LocalRJVM.java:97)
at weblogic.rjvm.LocalRJVM.<init>(LocalRJVM.java:28)
at weblogic.rjvm.LocalRJVM$LocalRJVMMaker.<clinit>(LocalRJVM.java:31)
at weblogic.rjvm.LocalRJVM.getLocalRJVM(LocalRJVM.java:72)
at weblogic.xml.crypto.utils.DOMUtils.generateId(DOMUtils.java:403)
at weblogic.xml.crypto.utils.DOMUtils.generateId(DOMUtils.java:395)
at weblogic.xml.crypto.utils.DOMUtils.assignId(DOMUtils.java:374)
at weblogic.xml.crypto.wss.SecurityBuilderImpl.assignUri(SecurityBuilderImpl.java:148)
at weblogic.wsee.security.policy.SigningReferencesFactory.getSigningReferences(SigningReferencesFactory.java:100)
at weblogic.wsee.security.wss.policy.wssp.SigningPolicyBlueprintImpl.addSignatureNodeListToReference(SigningPolicyBlueprintImpl.java:446)
at weblogic.wsee.security.wss.policy.wssp.SigningPolicyBlueprintImpl.addSignatureNodeListToReference(SigningPolicyBlueprintImpl.java:335)
at weblogic.wsee.security.wss.plan.SecurityMessageArchitect.resolveSignatureList(SecurityMessageArchitect.java:574)
at weblogic.wsee.security.wss.plan.SecurityMessageArchitect.resolveSignatureList(SecurityMessageArchitect.java:428)
at weblogic.wsee.security.wss.plan.SecurityMessageArchitect.constructMessage(SecurityMessageArchitect.java:304)
at weblogic.wsee.security.wss.plan.SecurityMessageArchitect.buildWssMessage(SecurityMessageArchitect.java:138)
at weblogic.wsee.security.wss.plan.SecurityMessageArchitect.buildWssMessage(SecurityMessageArchitect.java:121)
at weblogic.wsee.security.wss.SecurityPolicyArchitect.processOutbound(SecurityPolicyArchitect.java:225)
at weblogic.wsee.security.wss.SecurityPolicyArchitect.processMessagePolicy(SecurityPolicyArchitect.java:123)
at weblogic.wsee.security.wss.SecurityPolicyConductor.processRequestOutbound(SecurityPolicyConductor.java:119)
at weblogic.wsee.security.wss.SecurityPolicyConductor.processRequestOutbound(SecurityPolicyConductor.java:91)
at weblogic.wsee.security.wssp.handlers.WssClientHandler.processOutbound(WssClientHandler.java:117)
at weblogic.wsee.security.wssp.handlers.WssClientHandler.processRequest(WssClientHandler.java:69)
at weblogic.wsee.security.wssp.handlers.WssHandler.handleRequest(WssHandler.java:112)
at weblogic.wsee.jaxws.framework.jaxrpc.TubeFactory$JAXRPCTube.processRequest(TubeFactory.java:222)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:866)
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:815)
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:778)
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:680)
at com.sun.xml.ws.client.Stub.process(Stub.java:272)
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:153)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:115)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:95)
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:136)
at $Proxy29.fetchCSD(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at weblogic.wsee.jaxws.spi.ClientInstanceInvocationHandler.invoke(ClientInstanceInvocationHandler.java:84)
at $Proxy30.fetchCSD(Unknown Source)
at exchangecsdclient.CSDExchangeClientImpl.fetchCSD(CSDExchangeClientImpl.java:151)
at pdfutil.PDFUtilApp.initialize(PDFUtilApp.java:55)
at org.jdesktop.application.Application$1.run(Application.java:170)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)


Deadlock situation...

The good thing is that I discovered numerous veins in my face, the existence of which was a  total mystery for me. The bad thing is that I expressed my fury to my (alarmingly terrified) colleagues. Sorry guys...

The !@#$*@! WorkManager (see here) is initiated more than once! 

Classloading maze...

I posted the problem in the weblogic forums and I still wait for an answer. I could try to find a clean solution, but you know that, in the vast majority of situation, there is no time for that. So, I chose (with a relatively clean consciousness) another solution: bundle all jars in a single jar along with a small spawning class that simply sets the classpath (placing all jars in a temp directory) and initiates the original client in a new process.

It's not clean, I admit it! But it works! It works in both windows and linux environments (there is some additional testing to be done).

"A principle is the expression of perfection, and as imperfect beings like us cannot practice perfection, we devise every moment limits of its compromise in practice" - Mohandas Gandhi

5 comments:

  1. Hi:
    I need guidance to achieve this: "a small spawning class that simply sets the classpath (placing all jars in a temp directory) and initiates the original client in a new process". I've tested the client in a different pc and it works just fine with your dependencies or with only 2 jar dependencies: oracle.webservices.standalone.client.jar and weblogic.jar, I found this 2 last in jdeveloper ide. Only when I run the client in java web start fails, with the same error: "java.lang.InternalError: error initializing kernel caused by: java.lang.AssertionError: Duplicate initialization of WorkManager".

    ReplyDelete
  2. Hi Paul, I did it! Firts of all, you don't need any weblogic dependencies. I found this in Oracle forums:

    "When you just use the JAX-WS standard you do not need any WebLogic related classes to create a client for your web service.

    When the web service is deployed the WSDL can be accessed. By using this WSDL you can create your client by using tools
    such as wsimport (which comes with a JDK distribution and is located in the JDK_HOME/bin directory)

    wsimport -keep -d C:\YOUR_PROJECT\src http://hostname:port/context/SomeWebService?wsdl

    the 'keep' option make sure the generated .java files are not thrown away."

    Then I tried to send the security in many ways until I found this: http://www.javadb.com/using-a-message-handler-to-alter-the-soap-header-in-a-web-service-client and voila, i made a client with no weblogic dependencies. It runs ok in desktop and in java web start.
    Hope it works for u.

    ReplyDelete
    Replies
    1. Hi Rafael,

      I'm glad that you found the solution to your problem. Good job! It is common knowledge that anyone that gets involved with WS-* technologies needs to be equipped with infinite amounts of patience and coffee (or any other stimulant beverage you prefer). Simply put... it's a mess! The available implementations have various problems and almost never work together without any manual intervention.

      In the case of weblogic - jax-ws world, things are pretty simple when security is not a concern. So, if you simply want to invoke a non-secure web service then your dependencies are almost zero. Things get really complicated when security enters the game. It is true that for lower security levels that have a slight influence on SOAP headers, you can manually add your own headers. But you cannot continue with this approach in complex cases where e.g. you need to sign and encrypt SOAP header and/or body. That is the very reason for using security annotations (declarative paradigm) instead of manually programming the security aspect of your system.

      Thanks for your comments and keep up the good work.

      Cheers,
      Paul.

      Delete