Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Monday, 29 April 2019

Oracle Java Support: why should I pay for something that used to be free?


A few weeks ago, I discussed with a colleague about the new licensing model of Oracle Java.

Customers may have concerns about this, since until now a customer was used to be entitled to download Java Updates for free. At least I was.

During the discussion I posed a way of thinking that made sense to me, and that seems to be supported by document references.

For some time now you can download Open JDK, which is an open source reference implementation based on Oracle JDK, as I understand it. It states that it is a production ready. Although this story may be a bit more nuanced as I state here. In the past it was considered to be inferior to the Oracle JDK, whilst the Oracle JDK was also free. With the new release cycles, introduced with Java 9, Oracle committed to make OpenJDK as indistinguishable from OracleJDK as possible. So functional and security features are up to the level of Oracle JDK.

In short, if you don't want to pay for support you can go and use Open JDK. Or stay on your current version.

But, since Oracle is a Sales based organization, I'm not surprised if they want to be payed for delivering (Long Time) support on Java. Especially, when more and more software is from other vendors is based on Java, and when the competitors of Cloud platforms rely on it.

If you want to have support for Java, you should have a Support contract.

I mentioned already above, but what also changed, with Oracle 9, is the release cycle. Until Oracle Java 8, Oracle supported the JDK for a very, very long time. The globally, publicly available major Java versions were released in a few years pace. Java 7 was around for around 4 years, before Java 8 was released. Java 8 has been a round for 5, before Java 9 saw the light.

To get more in pace with the developments in the market, Oracle decided to start with half-yearly release cycles, starting with Java 9 in 2017. And now with every 6 months, a new Java version is released with new features. Features that do not make the cut, are delayed to the next release when they are ready. But the major Java version gets released. With that, also the support of the version is changed: the support on the release only lasts for the live span of the release, which is 6 months. To keep up with security and features, you to need to move on to the next major version, to keep supported. Currently we're at Java 12, from march '19.

If you can't keep up with that, Oracle provides a Long Time Support version, that is supported for time frame comparable of those of Java 6, 7 and 8. One of those half yearly releases are denoted LTS, and currently it is Java 11. It's most comparable with for instance RedHat Linux, providing Fedora as an open, publicly available version (like OpenJDK) and ReadHat Enterprise Linux, which is the LTS version.


Now, what if you have Licenses for Oracle products that rely on Java? Fusion Middleware, for instance, is only supported on Oracle Java, currently Oracle Java 8. You may have licenses for Weblogic, Coherence, Forms & Reports, etc. In those cases you have a restricted license of Oracle Java. Much like when you have an E-Business Suite, Siebel or any other Enterprise Information System of Oracle that uses the database. Then you can use the database, when you use it to support that setup. You cannot run custom code in it, do reporting on it or use the database in any other way.

The same counts for Java. If you run Weblogic, or have an application that uses Coherence, etc., you're entitled to download the updates for Java. See for instance this document about Restricted Oracle Java SE License in combination with Weblogic, or Support Entitlement for Java SE When Used As Part of Another Oracle Product. Also interesting: you can file support requests against that Oracle product, but not directly against Java SE, unless you have Java Support.

And, products like SQLDeveloper, sqlcl and ORDS are supported through the Database license, which also uses Java. So, having a Database license, you have support on SQLDeveloper and the Oracle Java, used by SQLDeveloper.

Notice that if you have a Weblogic License, but also have a custom java application not running in a Weblogic instance, it's not allowed to use the same JDK Updates! If the application uses HTTP to communicate with a Weblogic Server, for instance to call a REST or SOAP Service, you're not allowed to download updates for that Java Home.

Also, if you hava a custom java application that uses JDBC drivers to connect to a licensed Oracle database, then you're not allowed to download the Java updates. Oracle states that the JDBC drivers do not use an Oracle product-specific protocol.

I encountered a little while ago that JavaDB is not delivered with Oracle JDK anymore. I suppose that this is related to the changed licensing of Java.

I hope that this little article makes sense, to you and helps to understand the licensing model.

To sum up, the options you have
  1. Stay on the version you currently use, with out changes. If you can live or cope with being behind with security updates, this can be an acceptable choice.
  2. Keep up with the 6 months major version update pace, you can use OpenJDK. You keep up to date with the major versions and are secure.
  3. Stay on a LTS release and move with to another LTS at your own pace (but only for Oracle JDK).
This article have been simmering for a few weeks, since I've been busy with other stuff and I've got some review tips. But today I saw an article of Jeff Smith on the Oracle JDK with SQL Developer. So, this triggered me to update this article right away.

I did my best to blend my thoughts, with the review tips, and the notes of support. I put down what I think and learned in my own words, but I might have rephrased things a bit incorrectly.  Check out these more formal articles and statements:

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.

Wednesday, 5 September 2018

Zipping is easy in Java/Spring/SOASuite

Why using Spring in SOASuite?

This post is actually about Spring in SOASuite. Last few weeks I've come around several questions and implementations using Embedded Java in BPEL. The Java activity in Oracle BPEL is ideal for very small java snippets, to do a simple Base64 encode/decode for example. However, in the situations I encountered these weeks, the code samples were more complex. And my very first recommendation in these situations is to use the SpringContext component, a technique introduced in SOASuite 11g, that I wrote about in 2012 already. I called it 'Forget about WSIF: welcome Spring', but really: forget about Embedded Java in most cases too! So, with this article, I want to showcase the SpringContext component in SOASuite.

A few disadvantages of the Embedded Java activity:
  • To get to, and set or manipulate, your BPEL data, you'll need to compose getVariableData() and setVariableData() functions with xpath-references. You can use temporary Assign activities with copy rules, from where you can extract the actual variable and xpath references, to copy and paste into the getVariableData() functions. But best workaround, to me is to declare a few xsd:string based variables and use Assigns before and after to exchange the data with the java snippet.
  • You can't test/debug the java snippets properly.
  • Catching and handling exceptions may not behave as you might want.
  • You can introspect variables that are referenced in your java snippet from the flow trace. But you must rely on the visual code compare between bpel and java snippet.
The advantages of using a SpringContext component:
  • You can test your java code standalone, and just reuse the methods as is.
  • Wiring your Spring Component to your BPEL will result in a WSDL and a PartnerLink. From BPEL you just invoke it like any other service. 
  • In the flowtrace you will see the input and output variables as is (provided that you set the audit level on Development). And you can just assign the data to and fro the variables based on the wsdl message types.
  • It's all declarative.
A disadvantage might be that it is a bit more complex. It might feel like a hazzle, so you wouldn't use it for 2 lines of Java code, probably. At the other hand, try it out, I guess you might experience it as so much easier and more reliable.

The case at hand

In the case at hand, as referred to in the title,  we need to read a zip file. We get files from Oracle B2B, that are saved on the filesystem with a system generated filename. That is different from the file as send, and provided using the context-disposition MIME header. The client wants to have the file moved and saved using the originating filename. We couldn't get it from B2B in an acceptable way. B2B saves the file using a filename like 'M1234987309891.2341921@myTPR1234_te2309098033.dat'. But it turns out to be a zip file that contains the file, with the name as we want it. So, we could just introspect the file to determine the name as which we want to move it. This means of course that we need to read the file, from Java. And to me it does not feel right to do that from a Java activity in BPEL. It's something more or less functional, so I want to abstract that in a service, based on a java class.

Unzip in Java

I found an unzip method in Java that does just about what I want here. It comes down to:
package nl.darwinit.ziputils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import nl.darwinit.ziputils.beans.ListZipEntriesRequest;
import nl.darwinit.ziputils.beans.ListZipEntriesResponse;
import nl.darwinit.ziputils.beans.ZipEntryFile;

public class Unzip implements IUnzip {

    public static final String className = "nl.darwinit.ziputils.Unzip";

    public Unzip() {
        super();
    }
...

...
    private static void unzip(String zipFilePath, String destDir) {
        File dir = new File(destDir);
        // create output directory if it doesn't exist
        if (!dir.exists())
            dir.mkdirs();
        FileInputStream fis;
        //buffer for read and write data to file
        byte[] buffer = new byte[1024];
        try {
            fis = new FileInputStream(zipFilePath);
            ZipInputStream zis = new ZipInputStream(fis);
            ZipEntry ze = zis.getNextEntry();
            while (ze != null) {
                String fileName = ze.getName();
                File newFile = new File(destDir + File.separator + fileName);
                System.out.println("Unzipping to " + newFile.getAbsolutePath());
                //create directories for sub directories in zip
                new File(newFile.getParent()).mkdirs();
                FileOutputStream fos = new FileOutputStream(newFile);
                int len;
                while ((len = zis.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                fos.close();
                //close this ZipEntry
                zis.closeEntry();
                ze = zis.getNextEntry();
            }
            //close last ZipEntry
            zis.closeEntry();
            zis.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Unzip unzip = new Unzip();
        int i = 0;
        for (String arg : args) {
            log("main", "arg[" + i++ + "]: " + arg);
        }
        ListZipEntriesRequest listZipEntriesRequest = new ListZipEntriesRequest();
        listZipEntriesRequest.setZipFileFolder(args[0]);
        listZipEntriesRequest.setZipFileName(args[1]);
        ListZipEntriesResponse listZipEntriesResponse = unzip.listZipEntries(listZipEntriesRequest);
        log("main", listZipEntriesResponse.toString());
    }
}


Besides reading all the ZipEntries in the zip file one by one, this does also save it using a FileOutputStream. But I do not want to output the files, but just get the filename. Another thing: this example shows a private static function. But for the SpringContext I need an instantiatable java class, so with a constructor, and a public method. So I created an Unzip class (with default constructor) and the following method:
    public ListZipEntriesResponse listZipEntries(ListZipEntriesRequest listZipEntriesRequest) {
        final String methodName = "ListZipEntriesResponse";
        logStart(methodName);
        ListZipEntriesResponse listZipEntriesResponse = new ListZipEntriesResponse();
        FileInputStream fileInputStream;
        //buffer for read and write data to file
        log(methodName, "List entries of " + listZipEntriesRequest.getZipFilePath());
        try {

            fileInputStream = new FileInputStream(listZipEntriesRequest.getZipFilePath());
            ZipInputStream zipInputStream = new ZipInputStream(fileInputStream);
            ZipEntry zipEntry = zipInputStream.getNextEntry();
            while (zipEntry != null) {
                String fileName = zipEntry.getName();
                log(methodName, "Entry: " + fileName);
                ZipEntryFile zipEntryFile = new ZipEntryFile();
                zipEntryFile.setFileName(fileName);
                listZipEntriesResponse.addZipEntryFile(zipEntryFile);
                //close this ZipEntry
                zipInputStream.closeEntry();
                zipEntry = zipInputStream.getNextEntry();
            }
            //close last ZipEntry
            zipInputStream.closeEntry();
            zipInputStream.close();
            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        logEnd(methodName);
        return listZipEntriesResponse;
    }

The logStart, logEnd and log methods are just wrappers around System.out.println(). As an input I have a bean with:
package nl.darwinit.ziputils.beans;

import java.io.File;


public class ListZipEntriesRequest implements IListZipEntriesRequest {
  
  private String zipFileFolder;
    private String zipFileName;

    public void setZipFileFolder(String zipFileFolder) {
        this.zipFileFolder = zipFileFolder;
    }

    public String getZipFileFolder() {
        return zipFileFolder;
    }

    public void setZipFileName(String zipFileName) {
        this.zipFileName = zipFileName;
    }

    public String getZipFileName() {
        return zipFileName;
    }

    public String getZipFilePath() {
        return getZipFileFolder() +File.separator+ getZipFileName();
    }    
}

Then as a response a bean with:
package nl.darwinit.ziputils.beans;

import java.util.ArrayList;
import java.util.List;

public class ListZipEntriesResponse implements IListZipEntriesResponse {
    private List zipEntryFiles;

    public void setZipEntryFiles(List zipEntryFiles) {
        this.zipEntryFiles = zipEntryFiles;
    }

    public List getZipEntryFiles() {
        return zipEntryFiles;
    }

    public void addZipEntryFile(ZipEntryFile zipEntryFile) {
        if (zipEntryFiles == null) {
            zipEntryFiles = new ArrayList();
        }
        zipEntryFiles.add(zipEntryFile);
    }
    
    public String toString(){
        StringBuffer strBuf = new StringBuffer("ListZipEntriesResponse\n");
        for (ZipEntryFile zipEntryFile : zipEntryFiles){
            strBuf.append("ZipEntryFile: "+zipEntryFile.toString()+"\n");
        }        
        return strBuf.toString();
    }
}

That uses a ZipEntryFile bean, as a List:
package nl.darwinit.ziputils.beans;


public class ZipEntryFile implements IZipEntryFile {
   private String fileName;

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getFileName() {
        return fileName;
    }
    
    public String toString(){
        return "fileName: "+getFileName()+"\n";
    }
}

Place your the source of your classes in the src of your SOA project. The classes are compiled in the the SOA/SCA-INF/classes folder (in 11g, you won't have the SOA subfolder). In 12c the sources can also be placed in the SOA/SCA-INF/src folder. But I found that this will cause a misterious nullpointer exception.

The spring context

My article 'Forget about WSIF: welcome Spring' neatly describes how to create a spring context in 11g. In 12c it does not differ much. You don't need to define separate Spring components per bean, though, but you actually can as shown in the article.  You do need to extract an Interface out of the main class. With only those methods you need in the interface. I actually extracted interfaces from all the beans, but you shouldn't have to.

In this case the Service xml would look like:
<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="Unzip" target="UnzipBean"
               type="nl.minvenj.ind.indigo.ziputils.IUnzip"/>
  <bean id="UnzipBean" class="nl.minvenj.ind.indigo.ziputils.Unzip"/>
  <bean id="ListZipEntriesRequestBean" class="nl.darwinit.ziputils.beans.ListZipEntriesRequest"/>
  <bean id="ListZipEntriesResponseBean" class="nl.darwinit.ziputils.beans.ListZipEntriesResponse"/>
  <bean id="ZipEntryFileBean" class="nl.darwinit.ziputils.beans.ZipEntryFile"/>
</beans>

Wire your Spring component to the BPEL component, and then you should have a dialog telling you that the WSDL is created, probably with an accompanying wrapper wsdl for the partnerlink roles.

And then in BPEL just invoke the code.

Happy Spring in SOA (better that than the otherway around if you speak Dutch...).


Friday, 4 May 2018

Enhance Vagrant provisioning: install java and database

In my previous blog posts (here and here), I wrote about how to create a base box and a create and start a virtual machine out of it. I started with provisioning, to have the vagrant user adapt the kernel settings, add a install user/owner and create a filesystem on an added disk.

Now let's make the provisioning a bit more interesting and install actual software in it.

Prepare new Vagrant project 

For this article I copied the project created from the previous blog. I called it ol75_db12c, since the goal is database 12c. But we'll also add java.
Now edit the Vagrantfile, since we want a new VM with another name:
So adapt the VM_NAME variable to something like "OL75U5_DB12c". You see how convenient it is to have those properties set as a global variable?

You could already try to do vagrant up to try it out. Remember, you can just do vagrant destroy to recreate it.

Also remove (or don't copy) the .vagrant subfolder, otherwise Vagrant would probably assume the box is already provisioned.  If that is the case, then either do a vagrant destroy to destroy the VM altogether, or vagrant provision to just re-provision the box.

Java

In the copied project, lets start with Java. There are several possibilities to install java, you could download the RPM from OTN. But one of the recommended practice I found in installing Java on a server is to put it in a path that hasn't got the java version (especially  the update) in it. When using it to install Weblogic, for instance, this path ends up in several places in scripts. Although it's a handfull, it's more than once.
Upgrading java is then just bringing down the servers/services using it, backup the version and unzip/untar the new version in the same folder. And there you have it: I like a java distribution that comes in an archive.

In Oracle Support you can find it by searching for the document All Java SE Downloads on MOS (Doc ID 1439822.1). There you find the current versions of the Java SE pacakges. For this article I used the public version JDK 8 Update 172, that you can download as patch 27412872:
 This contains the rpm as well as a .tar.gz package, that we'll use. Make sure that you download the x86_64 version:
And copy the download in the Stages folder in your vagrant main project folder (see previous blog).

To install it, I have the following script installJava.sh:
#!/bin/bash
#
#Download a zip with tar.gz containing complete JDK
#On MOS: Search for Doc ID 1439822.1
#Download latest 1.8 (public) patch, eg.:
#27412872  Oracle JDK 8 Update 172 (complete JDK, incl. jmc, jvisualvm)
#
SCRIPTPATH=$(dirname $0)
#
. $SCRIPTPATH/fmw12c_env.sh
#
TMP_DIR=/tmp
STAGE_HOME=/media/sf_Stage
EXTRACT_HOME=$STAGE_HOME/Extracted
JAVA_ZIP_HOME=$STAGE_HOME/Java
JAVA_INSTALL_HOME=$EXTRACT_HOME/Java
JAVA_INSTALL_TMP=$EXTRACT_HOME/jdk
JAVA_INSTALL_ZIP=p27412872_180172_Linux-x86-64.zip
JAVA_INSTALL_TAR=jdk-8u172-linux-x64.tar.gz
JAVA_INSTALL_NAME=jdk1.8.0_172

#
echo "Checking Java Home: "$JAVA_HOME
if [ ! -f "$JAVA_HOME/bin/java" ]; then
 #
  #Unzip Java
if [ ! -f "$JAVA_INSTALL_HOME/$JAVA_INSTALL_TAR" ]; then
    if [ -f "$JAVA_ZIP_HOME/$JAVA_INSTALL_ZIP" ]; then
      echo Unzip $JAVA_ZIP_HOME/$JAVA_INSTALL_ZIP  to $JAVA_INSTALL_HOME/$JAVA_INSTALL_TAR
      mkdir -p $JAVA_INSTALL_HOME
      unzip -o $JAVA_ZIP_HOME/$JAVA_INSTALL_ZIP -d $JAVA_INSTALL_HOME
    else
      echo JAVA Zip File $JAVA_ZIP_HOME/$JAVA_INSTALL_ZIP does not exist!
    fi  
  else
    echo $JAVA_INSTALL_TAR already unzipped
  fi
  # Install jdk
  echo Install jdk 
  echo create folder $JAVA_INSTALL_TMP
  mkdir -p $JAVA_INSTALL_TMP
  echo create JAVA_HOME $JAVA_HOME
  mkdir -p $JAVA_HOME
  echo Untar $JAVA_ZIP_HOME/$JAVA_INSTALL_TAR to $JAVA_INSTALL_TMP
  tar -xf $JAVA_INSTALL_HOME/$JAVA_INSTALL_TAR -C $JAVA_INSTALL_TMP
  echo Move $JAVA_INSTALL_TMP/$JAVA_INSTALL_NAME/* to $JAVA_HOME
  mv  $JAVA_INSTALL_TMP/$JAVA_INSTALL_NAME/* $JAVA_HOME
  #cp -R $JAVA_INSTALL_RPM/* $JAVA_HOME
  #sudo rpm -ihv $JAVA_INSTALL_HOME/$JAVA_INSTALL_RPM  
else
  echo jdk 1.8 already installed
fi

That uses the fmw12c_env.sh script:
#!/bin/bash
echo set Fusion MiddleWare 12cR2 environment
export ORACLE_BASE=/app/oracle
export INVENTORY_DIRECTORY=/app/oraInventory
export JAVA_HOME=$ORACLE_BASE/product/jdk

The script checks java already exists. If not then it checks if the tar or zip file exist in /media/sf_Stage/Java. If the tar file does not exist it will unzip the zip file. If the tar file does exist, then it will create a temp folder to extract the tar file into. Then it will create the java-home folder and move the extracted jdk folder to it.

I put these files in the scripts/fmw folder of my project:

Call script from provisioning

Now we're at the point that took me a lot of time to figure out last winter. How do I call this scripts form the provisioning. Simple question, but let me try to explain the difficulty.
The provisioning is done using the vagrant user. The vagrant user is in the sudoers list, so it's able to run a script using the permissions of another user, usually the super user (root). But it is still the running vagrant user who owns the resulting files and folders. I could do something like sudo su - oracle -c "script"... However, it still results in all the files owned by vagrant. So, if I run the Java install script, the complete java tree is owned by vagrant. But I want oracle to own it. Now,  I could create another base box and replace vagrant by oracle as the install user. But that is not the idea.

It took me some time to finally find the great utility runuser. This allows me to run the script as another substitute user. The runuser utility must be run as root, but that's no problem since vagrant is in the sudoers list.

So add the following lines to your provisioning part of the Vagrantfile:
    echo _______________________________________________________________________________
    echo 3. Java SDK 8
    sudo runuser -l oracle -c '/vagrant/scripts/fmw/installJava.sh'

With the -l argument I denote the user and with -c the command to run.

First try

Having done that, you could do a first try of the provisioning by upping the box.
Open a command window and start it the first time with vagrant up. Then if all goes well the provisioning ends with:
    darwin: _______________________________________________________________________________
    darwin: 3. Java SDK 8
    darwin: set Fusion MiddleWare 12cR2 environment
    darwin: Checking Java Home: /app/oracle/product/jdk
    darwin: Unzip /media/sf_Stage/Java/p27412872_180172_Linux-x86-64.zip to /media/sf_Stage/Extracted/Java/jdk-8u172-linux-x64.tar.gz
    darwin: Archive:  /media/sf_Stage/Java/p27412872_180172_Linux-x86-64.zip
    darwin:   inflating: /media/sf_Stage/Extracted/Java/jdk-8u172-linux-x64.rpm
    darwin:   inflating: /media/sf_Stage/Extracted/Java/jdk-8u172-linux-x64.tar.gz
    darwin:   inflating: /media/sf_Stage/Extracted/Java/readme.txt
    darwin: Install jdk
    darwin: create folder /media/sf_Stage/Extracted/jdk
    darwin: create JAVA_HOME /app/oracle/product/jdk
    darwin: Untar /media/sf_Stage/Java/jdk-8u172-linux-x64.tar.gz to /media/sf_Stage/Extracted/jdk
    darwin: tar: jdk1.8.0_172/bin/ControlPanel: Cannot create symlink to `jcontrol': Protocol error
    darwin: tar: jdk1.8.0_172/man/ja: Cannot create symlink to `ja_JP.UTF-8': Protocol error
    darwin: tar: jdk1.8.0_172/jre/bin/ControlPanel: Cannot create symlink to `jcontrol': Protocol error
    darwin: tar: jdk1.8.0_172/jre/lib/amd64/server/libjsig.so: Cannot create symlink to `../libjsig.so': Protocol error
    darwin: tar: Exiting with failure status due to previous errors
    darwin: Move /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/bin /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/COPYRIGHT /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/db /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/include /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/javafx-src.zip /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/jre /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/lib /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/LICENSE /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/man /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/README.html /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/release /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/src.zip /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/THIRDPARTYLICENSEREADME-JAVAFX.txt /media/sf_Stage/Extracted/jdk/jdk1.8.0_172/THIRDPARTYLICENSEREADME.txt to /app/oracle/product/jdk

It it all went well you can just destroy it:
d:\Projects\vagrant\ol75_db12c>vagrant destroy
    darwin: Are you sure you want to destroy the 'darwin' VM? [y/N] y
==> darwin: Forcing shutdown of VM...
==> darwin: Destroying VM and associated drives...

d:\Projects\vagrant\ol75_db12c>

Install database

Installing the database is a bit more complex. I'll not post every file, the code can be found here in github, as in fact the rest of my project files.
It expects the database installation as two zip files, V46095-01_2of2.zip and V46095-01_1of2.zip in the folder /media/sf_Stage/DBInstallation/12.1.0.2/x86_64/Zipped. Like for the rest it works much like my earlier install Fusion Middleware scripts. It unzips the files, installs the database based on the template response files. And it runs the database creation utility.

I also added scripts to install/unzip sqldeveloper and sql commandline.

To install the database and possibly SQLDeveloper and/or SQLcl, just add the following lines to your vagrant file:
    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'

And run vagrant up again.

Conclusion

I didn't get much into the install of the database. I feel that I wrote quite a lot in the past on installing Oracle software. In the upcoming period I'll wrap these lasts blogs into a presentation on creating boxes with Vagrant. This an input to my talk on this subject at the NLOUG Tech Experience 2018.

By now you should be able to replicate my findings and create other boxes as well. In the next blogs I might write about transforming my other FMW install scripts into vagrant project. We should now be able to create Vagrant projects to install OSB, SOA/BPM Suite, SOA/BPM Quickstart, etc.

Lately I did a Docker install in an Ubuntu 16 base box. Ubuntu has some peculiarities in creating users and logical volume management as opposed to RedHat/Oracle Linux. I might also write about that. But feel free to leave a comment if you have particular wishes. However, please have a bit of patience with me, since I first need to get my talk on the Tech Experience ready.


Tuesday, 4 July 2017

Weblogic log level mapping

For quite some time now I wondered about the differences in log-levels. For instance, if you configure the log levels in the classes in SB12c or SOA Suite,  You see levels as INCIDENT , ERROR, and TRACE, even with several sub leverls (1, 16, 32). But on the Server Log configuration in Weblogic, you see levels a s ERROR, NOTICE and DEBUG, or TRACE.  And then in Java, JDK logging we even have other log levels.

Today I experienced the urge to look it up and fout this nice document. It describes the configuration of logging in Fusion Middleware. It contains this nice table:


ODL
WebLogic Server
Java
OFF OFF 2147483647 - OFF
INCIDENT_ERROR:1 (EMERGENCY) 1100
INCIDENT_ERROR:4 EMERGENCY 1090
INCIDENT_ERROR:14 ALERT 1060
INCIDENT_ERROR:24 CRITICAL 1030
ERROR:1 (ERROR) 1000 - SEVERE
ERROR:7 ERROR 980
WARNING:1 WARNING 900 - WARNING
WARNING:7 NOTICE 880
NOTIFICATION:1 INFO 800 – INFO
NOTIFICATION:16 (DEBUG) 700 - CONFIG
TRACE:1 (DEBUG) 500 – FINE
TRACE:1 DEBUG 495
TRACE:16 (TRACE) 400 - FINER
TRACE:32 (TRACE) 300 - FINEST
TRACE:32 TRACE 295

This helps quite neatly. Especially, because to have ODL logging get through to the Server log-files, you need to set the appropriate level on the Weblogic server.

Monday, 2 June 2014

Message Correlation using JMS

Last year I created a few OSB services with the asynchronous request response message exchange pattern. OSB does not support this out of the box, since OSB is in fact synchronous in nature. Although OSB supports the WS-Addressing namespaces, you need to set the WS-Addressing elements programmatically.

Since OSB is synchronous the request and response flows in the Asynchronous Request/Response pattern are completely seperated implemented from eachother. That means that in the response flow you don't know what request message was responsible for the current response. Even worse: you don't know what client did the request and how to respond to that client in a way you can correlate to the initating instance. Using SOA/BPM Suite as a client, you want to correlate to the requesting process instance.

There are of course several ways to solve this. I choose to use a Universal Distributed Queue for several reasons, where knowledge of JMS and performance were a few. I only need to temporarly store a message against a key. Coherence was not on my CV yet. And a database table requires a database(connection) with the query-overhead, etc.

Unfortunately you can't use the OSB transports or SOASuite JMS adapters to get/browse for a message using a correlation-id in a synchronous way. When you create a proxy service on a jms transport or configure a JMS Adapter for read it will be a polling construction. But it's quite easy to do it in Java, so I created a java-method to get a message based on a CorrelationId.

One thing I did not know back then was that if you put a message on the queue from one OSB Server Node (having a JMS Server) it can't be read from the other node, as such. Messages are stored in the local JMS Server member of the Queue.

I found that you can quite easily reach the local member of a Universal Distributed Queue on a certain JMSServer on Weblogic by prefixing the JNDI name of the queue with the JMSServer separated with the at-sign ('@'):

    String jmsSvrQueueJndi = jmsServer +"@" +queueJndi

The problem now is: "how do you know which JMS Servers are available servicing your queue?"
I solved that by providing a comma seperated string of JMS Server names as a parameter in the Java-Callout in my proxy service. But that left me with a more or less hardcoded string of JMS Server names, that needs to be expanded when a second OSB Server node with a JMSServer instance is added. I figured that I should be able to query that from the WebLogic instance. And of course it can: that's I'm writing the blog.

First you need to get a connection to an MBeanServer in WebLogic. You can create a remote server connection, but since my java class is running on the OSB Server within WebLogic, a JNDI-lookup is better.

I reused a class that I created in the past for doing JNDI lookups of JDBC-Connections for both inside or outside the application server. I'll add it at the end of this blog entry.

To get a MBServer connection you simply get a JNDI Context from the JNDI Provider above do a lookup of the JNDI name "java:comp/env/jmx/runtime":
mbServer = (MBeanServer) jndiContext.lookup("java:comp/env/jmx/runtime");
 The documentation for this can be found here.

From the MBean Server you can get a list of JMS Servers by first instantiating a jmx 'ObjectName' using:
ObjectName queryObjName = new ObjectName("com.bea:Type=JMSServer,*");
Here we're about to ask for all MBeans of type 'JMSServer'.

 I named the JMSServers in my setup each with the same prefix 'OSBJMSServer' and a number like 'OSBJMSServer1', 'OSBJMSServer2'. So I want all the JMS Servers that start with 'OSBJMSServer'. For this I need to provide a jmx query expression. Luckily this is quite simple:
QueryExp queryExp = Query.initialSubString(Query.attr("Name"), Query.value("OSBJMSServer"));
Here I create a query expression that queries on the initial substring on the "Name" attribute of the ObjectName, that equals the query value "OSBJMSServer". I found a blog on the jmx query possibilities here.

Having this in place you only need to perform the query:
Set objNames = mbServer.queryNames(queryObjName, queryExp);
This gets you a set with all the ObjectName objects that matches the query-expression.

Now since I only need the plain name's of JMSServers (not their canonical names) I can add them easily into a List:
  List jmsServers = new Vector();
...
  for (ObjectName objName : objNames) {
   String jmsServerCanonicalName = objName.getCanonicalName();
   String jmsServerName = objName.getKeyProperty("Name");
   jmsServers.add(jmsServerName);
   lgr.debug(methodName, "JmsServerCanonicalName [" + jmsServers.size() + "]: " + jmsServerCanonicalName);
   lgr.debug(methodName, "JmsServerName [" + jmsServers.size() + "]: " + jmsServerName);
  }

For the canonical name there is a getter. For the Name property you need to use the getKeyProperty() method.

WebLogicJms Class

 The complete WeblogicJms class:
package nl.darwin-it.jms;

import java.util.List;
import java.util.Set;
import java.util.Vector;

import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.Query;
import javax.management.QueryExp;
import javax.naming.Context;
import javax.naming.NamingException;

import nl.darwin-it.jndi.JndiContextProvider;
import nl.darwin-it.log.Logger;

/**
 * @author Martien van den Akker, Darwin-IT Professionals Version 1.1 , 2014-06
 *         Class with methods to query JMS artifacts from WeblogicJMS
 */
public class WeblogicJms {
 public static final String MBSVR_JNDI = "java:comp/env/jmx/runtime";
 public static final String JMSSVR_OBJNAME_BASE_QRY = "com.bea:Type=JMSServer,*";
 public static final String JMSSVR_QRY_ATTR = "Name";
 public static final String JSBJMSSVR_NAME_PREFIX = "OSBJMSServer";

 private static final String className = "WeblogicJms";
 private static MBeanServer mbServer = null;
 private static Logger lgr = new Logger(className);

 /**
  * Get a lazy instantiation for the MBServer and cache it.
  * 
  * @return
  * @throws NamingException
  */
 private static MBeanServer getMBServer() throws NamingException {
  if (mbServer == null) {
   final Context jndiContext = JndiContextProvider.getJndiContext();
   mbServer = (MBeanServer) jndiContext.lookup(MBSVR_JNDI);
  }
  return mbServer;
 }

 /**
  * Get a list of JMS Servers belonging to OSB (OracleService Bus)
  * 
  * @return
  * @throws NamingException
  * @throws MalformedObjectNameException
  * @throws NullPointerException
  */
 public static List getOSBJMSServers() throws NamingException, MalformedObjectNameException,
   NullPointerException {
  final String methodName = "getOSBJMSServers";
  lgr.debugStart(methodName);

  List jmsServers = new Vector();

  final MBeanServer mbServer = getMBServer();

  ObjectName queryObjName = new ObjectName(JMSSVR_OBJNAME_BASE_QRY);

  QueryExp queryExp = Query.initialSubString(Query.attr(JMSSVR_QRY_ATTR), Query.value(JSBJMSSVR_NAME_PREFIX));

  Set objNames = mbServer.queryNames(queryObjName, queryExp);
  lgr.debug(methodName, "Found " + objNames.size() + " objects");
  for (ObjectName objName : objNames) {
   String jmsServerCanonicalName = objName.getCanonicalName();
   String jmsServerName = objName.getKeyProperty("Name");
   jmsServers.add(jmsServerName);
   lgr.debug(methodName, "JmsServerCanonicalName [" + jmsServers.size() + "]: " + jmsServerCanonicalName);
   lgr.debug(methodName, "JmsServerName [" + jmsServers.size() + "]: " + jmsServerName);
  }

  lgr.debugEnd(methodName);
  return jmsServers;
 }

 /**
  * Get the list of JSB JMS Servers in XML format serialized to string.
  * 
  * @return
  * @throws MalformedObjectNameException
  * @throws NullPointerException
  * @throws NamingException
  */
 public static String getOSBJMSServersXML() throws MalformedObjectNameException, NullPointerException,
   NamingException {
  final String methodName = "getOMSServersXML";
  lgr.debugStart(methodName);
  StringBuffer jmsServersXMLBuf = new StringBuffer("");
  List jmsServers = getOSBJMSServers();
  for (String jmsServer : jmsServers) {
   jmsServersXMLBuf.append("");
   jmsServersXMLBuf.append(jmsServer);
   jmsServersXMLBuf.append("");
  }

  jmsServersXMLBuf.append("");
  lgr.debugEnd(methodName);

  return jmsServersXMLBuf.toString();

 }

}

JNDI ProviderClass

The JNDI Provider Class:
package nl.darwin-it.jndi;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import nl.darwin-it.log.Logger;

/**
 * Class providing and initializing JndiContext.
 * 
 * @author Martien van den Akker
 * @author Darwin IT Professionals
 */
public abstract class JndiContextProvider {
 private static final String className = "JndiContextProvider";
 private static Logger lgr = new Logger(className);
 private static Context jndiContext = null;

 /**
  * Get an Initial Context based on the jndiContextFactory. If
  * jndiContextFactory is null the Initial context is fetched from the J2EE
  * environment. When not in a J2EE environment, the factory class is fetched
  * from the jndi.properties file that should be in the class path.
  * 
  * @return Context
  * @throws NamingException
  */
 private static Context getInitialContext() throws NamingException {
  final String methodName = "getInitialContext";
  lgr.debugStart(methodName);
  Context ctx = new InitialContext();
  lgr.debugEnd(methodName);
  return ctx;
 }

 /**
  * Create a Subcontext within the Initial Context
  * 
  * @param subcontext
  * @throws NamingException
  */
 public static void createSubcontext(String subcontext)
   throws NamingException {
  final String methodName = "createSubcontext";
  lgr.debugStart(methodName);
  lgr.debug(methodName, "Create Subcontext " + subcontext);
  Context ctx = getJndiContext();
  ctx.createSubcontext(subcontext);
  lgr.debugEnd(methodName);
 }

 /**
  * Set jndiContext;
  */
 public static void setJndiContext(Context newJndiContext) {
  jndiContext = newJndiContext;
 }

 /**
  * Get jndiContext
  */
 public static Context getJndiContext() throws NamingException {
  final String methodName = "getJndiContext";
  lgr.debugStart(methodName);
  if (jndiContext == null) {
   lgr.debug(methodName, "Get Initial Context");
   Context ctx = getInitialContext();
   setJndiContext(ctx);
  }
  lgr.debugEnd(methodName);
  return jndiContext;
 }
}

Wednesday, 12 September 2012

Forget about WSIF: welcome Spring

A few years ago I was very proud that I managed to call java from BPEL through WSIF. I think that the method pretty much should work in 11g. But my method back than used the deprecated tool Schema Compile. So I would need to figure out how to do that with JAXB or something. But now in the later patchsets of Weblogic/SOASuite there is a far more nicer method: Spring. Now, my experience with Spring starts since yesterday... Spring is a method to embed simple java objects (Plain Old Java Objects: POJO's) into an application server context. In SOASuite it is implemented in a real cool way, so that you can embed them in a real declarative way. If you google your way around you can find several examples on Spring and SOASuite. Documentation you can find here. And Edwin Biemond created an article about it. But unfortunately, it did not become clear to me how I had to implement my setup.

In my previous article I explained how to call Oracle Reports from java. I want to call that from BPEL or Mediator. Earlier I thought about the Socket adapter or the HTTP service-reference. But those options failed because the examples I found expect XML as an input (REST), where I needed to call an URL with parameters. Or they prevented me to call a url on port 80 (on which our reportservers listen...).
In that article you read that I had a service, and the input is a bean and the response is a bean. The reason of that was that I needed to pass multiple attributes as parameter and receive multiple attributes as a response. In reallife you might have a service with a bean structure as a request and response that reflect a complete hierarchical XML structure. The examples I found just talked about classes with elementary datatypes as  parameters and results. So how to set up your spring context to be able to do your call? At first I thought I needed just one context for the service. But it turns out that you have to create a context for every bean that plays a part in the interface (basically your service and every bean in the call parameter and response class structure).

To be able to expose the java classes to a WSDL (that is generated for you when you wire the service to BPEL) you have to generate Interfaces on your classes. So building further on my previous story, that would be on the classes ReportRequest, ReportResponse and CallReportsService.
This can be generated for you as follows. Open the ReportRequest class and right click in the source, choose Refactor and then Extract Interface:
Then you can select all the methods you want to expose:


In my previous article I put a method in my request bean to construct the Reports Server url (getReportsURL()). This method I don't want to have exposed in my WSDL. So I did not select that in the list. You can do the same for the other two classes.

Now it's time to declare the Spring-bean-structure. Open the composite editor and right-click:
When you choose Insert SpringContext, you might get a "Spring Not Available" error:
Go to Help->Check for Updates and search for the Spring & Weblogic SCA Addon:
Add it and restart JDeveloper.

Now you can add a SpringContext to your composite:
 Edit the Spring Context (CallReportsServer.xml). Then in the component palette you can look for the Weblogic SCA tab, and drag a service into the composite:

Name it CallReportsService, and as a target the CallReportsServiceBean. As a type you can browse to your classes, choose here the interface ICallReportsService.
Now you can add a bean to the context. The tag for this can be found on the "Spring 2.5 Core" page. But you can also type it in of course:
 Then you have to define it. Convenient is the Inspector palette:

 The Id of the bean should be the same as the target of the service: CallReportsServiceBean.
The class can be browsed the same way as the type in the Service. Choose here the implementation class "CallReportsService".

Now we can do the same for the other two beans. For the two input/output beans (ReportRequest and ReportResponse) I choose the bean implementation itself as the type. In my first setup I encountered that at Runtime SOASuite tries to call the public no-parameter-constructor of the type. Since an interface does not have a constructor, it failed. So I replaced that with the implementation bean.

If you have the three beans you can tie them together:
This gets you the following context definitions.
CallReportsServer.xml:
<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="CallReportsService" target="CallReportsServiceBean"
               type="nl.darwinit.soa.callreportsservice.ICallReportsService"/>
  <bean id="CallReportsServiceBean"
        class="nl.darwinit.soa.callreportsservice.CallReportsService"/>
  <sca:reference type="nl.darwinit.soa.callreportsservice.ReportRequest"
                 name="ReportRequest.ReportRequest"/>
  <sca:reference type="nl.darwinit.soa.callreportsservice.ReportResponse"
                 name="ReportResponse.ReportResponse"/>
</beans>

ReportRequest.xml:
<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="ReportRequest" target="ReportRequestBean"
               type="nl.darwinit.soa.callreportsservice.ReportRequest"/>
  <bean id="ReportRequestBean"
        class="nl.darwinit.soa.callreportsservice.ReportRequest"/>
</beans>

ReportResponse.xml:
<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="ReportResponse" target="ReportResponseBean"
               type="nl.darwinit.soa.callreportsservice.ReportResponse"/>
  <bean id="ReportResponseBean"
        class="nl.darwinit.soa.callreportsservice.ReportResponse"/>
</beans>

Well, actually that is about it. Now lets create a BPEL process.


I changed the xsd to:
<?xml version="1.0" encoding="UTF-8"?> 
<schema attributeFormDefault="unqualified"
 elementFormDefault="qualified"
 targetNamespace="http://xmlns.darwin-it.nl/CallReportsService/CallReportsService/CallReportsService"
 xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
 <element name="process">
  <complexType>
   <sequence>
    <xsd:element name="aanvraagID" type="xsd:string" minOccurs="0"/>
    <xsd:element name="destination" type="xsd:string" minOccurs="0"/>
    <xsd:element name="httpTimeOut" type="xsd:int" minOccurs="0"/>
    <xsd:element name="jobName" type="xsd:string" minOccurs="0"/>
    <xsd:element name="previewJN" type="xsd:string" minOccurs="0"/>
    <xsd:element name="reportName" type="xsd:string" minOccurs="0"/>
    <xsd:element name="reportsBaseUrl" type="xsd:string" minOccurs="0"/>
    <xsd:element name="userId" type="xsd:string" minOccurs="0"/>
   </sequence>
  </complexType>
 </element>
 <element name="processResponse">
  <complexType>
   <sequence>
    <xsd:element name="errorCode" type="xsd:string" minOccurs="0"/>
    <xsd:element name="errorMessage" type="xsd:string" minOccurs="0"/>
    <xsd:element name="jobId" type="xsd:string" minOccurs="0"/>
    <xsd:element name="jobStatus" type="xsd:string" minOccurs="0"/>
    <xsd:element name="jobStatusCode" type="xsd:string" minOccurs="0"/>
    <xsd:element name="responseXml" type="xsd:string" minOccurs="0"/>
   </sequence>
  </complexType>
 </element>
</schema>


Now we can tie the CallReportServer component to the BPEL Component:

Trying this will get a message that asks you to compile the classes, click yes:
 
 After the compilation, the wiring has to be redone, which will result in the message that the wsdl is created:
Now you can implement the BPEL Process.
Since I choose the ReportRequest implementation bean as a type in the ReportRequest Spring Context, it turns out that it generated an element for the reportURL getter. You could remove that from the wsdl, but since it is not exposed as a service, you can also leave it that way. Just don't map it to any input.

Now, you would think you're done. But there is one little caveat in the deployment of the spring contexts. We tied the beans neatly together. But the Spring Context of the CallReportServer.xml expects in the references Interfaces as a type. So change those to the interfaces of the beans:

<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  <sca:service name="CallReportsService" target="CallReportsServiceBean"
               type="nl.darwinit.soa.callreportsservice.ICallReportsService"/>
  <bean id="CallReportsServiceBean"
        class="nl.darwinit.soa.callreportsservice.CallReportsService"/>
  <sca:reference type="nl.darwinit.soa.callreportsservice.IReportRequest"
                 name="ReportRequest.ReportRequest"/>
  <sca:reference type="nl.darwinit.soa.callreportsservice.IReportResponse"
                 name="ReportResponse.ReportResponse"/>
</beans>
(nl.darwinit.soa.callreportsservice.ReportRequest changed to nl.darwinit.soa.callreportsservice.IReportRequest, same for IReportResponse)

Now you can deploy and test.

As you can see, it's a very declarative way to create a webservice on a java class.

I wrapped up my complete project here.