In Part I of this article (JDJ, Vol.6, issue I) we discussed solving legacy data integration problems with VisualAge for Java and WebSphere Studio. In Part 2 we'll discuss using the MQSeries Integrator and some of the steps for creating data translations and data flows.
MQSeries Integrator extends MQSeries by adding message
brokering that's driven by business rules. MQSI lets us add the
intelligence to route and transform messages or filter messages
(content-based or topic-based). It also lets us perform direct
database access so we can augment or warehouse messages. We'll look
primarily at routing and transforming as we build our solution.
The MQSI Control Center lies at the heart of MQSI's user
interface. We'll use this tool to create message types and message
flows. Figure 1 shows an example message flow, one that would be
applicable to our problem. Notice the similarity between this screen
and the VisualAge for Java Visual Composition Editor. Components are
dropped on the canvas and wired together in much the same manner as
Java objects are in VisualAge.
The Message Flow
In our sample message flow we left out any error or exception
handling so we could highlight the core tasks. As you can see from
Figure 1, the message flow describes our stated problem: it takes two
disparate inputs and merges them into a single XML output stream.
Both the C-formatted data and the XML data from our legacy
applications arrive on message queues - the MQInput nodes. The C
input is routed through a processor, or "compute node," that will
translate the data into XML. Both streams are then sent off to an XML
processor compute node, where they're merged and placed on an output
queue.
Notice that we haven't defined how the data gets onto the
input queues in the first place. In the complete solution, we'll
define another message flow that sends request messages to both
legacy applications with the appropriate search criteria for locating
a customer. The input messages for the "Combiner" flow represent the
replies from such a query. Different queries could be posed - a
search by name or postal code, for example. The message flow defined
here could be used to process the results of any such query.
Before we get too deeply into the message flow, we need to
use the MQSI Control Center's Message Repository Manager to define
the message that will encapsulate the C-formatted data. There are
three steps needed to create a Message Repository Manager (MRM)
message from a C structure:
- Create the message set.
- Import a C structure to create a new
message type within the message set.
- Create a message of that new
message type.
The creation of the message set only involves choosing a menu
option on the Message Sets pane and giving the new message set a
name. MQSI can read a C header file or a COBOL copybook, parse the
contents, and generate a message type based on the structures or
COMMAREAs defined in the file. We've used the C header file from
Listing 1 (reproduced from Part 1 of this article) to create a
completed message type with a minimal amount of typing and clicking
on our part.
Now we can return to our message flow. Each of the input
nodes has properties in which we can enter the queue name that will
be used and the message format that will be presented. For the CInput
node, the message domain will be MRM and the format will be our
generated type, C_CUSTOMER_TYPE. For the XMLInput node, the domain is
XML.
Notice how we've structured the flow so the C structure is
translated to XML before further processing. Figure 2 shows the
completed property sheet for the CtoXML Compute node. We added our
MRM message type C_CUSTOMER_TYPE and wrote the ESQL for the
translation. To convert the C structure to XML, we included the line:
SET OutputRoot.Properties.MessageFormat = 'XML';
We then used OutputRoot.XML in the lines that follow to set
the field values in the output XML. Notice that we changed the field
names between input and output and that we didn't include the fields
that aren't required by our Web application.
Because we chose to perform this translation before sending
both streams to a common processor, the XMLProcessor can concern
itself with the job of merging two XML streams without knowing that
one or more of its inputs started life in some other format. Again,
here's a good opportunity to create a reusable component.
Finally, we can use the MQSI Control Center to deploy our
message flow to the running broker. We can do this from the
Assignments page. Figure 3 shows a completed deployment.
Java Access to MQSeries
We have several ways to access MQSeries from the code we'll
develop in VisualAge for Java. MQSeries has featured a robust Java
API for several years. The original MQSeries Java API predates the
Java Message Service (JMS) API but since the introduction of JMS, IBM
also provides a JMS implementation for MQSeries so that developers
can write portable code to access MQSeries. Finally, the IBM Common
Connector Framework (CCF) features an MQ connector. CCF is quite
powerful and flexible, and it provides the basis for the upcoming
Java Connector API in J2EE.
We took a middle-of-the-road ap- proach here and created a
wrapper class called MQAccess that we'll use for our queue access.
Right now this class is implemented using the MQSeries "original
flavor" API, but we could change the implementation at any time
without affecting our business logic. This class is shown in Listing
2. It uses the IBM XML4J Parser for part of its data handling, so
that feature must be added to your VisualAge for Java workspace.
We've provided a main() method to illustrate how the methods
in the class should be called. To run this class, however, there are
other steps that still need to be taken, not the least of which is to
set up MQSeries, create something to mimic the legacy systems during
testing, and start the MQSI Broker. This all falls outside the scope
of this article but you can get a complete solution from the
"Patterns for e-business" kit that's available from IBM.
Finishing Off
Once the rest of the infrastructure is set up and our
MQAccess class is tested in the VisualAge for Java environment, the
next step is to move the class into WebSphere Studio so we can create
the wrapper servlet, HTML, and JSP for the Web application. From this
point we can deploy to test and production servers or to the
VisualAge for Java WebSphere Test Environment; move Java code back
and forth between Studio and VisualAge for editing, testing, and
debugging; and further enhance the HTML and JSP files. For a
discussion on how to do all these things, see JDJ (Vol. 5, issues 9,
10).
Summary
Hopefully, in this brief discussion of MQSeries Integrator
we've been able to show how valuable a message broker and
translation/routing engine based on business rules can be when you
need to solve a problem that involves the integration of legacy data,
regardless of the format and the location of the data or legacy
systems. Once we created our message flows, we created a flexible set
of tools - reusable components we can build upon as we need to access
back-end systems in new ways. The MQAccess class we created could
serve as the basis for a set of EJB components. It could service
stateless session beans for generic MQSeries access or even be the
engine for BMP entity beans.
Author Bio
Brady Flowers is a
software IT architect with IBM's WebSpeed team specializing in
WebSphere, Java, and the rest of IBM's suite of e-business
applications.
bradenf@us.ibm.com
Listing 1
#define CUSTNO_LEN 8
#define FNAME_LEN 24
#define LNAME_LEN 24
#define ADDR_LEN 24
#define CITY_LEN 24
#define STATE_LEN 2
#define ZIP_LEN 10
struct C_CUSTOMER {
char custno[CUSTNO_LEN];
char fname[FNAME_LEN];
char lname[LNAME_LEN];
char addr[ADDR_LEN];
char city[CITY_LEN];
char state[STATE_LEN];
char zip[ZIP_LEN];
double balancedue;
int datedue_month;
int datedue_day;
int datedue_year;
}
Listing 2
public class MQAccess {
private java.lang.String hostname = "localhost";
private java.lang.String channel = "JAVA.CHANNEL";
private java.lang.String userid = null;
private java.lang.String password = null;
private java.lang.String qManagerName = null;
private com.ibm.mq.MQQueueManager qManager = null;
private int defaultMaxMessageSize = 100;
public MQAccess() {
super();
}
public void connectQManager() throws com.ibm.mq.MQException {
disconnectQManager();
setEnvironment();
qManager = new com.ibm.mq.MQQueueManager(qManagerName);
}
public void connectQManager(String newQManagerName) throws
com.ibm.mq.MQException {
setQManagerName(newQManagerName);
connectQManager();
}
public void disconnectQManager()