Blog

JNBridge JMS Adapter for BizTalk Server supports BTS 2013

Microsoft has released BizTalk Server 2013, and the good news for users of our JMS adapter for BizTalk Server is that it already works with the new BTS release.

Simply install and configure the adapter in exactly the same way as you did with earlier versions of BizTalk Server.  It just works!

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.

 

JMS Adapters for .NET and BizTalk Server now support Oracle AQ

The latest 3.0.1 versions of the JMS Adapter for .NET and for BizTalk Server now support Oracle AQ. You can download them here, or take a look at the documentation for using the JNBridge JMS Adapter for BizTalk Server (pdf) or for .NET (pdf) with Oracle AQ.

Past versions of the adapters didn’t support Oracle AQ, as they relied on the standard JMS 1.1 specification, including JNDI. Instead of JNDI, Oracle AQ uses its own proprietary API. The adapters now include a JNDI scaffold, which is basically and implementation of JNDI on top of the proprietary Oracle AQ JMS calls. Also included are guidelines for configuring WebLogic.

This effort grew out of some specific requests to support the Oracle Retail Integration Bus (RIB) with .NET and BizTalk Server. Try it out for yourself, and let us know what you think!

Using a Java SSH library to build a BizTalk Adapter

One of the most frequently used BizTalk adapters is the File Adapter. While its most common use is in tutorials and customer evaluations, the File Adapter is essential in situations where BizTalk needs to be integrated with other applications that are self-contained and not designed for connectivity. In such situations, the other applications can simply drop documents in a folder, and the File Adapter simply reads the new documents as they appear. The adapter can also write documents to a folder, from where they can be read by the other applications.

Recently, we had a conversation with a customer who was using the standard File Adapter to process files. The files had been transfered to the Windows server from a Linux server via a Java Secure Shell (SSH) client, a perfect example of file-level interoperability between platforms. Secure Shell is a secure network protocol that enables shell services between client and server machines providing remote command execution and file transfer. SSH servers and clients are most prevalent on Linux/Unix platforms as a standard component of the OS. There are, of course, SSH clients and servers that run on Windows, but they are rarely used.

Obviously, for the customer, the best implementation would be a BizTalk adapter that supports SSH, specifically the SFTP channel for file transfer, in order to avoid the extra step in transferring the file via SSH, followed by reading it with the File Adapter. In addition, an SSH/SFTP adapter would enable secure, encrypted file transfer over the public internet between, for example, a company’s IT infrastructure on the ground and resources that reside in the cloud. We decided to investigate whether it would be possible to use the File Adapter example in the BizTalk Adapter Framework SDK as a starting point to build a SSH/SFTP adapter. This would require a SSH API, preferably one that’s open source. We based the adapter on a Java SSH client library, and used JNBridgePro to bridge from the .NET-based adapter framework because there’s no open source .NET SSH API that is current, or been released. There are several open source Java SSH class libraries available; we chose JCraft’s Java Secure Channel—JSch—because of its prevalence in Java developement tools and IDEs. JSch is distributed under a BSD-like license.

SSHAdapterArchitecture

JNBridgePro 6.0  allows us to proxy the JSch class library directly to .NET. During execution, a JNBridgePro .NET proxy represents an instance of a Java class running in the JVM. The interoperability bridge between the CLR and the JVM manages method calls between the proxy and the Java object, data marshaling/representation and object life cycle.  Here’s a closer look at how JNBridgePro works.

The BizTalk Adapter Framework .NET File Adapter example

The BizTalk Adapter Framework SDK is part of any BizTalk 2006, or greater, installation. The source for the .NET File Adapter example resides in
%INSTALL_DIR%Microsoft BizTalk Server 2010SDKSamplesAdaptersDevelopment
File Adapter
. We used the version in BizTalk Server 2010, although the example source hasn’t changed since BizTalk 2006. Visual Studio 2010 and JNBridgePro complete the development environment. First, copy the example’s root directory to a more suitable working directory.  The BizTalk Adapter Framework (BAF) base classes, a convienient layer that implements many important mechanisms required for a robust adapter, must also be added to the solution. The base classes can be found in the directory BaseAdapter.

Preliminary Tasks

The very first step is to make sure we can build and deploy the File Adapter before modifying the code. This involves building the three projects we’ve consolodated into one Visual Studio solution: AdapterManagement, DotNetFile and Microsoft.Samples.BizTalk.Adapter.Common. In order to build the adapter, the only thing we need to do is repoint the reference to the adapter base class library in the DotNetFile project because the path has changed. While it is possible to unload the project from Visual Studio and edit the project file as a text file, it is simpler and less error-prone to remove and then re-add the reference.

Deploying a BizTalk adapter is done using registry entries. There are a couple of ‘.reg’ files with the example. We’ll use the registration entries file, StaticAdapterManagement.reg. However, because the example was copied from the BizTalk install directory to a working directory, the regisitry file must be edited to modify the paths to the management and run-time assemblies. We also can edit the name of the adapter stored in the TransportType key from “Static DotNetFile” to “JNBPSshFileto avoid confusion.

Bridging the JSch Java API to .NET

JCraft’s open source SSH class library is written in Java. A BizTalk adapter is written in .NET C#. Use JNBridgePro to create .NET proxies of the JSch classes that represent the Java classes during development and at run-time. To do so, we’ll add a JNBridge DotNetToJavaProxies project  to the adapter solution using the provided Visual Studio plug-in. We also need the JSch library, the JAR file jsch-0.1.45.jar.

The first task is to configure the proxy project’s class path to point to the JSch JAR file. The JNBridgePro development environment consists of a proxy tool that uses reflection to expose the Java classes—the public methods and variables—in the JAR file. Before we start exposing the Java classes, we’ll need to tell the proxy tool what classes we need to proxy to .NET. We could expose everything in the JAR file, but by looking at JCraft’s sample Java source (which we’ll draw heavily from when we code in C#), we can determine in advance the pertinent classes. Add the classes from the class path using the proxy tool, as shown below.

AddClassesFromClassPath

Because we’ve checked the box Include supporting classes, the proxy tool will also load all class dependencies from the com.jcraft.* namespace as well as those from the JRE, like java.util.Vector. We can then choose exactly which Java classes we would like proxied into an assembly. The next screen shot shows the proxy tool with the loaded classes on the left and the selected classes on the right. The far right displays the interface of the class Channel. The abstract Channel class will be proxied along with the class that extends it, ChannelSftp.

ProxyTool1

Coding: Change from a File Adapter to a Secure FTP adapter

The adapter currently understands how to consume and place files locally. We need to modify the configuration interface for the adapter to support SSH/SFTP. We’ll leave the transport handler configurations alone and modify only the transmit and receive location configurations. We’ll be able to leverage some of the existing properties, like directory and fileMask, but will have to add properties that handle SSH connection and credential arguments. We’re going to keep things simple and only use simple security—a user name and password—instead of more robust public/private key pair encryption. Hardening the security of user credentials is left  as an exercise to the reader. We then add the following to the source file ReceiveLocation.xsd and TransmitLocation.xsd.

<xs:element name=”host” default =”localhost” type=”xs:string” >
<xs:annotation>
<xs:appinfo>
<baf:designer>
<baf:displayname _locID=”hostName”>in resource file</baf:displayname>
<baf:description _locID=”hostDesc”>in resource file</baf:description>
<baf:category _locID=”SSHCategory”>in resource file</baf:category>
</baf:designer>
</xs:appinfo>
</xs:annotation>
</xs:element>
<xs:element name=”port” default=”22″ type=”xs:int” >…</xs:element>
<xs:element name=”user” type=”xs:string” >…</xs:element>
<xs:element name=”password” type=”xs:string” >…</xs:element>

The block of XSD code shows the schema for the host property that’s used to populate the location property grid and create the XML document that contains the location’s configuration. The strings used for the display name, description, etc… are in the resource file, DotNetFileResource.resx. The schema for the remaining properties will be similar. To correctly parse the the XML document in order to retrieve the configuration values, add the following C# code (again, just using the host property as an example—the other properties will have similar code) to the classes DotNetFileReceiveProperties and DotNetFileTransmitProperties, both defined in the file DotNetFileProperties.cs. The static utility method IfExistsExtract() is defined in ConfigProperties.cs in the adapter base classes.

private string hostName;
public string HostName { get { return this.hostName; } }
public override void ReadLocationConfiguration (XmlDocument configDOM)
{
// to this method, add lines like this for each property:
this.hostName = IfExistsExtract(configDOM, “/Config/host”, “localhost”);
}

Once done with modifying the adapter’s properties to accommodate an interface for SSH, we can fire up the BizTalk Admin Console, create a receive location and open the property sheet. Here’s what it looks like.
BTS_LocationUI

Coding the SSH/SFTP adapter

All of the C# code will be added to the DotNetFile project. We’ve already added the properties code to DotNetFileProperties.cs, now it’s time to start coding the adapter. First, add a couple of references to the project: one to point to the JNBridgePro proxy assembly, the other to point to the JNBridgePro run-time, JNBShare.dll.

Initiating the Java/.NET bridge

The bridge between the CLR and the JVM will use a shared memory transport. The JVM will run as a thread within the BizTalk Server process. Thankfully, JNBridgePro hides all the details. A new source file, JNBridgeProInit.cs, will define the static class JNBridgeProInit. The class has one method, init(). Because a process can have only one JVM instance, we’ll need to provide a mechanism to ensure that JNBridgePro is initialized once, and only once, per process. The bridge needs a pointer to the Java Virtual Machine, the JNBridgePro run-time assemblies and JAR files and a class path pointing to the JSch JAR file.

public static class JNBridgeProInit
{
private static bool isJNBInitialized = false;
private static object jnbInitLock = new Object();
public static void init()
{
lock (jnbInitLock)
{
if (isJNBInitialized == false)
{
com.jnbridge.jnbproxy.JNBRemotingConfiguration.specifyRemotingConfiguration(
com.jnbridge.jnbproxy.JavaScheme.sharedmem
, @”C:Program FilesJavajre6binclientjvm.dll”
, @”C:Program FilesJNBridgeJNBridgePro v6.0jnbcorejnbcore.jar”
, @”C:Program FilesJNBridgeJNBridgePro v6.0jnbcorebcel-5.1-jnbridge.jar”
, @”C:SSH AdapterFile AdapterJschjsch-0.1.45.jar;);
isJNBInitialized = true;
}
}
}
}

Coding a .NET implementation of a Java interface

When connecting to a SSH server, the server negotiates security credentials by asking the client for a user name and password or a private key and pass phrase. JCraft’s JSch provides a convenient Java interface, com.jcraft.jsch.UserInfo, that when implemented returns the required information. When we built the proxy assembly using the JNBridgePro proxy tool, the Java interface was mapped to a .NET interface. We can implement the interface in the class UserInfoImpl, which we’ll place in the file UserInfoImpl.cs. Because an instance of UserInfoImpl will be created on the .NET side, but called on the Java side of the bridge, we’ll have to use a JNBridgePro Callback attribute to decorate the interface implementation.

Initiating JSch

Now it’s time to code against the JCraft JSch class library. Because our project references the proxy assembly we built in the JNBridgePro project, we can code in C# as if JSch were implemented in .NET. In fact, we can use the example Java code that ships with JSch as a starting point. First, we need a method to initiate a JSch context, connect to a SSH server and create the SFTP channel. We’ll add the following private instance variables to the two endpoint classes, DotNetFileTransmitterEndpoint and DotNetFileReceiverEndpoint, in DotNetFileReceiverEndpoint.cs and DotNetFileTransmitterEndpoint.cs.

private com.jcraft.jsch.JSch jschContext;
private com.jcraft.jsch.Session jschSession;
private com.jcraft.jsch.ChannelSftp sftpChannel;

We’ll now add the following method to the two endpoint classes.  Notice the line where an object of type Channel is cast to type ChannelSftp. If the cast to System.Object is not done first, Visual Studio will complain. Using the proxy tool, we can take a look at the class hierarchy. The public class ChannelSftp inherits from ChannelSession, which in turn inherits from the abstract base class Channel. The problem is that ChannelSession is not public while the abstract base class and ChannelSftp are public. Java has the concept of  default access—not specified private, public or protected—allowing the class to be accessible within its own package, like ChannelSession. Java allows a cast from a base class to subclass even if intermediate subclasses have default access. The C# language requires that the entire class hierarchy be public if any subclass is public. If not, Visual Studio refuses to build the assembly.  The point is that this is a language restriction, not a restriction, or lack of mechanism, in the target byte-code. JNBridgePro constructs the proxy assembly directly to MSIL, where classes are either Public or NotPublic. The intermediate cast to Object just gets past the language restriction imposed by the C# specification and Visual Studio. Yes, it’s a hack, but if we didn’t use the indirection, we’d have to code a Java wrapper to do the cast, create a .NET proxy of the wrapper and then call the wrapper to cast on the Java side.

private void initJsch()
{
UserInfoImpl ui = new UserInfoImpl(this.properties.PassWord, “”);
this.jschContext = new JSch();
this.jschSession = this.jschContext.getSession(this.properties.UserName
, this.properties.HostName
, this.properties.PortNumber);
this.jschSession.setUserInfo(ui);
this.jschSession.connect();
Channel chnl = this.jschSession.openChannel(“sftp”);
chnl.connect();
this.sftpChannel = (com.jcraft.jsch.ChannelSftp)((Object)chnl);
}

Paths

We’ll need to add some  code to perform transformations between Windows-style and Unix-style paths such as the directory property that tells the adapter where to look for, or place, files that end in the specified qualifier. Even when a SSH server is running on Windows, paths must be in a Unix-like format, so the path C:UsersMyAccountDownloads must be changed to /users/myaccount/downloads. Luckily, JSch provides a method for path transformation. We’ll add the path transformation code to the Open() method of both endpoint classes. Here’s  the receive endpoint code.

JNBridgeProInit.init();
this.initJsch();
string windowsPath = this.properties.Directory;
windowsPath = windowsPath.Substring(Path.GetPathRoot(windowsPath).Length);
windowsPath = (windowsPath.StartsWith(“/”) ? windowsPath : (“/” + windowsPath));
string unixPath = this.sftpChannel.realpath(windowsPath);
this.sftpChannel.cd(unixPath);

Receiving  Files: SFTP get

The Java type byte is signed. JNBridgePro converts Java signed bytes to the .NET type sbyte. Converting back to type byte involves a little indirection. The use of the unchecked keyword makes sure that there is no run-time exception. Using unchecked is equivalent to casting a void  pointer in C—the bits are taken at face value with no overflow checking. Besides using the JSch class library, this code uses two standard types from the Java run-time library, Vector and ByteArrayOutputStream.

The class BatchInfo maintains a list of the files that have been received. The ReceiveBatch constructor accepts as an argument the callback method BatchInfo.OnBatchComplete(). When the batch has been successfully submitted to the BizTalk Message Box, the callback method will delete the files.

com.jcraft.jsch.ChannelSftp.LsEntry entry;
List<BatchMessage> files = new List<BatchMessage>();
java.util.Vector list = this.sftpChannel.ls(this.properties.FileMask);
int len = list.size();
for (int ndx = 0; ndx < len; ndx++)
{
entry = (com.jcraft.jsch.ChannelSftp.LsEntry)list.elementAt(ndx);
string fileName = entry.getFilename();
java.io.ByteArrayOutputStream byteArrayOStrm = new java.io.ByteArrayOutputStream();
this.sftpChannel.get(fileName, byteArrayOStrm);
sbyte[] sbytes = byteArrayOStrm.toByteArray();
byte[] bytes;
unchecked
{
bytes = (byte[])((Array)sbytes);
}
fs = new MemoryStream(bytes);
IBaseMessagePart part = this.messageFactory.CreateMessagePart();
part.Data = fs;
IBaseMessage message = this.messageFactory.CreateMessage();
message.AddPart(MESSAGE_BODY, part, true);
SystemMessageContext context = new SystemMessageContext(message.Context);
context.InboundTransportLocation = this.properties.Uri;
context.InboundTransportType = this.transportType;
files.Add(new BatchMessage(message, fileName, BatchOperationType.Submit));
}
if (files.Count > 0)
{
BatchInfo batchInfo = new BatchInfo(files, this.sftpChannel);
using (ReceiveBatch batch = new ReceiveBatch(this.transportProxy
, this.controlledTermination
, batchInfo.OnBatchComplete
, this.properties.MaximumNumberOfFiles))
{
foreach (BatchMessage file in files)
batch.SubmitMessage(file.Message, file.UserData);
batch.Done(null);
}
}

Transmitting Files: SFTP put

The BizTalk Adapter Framework calls this method in DotNetFileTransmitterEndpoint whenever a BTS message is available to a subscribing transmit location. The method readStreamToBuffer() copies the message body to an array of type sbyte.

public override IBaseMessage ProcessMessage(IBaseMessage message)
{
Stream source = message.BodyPart.Data;
DotNetFileTransmitProperties props = new DotNetFileTransmitProperties(message
, propertyNamespace);
if (props.Directory != this.currentPath)
{
this.currentPath = props.Directory;
string aPath = props.Directory.Substring(Path.GetPathRoot(props.Directory).Length);
this.sftpChannel.cd(“/”);
aPath = this.sftpChannel.realpath(aPath);
this.sftpChannel.cd(aPath);
}
string filePath = DotNetFileTransmitProperties.CreateFileName(message, props.Uri);
filePath = filePath.Remove(0, filePath.LastIndexOf(“\”) + 1);
sbyte[] sbytes = this.readStreamToBuffer(source);
java.io.ByteArrayInputStream inputStream = new java.io.ByteArrayInputStream(sbytes);
this.sftpChannel.put(inputStream, filePath);
return null;
}

Running the adapter

Testing the adapter is simple. For example, we can create an Ubuntu VM running in Amazon’s EC2 using a pre-configured Amazon Machine Image. Using the standard File Adapter and the SSH/SFTP Adapter we’ve just created, we can configure two send ports and two receive ports. Using simple routing, we can drop a file into a directory on the local Windows machine where a receive location routes the message to a send port configured with the SSH/SFTP prototype adapter. The file is transmitted to /home/myaccount/Downloads on the Linux VM where a second SSH/SFTP receive location grabs the file and routes it to a send port which drops it into a directory back on the local machine.

Wrap-up

We’ve shown how it’s straightforward to create a BizTalk SSH/SFTP adapter using JNBridgePro and the BizTalk Adapter Framework SDK. An adapter like this one can be used to allow BizTalk Server to read and write files securely to and from the cloud, and to communicate easily with Unix/Linux machines.

Note that the instructions and code that are given here are just a foundation—a production adapter will likely need to be enhanced with more hardened credential management, more flexible configuration options, and enhanced exception handling. The adapter can also be enhanced to support additional SSH functionality, including additional file manipulation and securely running applications on remote machines. Also, the adapter is targeted toward the Jsch implementation of SSH, but it is a simple matter to retarget it to other SSH implementations.

While the adapter should be enhanced for most production situations, what we’ve provided here provides a solid start that should allow you to create a useful BizTalk SSH adapter for real-world situations.

If interested, the source is here.

Released: Version 2.1 of JMS Adapters for BizTalk and for .NET

We’ve just released version 2.1 of the JMS Adapter for BizTalk Server and the JMS Adapter for .NET.

Version 2.1 of the JMS Adapter for BizTalk Server adds:

  • Fault-tolerant connections for JMS Server shutdown scenarios. This feature provides the ability to configure specific behaviors for send and receive locations when the JMS server is explicitly brought down. Locations can be configured to either disable or periodically attempt to reconnect to the JMS server.
  • Support for ISO 8859-15 encoding. Used almost exclusively in Europe, ISO 8895-15 is a single byte encoding that includes the Euro currency character.
  • Ability to add a unique identifier to a receive location. This allows multiple receive locations to access the same JMS queue or topic providing a mechanism to support concurrent message consumption using JMS message selectors.
  • New configuration documents for SonicMQ and GlassFish/OpenMQ.
  • Improved logging and error reporting.

Version 2.1 of the JMS Adapter for .NET adds:

  • Support for .NET 4.0.
  • Support for version 2.0 of Microsoft’s WCF Line-of-Business adapter framework providing support for Visual Studio 2010.
  • Support for Microsoft’s Azure AppFabric API, allowing .NET client endpoints running in the cloud to consume and produce JMS messages from JMS servers on the ground.
  • Extended configuration and tuning documentation.

Both adapters are now:

  • Built on top of JNBridgePro 5.1, which adds support for .NET Framework 4 and Visual Studio 2010.

Check out the press release, or download now.

BizTalk Server 2010

We’ve just completed testing, and yes, the current version of the JMS Adapter for BizTalk Server works with the just-released BizTalk Server 2010!

Try it yourself: download a free trial or see how it works.

News relase: Announcing New Versions of JNBridgePro and the JNBridge JMS Adapters

We’ve just announced the forthcoming release of JNBridgePro 5.0 and v2.0 of the JMS Adapters for .NET and for BizTalk Server. The bits will be available on 11/16/09, and we’ll be demonstrating the new capabilities at PDC next week in Los Angeles.

JNBridgePro 5.0 adds support for cross-platform transactions between Java and .NET in both directions.

The JMS Adapter for BizTalk Server 2.0 adds support for request/response and solicit/response messaging patterns. Both adapters have greater architecture flexibility.

Read the full press release, and download new versions on or after next Monday, 11/16/09.

Healthcare Benefits Solution Provider Gains from Fast JMS and BizTalk Integration

We’ve posted a new case study featuring Benefitfocus and our JMS Adapter for BizTalk Server.

Business Challenge

How to streamline incoming carrier data from JMS (Java Message Service) with a BizTalk enterprise application integration infrastructure — and do it within a two-month software development window.

Solution

Use an off-the-shelf BizTalk adapter — the JNBridge JMS Adapter for BizTalk Server — to integrate applications into the enterprise infrastructure without the cost, risk, and time-to-deploy associated with an open source or internally developed solution.

Read the entire case study here.