Thursday, 6 June 2019

Weblogic 12.2.1.3 Signs SAML2 requests and responses with SHA-256

Today I reviewed a few responses on a 'What's new in Weblogic 12.2.1.3' question.
One of the responses mentioned the whats-new document.

Now, I'm not used to study these documents. But today I browsed through them and one thing caught my eye.

I did some implementations of Weblogic as a SAML2 Service Provider against MS ADFS. I'm even invited to do a talk 'SSO with ADFS for Apex Using Weblogic and ORDS: How I did it and Where I Tripped' at the UKOUG Southern Technology Summit 2019, july 2nd.

What's interesting here is that about 2 years ago I already wrote about my earlier experiences, and mentioned that Weblogic 12c did not support SHA-256 for the signing of SAML requests. So you had to configure ADFS to use SHA-1. In my latest implementation it stroke me that I did not have to force my ADFS counterpart to set that, at least I think I didn't. ADFS as you might expect for really some time now, uses SHA2 (SHA-256) as a default. But only today I saw that under Manageability Improvements -> Security is mentioned that Weblogic 12.2.1.3 also has SHA2 as a default now.

Knowing this will improve my talk greatly. I'm glad I saw this. It might seem to be a minor thing, but I think it's quite important.

I use Weblogic mostly as a FMW Infrastructure for SOA Suite, OSB, etc. And occasionally I do assignments with specifics like SAML2. If you're interested in what changed in a specific Weblogic version, I think it's important to know what you're looking for. Know the functionality that you're actively using or interested in.

Wednesday, 24 April 2019

Test Remote Asynchronous Request Response services

A few years ago, I described how you can test Asynchronous Request Response services.

The thing with Asynchronous Request Response services is, as I used to describe it, that they're in essence two complementary Request-Only (Fire and Forget) services. That is, the client submits a request to the Asynchronous Request Response service, and at a certain point waits for the response by listening to an endpoint.

To make this work, the responding Asynchronous Request Response service should be told, which endpoint it should call with the response and which correlation id should be used. The WS-Addressing standard is used for that. All nicely explained in the before mentioned article.

In most customer-cases the problem is that your Client SoapUI or ReadyAPI project should catch the response, but the service is running on a SOA Suite in the datacenter and is not allowed to get to your local machine.

MobaXterm makes it very easy to create a tunnel. You can have a remote tunnel, that enables a local listening endpoint, that forwards every request to a remote service. Very handy if you have a Vagrant project with only a NAT NetworkAdapter, where Vagrant enabled a ssh endpoint on port 2222. You can easily create a Local tunnel on port 7101, for instance, to the remote ssh session on port 2222, that enables you to get to the weblogic console on the remote VM running on http://darlin-vce:7101/console.

To create a tunnel, just open the MobaSSHTunnel - Grahpical port forwarding tool:
This will open:

You can create a new SSH tunnel or edit a current one using the cogs icon under settings. For instance, to be able to do the Local port forwarding to get to your Weblogic console on your Vagrant box, create a tunnel as follows:



On the left you can enter a local port. That is the port you can use on your localhost. On the top right you can enter an host and port for the address to post your request to (does not need to be localhost). Then bottom right you need to provide an ssh session. A bit inconvenient is that you can't select a session from the sessions pane. Provide a host, port and user to connect to your ssh server.

What happens is that MobaXterm creates an SSH session, and a local endpoint. Every thing posted to the local endpoint is posted on the remote server to the give address. In this case I can go on my browser and enter https;//localhost:7101/console and it will bring me to the Weblogic Console on my Vagrant box. Neat, isn't it?



To get the remote Async Service respond to your local machine, you can also create a we need a tunnel that works the otherway around: we need Remote Port Forwarding:

Configuring is similar to Local port forwarding, however, now on the remote server a listen endpoint is created, and everything that is posted to the localhost:7777 adress (in this example) is forwarded to the address entered on the local server. In this case it is forwarded to localhost:7777, but it could be something else.

In our ReadyAPI project I created a Groovy script as follows:
def testCase = testRunner.testCase
def env = testCase.testSuite.project.activeEnvironment.name
if (env != "o02-12c"  &&  env != "o02" ) {
  log.info "Environment: "+env+", so set callbackIp to "+InetAddress.localHost.hostAddress
  testRunner.testCase.setPropertyValue( "callbackIp", InetAddress.localHost.hostAddress)
} else {
  log.info "Environment: "+env+", so set callbackIp to localhost"
  testRunner.testCase.setPropertyValue( "callbackIp", "localhost")
}

In ReadyAPI you can define environments, with the project property activeEnvironment.name it can be queried.

If the environment points to one of our development environments, I set the callbackIp testcase property to "localhost". But for the default environment, I use InetAddress.localHost.hostAddress to get the local ip address. This will be the ip address of our CD/CI tool, that runs ReadyAPI from a script.

You can set the WS-Addressing ReplyTo address as follows, for instance:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:add="http://schemas.xmlsoap.org/ws/2003/03/addressing">
      <soapenv:Header>
       <add:ReplyTo>
         <add:Address>http://${#TestCase#callbackIp}:7777/MyMockResponseURI</add:Address>
      </add:ReplyTo>
   </soapenv:Header>
   <soapenv:Body>

Then this address is used to do the callback. Make sure the tunnel is started:

You can also have the tunnel auto started (with the blue man-running-icon) or auto-reconnected (with the purple lightning icon).

This may also be very relevant in testing services on Oracle SOA Cloud Service, or Integration Cloud.

Happy tunneling!

Thursday, 18 April 2019

Split your Vagrant provisioners

For a while now, I'm quite into Vagrant in combination with VirtualBox. A few years ago I started with trying to script FMW environments, and since my discovery, and resulting fancy, of Vagrant, I also created a project for creating and provisioning a SOA Suite box.

Until now, my project all had one shell-type provisioner looking like:
  config.vm.provision "shell", inline: <<-SHELL
    export SCRIPT_HOME=/vagrant/scripts
    . $SCRIPT_HOME/install_env.sh
    echo _______________________________________________________________________________
    echo 0. Prepare Oracle Linux
    $SCRIPT_HOME/0.PrepOEL.sh
    echo _______________________________________________________________________________
    echo 1. Create Filesystem
    $SCRIPT_HOME/1.FileSystem.sh
    echo _______________________________________________________________________________
    echo 2. Create Oracle User
    $SCRIPT_HOME/2.MakeOracleUser.sh
    #
    echo _______________________________________________________________________________
    echo 3. Java SDK 8
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/installJava.sh'
   SHELL

This seems quite simple, but for my SOA Suite box, I had quite a lengthy provisioner, that somehow failed at running the RCU and therefor with the creation of the domain.  There is a synchronization thingy with the database. The database is up, but at the time it reaches the RCU creation, it isn't able to connect. When running it seperately it works like a charm.

So, don't know how to solve that, but I want to re-provision only the part of the RCU and domain creation. Last night I fiddled around with it. Following the Vagrant Up basic usage explanation, you can create multiple provisioners with different names and different types. You can then force the provisioning for certain provisioners by type or by name.

I played around with that, because I couldn't get the syntax right. Although the explanation is proper, I wanted to have it slightly different and did not got it at first. Finally, I got it working.

Let's look into it.

First I split up my shell script, and found that I can put those in a variable. I now have a init script, that adapts the Linux OS, creates a new filesystem and creates an oracle user :
  $initScript = <<-SCRIPT
    export SCRIPT_HOME=/vagrant/scripts
    echo _______________________________________________________________________________
    echo 0. Prepare Oracle Linux
    $SCRIPT_HOME/0.PrepOEL.sh
    echo _______________________________________________________________________________
    echo 1. Create Filesystem
    $SCRIPT_HOME/1.FileSystem.sh
    echo _______________________________________________________________________________
    echo 2. Create Oracle User
    $SCRIPT_HOME/2.MakeOracleUser.sh
  SCRIPT

And one for installing the FMW software:
  $installFMWScript = <<-SCRIPT
    echo _______________________________________________________________________________
    echo 3. Java SDK 8
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/installJava.sh'
    echo _______________________________________________________________________________
    echo 4. Database 12c
    sudo runuser -l oracle -c '/vagrant/scripts/database/installDB.sh'
    echo _______________________________________________________________________________
    echo 5.1 SQLCL and SQLDeveloper
    sudo runuser -l oracle -c '/vagrant/scripts/database/installSqlcl.sh'
    echo _______________________________________________________________________________
    echo 5.2 SQLDeveloper
    sudo runuser -l oracle -c '/vagrant/scripts/database/installSqlDeveloper.sh'
    echo _______________________________________________________________________________
    echo 6. Fusion Middleware
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/installFMW.sh'
    echo _______________________________________________________________________________
    echo 6.1 Fusion Middleware - SOA
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/installSOA.sh'
    echo _______________________________________________________________________________
    echo 6.2 Fusion Middleware - SB    
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/installSB.sh'
    echo _______________________________________________________________________________
    echo 6.3 Fusion Middleware - OHS    
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/installOHS.sh'
    echo _______________________________________________________________________________
    echo 7. BPM Quickstart
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/installBpmQS.sh'
  SCRIPT

And one for configuring FMW, that is running the RCU and creating the domain:
  $configFMWScript = <<-SCRIPT
    echo _______________________________________________________________________________
    echo 8.1 Fusion Middleware - RCU SOA   
    sudo runuser -l oracle -c '/home/oracle/bin/startDB.sh'    
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/rcuSOA.sh'
    echo _______________________________________________________________________________
    echo 8.2 Fusion Middleware - Create Domain    
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/fmw1221_domain/1.recreateFMWDomain.sh'
    echo !!! TODO: Machine configuration update to use Plain - 5555
    echo !!! TODO: Modify domain creation and property naming to create machine in accordance to nodemanager config.
    echo _______________________________________________________________________________
    echo 8.3 Fusion Middleware - Modify Nodemanager     
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/fmw1221_domain/2.modifyNodeManager.sh'
    echo _______________________________________________________________________________
    echo 8.4 Fusion Middleware - Create Nodemanager service
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/fmw1221_domain/3.createNodemanagerService.sh'
    #
  SCRIPT

Cool, so far, right?

Now, after that we need to define the 3 provisioners:

  config.vm.provision "init", type: "shell", inline: $initScript
  config.vm.provision "installFMW", type: "shell", inline: $installFMWScript
  config.vm.provision "configFMW", type: "shell", inline: $configFMWScript

These provisioners

  • init -> provisioning/config of Oracle Linux, creation of oracle user, etc. This will be about equal for every box.
  • installFMW -> installation of all FMW software.
  • configFMW -> run the RCU and create domein.
Having that in place, you can run a specific provisioner. For instance during up:
  • vagrant up --provision-with configureFMW
or when reloading the box:
  • vagrant reload --provision-with configureFMW
But the following also works:
  • vagrant provision --provision-with configureFMW
Another cool thing is that you also could set a run option on the provisioner.
  config.vm.provision "init", run: "once", type: "shell", inline: $initScript
  config.vm.provision "installFMW", type: "shell", run: "once", inline: $installFMWScript
  config.vm.provision "configFMW", type: "shell", run: "once", inline: $configFMWScript

The run option has the following possible values:
  • "once": this is actually the default, the provisioner is only executed at first up. Or if you force it to run as described above.
  • "always": the provisioner is executed at every up. This can be used for something you want to be done every time you do up. A good one would be to start the database.
  • "never": this one is interesting. This makes the provisioner optional. That means it won't be executed, unless you ask for it. A good one would be to drop the RCU and delete the domain. So that you can reprovision the repository and the domain.
Happy Vagrant Upping!

Friday, 29 March 2019

My Seemless Linux Desktop using VirtualBox, Vagrant, MobaXterm and TotalCommander

Years ago I played around with VMWare Unity Mode in VMWare player or VirtualBox's Seemless mode. In those modes you start an application on your virtual machine, but the windows appear as running on your host system. Back in the days I ran OpenSuse on my laptop, and had Windows XP or 7 on a Virtual Machine for those cases I had to run PowerPoint.

I wasn't too enthousiastic about those modes. Of the two I found that VMWare implemented it the most transparent. But it was quite hard to work with multiple screens, and to start the applications using the embedded menu. Now, I didn't use this in a long time, so it might have been improved. But, lately I work with my VM's (mostly Oracle Linux)  using Vagrant most of the time. And I use MobaXterm to connect with them and shortly, I use it to start my Oracle tools from MobaXterm.

With the XWindows Server in MobaXterm, working with either SQLDeveloper or JDeveloper is very convenient. And it will allow me to cleanup even my local installments of SQLDeveloper or JDeveloper.

So, I have been looking for automating some startup and working on the most convenient setup for me. And I found it (for now) in the combination of:
  • VirtualBox
  • Vagrant
  • MobaXTerm 
  • And my All time favorite cockpit: TotalCommander

Let's go through them.

VirtualBox

It must have been around 2003 that I got introduced into VMWare Workstation by my former colleague Robert. It was an eyeopener to see that you could run multiple PCs on your laptop, separating different versions of Oracle Products that could even 'talk' to eachother!
Since all is saved in a folder of files, very handy was that I could do one install and share it with colleagues.

Since Oracle acquired VirtualBox, and because of several direction-changes at VMWare (introduction and revocation of VMWare Server, VMWare player that couldn't create VMs and then it could, and then it couldn't anymore,...), after a while it made sense to me to switch to VirtualBox fully. I had a time that I had both on my laptop.

So, it's VirtualBox now and we've come to version 6.0.4 at time of writing. Downloadable from the VirtualBox Download Page. Choose the download for your platform. Installing follows the familiar NNF-Pattern (Next-Next-Finish). And don't forget to download and install the Platform independent Extension Pack. I guess this is piece of cake for you followers.

Vagrant

This is quite new for me, since a bit over a year now. I transferred my main installations in a Vagrant project and it's still work in progress.

I already wrote a bit about my Vagrant solutions. Last few years I worked on scripting my installations of Oracle Products. Vagrant allows me to automate the creation and provisioning of my VM's. Relieving me from the need to keep multiple VMs up to date. Sharing can be both simpler but also harder. Simpler, because I could share my vagrant project and scripts. But also harder, because the provision scripts should be placed in the proper order. I should implement it in a VCS repo. But also the install-binaries should be placed with the proper name in the proper place.

I now have a Software Stage Repository on my second laptop disk, and  separate Vagrant projects. But they all have copies of provision scripts for several products. So, database, java, SOA/BPM QuickStart, Weblogic is duplicated per particular Vagrant Project. I want to split it up in a common provision folder and a Choose&Select approach in my Vagrant Projects in a way that I have a simpel Vagrant provisioning in where I can refer to the provisioning of particular products.

Anyway,  starting up and eventual provisioning of a box is simple: just issue the vagrant up command in the folder with the vagrant file is all you have to do. Suspending  a box is done using vagrant suspend.

My respected con-colleague Maarten Smeets wrote quite a bit about Vagrant and lately about a few good tips.

Vagrant has reached version 2.2.4 recently and can be downloaded here. It follows the famous NNF-pattern. But although it allows you to choose the install directory, it is very devoted to be installed in the c:\HashiCorp\Vagrant\. I quit trying to force it elsewhere.

MobaXterm

Many of my even respected coworkers stick with their all-time favorite Putty. Putty stays ubiquitous. It is simple, but I allways have found it a bit archaic. I thought I once noticed that the support was  terminated. However, just now they just released the stunning version number of 0.71. I don't mean to be sarcastic, and Putty has it's own right of existance. But give MobaXterm a try. It's loaded with nice features, including an SCP client that can follow your SSH session. And as said, also a XWindows Server. So, connect with ssh to your Linux server and run jmc, visualVM, Oracle Universal Installer, Weblogic Configurator, JDeveloper, SQLDeveloper, etc. etc. and the UI will pop-up on your desktop. Also MobaXterm includes cygwin so you're able to run a terminal session on Windows. It even allows you to do ps -ef to show your running windows apps!

I also discovered that really easy way to implement a tunnel with MobaXterm!

You can download it here, in a portable and an installer version. I choose the portable. The free edition includes games (why?) and  a limit number of sessions, macros and tunnels. But the professional edition only costs a few bucks/euros.

TotalCommander

Ever since the introduction of Windows '95, I disliked the Windows Explorer. Luckily I soon discovered Windows Commander, under pressure of Microsoft, renamed to Total Commander. And it's even  a better name, because it's about the second tool I install on a new Windows Desktop. Just after Firefox, to be able to close IE/Edge...

It's my cockpit, allows me to navigate to hot folders quickly, introspect files, navigate through archives, edit them or unpack them, multi-rename files, compare files, etc., etc. I just don't make coffee with it. One of the nice features is the button bar, where you can launch applications. And this is the thing I use for this blog.

Tie it all together

I realize that I overloaded you with sales talk about my favorite tools.

After installing all the tools and having your Vagrant project in place all can be tied together.

I have a project that provisions a VM with an Oracle Database, SQLDeveloper, SOASuite, and BPM QuickStart.

My TotalCommander Toolbar looks like:

You can right-click anywhere in the toolbar to edit it, create new buttons. A 'button' that is left empty (no command) is presented as a separation bar.

Startup & suspend the VM


Let's take a look at the Vagrant SOA Start button:

It's simple: the command is vagrant up and important here is that it should be executed in the folder where the vagrant file resides. I provide a tool tip, and I created an icon file from the Vagrant Logo using my favorite image app IrfanView.

Clicking the button will fire up the VM and potentially provision it. I copied the button to create a button to suspend the VM. The command there is vagrant suspend. But for the rest it is exactly the same.

MobaXterm local terminal

When you start MobaXterm you'll get to:
When you click on the 'Start local terminal' button, you get a shell window running in the user home folder that is presented by the tool:

You can ssh to a remote server from this terminal. Of course you can create a session to a remote server. Doing so for the first time, logging on to the particular user, allows you to save the password for that user. I've already done that, and then I can do a ssh oracle without the need to provide a password.

Start Database


On my remote server that is started already (using the TotalCommander Button), I have a script that starts the database.

When the VM is started with vagrant, by default it fires up an ssh deamon on port 2222. The command to start my database on the remote server is:
ssh oracle@localhost -p 2222  /home/oracle/bin/startDB.sh

And as you can see in the screendump: I put that in a script in the home folder.

MobaXterm provides several commandline options, that allows you to run  a script command at startup of MobaXterm. That is what I used to create a StartDatabase Button:
The command is just MobaXterm (I should put MobaXterm in a version-less folder name, with a version-less executable, or create a script for that).
As parameters I provided -newtab ./startDB.sh. This is to ensure that the script is started on a new tab in MobaXterm, in a new potential session. Little side affect is that it creates a tab on the MobaXterm every button click. So, I might end up closing a few tabs...

Start SQLDeveloper and JDeveloper (BPM QuickStart)

To start SQLDeveloper I have a sqldev.sh script with the following content:
nohup ssh oracle@localhost -p 2222  /home/oracle/bin/sqldev.sh  > sqldev.out 2>&1 &

And the button looks like:

Similarly, I have a button to start JDeveloper. And it alls and up in the following desktop:

It might need some tweeking. But for now I love it and it works like a charm.



Tuesday, 19 March 2019

SOASuite 12c upgrade - Composite DVMs

Today I found something curious in a composite upgraded from 11g to 12c, regarding DVMs. I sometimes use DVMs in BPEL to prevent the use of complex xpath expressions with many conditions. For instance, if I need to know if a JMSType is in a certain range and if it is I need to continue, I can create a DVM that has those JVMTypes correlated to an indicator.

Now, in 12c we have a new project structure. Where in 11g, about every component is in the root of the project, in 12c those are moved to a subfolder. That is, if you would create a new project:

Folder like xsd, wsdl, xsl in 11g are renated to Schemas, WSDLs and Transformations in 12c. We decided to refactor the upgraded projects to the new structure in 12c. So our BPEL processes are moved to the BPEL subfolder. This means that when referencing a transformation (xsl) you would adapt your xslt functions as:
        <assign name="Transform2MessageProperties">
          <bpelx:annotation>
            <bpelx:pattern>transformation</bpelx:pattern>
          </bpelx:annotation>
          <copy>
            <from expression="ora:doXSLTransformForDoc('../Transformations/Transform2MessageProperties.xsl', $Receive_InkomendBericht_InkomendBericht_InputVariable.InkomendBericht)"/>
            <to variable="messageProperties"/>
          </copy>
        </assign>
Here you see that the reference to the transformation is relative to the BPEL process, and thus '../Transformations'.

Keeping things transparant and uniform, we adapted the DVM references accordingly:
              <assign name="Assign_JmsType">
                <copy>
                  <from expression="dvm:lookupValue('../DVMs/DWN_Types.dvm', 'Type', bpws:getVariableData('messageProperties','/ns1:messageProperties/ns1:type') , 'JmsType', 'onbekend')"/>
                  <to variable="jmsType"/>
                </copy>
              </assign>

However, we got exceptions like:
<bpelFault><faultType>0</faultType><subLanguageExecutionFault xmlns="http://schemas.oracle.com/bpel/extension"><part name="summary"><summary>XPath expression failed to execute.
An error occurs while processing the XPath expression; the expression is dvm:lookupValue('../DVMs/DWN_Types.dvm', 'Type', bpws:getVariableData('messageProperties','/ns1:messageProperties/ns1:type') , 'JmsType', 'onbekend')
The XPath expression failed to execute; the reason was: oracle.fabric.common.xml.xpath.XPathFunctionException: Unable to get Metadata Manager for DVM "oramds:/deployed-composites/default/DVMs/DWN_Types.dvm"
Please ensure the Metadata Manager is available.
Check the detailed root cause described in the exception message text and verify that the XPath query is correct.
</summary></part><part name="code"><code>XPathExecutionError</code></part></subLanguageExecutionFault></bpelFault>


After some investigation and trial&error, I found it very peciuliar, that the reference apparently evoluated to: oramds:/deployed-composites/default/DVMs/DWN_Types.dvm. This actualy means that it searches for the DVM in the MDS relative to the deployed composite, but outside it's own folder. After the ../default/.. folder reference it should have the composites name and version/id. 

I tried it without the '../' reference in the path, and that worked!

Conclusion:

  • In SOA 12c the ora:doXSLTransformForDoc() function is apparently executed in the context of the BPEL process and thus relative references to the XSL file should be done relative to the BPEL process.
  • In SOA 12c the dvm:lookupValue() function appears to be executed in the context of the composite, so the reference to the DVM file should relative to the composite (composite.xml).
Now, you might state that DVM's should be in the MDS and then it should not give any problems. But in this case, the DVMs are strictly, particularly meant to drive the execution of the BPEL process. And thus should be part of the Composite. You could see them as an simple alternative for a business rule, or a more configurable condition-evaluation. DVMs in this category should not be shared through the MDS.





Friday, 15 March 2019

JavaDB not bundled anymore with JDK 8, as of U181

Today I was struggling with helping a colleague with a deployment of a SOA Project of his.
I couldn't get it deployed. It seemed I hit the problem described here. However when trying to connect to my Derby DB I got the following error:

I was very surprised. I checked and double checked my config. And check the library:
So, I checked those folders and found that they're not existing!
Now searching around I found in these release notes that as of Update 181 (let it just be the case that I just had this version of the JDK!) Java DB isn't bundled anymore:

Following the links it turns out that you should download it here.


I choose the zip and copied and unzipped it into my jdk:
[oracle@darlin-vce jdk]$ cp /media/sf_Stage/OpenSource/JavaDB/db-derby-10.14.2.0                                                                               -bin.zip .
[oracle@darlin-vce jdk]$ unzip db-derby-10.14.2.0-bin.zip
Archive:  db-derby-10.14.2.0-bin.zip
   creating: db-derby-10.14.2.0-bin/
  inflating: db-derby-10.14.2.0-bin/KEYS
  inflating: db-derby-10.14.2.0-bin/LICENSE
  inflating: db-derby-10.14.2.0-bin/NOTICE
  inflating: db-derby-10.14.2.0-bin/RELEASE-NOTES.html
...

Then I moved/renamed the folder to 'db':
[oracle@darlin-vce jdk]$ mv db-derby-10.14.2.0-bin db
[oracle@darlin-vce jdk]$ ls db/lib/
derbyclient.jar        derbyLocale_it.jar     derbyLocale_zh_TW.jar
derby.jar              derbyLocale_ja_JP.jar  derbynet.jar
derbyLocale_cs.jar     derbyLocale_ko_KR.jar  derbyoptionaltools.jar
derbyLocale_de_DE.jar  derbyLocale_pl.jar     derbyrun.jar
derbyLocale_es.jar     derbyLocale_pt_BR.jar  derbytools.jar
derbyLocale_fr.jar     derbyLocale_ru.jar     derby.war
derbyLocale_hu.jar     derbyLocale_zh_CN.jar
[oracle@darlin-vce jdk]$

After this I'm able to connect to the JavaDB:





So, that was my discovery of the day!

Monday, 11 March 2019

Upgrade SOASuite process to 12c - Sensor Actions JMS to AQ

At my current customer we're busy with upgrading our projects from 11g to 12c.

One of the solution my predecessors implemented, is to kick of archive processes using sensor actions.The archive processes listen to JMS Queues, that are implemented as AQ Queues. For that a Foreign Server is configured:

The Foreign Server has a reference to the datasource that points to the schema owning the queues. It has also one or more Connnection Factories:

And the queues have a mapping from a local JNDI to a remote JNDI. The remote JNDI is the name of the particular queue prefixed with Queue:

In the sensor actions we used to have a JMS Adapter configured with as a connection factory the JNDI name of the outbound connection pool, for instance eis/aqjms/DwnQueueDB. The connection factory in that outbound connection factory refers to the JNDI of the connection factory in the Foreign Server.

Now, it turned out that our archiving processes weren't kicked off. I found a few things.

Sensor property files

The sensors can be configured using in the Monitor view of the BPEL Designer. It can be accessed using the Monitor Icon top left. When an Sensor is defined you can click the attena icon. You can of course create new ones by right clicking on the activity.
Sensor actions can be edited by selecting them and click the pencil-edit-icon.


In 11g, all the artefacts land in the root folder of the composite by default. We refactored the composites by moving artefacts to respective folders, like SOA Suite 12c would do in a new project.
But we skipped the files ${bpel-process-name}_sensor.xml and ${bpel-process-name}_sensorAction.xml.  I moved those to the same folder as the BPEL process. With a refresh, the attena-icons re-appeared.

But, also the files are referenced in the composite.xml:
…
</componentType>
    <property name="configuration.sensorLocation" type="xs:string" many="false">BPEL/${bpel-process-name}_sensor.xml</property>
    <property name="configuration.sensorActionLocation" type="xs:string" many="false">BPEL/${bpel-process-name}_sensorAction.xml</property>
  </component>

These references aren't updated automatically when moving them. But it turns out that the properties are renamed as well (probably from 10g to 11g already):
  • pre-11g:  bpel.config.sensorLocation => 11g/12c onwards: configuration.sensorLocation
  • pre-11g:  bpel.config.sensorActionLocation=> 11g/12c onwards: configuration.sensorActionLocation

JMS Adapter doesn't register the properties

As said, we used to use the JMS Adapter. I found that after the modifications to properly reference the sensor/sensorAction files, the message was published, but not picked up. The Listening archive process had a Message Selector like: BPEL_SENSOR_NAME like 'MySensorName%'.

I have a query that allows me to select from the queue tables and introspect the queues as JMS Queues:
select  qtb.queue_table 
, qtb.queue 
, qtb.msg_id
, qtb.msg_state
, qtb.enq_timestamp
, qtb.user_data.header.replyto
, qtb.user_data.header.type type
, qtb.user_data.header.userid userid
, qtb.user_data.header.appid appid
, qtb.user_data.header.groupid groupid
, qtb.user_data.header.groupseq groupseq
, qtb.user_data.header.properties properties
, (select str_value from table (qtb.user_data.header.properties) prp where prp.name = 'tracking_compositeInstanceId') tracking_compositeInstanceId
, (select str_value from table (qtb.user_data.header.properties) prp where prp.name = 'JMS_OracleDeliveryMode') JMS_OracleDeliveryMode
, (select str_value from table (qtb.user_data.header.properties) prp where prp.name = 'tracking_ecid') tracking_ecid
, (select num_value from table (qtb.user_data.header.properties) prp where prp.name = 'JMS_OracleTimestamp') JMS_OracleTimestamp
, (select str_value from table (qtb.user_data.header.properties) prp where prp.name = 'tracking_parentComponentInstanceId') tracking_prtCptInstanceId
, (select str_value from table (qtb.user_data.header.properties) prp where prp.name = 'tracking_conversationId') tracking_conversationId
, (select str_value from table (qtb.user_data.header.properties) prp where prp.name = 'BPEL_SENSOR_NAME') bpel_sensor_name
, (select str_value from table (qtb.user_data.header.properties) prp where prp.name = 'BPEL_PROCESS_NAME') bpel_process_name
, (select str_value from table (qtb.user_data.header.properties) prp where prp.name = 'BPEL_PROCESS_REVISION') bpel_process_rev
, (select str_value from table (qtb.user_data.header.properties) prp where prp.name = 'BPEL_DOMAIN') bpel_domain
, qtb.user_data.header
, qtb.user_data.text_lob text
, qtb.expiration_reason
--, qtb.*
from ( select 'DWN_OUTBOUND_TABLE' queue_table
       , qtb.* 
       from aq$dwn_outbound_table qtb
       union all
       ...
       union all
       select 'DWN_INBOUND_TABLE' queue_table
       , qtb.*
       from AQ$DWN_INBOUND_TABLE qtb) qtb
order by enq_timestamp desc;

This query lists the contents of several queue tables (always query queue tables via their AQ$Queue_table_name view) unioned together. From that you can introspect the user data and their properties witht he dot notation. The UserData has a header object, that contains a properties collection, that holds the JMS properties. You can select those as seen above.  It turns out that Sensor Actions should set the followign properties:
  • BPEL_SENSOR_NAME
  • BPEL_PROCESS_NAME
  • BPEL_PROCESS_REVISION
  • BPEL_DOMAIN
I found that using the JMS Adapter as a publish type in the SensorAction, these properties aren't set in 12c. While they apparently were in 11g.

After some researching, with no luck, I figured that I could try using a JMS Queue configuration. I wondered what the difference would be. Well, it turned out that using a JMS Queue did work.
Reconfiguring the Sensor Action to use a JMS Queue  means:
  • Set Publish Type to JMS Queue (obviously)
  • The JMS Connection Factory need to hold the JNDI name of the connection factory to use. In our case the one registered at the Foreign Server. (With JMSAdapter as Publish Type, this property is called JMSConnectionName.)
  • The Publish Target is now the JNDI Name of the destination. In our example (see the screen shot above) it is the local jndi of the queue to publish to. With the JMS Adapter it was the queue name.

So, in our case this worked.



Wednesday, 20 February 2019

Generate a formatted guid from database - Use Snippets

A very simple quick post today. I'm re-engineering a few Java based Mock Webservices into a SOA Suite/BPEL service.

Some of those generate a soap fault when a message id contains "8888" for instance.
I'd like to generate a GUID based message id, that is formatted with groups of four digits.

Of course there are loads of methods to do that. For instance, the Oracle database has a sys_guid() function for years. It generates a guid like: '824F95ECCB1C0EB7E053120B260A2D0F'.

But, I'd like it in a form '824F-95EC-CB1F-0EB7-E053-120B-260A-2D0F'. It can easily done by concatenating substr() calls. But you do not want to re-generate the guid with every 4 digit substr().

So, I put it into the following select:
with get_guid as (select sys_guid() guid
from dual)
select guid
, substr(guid, 1, 4)||'-'||substr(guid, 5, 4)||'-'||substr(guid, 9, 4)||'-'||substr(guid, 13, 4)||'-'||substr(guid, 17, 4)||'-'||substr(guid, 21, 4)||'-'||substr(guid, 25, 4) ||'-'||substr(guid, 29, 4)guid_formatted
, length(guid) guid_length
from get_guid;

What might be less obvious for the regular SQL developer is the with class. It is explained excelently by Tim Hall (and although around since Oracle 9.2 already, only recently put in my personal skill-box). This allows me in this query to call the sysguid() and reuse the value in the three columns.

Although this is a very simple query, it might come in handy more often. And since I'll be around this customer for a longer period, I expect, I want to save it as a snippet.

A feature around in SQLDeveloper for years are the snippets. You can make them visible through the View menu:
I tabbed-it away to the left gutter, to have it out of my way, but still in reach:
Create and edit snippets through the indicated icons. You can create your own categories, by just enter a new category name. Name it, provide a tool tip and paste the snippet. Easy-piecy.

You'll find quite a number of predefined snippets categorized neatly.

If you have gathered several of those snippets like me, and maybe want to take them to other assignments, you might feel the need to backup them.

To reuse a snippet just drag and drop them from the list into your worksheet.

The Snippets are stored in the UserSnippets.xml in the roaming user profile of SQL Developer:
In Windows like 'c:\Users\makker\AppData\Roaming\SQL Developer\'. Just backup/copy the file. Here you see the CodeTemplate.xml file as well, that contains the shorthand acronyms/aliases to much typed pieces of code that you can create too.

By the way, googling "That Jeff Smith Snippets" brought me this archived article (yes, snippets are that old) and with a link to this nice still active library of snippets.

Friday, 15 February 2019

Upgraded my Virtualization environment

A few weeks ago VirtualBox 6.0.4 is released. A minor release of the recent major release of 6.0. Although already anounced by Tim ~Oracle Base ~ Hall, I had not upgraded yet. I was still on 5.2.x. Change log of VirtualBox can be here. There are some interesting improvements. For instance, I'm curious to see what we can expect from the Oracle Cloud integration. And on several points, like shared folders, the performance is improved.

The UI is refreshed. I like the separate Tools bar with quick buttons to Import, Export and create new VM's. But, since I work with Vagrant more and more, I will see this screen less and less.


Also Vagrant has a new version since beginning of january. And I upgraded to 2.2.3. Change log can be found here.

All seem to function fine together. With my recent uprgaded MobaXterm 11.1 I can start my JDeveloper 12c from the started VM perfectly.
 The VM was suspended with VBox 5.2.x and Vagrant 2.2.2. And started with the latest greated. With nothing on the hand.

By the way, VMWare Player had VMWare Unity and VirtualBox the Seamless mode for years. It allows you to start your apps in the VM and run them as if they run as separate windows on your host. Years ago when I used VMWare Player, I was quite impressed by it. But I never got used to the VBox Seamless mode. Nowadays my favorite way of working is to connect to start the VM without UI (set the  vb.gui property to false in your Vagrantfile), connect to it using MobaXterm and start the app (JDeveloper for instance). The X Server implementation of MobaXterm will take care of the rest. Works like a charm!

Wednesday, 13 February 2019

KafkaSeries: Starting KafkaServers in Java - Implementing the Observer pattern ... again

In my previous article I explained how I start a ZooKeeper Server (potentially more of them) in Java using the Observer pattern. As promised, in this article I will explain how I implement the starting of KafkaServers in about the same way. Again, using the Observer pattern.

In principle we need one ZooKeeper, although you can have run multiple instances in a HighAvailable version. I have to figure that out, by the way.

But we can have multiple KafkaServers. And that makes sense. You might remember that I'm planning to use Kafka in a Weblogic environment, where you can have multiple Managed Servers (for instance OSB or SOA) that run side-by-side in a cluster possibly on mulitple machines. You probably want to have the Kafka Clients (consumers & producers) connect to the local instance. I would. But, they should work together, exchanging messages, so you can track events that originated on the other instance.

So I implemented a KafkaServerDriver extending the Observable class the same way as the ZooKeeperDriver  in my previous article (I in fact copied it). I changed it in a way that it can start multiple instances of KafkaObserver.

So, let me go over the particular metods again.


 /**
     * Run from a ServerConfig.
     * @param config ServerConfig to use.
     * @throws IOException
     */
    public void runFromProperties(Properties ksProperties) throws IOException {
        final String methodName = "runFromProperties";
        log.start(methodName);
        log.info(methodName, "Starting server");
        KafkaConfig config = KafkaConfig.fromProps(ksProperties);
        //VerifiableProperties verifiableProps = new VerifiableProperties(ksProperties);
        Seq reporters = new ArraySeq(0);
        // Seq reporters = (Seq) KafkaMetricsReporter$.MODULE$.startReporters(verifiableProps);
        KafkaServer kafkaServer = new KafkaServer(config, new SystemTime(), Option.apply("prefix"), reporters);
        setKafkaServer(kafkaServer);
        kafkaServer.startup();
        log.end(methodName);
    }
This is essentially the method to start a Kafka Server. It begins with creating a KafkaConfig  object, from a plain java.util.Properties object. Again I created an own KafkaServer Properties class that extends the java.util.Properties object. In the ZooKeeper article I explained that I needed a few extra methods to get Int based properties or to default a property based on the value of another propertie. In this case another reason is that I want to be able to differentiate over KafkaServers, each having their own property files. We'll get into that later on.
The KafkaServer(s) allow for injecting MetricReporters that can do reporting of ruintme behavior of the particular KafkaServer in a desired way. I did not get that to work in my JDeveloper project, since these are Scala object that JDeveloper got confused by, so to speak. So, in this version I provide an empty Reporters Array.

Then we create a new KafkaServer object. The constructor expects the following parameters.
  • config: the KafkaConfig object, created from the properties.
  • new SystemTime(): a new org.apache.kafka.common.utils.SystemTime object.
  • Option.apply("prefix"): Option is a Scala way of defining a Map (Kafka is build in Scala). The value "prefix" is used to give a name to the Thread the KafkaServer will run in.
  • reporters: a list of reporters that can be provided to the KafkaServer, to monitor it.
Note by the way that the KafkaServer apparently will spawn a thread it self, that it will give a name. In our Observer pattern we'll put the KafkaServer in our own Thread.

To get a hold of the instantiated KafkaServer, we set it in our private attribute, and then startup the server.

KafkaServerDriver Properties 

We can have multiple KafaServers running in our environment. We could have multiple on the same host, or distributed over multiple hosts. Each of them will have their own property-files, since, especially when running on the same host, they need at least their own broker.id and also their own port and data/log folder.

To be able to differentiate over the different Kafka Servers and define which one of them should be started up on the particular host, I introduced my own KafkaServerDriverProperties file.
It looks like:
kafkaservers=server0,server1
server0.id=0
server0.propertyfile=server0.properties
server0.startupEnabled=true
server1.id=1
server1.propertyfile=server1.properties
server1.startupEnabled=true

This defines a list of kafkaservers (server0 and server1 in this example) and then for each of those a list of attributes. Of importance are the properties:
  • <server-name>.propertyfile: naming a copy of the server.properties file that is used for this server. It it's loaded from the classpath, so only the name should be provided.
  • <server-name>.startupEnabled: should the server be started on this host (true or false)?
To work with this conveniently I added another properties class: KafkaServerDriverProperties. An object from this class fetched from PropertiesFactory.getKSDProperties();, where it is instantiated based on the kafkaserverdriver.properties loaded from the classpath.
It transforms the comma-separated list into a List object, that enables you to iterate over it. And for each servername on the list it will get the propertyfile and startupEnabled properties and put that, wrapped in a properties object, in a HashMap, identified by servername. The getServerProperties(String serverName) method enables you to fetch those properties for a certain serverName.

Observing the KafkaServer Observable

 Having the above in place, the KafkaServerDriver Observable can be implemented with the ZooKeeperDriver as an example. But, since we want to be able to fire up multiple KafkaServers, this is slightly more complicated.

Start

The start method within the KafkaServerDriver looks like:
/**
     * Start KafkaServers
     */
    public void start() {
        final String methodName = "start";
        log.start(methodName);
        for (String kafkaServerName : ksdProperties.getKafkaServerList()) {
            log.debug(methodName, "Start KafkaServer: " + kafkaServerName);
            addKafkaServer(kafkaServerName);
        }
        //addKafkaServer();
        log.end(methodName);
}

It loops over the server names from the KafkaServerList from the KafkaServerDriverProperties. For each listed servername it will add a KafkaServer.

addKafkaServer

This method has some overloaded variants. One parameterless, that loads the default server.properties file from the class path and calls the variant that takes in a properties parameter.

But let's start with the addKafkaServer(String) variant:
     /**
     * Add a KafkaServer
     * @param kafkaServerName
     */
    public void addKafkaServer(String kafkaServerName) {
        final String methodName = "addKafkaServer(String)";
        log.start(methodName);
        try {
            Properties serverProperties = ksdProperties.getServerProperties(kafkaServerName);

            if (serverProperties.getBoolValue("startupEnabled")) {
                log.info(methodName, "Start KafkaServer " + kafkaServerName);
                String serverPropertiesFileName = serverProperties.getStringValue("propertyfile");
                log.debug(methodName, "KafkaServer propertyfile: " + serverPropertiesFileName);
                Properties ksProperties = null;
                if (serverPropertiesFileName != null) {
                    ksProperties = PropertiesFactory.getKSProperties(serverPropertiesFileName);
                } else {
                    ksProperties = PropertiesFactory.getKSProperties();
                }
                addKafkaServer(ksProperties);
            } else {
                log.info(methodName, "KafkaServer " + kafkaServerName + " has startupEnabled == false!");

            }
        } catch (IOException e) {
            log.error(methodName, "Failed to load properties!", e);
            throw new RuntimeException(e);
        }
        log.end(methodName);
}

This one takes in the kafkaServerName and gets the approppriate server Properties from the  KafkaServerDriverProperties  object. It it has the startupEnabled property set to true, then it will fetch the serverProperties file, and load that one. Using that Properties object it will call the addKafkaServer(Properties) variant:

    /**
     * Add a KafkaServer from properties
     * @param ksProperties
     */
    public void addKafkaServer(Properties ksProperties) {
        final String methodName = "addKafkaServer";
        log.start(methodName);
        KafkaObserver kafkaServer = new KafkaObserver(this, ksProperties);
        Thread newKSThread = new Thread(kafkaServer);
        newKSThread.setName("KafkaServer" + ksProperties.getProperty(PRP_BRKR_ID));
        kafkaServer.setKsThread(newKSThread);
        newKSThread.start();

        log.end(methodName);
}

What this does is pretty much equal to the addZookeeper() method in the ZooKeeperDriver class. Create a new KafkaObserver providing the KafkaServerDriver object (this) as a reference and the Kafka Server Properties object. And create a new Thread for it. New is (I didn't had that when I wrote the previous article about starting the ZooKeeper) is that I set the name of the Thread. Then I add the new thread tho the KafkaServer.

Construct a KafkaObserver


We saw that in the addKafkaServer a KafkaObserver is instantiated using a reference to the KafkaServerDriver object as an Observable and the KafkaServer Properties object.

The constructor to do so is as follows:

    public KafkaObserver(Observable kafkaServerDriver, Properties ksProperties) {
        super();
        final String methodName = "KafkaObserver(Observable, Properties)";
        log.start(methodName);
        this.setKsProperties(ksProperties);
        if (kafkaServerDriver instanceof KafkaServerDriver) {
            log.info(methodName,
                     "Add observer " + this.getClass().getName() + " to observable " +
                     kafkaServerDriver.getClass().getName());
            setKafkaServerDriver((KafkaServerDriver) kafkaServerDriver);
            kafkaServerDriver.addObserver(this);
        }
        log.end(methodName);
}

In it we set the properties, and register the KafkaserverDriver and add this new object as an observer to the referenced KafkaserverDriver.

Run the KafkaObserver

Since the KafkaObserver is a Runnable we need to implement the run() method:
    public void run() {
        final String methodName = "run";
        log.start(methodName);
        try {
            runFromProperties(getKsProperties());
        } catch (IOException ioe) {
            log.error(methodName, "Run failed!", ioe);
        }
        log.end(methodName);

}

Shutdown


Shutdown within KafkaObserver the is as easy as:
    /**
     * Shutdown the serving instance
     */
    public void shutdown() {
        final String methodName = "shutdown";
        log.start(methodName);
        log.info(methodName, "Let me shutdown " + getKsThread().getName());
        KafkaServer kafkaServer = getKafkaServer();
        kafkaServer.shutdown();
        log.end(methodName);
}

The KafkaServerDriver also has a shutdown() method:
 /**
     * Shutdown all KafkaServers
     */
    public void shutdown() {
        final String methodName = "shutdown";
        log.start(methodName);
        setShutdownKafkaServers(true);
        log.info(methodName, "Notify Observers to shutdown!");
        this.setChanged();
        this.notifyObservers();
        log.end(methodName);
}

It sets the shutdownKafkaServers indicator, as well as the changed indicator. Then it notifies the Observers. This will result in a signal to the update() method of all registered KafkaObservers:
    public void update(Observable o, Object arg) {
        final String methodName = "update(Observable,Object)";
        log.start(methodName);
        Thread ksThread = getKsThread();
        log.info(methodName, ksThread.getName() + " - Got status update from Observable!");
        KafkaServerDriver ksDriver = getKafkaServerDriver();
        if (ksDriver.isShutdownKafkaServers()) {
            log.info(methodName, ksThread.getName() + " - Apparently I´ve got to shutdown myself!");
            shutdown();
        } else {
            log.info(methodName, ksThread.getName() + " - Don't know what to do with this status update!");
        }
        log.end(methodName);
}

It checks if the registered KafkaServerDriver has the shutdownKafkaServers indicator set. If so (and it obvious will), it will call the shutdown() method, mentioned earlier.

Start & Shutdown

As with the ZooKeeperDriver you need to store the KafkaServerDriver object in a static variable, and call the respective start and shutdown methods. Using the mentioned KafkaServerDriverProperties file in the class path, the particular instance will know which KafkaServers need to be started. Make sure that for each kafkaserver you have a copy of the server.properties file as found in the Kafka distribution (for instance Confluent). Each copy need to have a unique broker.id and references to the data/log folders. And possibly a unique listen-port.

Libraries and Classpath

One of the things I often miss in articles like this (my excuses that I did not add it to the previous article, is a list of libraries to add to get the lot compiled.
If you take a look at the scripts, you'll find that it would just add all the libraries in the particular folder. I like to know what particular jar's I really need to get things compiled. The following jar files in the Confluent distribution are found to be needed for both having the project compiled as well as being able to run:
  • confluent/share/java/kafka/kafka.jar
  • confluent/share/java/kafka/kafka-clients-2.0.0-cp1.jar
  • confluent/share/java/kafka/log4j-1.2.17.jar
  • confluent/share/java/kafka/slf4j-log4j12-1.7.25.jar
  • confluent/share/java/kafka/slf4j-api-1.7.25.jar
  • confluent/share/java/kafka/kafka-log4j-appender-2.0.0-cp1.jar
  • confluent/share/java/kafka/zookeeper-3.4.13.jar
  • confluent/share/java/kafka/scala-library-2.11.12.jar
  • confluent/share/java/confluent-common/common-metrics-5.0.0.jar
  • confluent/share/java/kafka/scala-logging_2.11-3.9.0.jar
  • confluent/share/java/kafka/metrics-core-2.2.0.jar
  • confluent/share/java/kafka/jackson-core-2.9.6.jar
  • confluent/share/java/kafka/jackson-databind-2.9.6.jar
  • confluent/share/java/kafka/jackson-annotations-2.9.6.jar

Added to that I have the following folders added in my project's library listing:
  • confluent/etc/kafka/
  • KafkaClient/config
These contain the Kafka and Zookeeper property files, and also my own extra property files. They're loaded using a class loader, so they need to be on the class path.`

Conclusion

Well, that's about it for now. Next stop: create a Weblogic domain and try to add the startup and shutdown classes to it and see if I can have ZooKeeper and KafaServers booted with Weblogic.
And of course the proof of the pudding:  produce and consume messages.

Wednesday, 23 January 2019

KafkaSeries: Start Zookeeper from Java - Implementing the Observer pattern (while I can)

Introduction

Since a few months I'm diving into Apache Kafka. I've always been fascinated by queuing mechanisms.  And Apache Kafka nowadays is the most modern alternative. Lately I did a presentation on an introduction to Apache Kafka:




But now I'm investigating what I can do with it. Since Weblogic is one of my focus areas, I wanted to explore how I can embed Kafka into Weblogic.

I reasoned that when I want to use Kafka with a current customer, the administrators have to install kafka (eg. unzip the Confluent distribution), on a separate virtual server.
By default the distribution comes with startup and shutdown scripts. The administrators should use those, or create their own, and startup the Kafka and Zookeeper services. And of course keep those up-and-running.

I figured that when I would be able to start the services as a thread under a Weblogic server, no additional infra structure is needed. Also starting the Weblogic server would start the Kafka services as well.

Kafka needs a ZooKeeper service. You can see the ZooKeeper as a directory service for a Kafka infrastructure. Slightly comparable to an AdminServer in Weblogic. So it would make sense, as I see it, to start the ZooKeeper with the AdminServer. The Kafka Servers can be started as part of the Weblogic Managed Server(s)

Weblogic has a mechanism to do initializations and finalizations, using startup and shutdown classes, see these documentation. From there the ZooKeeper and KafkaServers can be started.

So I had to figure out how to start those from Java. Let's start with the ZooKeeper.
I put my sources on GitHub, so you can review them. But keep in mind that they're still under construction.

Starting a ZooKeeper

My starting point was this question on StackOverflow, that handles starting a ZooKeeperServer in Java, based on the ZooKeeperServerMain.java class. It was quite promising and soon I had a first version of my startup class working. Quite simple really. But, since I also want to be able to shut it down, I soon ran into some restrictions. Some methods and attributes I needed were protected and only reachable from the same package, for instance. I wasn't quite pleased with the implementation. Digging a bit further I ran into the source of that class over here. I decided to take that class, study it and based on that knowledge implement my own class.

I created a ZooKeeperObserver class, and transformed the public void runFromConfig(ServerConfig config) method from ZooKeeperServerMain.java class, into a public void runFromProperties(ZooKeeperProperties zkProperties) method.

It takes in a properties object, that is interpretted and used to start the ZooKeeper.

Zookeeper Properties

To keep things transparent and simple, I created a PropertiesFactory class that provides a method to read the zookeeper.properties from the class path (therefor we should add the /etc/kafka folder to it).
I also created an own Properties class extending java.util.Properties to add a few property getter methods, like getting an int value and defaulting a property based on an other property.

Lastly, I created the ZooKeeperProperties bean, to interpret the relevant ZooKeeper properties, from a read Properties object.

The relevant properties are:

Property
Meaning
Default
dataDir The location where ZooKeeper will store the in-memory database snapshots and, unless specified otherwise, the transaction log of updates to the database. /tmp/zookeeper
dataLogDir This option will direct the machine to write the transaction log to the dataLogDir rather than the dataDir. dataDir
clientPort The port to listen for client connections; that is, the port that clients attempt to connect to. 2181
clientPortAddress The address (ipv4, ipv6 or hostname) to listen for client connections; that is, the address that clients attempt to connect to. Empty: every NIC in the server host.
maxClientCnxns Limits the number of concurrent connections (at the socket level) that a single client, identified by IP address. 0: disabled, since this is a non-production config.
tickTime The length of a single tick, which is the basic time unit used by ZooKeeper, as measured in milliseconds. ZooKeeperServer.DEFAULT_TICK_TIME
minSessionTimeout The minimum session timeout in milliseconds that the server will allow the client to negotiate. Defaults to 2 times the tickTime. -1: Disabled
maxSessionTimeout The maximum session timeout in milliseconds that the server will allow the client to negotiate. Defaults to 20 times the tickTime. -1: Disabled

Only the properties dataDir, clientPort and maxClientCnxns are set explicitly in the zookeeper.properties file. See the Zookeeper Administration docs for more info (apparently Zookeeper is created/invented in the Hadoop project).

Run from Properties

The runFromProperties is the one that actually starts a ZooKeeperServer instance:
    /**
     * Run from ZooKeeperProperties .
     * @param zkProperties ZooKeeperProperties to use.
     * @throws IOException
     */
    public void runFromProperties(ZooKeeperProperties zkProperties) throws IOException {
        final String methodName = "runFromProperties";
        log.start(methodName);
        log.info(methodName, "Starting server");
        FileTxnSnapLog txnLog = null;
        try {
            // Note that this thread isn't going to be doing anything else,
            // so rather than spawning another thread, we will just call
            // run() in this thread.
            // create a file logger url from the command line args
            ZooKeeperServer zkServer = new ZooKeeperServer();

            txnLog = new FileTxnSnapLog(new File(zkProperties.getDataLogDir()), new File(zkProperties.getDataDir()));
            zkServer.setTxnLogFactory(txnLog);
            zkServer.setTickTime(zkProperties.getTickTime());
            zkServer.setMinSessionTimeout(zkProperties.getMinSessionTimeout());
            zkServer.setMaxSessionTimeout(zkProperties.getMaxSessionTimeout());
            setZooKeeperServer(zkServer);

            cnxnFactory = ServerCnxnFactory.createFactory();
            log.debug(methodName, "Create Server Connection Factory");
            log.debug(methodName, "Server Tick Time: " + zkServer.getTickTime());
            log.debug(methodName, "ClientPortAddress: " + zkProperties.getClientPortAddress());
            log.debug(methodName, "Max Client Connections: " + zkProperties.getMaxClientCnxns());
            cnxnFactory.configure(zkProperties.getClientPortAddress(), zkProperties.getMaxClientCnxns());
            log.debug(methodName, "Startup Server Connection Factory");
            cnxnFactory.startup(zkServer);
            cnxnFactory.join();
            if (zkServer.isRunning()) {
                zkServer.shutdown();
            }
        } catch (InterruptedException e) {
            // warn, but generally this is ok
            log.warn(methodName, "Server interrupted", e);
        } finally {
            if (txnLog != null) {
                txnLog.close();
            }
        }
        log.end(methodName);
}
Here you see that a ZooKeeperProperties is passed. A FileTxnSnapLog is initialized for the dataDir and dataLogDir. A ZooKeeperServer is instantiated, and the particular properties are set. Then a ServerCnxnFactory is created (as a class attribute for later use). The connection factory is used to startup the ZooKeeperServer.  Actually, at that point the control is handed over to the ZooKeeperServer. So, you want to have this done in a separate thread.

Observing the Observable

Now, you might think: What is it with the name ZooKeeperObserver? Earlier, I named it EmbeddedZooKeeperServer. But I found that name long and not nice. I found it funny that Observer has the word Server in it.

As mentioned in the previous section, when starting up the ConnectionFactory/ZookeeperServer, the control is handed over. The method is not left, until the ZooKeeperServer stops running.

I therefor want (as in many implementations) that the ZooKeeperServer, runs in a seperate thread, that I can control. That is, I want to be able to send a shutdown signal to it. For that I found the Observer pattern suitable. In this pattern, the Observable or Subject maintains a list of Observers that can be notified about an update in the Observable. To do so, the Observable extends the java.util.Observable class. And the Observer implements the java.util.Observer and Runnable interfaces.

How does it work? Let's go through the applicable methods.

Start and Add a ZooKeeper

The Observable is implemented by ZooKeeperDriver. In it we'll find a method start():
    public void start() {
        final String methodName = "start";
        log.start(methodName);
        addZooKeeper();
        log.end(methodName);
}
That's not too exiting, but it calls the method addZooKeeper():
    public void addZooKeeper() {
        final String methodName = "addZooKeeper";
        log.start(methodName);
        try {
            ZooKeeperProperties zkProperties = PropertiesFactory.getZKProperties();
            ZooKeeperObserver zooKeeperServer = new ZooKeeperObserver(this, zkProperties);
            Thread newZooKeeperThread = new Thread(zooKeeperServer);
            zooKeeperServer.setMyThread(newZooKeeperThread);
            newZooKeeperThread.start();
        } catch (IOException e) {
            log.error(methodName, "ZooKeeper Failed", e);
        }
        log.end(methodName);
    }

Here you see that the ZooKeeperProperties are fetched and a new ZooKeeperObserver is instantiated, using a reference to the ZooKeeperDriver object and the ZooKeeperProperties. Since the ZooKeeperObserver is a Runnable we can add it to a new Thread. That thread is also set to the ZooKeeperObserver so that it has a hold of it's own thread, when that come in handy.
And then the new thread is started.

Instantiate the ZooKeeperObserver

In the previous section, we saw that the ZooKeeperObserver is instantiated using a reference to the ZooKeeperDriver object. Let's see how it looks like:
    public ZooKeeperObserver(Observable zooKeeperDriver, ZooKeeperProperties zkProperties) {
        super();
        final String methodName="ZooKeeperObserver(Observable, ZooKeeperProperties)";
        log.start(methodName);
        this.setZkProperties(zkProperties);
        if (zooKeeperDriver instanceof ZooKeeperDriver) {
            log.info(methodName, "Add observer "+this.getClass().getName()+" to observable "+zooKeeperDriver.getClass().getName());
            setZooKeeperDriver((ZooKeeperDriver) zooKeeperDriver);
            zooKeeperDriver.addObserver(this);
        }
        log.end(methodName);
}

The ZooKeeperProperties are set. And then it checks if the Observable that is passed is indeed a ZooKeeperDriver. The ZooKeeperDriver is also set, and then the ZooKeeperObserver object is added as an Observer to the ZooKeeperDriver using the addObserver(this) method. This method is part of the java.util.Observable object that is extended. It adds the ZooKeeperObserver to a list, that is used to send the update signal to every instance on the list.

Run the ZooKeeperObserver

The ZooKeeperObserver is a Runnable so the run() method is implemented:


    public void run() {
        final String methodName = "run";
        log.start(methodName);
        try {
            runFromProperties(getZkProperties());
        } catch (IOException ioe) {
            log.error(methodName, "Run failed!", ioe);
        }
        log.end(methodName);
}

It calls the  runFromProperties(), that is explained earlier.

Shutdown

The ZooKeeperDriver has a shutdown() method:

    public void shutdown() {
        final String methodName = "shutdown";
        log.start(methodName);
        setShutdownZooKeepers(true);
        log.info(methodName, "Notify Observers to shutdown!");
        this.setChanged();
        this.notifyObservers();
        log.end(methodName);
}

It sets the shutdownZooKeepers indicator to true. This is an attribute that indicates what has been updated. In a more complex Observer pattern more kinds of updates can occur. So, you need to indicate what drove the update.
The most interesting statement is the call to the notifyObservers() method. It will call the implemeneted update() on every Observer in the list.

I implemented this earlier in another situation, a few years ago. And I reused it. But at first it did not work. I found that, apparently changed in Java 7 or 8, I had to add a call to the setChanged() method. The notification to the Observers only works after that call.

As said, notifyObservers() calls the update() method in the Observer:

    public void update(Observable o, Object arg) {
        final String methodName = "update(Observable,Object)";
        log.start(methodName);
        log.info(methodName, getMyThread().getName() + " - Got status update from Observable!");
        ZooKeeperDriver zkDriver = getZooKeeperDriver();
        if (zkDriver.isShutdownZooKeepers()) {
            log.info(methodName, getMyThread().getName() + " - Apparently I´ve got to shutdown myself!");
            shutdown();
        } else {
            log.info(methodName, getMyThread().getName() + " - Don't know what to do with this status update!");
        }
        log.end(methodName);
}

And this one actually checks in the ZooKeeperDriver if the change is because of the shutDownZooKeepers indicator.
If so, it calls it's own shutdown() method. If not, then the update is ignored. The shutdown does the following:
        final String methodName = "shutdown";
        log.start(methodName);
        log.info(methodName,"Let me shutdown "+myThread.getName());
        ZooKeeperServer zkServer = getZooKeeperServer();
        ServerCnxnFactory cnxnFactory = getCnxnFactory();
        cnxnFactory.shutdown();
        if (zkServer.isRunning()) {
            zkServer.shutdown();
        }
        log.end(methodName);
}

It gets the Connection factory and sends a shutdown() signal to it. if the ZooKeeper is still running (it shouldn't be), then it gets a shutdown() signal also.

Start and Shutdown

In the end you need to create an instance of the ZooKeeperDriver and save it into a static variable. Then you can call the start() method and later get the object again from the static variable, to call the shutdown() method.

Conclusion

This may look a quite complex to you, to start a server. But, again, I want to be able to embed the Kafka infrastructure in an other system, in my situation Weblogic. This method I'll use to do the same for the Kafka Servers. I'll write about that in a follow-up article. And then, I'll create a set of startup and shutdown classes for Weblogic.

It was fun to implement the Observer pattern again. But, when I encountered that the notifyObserver method did not work as expected at first, searching for a solution, I found that it is deprecated in Java 9. It will still work, but apparently people found that it has it's limitations and a better way of implementing it is developed.