Monday Nov 10, 2014

Caching in OSB 12c without Out-Of-Process Coherence Servers

This article was published in the Oracle A-Team Chronicles web site, check it out: http://www.ateam-oracle.com/result-caching-in-osb-12c-without-out-of-process-coherence-servers/

Thursday Jul 10, 2014

Interoperability between Microsoft and SOA Suite 12c

This blog was published in the Oracle A-Team Chronicles web site, check it out: http://www.ateam-oracle.com/interoperability-between-microsoft-and-soa-12c/

Tuesday Apr 29, 2014

Enabling WAN Replication for Oracle Service Bus Result Cache

The result cache is a very cool functionality introduced in Oracle Service Bus to allow ESB developers to automatically cache responses from a external service in OSB's built-in in-memory data grid caching system, which is Oracle Coherence. No matter which external service you are planning dealing with, an web service, an REST API, an directory in the file system or an CICS transaction, if the result cache functionality is activated for that external service, the response payload of an specific request message will be putted in the caching system for future reuse if the same request message is received again. The result cache functionality also allows you to define a expiration criteria, so the response payloads entries can eventually expire.


ESB developers will activate this functionality in OSB neither to protect critical back-end external services, to offload it or to short its response time. In the scenario that wants to protect back-end external services, perhaps those services have some cost associate every time you send a message for them. This cost would have various meanings, like per-request-basis (a paid external service that allows customer's credit history querying), IT budget (an CICS transaction service in which each call consumes MIPS) or even performance costs. In the case of performance costs is that when we start thinking in offloading. When services are originally designed, we measure some approximate throughput and average latency, and we put enough hardware resources to sustain that measure. When a ESB is situated in front of those services, you are enabling more channels to interact with that service and maybe the new amount of channels can be too high for the existing hardware resources. Finally, you could enable this functionality to short the response time of some services. If some services are sensitive in terms of response time latency, so the result cache is a must have.

A common practice used by customers around the world is to have replicas of their system architecture in different data centers, allowing them to survive in case of catastrophes. But only having a replica of their system architecture in different data centers is not enough. There is a need to provide business continuity, which means that every single detail of the system architecture should be constantly synchronized between the data centers, so when a backup data center take place in a catastrophe scenario, the down time should be minimal. There is also scenarios when even small periods of down time are not acceptable. All the data centers should be in stand-by/active mode to take over the entire processing in any moment. The challenge here is to keep two types of things synchronized: system architecture artifacts and system transactions. System architecture artifacts are any piece of data that the run-time system architecture needs to properly work. Common examples of artifacts are XML configuration files, applications, log files, data files and storage. System transactions are a unit-of-work of a business transaction. A business transaction represents a single or multiple business processes of the organization, and most of the times a business transaction are associated to a monetary need. E-commerce sites for instance are good examples of business transactions that are associated to a monetary need. If the site loses a single transaction, that lost represents less incoming money. And that is a situation that no CFO/CEO likes to tolerate.

Back to the result cache functionality, imagine that you have OSB deployed in two or more data centers operating in active-active mode. A corporate load balancer distributes load across each data center though its exposed services. When a request arrives in one data center, OSB take that request and start processing it, causing one or more entries to be stored in the result cache for future reuse. If the same request arrives in another data center, the desire is that OSB pick the already processed result from the result cache instead of processing it again. This is true because from the customer/client point of view, it is the same service and invocation request. But what will really happen is that the request will be processed again since result cache by default do not replicate entries across data centers, only across clusters in the same local network. So the challenge here is to find a way to enable entries being replicated from one local network (a.k.a "LAN") to a remote network (a.k.a, "WAN") even if this remote network is geographically distant.

In this article, I will show step by step how to enable result cache data replication across different data centers connected through a WAN. Thanks to OSB's great product architecture, this configuration is very straightforward and you will not have to change nothing in your SOA services, neither even in the OSB deployment. Everything is done out-of-the-box by Oracle Coherence. This article will help you even if WAN replication is not your primary objective. If you have different OSB domains (in the same or different networks) in which some services are exactly the same in those domains, the same technique should apply. All the examples created in this article were based on Oracle Service Bus 11gR1 default installation, which comprises WebLogic 10.3.6, Coherence 3.7.1.1 and Service Bus 11.1.1.7.

Patching Oracle Coherence from Middleware's Home

Before starting using the Push Replication Pattern feature available in Coherence Incubator (it will be explained in the next topic) we need to patch the Coherence installation that come with WebLogic. When you install the WebLogic pre-requisite for OSB which is the WebLogic 11gR1 + Coherence package installer, the Coherence 3.7.1.1 version is installed in the middleware home location. We need to patch this Coherence installation so we can take advantage of the latest features of the Push Replication Pattern.

Update Coherence to the 3.7.1.11 version. You can get access to this version in the Oracle Support website. After logged in the Oracle Support Self-Service portal, go to the "Patches and Updates" tab and search for the following patch number: 17897749. Download this patch and update the Coherence installation according to the instructions available inside of the patch file.

Installing the Oracle Coherence Push Replication Pattern

The Push Replication Pattern is a extension for the Oracle Coherence product to allow remote clusters to exchange data across WAN networks. It is part of the Coherence Incubator project, an very cool initiative to enhance the Coherence product through community based feedback. It hosts a collection of projects with implementations of real world needs, in a form of design patterns. Even being open in terms of source code access, it is responsibility of Oracle engineers to provide new features, correction of bugs and documentation.

You need to download a compatible version of Coherence Incubator to the Coherence 3.7.1.11 release. Use the following link to get instructions about how to download the source code. After downloading the source code, you need to compile and build the run-time packages. To accomplish that, you will need the Apache Maven project management tool. With Apache Maven properly installed, follow the instructions of this link to compile and build the Coherence Incubator run-time packages.

Setting Up a Coherence Cluster with WAN Replication Support

Let's set up a Coherence cluster that allows data replication across a WAN network. The first thing to do is the definition of cache configuration files for both sites. The idea for those cache configuration files is that it should contains definitions for publishing and receiving endpoints. That means that one site should expose one or more endpoints to receive events from the other site and also define a remove invocation service to connect to the other site to publish events. It is a bi-directional communication across the sites in which the Push Replication Pattern takes care about when to publish/receive events using the endpoints. The listing code below shows the cache configuration file for site-01:

<?xml version="1.0" encoding="UTF-8"?>

<cache-config
    xmlns:element="class://com.oracle.coherence.environment.extensible.namespaces.XmlElementProcessingNamespaceContentHandler" 
    xmlns:event="class://com.oracle.coherence.patterns.eventdistribution.configuration.EventDistributionNamespaceContentHandler"
    xmlns:cr="class:com.oracle.coherence.environment.extensible.namespaces.InstanceNamespaceContentHandler">

    <caching-scheme-mapping>

        <cache-mapping>
            <cache-name>/osb/service/ResultCache</cache-name>
            <scheme-name>distributed-scheme-with-wan-replication</scheme-name>
            <event:distributor>
                <event:distributor-name>{cache-name}</event:distributor-name>
                <event:distributor-external-name>{site-name}-{cluster-name}-{cache-name}</event:distributor-external-name>
                <event:distributor-scheme>
                    <event:coherence-based-distributor-scheme/>
                </event:distributor-scheme>
                <event:distribution-channels>
                    <event:distribution-channel>
                        <event:channel-name>site-01-channel</event:channel-name>
                        <event:starting-mode system-property="channel.starting.mode">enabled</event:starting-mode>
                        <event:channel-scheme>
                            <event:remote-cluster-channel-scheme>
                                <event:remote-invocation-service-name>site-02-sync-proxy-service</event:remote-invocation-service-name>
                                <event:remote-channel-scheme>
                                    <event:local-cache-channel-scheme>
                                        <event:target-cache-name>/osb/service/ResultCache</event:target-cache-name>
                                        <event:conflict-resolver-scheme>
                                            <cr:class classname="com.oracle.coherence.patterns.eventdistribution.channels.cache.BruteForceConflictResolver" />
                                        </event:conflict-resolver-scheme>
                                    </event:local-cache-channel-scheme>
                                </event:remote-channel-scheme>
                            </event:remote-cluster-channel-scheme>
                        </event:channel-scheme>
                    </event:distribution-channel>
                </event:distribution-channels>
            </event:distributor>
        </cache-mapping>

    </caching-scheme-mapping>

    <caching-schemes>

        <distributed-scheme>
            <scheme-name>distributed-scheme-with-wan-replication</scheme-name>
            <service-name>wan-sync-enabled-distributed-cache-svc</service-name>
            <backing-map-scheme>
                <read-write-backing-map-scheme>
                    <internal-cache-scheme>
                        <local-scheme />
                    </internal-cache-scheme>
                    <cachestore-scheme>
                        <class-scheme>
                            <class-name>com.oracle.coherence.patterns.pushreplication.PublishingCacheStore</class-name>
                            <init-params>
                                <init-param>
                                    <param-type>java.lang.String</param-type>
                                    <param-value>{cache-name}</param-value>
                                </init-param>
                            </init-params>
                        </class-scheme>
                    </cachestore-scheme>
                </read-write-backing-map-scheme>
            </backing-map-scheme>
            <autostart>true</autostart>
        </distributed-scheme>

        <remote-invocation-scheme>
            <service-name>site-02-sync-proxy-service</service-name>
            <initiator-config>
                <tcp-initiator>
                    <remote-addresses>
                        <socket-address>
                            <address>soa.suite.machine</address>
                            <port>30002</port>
                        </socket-address>
                    </remote-addresses>
                    <connect-timeout>2s</connect-timeout>
                </tcp-initiator>
                <outgoing-message-handler>
                    <request-timeout>5s</request-timeout>
                </outgoing-message-handler>
            </initiator-config>
        </remote-invocation-scheme>
        
        <proxy-scheme>
            <service-name>site-01-trans-proxy-service</service-name>
            <acceptor-config>
                <tcp-acceptor>
                    <local-address>
                        <address>soa.suite.machine</address>
                        <port>20001</port>
                    </local-address>
                </tcp-acceptor>
            </acceptor-config>
            <autostart>true</autostart>
        </proxy-scheme>

        <proxy-scheme>
            <service-name>site-01-sync-proxy-service</service-name>
            <acceptor-config>
                <tcp-acceptor>
                    <local-address>
                        <address>soa.suite.machine</address>
                        <port>30001</port>
                    </local-address>
                </tcp-acceptor>
            </acceptor-config>
            <autostart>true</autostart>
        </proxy-scheme>

    </caching-schemes>

</cache-config>

Save this cache configuration file as coherence-cache-config-site-01.xml. Before we continue, let's spend some time understanding the code. If you look at the top of the configuration file you will see the mapping for the cache /osb/services/ResultCache. This cache name matches with the one the come bundled with OSB. Also in the cache mapping, you will see a section that starts with the tag event:distributor. This XML tag is part of the Coherence Incubator implementation as you probably have seen in the namespaces declaration section. The event:distributor section basically states for declaring which remote sites should receive events from created, modified, removed or expired entries of the local cache. In the declaration, it is defined that the site-02 will be updated through a remote invocation service declared as site-02-sync-proxy-service later in the configuration file.

Special attemption for the event:conflict-resolver-scheme section. This should be used when you are expecting that entries from one site conflicts with entries of another site, most of the time because synchronization failures due unstable network links. Using this section, you can plug custom implementations that would decide which entry should be considered. The BruteForceConflictResolver class used in this example is a out-of-the-box implementation that came with the Event Distribution Pattern, another pattern that is part of the Coherence Incubator project.

Finally, you also have two proxy-scheme declarations in the configuration file. The purpose of the site-01-trans-proxy-service is for receiving local events from the same site. As for the site-01-sync-proxy-service, it is used to receive remote events from the foreign sites. Using two different proxies, one for transaction and another for synchronization gives you the ability to fine tune each proxy throughput independently, configuring for instance a different pool of threads for each one. In theory, you should balance the same number of threads for both proxies to ensure a well synchronized cluster. The Push Replication Pattern executes its synchronization job between sites completely asynchronous, meaning that the thread that updates the local cache does not have to wait the thread the replicates the entry for a remote site. That is the reason why is so important have different proxies.

Now let's create the cache configuration file for the site-02. The listing code below is almost identical to the previous listing, except from the fact that this time we are defining how site-02 will synchronize with site-01:

<?xml version="1.0" encoding="UTF-8"?>

<cache-config
    xmlns:element="class://com.oracle.coherence.environment.extensible.namespaces.XmlElementProcessingNamespaceContentHandler" 
    xmlns:event="class://com.oracle.coherence.patterns.eventdistribution.configuration.EventDistributionNamespaceContentHandler"
    xmlns:cr="class:com.oracle.coherence.environment.extensible.namespaces.InstanceNamespaceContentHandler">

    <caching-scheme-mapping>

        <cache-mapping>
            <cache-name>/osb/service/ResultCache</cache-name>
            <scheme-name>distributed-scheme-with-wan-replication</scheme-name>
            <event:distributor>
                <event:distributor-name>{cache-name}</event:distributor-name>
                <event:distributor-external-name>{site-name}-{cluster-name}-{cache-name}</event:distributor-external-name>
                <event:distributor-scheme>
                    <event:coherence-based-distributor-scheme/>
                </event:distributor-scheme>
                <event:distribution-channels>
                    <event:distribution-channel>
                        <event:channel-name>site-02-channel</event:channel-name>
                        <event:starting-mode system-property="channel.starting.mode">enabled</event:starting-mode>
                        <event:channel-scheme>
                            <event:remote-cluster-channel-scheme>
                                <event:remote-invocation-service-name>site-01-sync-proxy-service</event:remote-invocation-service-name>
                                <event:remote-channel-scheme>
                                    <event:local-cache-channel-scheme>
                                        <event:target-cache-name>/osb/service/ResultCache</event:target-cache-name>
                                        <event:conflict-resolver-scheme>
                                            <cr:class classname="com.oracle.coherence.patterns.eventdistribution.channels.cache.BruteForceConflictResolver" />
                                        </event:conflict-resolver-scheme>
                                    </event:local-cache-channel-scheme>
                                </event:remote-channel-scheme>
                            </event:remote-cluster-channel-scheme>
                        </event:channel-scheme>
                    </event:distribution-channel>
                </event:distribution-channels>
            </event:distributor>
        </cache-mapping>

    </caching-scheme-mapping>

    <caching-schemes>

        <distributed-scheme>
            <scheme-name>distributed-scheme-with-wan-replication</scheme-name>
            <service-name>wan-sync-enabled-distributed-cache-svc</service-name>
            <backing-map-scheme>
                <read-write-backing-map-scheme>
                    <internal-cache-scheme>
                        <local-scheme />
                    </internal-cache-scheme>
                    <cachestore-scheme>
                        <class-scheme>
                            <class-name>com.oracle.coherence.patterns.pushreplication.PublishingCacheStore</class-name>
                            <init-params>
                                <init-param>
                                    <param-type>java.lang.String</param-type>
                                    <param-value>{cache-name}</param-value>
                                </init-param>
                            </init-params>
                        </class-scheme>
                    </cachestore-scheme>
                </read-write-backing-map-scheme>
            </backing-map-scheme>
            <autostart>true</autostart>
        </distributed-scheme>

        <remote-invocation-scheme>
            <service-name>site-01-sync-proxy-service</service-name>
            <initiator-config>
                <tcp-initiator>
                    <remote-addresses>
                        <socket-address>
                            <address>soa.suite.machine</address>
                            <port>30001</port>
                        </socket-address>
                    </remote-addresses>
                    <connect-timeout>2s</connect-timeout>
                </tcp-initiator>
                <outgoing-message-handler>
                    <request-timeout>5s</request-timeout>
                </outgoing-message-handler>
            </initiator-config>
        </remote-invocation-scheme>
        
        <proxy-scheme>
            <service-name>site-02-trans-proxy-service</service-name>
            <acceptor-config>
                <tcp-acceptor>
                    <local-address>
                        <address>soa.suite.machine</address>
                        <port>20002</port>
                    </local-address>
                </tcp-acceptor>
            </acceptor-config>
            <autostart>true</autostart>
        </proxy-scheme>

        <proxy-scheme>
            <service-name>site-02-sync-proxy-service</service-name>
            <acceptor-config>
                <tcp-acceptor>
                    <local-address>
                        <address>soa.suite.machine</address>
                        <port>30002</port>
                    </local-address>
                </tcp-acceptor>
            </acceptor-config>
            <autostart>true</autostart>
        </proxy-scheme>

    </caching-schemes>

</cache-config>

Save this cache configuration file as coherence-cache-config-site-02.xml. Now that we have cache configuration files from both sites in place, we can set up the Coherence cluster that will hold the WAN replication enabled caches. For the site-01, create one shell script file named coherence-cache-server-site-01.sh and write the following code:

JAVA_HOME=/oracle/fmw/jrockit-jdk1.6.0_45-R28.2.7-4.1.0
COHE_HOME=/oracle/fmw/coherence_3.7
COHE_INCU=/oracle/fmw/coherence-incubator
OWLS_HOME=/oracle/fmw/wlserver_10.3
ALSB_HOME=/oracle/fmw/Oracle_OSB1

CLASSPATH=$COHE_HOME/lib/coherence.jar:$OWLS_HOME/server/lib/wljmsclient.jar
CLASSPATH=$CLASSPATH:$COHE_INCU/coherence-common/target/coherence-common-11.3.1-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$COHE_INCU/coherence-eventdistributionpattern/target/coherence-eventdistributionpattern-11.3.1-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$COHE_INCU/coherence-messagingpattern/target/coherence-messagingpattern-11.3.1-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$COHE_INCU/coherence-pushreplicationpattern/target/coherence-pushreplicationpattern-11.3.1-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$ALSB_HOME/lib/osb-coherence-client.jar

JAVA_ARGS="-d64 -Xms256m -Xmx256m -XgcPrio:deterministic -XpauseTarget=250ms"
JAVA_ARGS="$JAVA_ARGS -Dtangosol.coherence.cacheconfig=coherence-cache-config-site-01.xml"
JAVA_ARGS="$JAVA_ARGS -Dtangosol.coherence.distributed.localstorage=true"

$JAVA_HOME/bin/java -cp $CLASSPATH $JAVA_ARGS com.tangosol.net.DefaultCacheServer

The given shell script code is self explanatory, so I will not enter in too much details. Just keep in mind that this type of cluster was designed to scale out, so if you need more storage capacity in the Coherence layer, just raise up more JVM nodes with the same configuration. Since there are no cluster defined, each JVM node that come up with will join the cluster automatically. Also, adjust the minimum and maximum heap sizes accordingly to suit your needs. Not to mention that you will need to adjust the global variables to your specific path needs.

For the site-02, create one shell script file named coherence-cache-server-site-02.sh and write the following code:

JAVA_HOME=/oracle/fmw/jrockit-jdk1.6.0_45-R28.2.7-4.1.0
COHE_HOME=/oracle/fmw/coherence_3.7
COHE_INCU=/oracle/fmw/coherence-incubator
OWLS_HOME=/oracle/fmw/wlserver_10.3
ALSB_HOME=/oracle/fmw/Oracle_OSB1

CLASSPATH=$COHE_HOME/lib/coherence.jar:$OWLS_HOME/server/lib/wljmsclient.jar
CLASSPATH=$CLASSPATH:$COHE_INCU/coherence-common/target/coherence-common-11.3.1-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$COHE_INCU/coherence-eventdistributionpattern/target/coherence-eventdistributionpattern-11.3.1-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$COHE_INCU/coherence-messagingpattern/target/coherence-messagingpattern-11.3.1-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$COHE_INCU/coherence-pushreplicationpattern/target/coherence-pushreplicationpattern-11.3.1-SNAPSHOT.jar
CLASSPATH=$CLASSPATH:$ALSB_HOME/lib/osb-coherence-client.jar

JAVA_ARGS="-d64 -Xms256m -Xmx256m -XgcPrio:deterministic -XpauseTarget=250ms"
JAVA_ARGS="$JAVA_ARGS -Dtangosol.coherence.cacheconfig=coherence-cache-config-site-02.xml"
JAVA_ARGS="$JAVA_ARGS -Dtangosol.coherence.distributed.localstorage=true"

$JAVA_HOME/bin/java -cp $CLASSPATH $JAVA_ARGS com.tangosol.net.DefaultCacheServer

Execute each script on its respective site. Keep they up and running while we start the configuration of how each local OSB will connect to those clusters to delegate its caching needs.

Changing Oracle Service Bus Default Caching Configuration

The last part of the configuration is both the most simple and important one. We need to teach OSB about how to connect to a external cluster (created and configured in the previous topic) instead of using its built-in Coherence cluster. Let's start with the site-01. Edit the internal Coherence cache configuration file used by OSB located in the following folder: <DOMAIN_HOME>/config/osb/coherence/osb-coherence-cache-config.xml. You will need to change the contents of the original file with the contents of the following list below:

<?xml version="1.0"?>

<!DOCTYPE cache-config SYSTEM "cache-config.dtd">

<cache-config>

    <caching-scheme-mapping>
        <cache-mapping>
            <cache-name>/osb/service/ResultCache</cache-name>
            <scheme-name>remote-scheme-with-wan-replication</scheme-name>
        </cache-mapping>
    </caching-scheme-mapping>

    <caching-schemes>
        <near-scheme>
            <scheme-name>remote-scheme-with-wan-replication</scheme-name>
            <invalidation-strategy>all</invalidation-strategy>
            <autostart>true</autostart>
            <front-scheme>
                <local-scheme />
            </front-scheme>
            <back-scheme>
                <remote-cache-scheme>
                    <scheme-name>distributed-scheme-with-wan-replication</scheme-name>
                    <service-name>ORA-OSB-deployments</service-name>
                    <initiator-config>
                        <tcp-initiator>
                            <remote-addresses>
                                <socket-address>
                                    <address>soa.suite.machine</address>
                                    <port>20001</port>
                                </socket-address>
                            </remote-addresses>
                        </tcp-initiator>
                    </initiator-config>
                </remote-cache-scheme>
            </back-scheme>
        </near-scheme>
    </caching-schemes>
	
</cache-config>

Let's understand what is being done here. Internally, OSB was built to invoke a cache named /osb/services/ResultCache when the result cache functionality is activated for a business service. Since we have changed its caching scheme, now when the cache is accessed, it will trigger remote invocations over TCP to the distributed cache available in the 20001 port. With the usage of a near-scheme type of cache, OSB can benefit from the best of worlds: part of the most recently data stored on its heap for rapid retrieval and the other part stored in a remote distributed cache. This configuration provides both high performance and scalability with the plus of easy administration, since all the data is stored in a cluster separated of OSB.

Here is the OSB cache configuration file for site-02:

<?xml version="1.0"?>

<!DOCTYPE cache-config SYSTEM "cache-config.dtd">

<cache-config>

    <caching-scheme-mapping>
        <cache-mapping>
            <cache-name>/osb/service/ResultCache</cache-name>
            <scheme-name>remote-scheme-with-wan-replication</scheme-name>
        </cache-mapping>
    </caching-scheme-mapping>

    <caching-schemes>
        <near-scheme>
            <scheme-name>remote-scheme-with-wan-replication</scheme-name>
            <invalidation-strategy>all</invalidation-strategy>
            <autostart>true</autostart>
            <front-scheme>
                <local-scheme />
            </front-scheme>
            <back-scheme>
                <remote-cache-scheme>
                    <scheme-name>distributed-scheme-with-wan-replication</scheme-name>
                    <service-name>ORA-OSB-deployments</service-name>
                    <initiator-config>
                        <tcp-initiator>
                            <remote-addresses>
                                <socket-address>
                                    <address>soa.suite.machine</address>
                                    <port>20002</port>
                                </socket-address>
                            </remote-addresses>
                        </tcp-initiator>
                    </initiator-config>
                </remote-cache-scheme>
            </back-scheme>
        </near-scheme>
    </caching-schemes>
	
</cache-config>

As you can see, it is the same code with the same techniques. The only difference is that instead of pointing to the Coherence cluster of site-01 on port 20001, it points to the Coherence cluster of site-02 on port 20002. That's all what we need to have OSB delegating its caching needs to a remote cluster. The diagram below gives you an overview of what we have done so far.

Start OSB in both sites. During start up, OSB will connect to the Coherence cluster and establish a connection. Because of this, consider as a deployment procedure start first the Coherence cluster to after the OSB cluster. Now that we have WAN replication properly configured, let's start some tests.

Testing the WAN Replication Behavior in Oracle Service Bus

In order to test the WAN replication behavior, I have developed a simple web service which takes ten seconds to complete each request. The idea is to have this web service as a OSB business service with result cache activated. Then, you need to create a proxy service in which its only job is to route its requests to the business service. Both the proxy service and the business service should be deployed at all the sites, along with the Web Service deployment. Here is the snippet code from the web service implementation:

package com.oracle.fmw.soa.osb.demo.services;

import java.util.Date;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

import com.oracle.fmw.soa.osb.demo.domain.Customer;

@WebService(name = "VerySlowWebService", serviceName = "VerySlowWebService")
public class VerySlowWebService {
	
	@SuppressWarnings("deprecation")
	@WebMethod(operationName = "findCustomerBySSN")
	public @WebResult(name = "customer") Customer findCustomerBySSN(
			@WebParam(name = "ssn") String ssn) {
		
		System.out.println("---> Entering in the Web Service Method Invocation...");
		
		try {
			
			Thread.sleep(10000);
			
		} catch (Exception ex) {
			
			ex.printStackTrace();
			
		}
		
		Customer customer = new Customer();
		customer.setSsn("832552291");
		customer.setFirstName("Ricardo");
		customer.setLastName("Ferreira");
		customer.setBirthDate(new Date(1981, 10, 05));
		
		return customer;
		
	}

}

A simple battery of tests to validate if everything is working should be:

  • Using the proxy service from site-01, make a request with "123456789" as the value of the SSN parameter. That request should take ~10 seconds to complete.
  • Using the proxy service from site-02, make a request with "123456789" as the value of the SSN parameter. That request should take ~01 second or less to complete.
  • Using the proxy service from site-02, make a request with "987654321" as the value of the SSN parameter. That request should take ~10 seconds to complete.
  • Using the proxy service from site-01, make a request with "987654321" as the value of the SSN parameter. That request should take ~01 second or less to complete.
  • Using the proxy service from site-01, make a request with "111111111" as the value of the SSN. Wait for the expiration of that entry in site-01. When it expires, check in the site-02 if the entry also expired.

Thinking in making things easier for you, I have made available all the project artifacts and OSB projects. Click in the links below to download them.

Wednesday Jan 08, 2014

Service Enablement of CORBA Applications through Oracle Service Bus

An quick overview in the definition of an ESB will tell us that one of its main responsibilities is among other things, the enablement of existing systems to provide new fresh services, using the same or maybe different protocols and/or contracts. This is when the ESB make magical things happen, virtualizing existing services and making them available for the outside world, abstracting from the applications that will consume the service details about the underlying system.

With this statement in mind, it is reasonable to think that an good ESB must be able to handle different types of systems and technologies found in legacy systems, no matter if this system was built last year, five years ago or even in the last decade. Those systems represents the assets of an organization in terms of their business building blocks, so there is a huge chance that those systems carries a substantial number of business services that could be leveraged by an SOA initiative.

CORBA is a distributed technology very powerful, that was pretty popular in the 90's and the beginning of 2000 year. Many industries that demands an robust infrastructure to handle their business transactions, critical by nature and extreme sensitive in terms of performance and reliability, relied on CORBA as technology implementation. It is pretty common to find communications companies like internet providers, mobile operators and pre-paid services chain that built its foundation (also known as engineering services) on top of CORBA systems.

This article will show how to enable CORBA systems through OSB, the ESB implementation from Oracle that is part of the SOA Suite. Through the steps showed here, you will be able to leverage existing CORBA systems and expose that business logic (defined as CORBA objects) in different transports, protocols and contracts, making the reuse of that business logic both possible and viable. This article will not cover any CORBA specific ORB to make the techniques available here reproducible in different contexts.

The Interface Definition Language

The definition of any CORBA object is written in an neutral description language called IDL, acronym of Interface Definition Language. For this example, I will consider that OSB will service enable an functionality that sends SMS messages, and this functionality is currently implemented as an object of an CORBA system. The IDL of this object is described below:

module corbaEnablementExample
{

    struct Message
    {

        string content;
        string phoneId;

    };

    interface SMSGateway
    {

        oneway void send(in Message message);

    };

};

As you can see, this is a very simple object that accepts an message as main parameter and the message has attributes that represents the content to be sent as an SMS message, and the mobile phone number that will receive the content.

The CORBA Server Application

It does not matter for the didactic of this article in which programming language the server part of the CORBA application will be implemented. What really matters is which ORB the CORBA server application will register its implementation stub. To illustrate the example, lets suppose that this CORBA object is implemented in Java.

package corbaEnablementApplication;

import org.omg.CORBA.ORB;
import org.omg.CORBA.Object;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;

import corbaEnablementExample.SMSGateway;
import corbaEnablementExample.SMSGatewayHelper;

public class ServerApplication {

	public static void main(String[] args) {
		
		ORB orb = null;
		Object tmp = null;
		POA rootPOA = null;
		SMSGatewayImpl impl = null;
		Object ref, objRef = null;
		SMSGateway href = null;
		NamingContextExt ncRef = null;
		NameComponent path[] = null;
		
		try {
			
			System.setProperty("org.omg.CORBA.ORBInitialHost", "soa.suite.machine");
			System.setProperty("org.omg.CORBA.ORBInitialPort", "8001");
			orb = ORB.init(args, null);
			
			tmp = orb.resolve_initial_references("RootPOA");
			rootPOA = POAHelper.narrow(tmp);
			rootPOA.the_POAManager().activate();
			
			impl = new SMSGatewayImpl();
			ref = rootPOA.servant_to_reference(impl);
			href = SMSGatewayHelper.narrow(ref);
			
			objRef = orb.resolve_initial_references("NameService");
			ncRef = NamingContextExtHelper.narrow(objRef);
			
			path = ncRef.to_name("sms-gateway");
			ncRef.rebind(path, href);
			
			System.out.println("----------------------------------------------------");
			System.out.println("  CORBA Server is Running and waiting for Requests  ");
			System.out.println("----------------------------------------------------");
			
			orb.run();
			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
	}

}

The code listing above shows an CORBA server application that connects onto a ORB available on one 8001 TCP/IP port. After retrieve the POA from the ORB, it get access to the naming service that will be used to register the object implementation. Finally, the application binds the object implementation under the name of "sms-gateway", the name which the CORBA object will be known from the outside world. In order to test this CORBA server application, start an ORB under the port 8001 and execute the program using one JVM. If you don't have any commercial ORB available, you can use the ORB which comes with the JDK. Just enter in the /bin folder of your JDK and type:

orbd -ORBInitialHost soa.suite.machine -ORBInitialPort 8001

To check if this remote object is working properly, you need to write an CORBA client application. Here is an example of an CORBA client written upon the same IDL interface which the server was written:

package corbaEnablementApplication;

import java.util.Random;
import java.util.UUID;

import org.omg.CORBA.ORB;
import org.omg.CORBA.Object;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;

import corbaEnablementExample.Message;
import corbaEnablementExample.SMSGateway;
import corbaEnablementExample.SMSGatewayHelper;

public class ClientApplication {
	
	private static final Random random = new Random();

	public static void main(String[] args) {
		
		ORB orb = null;
		Object objRef = null;
		NamingContextExt ncRef = null;
		SMSGateway smsGateway = null;
		
		try {
			
			System.setProperty("org.omg.CORBA.ORBInitialHost", "soa.suite.machine");
			System.setProperty("org.omg.CORBA.ORBInitialPort", "8001");
			orb = ORB.init(args, null);
			
			objRef = orb.resolve_initial_references("NameService");
			ncRef = NamingContextExtHelper.narrow(objRef);
			
			smsGateway = SMSGatewayHelper.narrow(ncRef.resolve_str("sms-gateway"));
			Message message = createNewRandomMessage();
			smsGateway.send(message);
			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
	}

	private static Message createNewRandomMessage() {
		String content = UUID.randomUUID().toString();
		String phoneId = String.valueOf(random.nextLong());
		Message message = new Message(content, phoneId);
		return message;
	}

}

The Business Services Layer

In order to OSB get access to the remote object, it is necessary to create an mechanism that can translate the IIOP protocol (the protocol used in pure CORBA systems) for one protocol that OSB can understand, which could be RMI/IIOP or pure RMI. To accomplish that, the best way is to implement the wrapper pattern. Write down one EJB 3.0 service that encapsulates the CORBA remote object, and delegates its service calls to this object. The interface for this EJB 3.0 service should be something simpler like this:

package com.oracle.fmw.soa.osb.corba;

public interface SMSGateway {
	
	public void sendMessage(String content, String phoneId);

}

The implementation of this EJB 3.0 service should perform a job similar to the CORBA client application described previously, but quite different in terms of how it connect to an ORB:

package com.oracle.fmw.soa.osb.corba;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Remote;
import javax.ejb.Stateless;

import org.omg.CORBA.ORB;
import org.omg.CORBA.Object;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;

import corbaEnablementExample.Message;
import corbaEnablementExample.SMSGatewayHelper;

@Remote(value = SMSGateway.class)
@Stateless(name = "SMSGateway", mappedName = "SMSGateway")
public class SMSGatewayImpl implements SMSGateway {
	
	private corbaEnablementExample.SMSGateway smsGateway;
	
	@Resource(name = "namingService")
	private String namingService;
	
	@Resource(name = "objectName")
	private String objectName;
	
	@PostConstruct
	@SuppressWarnings("unused")
	private void retrieveStub() {
		
		ORB orb = null;
		Object objRef = null;
		NamingContextExt ncRef = null;
		
		try {
			
			orb = ORB.init();
			objRef = orb.resolve_initial_references(namingService);
			ncRef = NamingContextExtHelper.narrow(objRef);
			smsGateway = SMSGatewayHelper.narrow(ncRef.resolve_str(objectName));
			
		} catch (Exception ex) {
			
			throw new RuntimeException("EJB wrapper failed in the retrieval of the CORBA stub.", ex);
			
		}
		
	}

	@Override
	public void sendMessage(String content, String phoneId) {
		
		smsGateway.send(new Message(content, phoneId));
			
	}

}

The code is very similar to the CORBA client application showed before, with one important difference: it has no information about which ORB to connect. In this case, the EJB will reside in on WebLogic JVM. Each WebLogic JVM has one ORB implementation out-of-the-box. So when you write EJB objects that will wrap-up CORBA remote objects, you don't need to worry about which ORB to use. WebLogic it is already an ORB.


Note: as explained before, this article will not enter in details of any commercial ORB available, in defense of the clarity and didactic of the article. But keep in mind that the steps shown here for the stub retrieval can be quite different if you are using another ORB. If you are using Borland VisiBroker for instance, there is a unique way to access the ORB which is using an service called "Smart Agent", which dynamically finds another objects in the network. IONA Orbix has another unique way to connect to an ORB, which is by the use of the domain configuration location of the Orbix network.


Create one WebLogic domain and execute one or more WebLogic managed servers, and re-run the CORBA server application again. Remember that now, the CORBA server application should point to the WebLogic port since the ORB now should be the one available in the WebLogic subsystem. If you check the JNDI tree of the WebLogic JVM, you should see something like this:

This means that the remote CORBA object was properly registered in the CosNaming service available in the WebLogic ORB. Package the EJB 3.0 implementation into a JAR or an EAR and deploy it in the same WebLogic JVM that the CORBA remote object was registered. Now we have everything in place to start the development of the OSB project. For the purposes of this article, I will assume that the EJB 3.0 object is available under the following JNDI name: "SMSGateway#com.oracle.fmw.soa.osb.corba.SMSGateway".

The OSB Project

In the OSB side, all you have to do is to create an business service that points to one or more endpoints of the EJB 3.0 that is running in the one or more servers of the WebLogic domain. In order to accomplish that, you will need to teach OSB about how to communicate with this foreign WebLogic domain. This is done creating an JNDI provider for the OSB configuration scheme:


OSB also needs to access the EJB 3.0 interfaces (and any other helper classes) to instantiate client proxies, so you need to package all the EJB 3.0 artifacts (except of course from the enterprise bean implementation) and deploy it onto your OSB project:

Now we have everything in place. It is time to create the business service that will point to the EJB 3.0 wrapper. Create one business service and set its service type to "Transport Typed":

Configure the business service protocol as "EJB" and set its endpoint URI to the prefix "ejb:" plus the name of the JNDI provider and plus the JNDI name of the EJB 3.0:

Finally, you need to configure the client interface of the EJB 3.0 endpoint in the business service configuration page. Check the "EJB 3.0" checkbox and choose from the drop-down list which interface will be used for message communication.

Finish the creation of the business service and save the changes. You can now test your business service using the testing tool available on OSB:

After making an request to the business service using the OSB testing tool, you can check the CORBA server application log to see the results of this invocation. Here is an example:

With the business service in place, you can easily create one or more proxy services to access the remote CORBA object with minimal effort. For the OSB perspective, it is all about routing messages to the business service that you created, making the fact that this business service is a CORBA remote object really irrelevant.

No matter what will be your use case, now you have the CORBA remote object available in OSB for virtually anything. You can expose it directly using one of the available transports, you can forward messages for it in the middle of your pipeline, you can use it as enrichment mechanism using service callouts or you can just use the business service as one of the choices of an dynamic routing. If you choose to expose this business service into a new protocol, you can play with SOAP, REST, HTTP, JMS, Email, Tuxedo, File and FTP with zero-coding. OSB will take care of the protocol translation during messages exchanges.

You can download the project artifacts created in this article here.

About

Ricardo Ferreira is just a regular person passionate for technology, traveling, movies and his family. He works for Oracle, member of the Cloud Solution Architects Team, otherwise known as the A-Team.

Search

Categories
Archives
« May 2015
SunMonTueWedThuFriSat
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
      
Today