Blog

Using the JNBridge JMS BizTalk Adapter with Oracle RIB and AQ

Oracle RIB (Retail Information Bus) is a message-based integration platform peaked for retail outlets. The messaging layer is Java Message Service, and the messaging provider is Oracle AQ.  One of the enterprise solutions that’s frequently integrated with Oracle RIB is Microsoft’s BizTalk Server. While the JNBridge JMS Adapter for BizTalk is a key component enabling this integration, Oracle RIB presents an integration challenge for stand-alone JMS clients like the adapter. This blog entry will discuss how to integrate Oracle RIB to BizTalk Server using the JMS adapter, but first it might be worthwhile to understand the particular problems with integrating stand-alone JMS clients.

Stand-alone JMS clients

JMS clients usually execute in a Java EE container within an application server, for example a Message Driven Bean. The container provides services to the client including transaction enlistment and implicit naming environments. A generic stand-alone JMS client does not execute in a container and must explicitly use the Java Naming and Directory Interface (JNDI) to acquire connection factories and destinations. Because of this, stand-alone JMS clients often require special libraries—the JAR files in the classpath—and configuration properties that are different from those used for a MDB. Some vendors, like IBM, and some products, like WebLogic, provide special thin-client JAR files. For other vendors, it is necessary to choose a subset of JAR files from the Java EE server implementation, copying them to the JMS client machine. Most times, the vendor will actually publish the required JAR files for each version, other times it is necessary to build the class path one ClassNotFoundException at a time.

Depending on the implementation of the JMS server, a stand-alone client may not have access to a JNDI service. Some JMS implementations don’t provide JNDI, relying instead on the host Java EE application server to provide naming and directory services. If the JMS implementation is not integrated into an application server, but is running as a stand-alone broker, all clients must access connection factories and destinations through proprietary APIs. That’s fine, but it isn’t portable, which is the whole idea behind the JMS specification.

Oracle AQ is an example of a JMS implementation that can run as a stand-alone broker or run in a Java EE application server like Oracle WebLogic. This blog will address both scenarios: a stand-alone JMS client, in this case the JNBridge JMS Adapter for BizTalk Server, connecting to Oracle AQ as a stand-alone broker and as a JMS service provider within WebLogic. As we’ll see, both scenarios require some extra work to support a stand-alone JMS client.

Oracle AQ configured as a Foreign Server within WebLogic

Generally speaking, the JMS specification was intended as an API that provides a generic surface on proprietary messaging middleware. The JMS surface is provided by a Java EE application server which also provides JNDI, another generic API for obtaining connection factories and destinations. Usually, the integration of the messaging provider to JMS and JNDI within the application server is accomplished by a Java Connection Architecture resource adapter. However, the preferred method required by Oracle RIB for integrating Oracle AQ to WebLogic is to configure the message provider as a foreign server.

Oracle AQ uses an Oracle Database as its message store, mapping destinations to tables. Because of the transactional nature of the DB, a Java Data Source is used in the foreign server configuration. A data source automatically handles DB transactions by enlisting in a transaction handled by the Java EE transaction manager, a service provided by WebLogic. This works well when the JMS client is executing in a Java EE container because the data source provides the transactional support. However, a stand-alone client is unable to use a data source because there’s no transaction manager available—it’s executing outside the transactional scope. While a JMS client can use local transactions, either explicitly calling commit or rollback, it cannot participate in a distributed transaction.

Foreign Server using a Data Source

Using WebLogic 10.3 and Oracle AQ 11.2, the configuration as a foreign server starts with configuring the data source.  A data source for an Oracle DB is a transaction aware wrapper around the database connection URL. This screen shot shows the data source configuration within WebLogic. Note the standard Oracle connection URL at the top with the default SID, orcl.

Here’s a screen shot of the foreign server configuration using  the above data source which has been mapped to the JNDI name, jms/oracleDS.

This foreign server configuration will work fine for JMS clients executing inside containers in WebLogic. However, if a stand-alone JMS client attempts to connect to WebLogic, this exception is thrown.

cannot assign instance of weblogic.jdbc.common.internal.RmiDataSource_1033_WLStub to field oracle.jms.AQjmsConnectionFactory.data_source of type javax.sql.DataSource in instance of oracle.jms.AQjmsXATopicConnectionFactory

 Foreign Server using a Connection URL

The problem is the data source. If the foreign server configuration does not use a data source, but instead uses the connection URL, then the stand-alone JMS client will be able to connect. The following screen shot shows the foreign server configuration using a connection URL instead of a data source.

Mutually Exclusive: Requires Two Foreign Servers

Now, a stand-alone JMS client can connect to WebLogic and access Oracle AQ queues and topics. However, a JMS client running inside a container in WebLogic cannot use this foreign server configuration. The internal JMS client requires a data source to provide transaction enlistment. The solution is to configure two foreign servers, one using a data source for the WebLogic JMS clients, the other using a connection URL for the external stand-alone JMS clients. Each foreign server configuration will have its own connection factory with a unique JNDI name. Each foreign server will also point to the same Oracle AQ queues or topics, but will use unique JNDI names.

The configuration for the JNBridge JMS Adapter for BizTalk when using Oracle AQ configured as a WebLogic Foreign Server can be found here.

 Oracle AQ as a stand-alone broker.

The JMS specification is a generic interface, but there is no specified mechanism for obtaining connection factories or destinations. Generic access is usually provided by JNDI, however the specification does not preclude a non-JNDI mechanism, i.e. a proprietary extension. Oracle AQ is an example of a JMS implementation that doesn’t require JNDI if it is running as a stand-alone broker. However, because the API for creating connection factories and accessing destinations is proprietary, the JNBridge JMS Adapter for BizTalk can only support stand-alone Oracle AQ if there is a JNDI service available.

One way to get around this problem is to write a Java wrapper that mimics JNDI, but uses the Oracle AQ API to create and return connection factories and destinations. JNDI requires the implementation of two Java naming interfaces, InitialContextFactory and Context. When a Java EE client requires remote access to objects in the JNDI repository, it creates an InitialContext from the InitialContextFactory. The client then uses the InitialContext to look-up connection factories and destinations. The InitialContextFactory must implement the method getInitialContext(). Here’s the Java code for that method.

import java.util.Hashtable;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.NamingException;
import java.net.URI;
public Context getInitialContext(Hashtable<?, ?> environment)
throws NamingException
{
String dbURL = this.getDBURL(environment);
String hostname = this.getHostname(environment);
int port = this.getPort(environment);
String sid = this.getSID();
String username = this.getUsername(environment);
String password = this.getPassword(environment);
OAQContext oaqCtx = null;
try {
oaqCtx = new OAQContext(hostname, username, password, sid, port);
}
catch (Exception ex) {
throw new NamingException(“Unable to create OAQContext: ” + ex.getMessage());
}
return oaqCtx;
}

This method simply parses the Hashtable object, environment, that’s an argument to the InitialContext constructor. The JMS Adapter for BizTalk constructs the Hashtable object, populating it with connection values, passing it as an argument to the InitialContext constructor in the factory class where the  method getInitialContext() is invoked. The method then creates an instance of OAQContext passing in the connection properties, where they’re stored in instance variables, and returns it. The OAQContext object is an implementation of the Context interface and therefore must implement the lookup() method.

Here’s the implementation for lookup(), notice the casts of the interface javax.jms.Session to the subclass AQjmsSession. This allows the code to invoke the proprietary methods getQueue() and getTopic().

import java.util.Hashtable;
import java.util.Properties;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import oracle.AQ.*;
import oracle.jms.*;
import javax.jms.*;
public Object lookup(String name) throws NamingException
{
ConnectionFactory cf = null;
Connection connection = null;
Session session = null;
Object rtn = null;
cf = this.getCF(name);
if (name.equalsIgnoreCase(“QueueConnectionFactory”) || name.equalsIgnoreCase(“TopicConnectionFactory”))
{
rtn = cf;
}
else {
try {
connection = cf.createConnection(this.username, this.password);
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
catch (Exception ex) {
throw new NamingException(“Unable to create a connection and session: “ + ex.getMessage());
}
try {
Queue q = ((AQjmsSession)session).getQueue(this.username, name);
rtn = q;
}
catch(Exception ex) { }
if ( rtn == null) {
try {
Topic t = ((AQjmsSession)session).getTopic(this.username, name);
rtn = t;
}
catch (Exception ex) { }
}
}
if ( session != null) {
try {
session.close();
}
catch (JMSException e) { }
}
if ( connection != null) {
try {
connection.close();
}
catch (JMSException e) { }
}
if (rtn == null) {
throw new NamingException(“Unable to find object: “ + name);
}
return rtn;
}

The private method getCF() creates a ConnectionFactory object using the Oracle AQ API. If the name of the object to look-up matches the the name “QueueConnectionFactory” or “TopicConnactionFactory”, then the appropriate connection factory is returned. In the lookup() method, if a queue or topic name is the target of the look-up, then the connection factory is used to create a connection followed by a session. This is the private method getCF().

private ConnectionFactory getCF(String name) throws NamingException
{
ConnectionFactory cf = null;
try
{
if (name.equalsIgnoreCase(“QueueConnectionFactory”)) {
cf = AQjmsFactory.getQueueConnectionFactory(this.hostname, this.sid, this.port, “thin” );
}
else if (name.equalsIgnoreCase(“TopicConnectionFactory”)) {
cf = AQjmsFactory.getTopicConnectionFactory(this.hostname, this.sid, this.port, “thin” );
}
else {
cf = AQjmsFactory.getConnectionFactory(this.hostname, this.sid, this.port, “thin” );
}
}
catch (Exception ex) {
throw new NamingException(“Unable to get OAQ connection factory: “ + ex.getMessage());
}
return cf;
}

The two classes, com.jnbridge.adapters.oaq.InitialContextFactory and com.jnbridge.adapters.oaq.OAQContext must be archived in a JAR file and added to the class path property in the BizTalk transport handlers. In order to correctly build the connection URL for the Oracle Database behind AQ, the Oracle SID must be used. For example, this connection URL,

jdbc:oracle:thin:@stravinsky:1521:etude

has a SID name of etude. While the other values in the connection URL can be obtained from the InitialContext constructor argument, the SID name must use a system property. System properties can be defined and set using arguments to the Java Virtual Machine. For that reason, when configuring the JMS Adapter in BizTalk, the JVM Arguments property in the BTS transport handlers must supply this argument:

-Dcom.jnbridge.adapters.oaq.dbname=[OracleSID]

For more information on configuring the JNBridge JMS Adapter for BizTalk using the JNDI wrapper, please go here.

To download the source for the JNDI wrapper, please go here.

For more information on configuring Oracle AQ as a foreign server in Oracle WebLogic (albeit using a data source), please go here.

 

Java 7 update “silently” deletes Java 6, breaks applications

Software updates shouldn’t do unexpected things. They particularly shouldn’t remove software other than what they’re ostensibly updating, and they shouldn’t break running applications. It’s even worse when this all happens automatically and without warning.

The other day, one of our customers, an ISV that uses JNBridgePro in one of their applications that includes both Java and .NET, told us that several of their customers had reported that their applications stopped working after the customers updated their installations of Java 7. The strange thing is that the applications didn’t use Java 7; they used Java 6. The problem was fixed by reconfiguring JNBridgePro on those machines to point to Java 7 rather than Java 6. Our customer asked us whether JNBridgePro had problems with this update, or with Java 7. We answered that there should be no problem: JNBridgePro works fine with both Java 6 and Java 7, including the latest updates.

Something odd was going on, so we started digging deeper. Running the auto-installer for the new Java 7 update, we saw the following screen, with the relevant message buried in it:

Then we found the following notice on the Oracle website:

About the Java 6 Auto-Update to Java 7

Oracle will start auto-updating Windows 32-bit, Java Runtime Environment (JRE) users from JRE 6 to JRE 7 in December 2012.

The Java auto-update mechanism is designed to keep Java users up-to-date with the latest security fixes. To achieve this goal Windows users that rely on Java’s auto-update mechanism will have their JRE 6 replaced with JRE 7.

In December 2012 Oracle will start to auto-update a sample of users from JRE 6 to JRE 7 to evaluate the auto-update mechanism, user experience and seamless migration. Oracle will then start auto-updating all Windows 32-bit users from JRE 6 to JRE 7 with the update release of Java, Java SE 7 Update 11 (Java SE 7u11), due in February 2013.

    • JRE 7 has been the default version on Java.com since April 2012 and is now being used by millions of users.
    • As we did when JRE 5 was replaced by JRE 6, we will auto-update users of the older release to the newer version of Java.
    • As always, all users are encouraged to update to the most recent Java versions available for public download.
    • In February 2011 Oracle announced the End of Public Updates for their Java SE 6 products for July 2012. In February 2012 Oracle extended the End of Public Updates for 4 months, to November 2012. See:

• Oracle is now extending the End of Public Updates again for 4 additional months to provide developers and users with additional time to migrate to Java 7. The last publicly available release of Java 6 will be in February of 2013 with the release of Java SE 6 Update 39 (Java SE 6u39).

Java 6 End of Public Updates extended to February 2013

(Emphasis ours.)

This is absolutely astonishing. Oracle has decided that, in order to fix extensively-reported security problems, they will not only update Java 7 (their latest version of Java), they will also completely delete a completely separate product. Yes, Java 6 is a separate product from Java 7. They can be installed side-by-side, and many users have both Java 6 and Java 7 installed on their machines. Some of their applications depend on Java 6, and others might depend on Java 7, and these dependencies are typically hard-coded or configured to point to the correct, and different, file locations. Can you imagine if Microsoft released an update to .NET 4.0 that also removed .NET 2.0? This is just as serious.

Worse, it appears that they are taking it upon themselves to replace installations of Java 6 with Java 7 even if the users have only Java 6 on their machines.

How is this different from, say, Microsoft updating Office by replacing one version by another? That’s an update-in-place, so hard-coded paths will often still work. Even so, updating one version of Office to another likely won’t involve an auto-update, but rather an explicit re-installation, and one would expect dependencies to break. Java 6 and 7, on the other hand, are side-by-side installations, and one doesn’t expect an update to one to affect the other in any way.

Let’s look at this from Oracle’s point of view. The security holes that they plugged in Java 7 likely also exist in Java 6, and they have stopped providing new updates to Java 6. Why not replace Java 6 with Java 7 and fix these problems?

This strategy might make sense for less sophisticated users who only use Java inside their browsers. They likely do not know which version of Java they have, or even if they Java at all. In this case, it makes sense to fix the problem by updating the Java installations, so that machines aren’t infected with malware by visiting rogue websites.

However, most of our customers aren’t using Java in their browsers. Their Java is running on servers, or in self-contained desktop applications that, if they connect to the Internet, only connect to specific sites. Their applications depend on specific versions of Java, or on Java files being in specific places. JNBridgePro’s shared memory mechanism, in particular, depends on an absolute path to a specific jvm.dll, but that’s not the only case where dependencies like this occur. With their update, Oracle has silently pulled the rug out from under many running applications.

Why do I say “silently”? Even though the update installer mentions that Java 6 “might” be removed, and the notice on the website says it “will” be removed, very few people will read the text in the installer; they will likely just click through it, since nobody expects a Java 7 updater to remove Java 6. And almost nobody will read the notice on the website unless they are specifically searching for it.

One could say that IT shops should turn off automatic updates, and apply updates in a controlled process after extensive testing. That’s true, but clearly auto-updates can still happen; it’s not reasonable to assume that all business users have sufficient IT support. After all, it happened to our customer’s customers. It’s also the case that a situation like Java 6 being removed in a Java 7 update might not be found in a controlled test, since most such tests will only try to see whether applications that use Java 7 will be affected. To make matters more difficult, the mechanism for turning off automatic Java updates isn’t obvious.

If you find yourself with a broken application that uses JNBridgePro and Java 6 after updating Java 7, here’s what you can do:

  • You can reconfigure your application (and particularly the JNBridgePro component) to use Java 7 rather than Java 6. JNBridgePro will have absolutely no problem with Java 7. Whether your Java code will work with Java 7 is something that you will need to determine yourself.
  • You can go to the Oracle Java website and download and reinstall Java 6. Then, you’ll be back where you were.

Finally, you should strongly consider turning off automatic Java updates. As I said it isn’t immediately obvious how to do this, since the Java control panel, by default, doesn’t display the Update tab that contains the switch that turns off updating. The Update tab only appears when the control panel is run as administrator. You can turn off the auto-update switch as follows:

  1. In Windows Explorer, navigate to your JRE’s bin folder (for example, C:Program Files (x86)Javajre7bin, although it might be different on your machine).
  2. Once you’re there, find javacpl.exe. Right-click on it, and select “Run as administrator.”
  3. Inside the control panel, you can now see the Update tab. Select it, then uncheck the “Check for Updates Automatically” checkbox.

The control panel will ask if you really want to do this. Trust me, you do. Then click on the OK button.

Note that if you do this, it’s your responsibility to make sure that your Java installations are up to date, and that you engage in good security practices. You will need to keep track of the latest Java security problems and the latest updates when they become available, and you can download them from Oracle’s Java site. The downloaded updaters will only update the specific Java versions, and they won’t pull the rug out from under you by removing completely different versions of Java that your software might depend on.

In summary, Oracle’s latest automatic Java update is dangerous and irresponsible because it “silently” removes software other than the software it ostensibly updates, thereby breaking running code. By all means update and secure the Java running inside browsers, but leave our server and desktop software alone.

Follow-up to the Java update post

We had a great response to our recent post on Oracle’s new Java 7 auto-updates, their silent removal of Java 6, and the problems that can cause. We had coverage in The Register, InfoQ, and DZone. The Register article in particular had a great comment thread, and I urge you to check it out.

At the end of that thread, I wrote a response to several points that were brought up, and I thought I’d post a version of that response here.

Why not just support Java 7 and be done with it? Why make people use Java 6?

Our product does handle Java 7 (and 6, and 5, etc — our stuff works with Java back to 1.3.1, although we’ll probably move that up to Java 5 in the next release) just fine. But it’s a tool that customers use to run and deploy their own software — it allows .NET code to communicate with Java code. The Java runs in its own JVM, and the users get to choose whichever JRE they want — it can be any version, it can be 32-bit or 64-bit. It can be from just about any vendor. That’s a good thing, because our users have their own environments, and it’s their own business — we don’t dictate or judge. So, the problem isn’t ours (we’re not making people use Java 6 — but our customers might choose to use Java 6), except that our customers’ problems become our problems, and then we have to scramble. But it bothers me when we have to scramble to solve a problem that really wasn’t caused by us, and which really shouldn’t have been a problem to begin with.

Why not just get version of the latest installed Java from the registry and use that?

The problem is that that only tells us what Java is on the machine — it doesn’t tell us what Java the user wants or needs. Again, we let the user make that decision — checking the registry won’t tell us what we want to know. (Nor will JAVA_HOME, as someone else suggested.)

Why would an enterprise user allow auto-updates, when unexpected things can clearly happen?

The short answer is that they shouldn’t. But clearly it happens — it happened to the customers of our customer. (Our customer is an ISV that uses our product. Their customers are the end users.) And when it happened, our customer heard about it from their customer, and called us, and we had to scramble, and the problem was easily corrected, but it shouldn’t have been a problem in the first place.

Why not just supply the jvm.dll?

First, because it should be up to our users to determine which version they need — we can handle just about any one chosen and don’t dictate. Second, because jvm.dll doesn’t work in isolation and we’d have to supply an entire private JRE — it’s much more than a single file.

Finally, I just want to point out that in our case, the problem is just the validity of a file path — Java 6 and Java 7 reside in different places, and a single path won’t work with both. However, the comment thread on The Register’s article has certainly come up with plenty of examples of Java software that works with Java 6 that simply won’t work with Java 7, so for other users this is a much bigger issue than just an invalid file path.