Hardcore DevOps: Building A Portable Weblogic Client on the CLI
Written by Nikos Vaggalis   
Monday, 18 September 2017
Article Index
Hardcore DevOps: Building A Portable Weblogic Client on the CLI
CLI to the rescue
Dealing with dependencies
One fat jar

It shouldn't be that difficult to build a standalone Weblogic WS-Security enabled client for invoking JAX Web Services, but the reality is that it is. Let's find out why.

 

JAX-RPC Using a Stand-Alone Client JAR File When Invoking Web Services.

It is assumed in this document that, when you invoke a Web service using the

client-side artifacts generated by the clientgen or wsdlc Ant tasks, you have the

entire set of WebLogic Server classes in your CLASSPATH.

If, however, your computer does not have WebLogic Server installed, you can still invoke a Web service by using the stand-alone WebLogic Web services client

JAR file, as described in this section.

The standalone client JAR file supports basic client-side functionality, such as:

Use with client-side artifacts created by both the clientgen Ant tasks
Processing SOAP messages
Using client-side SOAP message handlers
Using MTOM
Invoking JAX-RPC Web services
Using SSL

The stand-alone client JAR file does not, however, support invoking Web services

that use the following advanced features:

Web services reliable SOAP messaging
Message-level security (WS-Security)
Conversations
Asynchronous request-response
Buffering
JMS transport

 

JAX-WS
You can invoke a Web service from any Java SE or Java EE application running on WebLogic Server (with access to the WebLogic Server classpath). Support for stand-alone Java applications that are running in an environment where WebLogic Server libraries are not available is not available in this release of JAX-WS. 

Furthermore :

Learn how to create the wlfullclient.jar using the WebLogic JarBuilder tool.Use the following steps to create a wlfullclient.jar file for a JDK 1.6 client application:

Change directories to the server/lib directory.
    $ cd WL_HOME/server/lib

Use the following command to create wlfullclient.jar in the server/lib directory:
    $ java -jar wljarbuilder.jar

You can now copy and bundle the wlfullclient.jar along with cryptoj.jar with client applications. The wlfullclient.jar and cryptoj.jar must be kept in the same directory as the wlfullcient.jar references cryptoj.jar in its manifest Class-Path.
Add the wlfullclient.jar to the client application's classpath.

 

What is recurring in the above passages is 'classpath' and the need for the Weblogic Server libraries to be available to it.Thing is I don't want to go through setting up a WebLogic instance just to get  to those libraries, or build a client that depends on the classpath as well as the machine's or host OS's intricacies.Furthermore, any setup would have to be multiplied by 10, the number of machines looking to access the same web service.

In any case my Java client should always call into a Weblogic, well, 'client' library in order to consume the necessary functionality.Turns out that choosing the appropriate one is convoluted:

 

sclients1

 

Do I need wlfullclient.jar, wlthint3client.jar, wlclient.jar, wljmsclient.jar, wlclient.jar, or maybe wlsafclient.jar? Also, don't forget wljmsclient.jar, wljmxclient.jar and wseeclient.jar.Spoiler alert, the one I used was wlsafclient.jar.

Terminology is not of much help either.You see, the manuals use the term 'standalone client' in a vague way, as in the JAX-RPC section we saw earlier, with The stand-alone client JAR file does not, however, support invoking Web services that use the following advanced features.But what is meant by that;is it referring to the 'standalone' Java 'client' I'm looking to build? Turns out that by 'client' is meant the jar that my Java client should call into in order to invoke the web service.In JAX-RPC's occasion this is wseeclient.jar.

But from now on and for the purposes of this article, 'client' will always mean 'my Java client' in either source or packaged form.

 

Where it all began

What prompted this quest was being handed a copy of a program's source, aka the 'client', that invokes a message level WS-Security Weblogic powered web service.This client software is expected to be receiving HL7 messages from another application and forward them to the Weblogic (JAX-WS) service.This setup then had to be replicated across 10 CentOS Linux production machines.eing 

Going back to square one I wasn't particularly fond of this replication and the technical glitches I might have to confront.Instead I needed a solution able to work anywhere and under any environment with the minimum requirements possible, something that mostly boiled down to locating and importing the necessary library dependencies at runtime in a portable way.In other words, take care of those pesky import statements: 


package com.wlogic.client; import weblogic.xml.crypto.wss.WSSecurityContext; import weblogic.xml.crypto.wss.provider.CredentialProvider; import weblogic.wsee.security.bst.ClientBSTCredentialProvider; import weblogic.wsee.security.saml.SAMLTrustCredentialProvider; import weblogic.wsee.security.unt.ClientUNTCredentialProvider; import weblogic.wsee.security.util.CertUtils; import weblogic.security.SSL.TrustManager; import javax.annotation.Generated; import javax.xml.ws.BindingProvider; import javax.xml.ws.WebServiceRef; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Map; public class App { public static void main( String[] args ) { System.out.println( "Hello World with WebLogic dependencies!" ); } }

(Note that in order to keep the example simple, the rest of the body's code was removed)

So to simplify matters and be truly portable, I needed, on each machine, to:

  • not install an instance of Weblogic server
  • not install Jdeveloper or another IDE, as I didn't wish to be held ransom to the tool's intricacies
  • not install a GUI repo manager
  • have the lightest configuration possible
  • the client should be able to run anywhere and be fed every library dependency in an independent of environment and machine way.Java's "write once, run anywhere" philosophy anyone?

In other words no strings attached, portability being the prime target. It sounded like the perfect scenario for an all-inclusive fat jar client.

Talking about limitations imposed by IDEs, in trying to run the client from source from within JDeveloper on Windows, I had a close encounter with the dreadful  CreateProcess error=206, The filename or extension is too long error.

 

jdev1

 

The same error but under a different code, 87, was filed as an Eclipse bug back in 2012, and as it seems still at large in year 2017 and on Jdeveloper on Windows 7.The following extract from the original bug filing thread reveals the issue behind it:

The bug here describes that the junit or java launcher can not be used in case the classpath or command line arguments exceed the limitation of the operating system. To solve that problem outside of eclipse is a total different story and should be discussed on another platform (stack overflow or whatever).

Your post is about two issues, one is how to find out the generated commandline and the other one is how to get the process running inside eclipse.

To find out how the command line gets generated, I suggest the following:

  1. make a sample project according to yours but one which works (reduce it to the max)

  2. start the application in debug mode, if you can not attach the debugger modify the jvm arguments so you have first to connect the debugger e.g. -Xrunjdwp:transport=dt_socket,
    server=y,suspend=y,
    address=7570

  3. while the application is waiting use a third party tool as the process explorer to investigate the process details.

 

To get your application inside eclipse running, you need to reduce the command line. As mentioned there are different strategies. You need to find out which one is the best for you:

  • reduce the classpath
  • use directories instead of jar files
  • use a packed jar files which contains all other jars, use the classpath variable inside the manifest file to point to the other jars
  • use a special class loader which reads the classpath from a config file
  • try to use one of the attached patches here
  • use an own wrapper e.g. ant

So although not an Eclipse/Jdeveloper issue per se but more prevalent to the command line limitations of Windows, the point is that JDeveloper failed me and as discovered down the same thread, this  constituted a reason for abandoning Eclipse all together :

I thought I would give some information regarding this bugs impact on the use of the Eclipse product as I am a faithful Eclipse user. This bug has caused the entire engineering department at Expedia.com (approax. 200 devs) to move to a paid product, IDEA intellij, due to their inability to use the IDE for debugging or running JUnits.

and

As an employee for expedia.com, I can say that we support both Eclipse and IntelliJ and we currently have developers using both. Some developers have switched to IntelliJ and this bug was a contributing factor.  I use and prefer IntelliJ for several reasons, and have been using it since before we knew about this issue.


This situation nicely paves the way to laying forward my objection, that is, that nowadays all if not most Java development is done on IDEs which hide the underlying procedures and  complexities, to the point of questioning the Java notion of 'openness', as you're now attached  to these massive toolkits instead of the tool versions of the command line.

Of course since in reality this is a Windows OS problem, firing CLI tools like Maven or Gradle would also be prone to the very same issue, but at least you are in control therefore enabled to respond better, also hinted by the tools' switches verbose output;Maven's -e -X or Gradle's --debug.It's all about control, IDEs for Automatic driving vs CLI for Manual...

Surprisingly some of the proposed solutions do involve packing a "pathing jar" on the command line, as seen here and here:

The Windows command line is very limiting in this regard. A workaround is to create a 'pathing jar'. This is a jar containing only a Manifest.mf file, whose Class-Path specifies the disk paths of your long list of jars, etc. Now just add this pathing jar to your command line classpath. This is usually more convenient than packaging the actual resources together.

As I recall, the disk paths can be relative to the pathing jar itself. So the Manifest.mf might look something like this:

Class-Path: this.jar that.jar ../lib/other.jar

If your pathing jar contains mainly foundational resources, then it won't change too frequently, but you will probably still want to generate it somewhere in your build.

 



Last Updated ( Monday, 18 September 2017 )