Monday, January 30, 2012

Haven't you heard of Service Provider Interface?

ServiceLoader javadoc states that:
"A service is a well-known set of interfaces and (usually abstract) classes. A service provider is a specific implementation of a service. The classes in a provider typically implement the interfaces and subclass the classes defined in the service itself. Service providers can be installed in an implementation of the Java platform in the form of extensions, that is, jar files placed into any of the usual extension directories. Providers can also be made available by adding them to the application's class path or by some other platform-specific means."
Every Java developer that may call himself a Java developer is obliged to know this!

Our story goes like this...
In the year of our lord two thousand and six, I was working in a project revolving around building Digital Libraries based on the SOA paradigm. One of my tasks was to build a pluggable execution engine, that is, first define a formal language that would describe the business process along with an API of the execution engine.  There should also be a monitoring service along with statistical analysis component. These artifacts would be independent of the actual implementation, in a JNDI-like manner.

Please, don't start wondering: hey, why didn't use something standardized like BPEL?

A suitable Design Pattern for this is the Abstract Factory Pattern (see here and here). Here is a simplified version of the UML diagram:
UML Diagram - Abstract Factory with two factory concretizations
As shown above, the starting point is the ExecServiceFactory which is an abstract class that exposes the basic set of business objects (interfaces), namely StatisticsService, MonitoringService and ExecEngine.

There are two implementations of this set, an internal execution service and an adaptor to an existing BPEL engine. The entry points to these concretizations are InternalExecServiceFactory and BPELExecServiceFactory respectively. These factories extend the ExecServiceFactory and provide implementations for the abstract operations getExecEngine, getMonitorService and getStatisticsService. These operations return implementations of the StatisticsService, MonitoringService and ExecEngine interfaces.

So, the first step was easy. The only thing that I had to do is register the two implementations and dynamically select one of them at runtime. It shouldn't be hard. Well... for a novice programmer like myself at that time (many would argue that this is still the case) it is hard , for a single reason: extensibility. But first things first...

The business requirement was to choose one implementation based on some criteria,. To tackle this issue every implementation should provide:
satisfyCriteria(Set<Criterion> criteria) :: Score 
 so that the highest scoring implementation should be selected at runtime based on the given criteria.

So, how should I automatically register the implementations so as to run through them and employ the highest scoring one at runtime? There are two solutions:
  1.  The registry component should be aware of the implementations.
  2.  The implementations should be aware of the registry  and register themselves.

Solution 1 is quite simple. If the registry component has knowledge of the extending factories then it only has to store the corresponding classes in a list and iterate over them at runtime, so as to select the highest scoring implementation. But this has the obvious disadvantage of extensibility. What does this mean? If you want to add a new implementation then you have to change the existing registry code.

Solution 2 is easier. At class initialization time, the implementation simply invokes a registerMe method of the registry component. But there's a small catch. How/When is a class initialized?

According to Java Programming Language Concepts:
Initialization of a class or interface consists of invoking its static initializers (§2.11) and the initializers for static fields (§2.9.2) declared in the class. This process is described in more detail in §2.17.4 and §2.17.5.
A class or interface may be initialized only as a result of:
  • The execution of any one of the Java virtual machine instructions new, getstatic, putstatic, or invokestatic that references the class or interface. Each of these instructions corresponds to one of the conditions in §2.17.4. All of the previously listed instructions reference a class directly or indirectly through either a field reference or a method reference. Upon execution of a new instruction, the referenced class or interface is initialized if it has not been initialized already. Upon execution of a getstatic, putstatic, or invokestatic
    instruction, the class or interface that declared the resolved field or method is initialized if it has not been initialized already.
  • Invocation of certain reflective methods in the class library (§3.12), for example, in class Class or in package java.lang.reflect.
  • The initialization of one of its subclasses.
  • Its designation as the initial class at Java virtual machine start-up (§5.2).
Prior to initialization a class or interface must be linked, that is, verified, prepared, and optionally resolved.
This basically means that an entity must somehow refer to that class. But we don't want that... So, solution 1 is the only way.

Back in 2006 I followed that solution. The registry component was embedded in the ExecServiceFactory and loaded the implementation set from a .properties file. Hard-coded solution but it worked.

In the project that I worked in we employed an integration tool called ETICS (see here).
...it provides a service to help software developers, managers and users to better manage complexity and improve the quality of their software. The service allows to fully automate the way a software is built and tested. It provides "out-of-the-box" build and test system, powered with a product repository.
ETICS was working fine but it had a serious disadvantage (at least at that time)... it was damn complex and not fun-to-play-with. It required lots of time to add a new component, so you can understand that all developers wanted to have the smallest possible interaction with it.

We had nightly builds and periodic release cycles (every 1-2 months). These cycles were quite inflexible and once the cycle ended then you had to wait for the next cycle to start before adding a new component or making any changes to the existing ones.

The problem begun when I needed to add a new implementation. Creating a new configuration wasn't much of a problem. Forgetting to add a new configuration version for the ExecServiceFactory component is totally another issue!

Adding a new component meant changing the properties file that enumerated the existing implementations and lied within the ExecServiceFactory component. So I had to create a new version of this component and provide a corresponding ETICS configuration for this. In short, I've missed the cycle and the new feature wasn't delivered according to schedule...

Had I known Java's Service Provider Interface (SPI) would save me much trouble.

So what is this SPI thing? Simply put, it's a methodology/mechanism of creating extensible Java applications. An extensible application is one that you can extend easily without modifying its original code base. You can enhance its functionality with new plug-ins or modules. Developers, software vendors, and even customers can add new functionality or application programming interfaces (APIs) by simply adding a new Java Archive (JAR) file onto the application classpath or into an application-specific extension directory.

Here, here, here and here you may find all the necessary information. All you have to do is create a new implementation (or service provider according to the SPI terminology) packaged in a new JAR, create a new file under META-INF/services directory with the name of the interface class (ExecServiceFactory in our case prefixed with the package name) and just place the implementation class full name in it (e.g. mypkg.InternalExecServiceFactory). Then, in the registry code, you can iterate through the available implementations using
 private static ServiceLoader<ExecServiceFactory> esFactoryLoader
     = ServiceLoader.load(ExecServiceFactory.class);
The selection of the highest scoring implementation, based on a given criteria, is accomplished by this piece of code.
 public ExecServiceFactory getImpl(Set<Criterion> criteria) {
     Score tScore = Score.MIN_VALID;
     ExecServiceFactory ret = null;
     for(Iterator<ExecServiceFactory> it = loader.iterator();it.hasNext();) {
         ExecServiceFactory cp = it.next();
         Score score = cp.satisfyCriteria(criteria);
         if(score<tScore) {
             ret = cp;
             tScore = score
         }
     }
     return ret;
 }
So... no code change, only jar addition. And that's all! SPI is widely used in may Java technologies, such as JDBC and JNDI and quite frankly I should have been aware of it... Anyway, I learned it some months later. Better late than ever!
"Ignorance is the curse of God; knowledge is the wing wherewith we fly to heaven" - William Shakespeare

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

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

It was a beautiful sunny Monday morning in the capital - I live and work in Athens, Greece - despite the low temperature (at least for the Athenian criteria). Birds were chirping, people were whining about the known financial problems of Greece... the usual stuff... Nothing could foreshadow what would trouble me for the rest of the week.

I prepared a monstrous-in-dimensions cup of coffee and opened my - really huge and exponentially growing - task list. The task-in-focus seemed simple (ignorant me)!

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

The requirement was to build a java web start application that would manage PDF documents. That client should communicate with a server exchanging information like the PDF document itself and some other less significant stuff. The building blocks were there, referring to the server and client components. The actual task was to secure their communication. The server component is implemented as a web service running on WebLogic, so the secure communication would rely on WS-security.

Don't get me any wrong... I love WebLogic! Apart from the licensing policy, which requires thorough analysis and infinite amount of usually dead-end discussion, there are religious arguments against or in favor of WebLogic. I cannot say that I have been particularly involved with many application servers. I do have some professional experience with Tomcat (it's actually a Servlet container and not an full-blown application server, but anyway...), Glassfish and JBoss (not a full JavaEE6 app server, but implements the Web Profile - see the differences here). All these are perfectly OK (especially JBoss). They do their work. There are strong communities behind them that can help you with almost anything. But they are not WebLogic! It's a matter of taste... a purely subjective factor. I won't delve into much detail about the pros and cons of WebLogic or a JavaEE app server comparison (see here). I can only say one thing... WebLogic administration panel simply rocks! It's the best... extremely powerful and easy-to-use.

Leaving behind the discussion about app servers, there is a java web start application that manages PDF documents and a web service, running in WebLogic 10.3.3, that communicates with the aforementioned javaws app. What I had to do is secure the communication. Since the security requirements were very high, the best solution is to adopt message-level security (both encryption and signing).

Oracle has published a very good (but certainly not complete) documentation here. The selected ws-security policy is Wssp1.2-2007-Wss1.1-X509-Basic256.xml with both encryption and signing in all methods. I won't go into any details regarding the theory behind this... it's PKI stuff (not trivial but easy to learn).
Three things had to be implemented:
  • Configuring WebLogic web service security
  • Coding the web service
  • Coding the web service client

Configuring WebLogic web service security

The following steps need to be taken:
  • Obtain two private key and digital certificate pairs to be used by the Web services runtime. One of the pairs is used for digitally signing the SOAP message and the other for encrypting it. Although not required, Oracle recommends that you obtain two pairs that will be used only by WebLogic Web services. You must also ensure that both of the certificate's key usage matches what you are configuring them to do. For example, if you are specifying that a certificate be used for encryption, be sure that the certificate's key usage is specified as for encryption or is undefined. Otherwise, the Web services security runtime will reject the certificate. You can use the Cert Gen utility or Sun Microsystem's keytool utility (see here) to perform this step. For development purposes, the keytool utility is the easiest way to get started. The first step is to create the client & server credentials. The type of these credentials is non other than private key - X509 certificate pairs. 
  • Create, if one does not currently exist, a custom identity keystore for WebLogic Server and load the private key and digital certificate pairs you obtained in the preceding step into the identity keystore. If you have already configured WebLogic Server for SSL, then you have already created an identity keystore that you can also use in this step. You can use WebLogic's ImportPrivateKey utility and Sun Microsystem's keytool utility (see here) to perform this step. For development purposes, the keytool utility is the easiest way to get started.
  • Using the Administration Console, configure WebLogic Server to locate the keystore you created in the preceding step. If you are using a keystore that has already been configured for WebLogic Server, you do not need to perform this step.
  • Using the Administration Console, create the default Web service security configuration, which must be named default_wss. The default Web service security configuration is used by all Web services in the domain unless they have been explicitly programmed to use a different configuration. For further details, click here. Below, there is a sample of the local WebLogic configuration.
<webservice-security>
  <name>x509AuthSignEncrWSS</name>
  <webservice-token-handler>
    <name>th1</name>
    <class-name>weblogic.xml.crypto.wss.BinarySecurityTokenHandler</class-name>
    <token-type>x509</token-type>
    <configuration-property>
      <name>UseX509ForIdentity</name>
      <encrypt-value-required>false</encrypt-value-required>
      <value>true</value>
    </configuration-property>
    <handling-order>1</handling-order>
  </webservice-token-handler>
</webservice-security>
 
  • Update the default Web services security configuration you created in the preceding step to use one of the private key and digital certificate pairs for digitally signing SOAP messages. See Specify the key pair used to sign SOAP messages in Oracle WebLogic Server Administration Console Help. In the procedure, when you create the properties used to identify the keystore and key pair, enter the exact value for the Name of each property (such as IntegrityKeyStore, IntegrityKeyStorePassword, and so on), but enter the value that identifies your own previously-created keystore and key pair in the Value fields.
  • Similarly, update the default Web services security configuration you created in a preceding step to use the second private key and digital certificate pair for encrypting SOAP messages. See Specify the key pair used to encrypt SOAP messages in Oracle WebLogic Server Administration Console Help. In the procedure, when you create the properties used to identify the keystore and key pair, enter the exact value for the Name of each property (such as ConfidentialityKeyStore. ConfidentialityKeyStorePassword, and so on), but enter the value that identifies your own previously-created keystore and key pair in the Value fields.
Coding the web service

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import weblogic.jws.Policies;
import weblogic.jws.Policy;

import weblogic.jws.WLHttpTransport;
import weblogic.jws.security.WssConfiguration;

@WebService()
@WssConfiguration(value="x509AuthSignEncrWSS")
@Policy(uri="Wssp1.2-2007-Wss1.1-X509-Basic256.xml")
public class MyFirstSecWS {

  @WebMethod(operationName = "sayHi")
  @Policies({
      @Policy(uri="policy:Wssp1.2-2007-SignBody.xml"),
      @Policy(uri="policy:Wssp1.2-2007-EncryptBody.xml")
  })
  public String sayHi(@WebParam(name = "name")
  String name) {
      return "Hi " + name;
  }
}
 
Nothing tricky here... very simple! We just denoted the security policy being used (Wssp1.2-2007-Wss1.1-X509-Basic256.xml) and the signing and encryption subpolicies (Wssp1.2-2007-SignBody.xml, Wssp1.2-2007-EncryptBody.xml) for the service method. Not that this is not the actual service, but just an example!!!


Coding the web service client
import java.io.FileInputStream;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.ws.BindingProvider;
import weblogic.security.SSL.TrustManager;

import weblogic.wsee.security.bst.ClientBSTCredentialProvider;
import weblogic.xml.crypto.wss.WSSecurityContext;
import weblogic.xml.crypto.wss.provider.CredentialProvider;

public class Main {

    public static void main(String[] args) {

// keystore that holds the client private key
String ksFile = <keystore filepath>
org.ciec.ws.client.MyFirstSecWSService service = new org.ciec.ws.client.MyFirstSecWSService();
org.ciec.ws.client.MyFirstSecWS port = service.getMyFirstSecWSPort();
//create credential provider and set it to the request context
List credProviders = new ArrayList();
//client side BinarySecurityToken credential provider -- x509
CredentialProvider cp = null;
    try {
 cp = new ClientBSTCredentialProvider(ksFile, <ksPassword>, <keyAlias>, <keyPassword>, <keystoreType - JKS or PKCS#12>,
  getPublicCertificate(<serverCertificatePath>));
    } catch (Exception ex) {
 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
    }

credProviders.add(cp);
Map<String, Object> requestContext = ((BindingProvider) port).getRequestContext();
requestContext.put(WSSecurityContext.CREDENTIAL_PROVIDER_LIST, credProviders);
requestContext.put(WSSecurityContext.TRUST_MANAGER, new TrustManager() {
 public boolean certificateCallback(X509Certificate[] xcs, int i) {
     // validation code here that inspects the X.509 certificates being passed
     return true;
 }
    });
try {
    String response = port.sayHi("World");
    System.out.println("response = " + response);
} catch (Throwable e) {
    e.printStackTrace();
}
}

    public static X509Certificate getPublicCertificate(String path) throws IOException, CertificateException {
      FileInputStream is = new FileInputStream(path);
      CertificateFactory cf = CertificateFactory.getInstance("X.509");
      X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
      return cert;
    }
}
 
It's not a proof of the PoincarĂ© conjecture... it's a simple piece of code defining some security attributes in respect to the web service port. Two things are established: the client's identity and trust resolution. Well... in the code shown above only the first one is trully established, defining all appropriate information for accessing the client's private key. This is done by creating a ClientBSTCredentialProvider object, which (according to javaDoc) creates client BST credential provider for the indicated keystore, certificate alias and server certificate. There are many signatures for the ClientBSTCredentialProvider constructor but only use the other that also specifies the server certificate as well, otherwise nothing will work! Unfortunately I could not find it in the oracle's documentation and took me some time to figure out...
Client handles server identification by defining  a TrustManager implementation with a proper callback function being responsible for returning true or false based on the  validity of theserver certificate it will be fed with. In the code shown above we simply trust everything (which of course we shouldn't).

Almost all the information mentioned up to this point is available through the oracle's online documentation. Everything worked just fine when the client was tested in NetBeans. I should also state here that I use NetBeans instead of Eclipse, without any concrete arguments - just a matter of choice. I have been using Eclipse for a number of years in the past. It's perfectly OK and to be honest with better look-n-feel than NetBeans!

As far as the client classpath is concerned, I used weblogic.jar which can be found in WL_HOME/server/lib. Oracle states that wlfullclient.jar should be used instead for WebLogic 10+. I'll go into that in a bit... Generally, there are numerous clients for WebLogic server components. The analysis of them can be found here.

The problems begun when I tried to make a truly stand-alone client in a workstation with no WebLogic installation... In my next post I will share my torture... Stay tuned for some purely technical headaches! 
“Never memorize something that you can look up” - Albert Einstein

Saturday, January 14, 2012

Prelude

All proper blogs (must) begin with the proper introductions. So here it is...

My name is Pavlos (or Paul if you prefer the English version) and as you may have guessed I work as a software architect / designer / programmer (I promise to dedicate one post elaborating on the aforementioned characterizations). My primary working experience stems from European research projects mainly revolving around Information Retrieval. In the context of those projects I became familiar with a wide range of computer science areas, from Web Services, Service Composition to Query Processing/Optimization and Artificial Intelligence. Due to the needs of my recent work, I have also invested on designing and building secure infrastructures (PKI).


So, what is this blog about? Well... I'm definitely not trying to sell anything, advertise myself or find a new job. I certainly don't want to give any advices on technical issues or any state-of-the-art solutions to specific problems. There are plenty of sites for that and many people more experienced that me. This blog is (as the title denotes) a recording of my daily life as a programmer. Don't worry, all posts will revolve around IT and not my diet or comments about the Rosie's nip-slip and Jason's reaction. I'll write about my experiences in designing and building computer systems, debugging ancient pieces of code (mostly mine) and grumbling about the programming tortures. In the vast majority of cases, the focus will be on Java-related technologies

I have the aspiration that this will not be a one-way experience, meaning that I'd love to have some feedback on my writings (e.g. corrections, objections or even statements of compassion). In any case, I hope it goes well...

"As for me, all I know is that I know nothing" - Socrates

P.S. I will close all my posts with a famous quote, beginning with a top hit.