The underlying concept of EDI (Electronic Document Interchange) is quite elegant. Rather than exchanging mountains of paper (purchase orders, invoices, etc.) just exchange data files containing the requisite information! Of course, everyone has to agree on a standard format of these electronic documents and, as is commonly the case these days, the detailed specifications are only available from standards organizations. Don't expect to find free documentation on the web; you can expect to pay something like US$500 to ISO in order to get the documents needed to implement a solution.
A client of mine recently asked me to look into EDI solutions in order to send files to one of their customers. A quick search of the web took me to the home page of OBOE. Since this solution is written in Java and is Open Source, it seemed to be a good fit. Code portability is very important to me (platform independence) and I've recently become a convert to the Open Source approach. The robustness of applications like Apache and the utility of packages such as JacORB has not been lost on me.
Downloading the product for evaluation was a different experience than I'm used to on the web. You have to fill out an in-secure form containing your e-mail address and submit it. At some later time, the software arrives as an attachment to an e-mail message restating the license terms. They apparently have some kind of address filtering in place since when I used a free account I hadn't received a reply for three days. When I used the address at the client site, the software arrived within hours. This approach concerns me since it's not as open as has become common: I'm used to clicking a link on a site and saving the software right from my browser.
That being said, the software does work, albeit this is definitely not a project for beginners! The documentation is thin (granted, it's a work in progress,) and the few sample programs included with the package didn't come close to addressing the application I had in mind. I couldn't access the documentation supposedly included in the package and had to use the web site version. I didn't want to spend too much time figuring out the documentation and I have high-speed internet access from home anyway. Also, there are some additional Java packages required: see below.
The "binary" version of the package consists of the aforementioned documentation and sample programs as well as the core: a JAR file containing all the required classes. Installing the prerequisite products and adding the various JAR files to your CLASSPATH is standard practice. You have to import the com.americancoders.edi.* classes as well as com.americancoders.edi.x12.* if you plan on generating X12 documents. Other than that, development was fairly straightforward: I keep a browser window open to the documentation and a couple of X windows for firing up vi and java. I don't know how easy it would be to import everything into an IDE (Integrated Development Environment) since I find using one to be a chore.
Here are the required downloads:
The first three of these packages are available from the Sun web site. You can download xalan from the Apache site. In order to install and configure the requisite files, follow these steps:
JARDIR=jar directory CLASSPATH=$CLASSPATH:$JARDIR/oboe.jar:$JARDIR/jce1_2-do.jar:$JARDIR/mail.jar:$JARDIR/activation.jar:$JARDIR/xerces.jar export CLASSPATH |
xmlPath: jar directory/xml/ |
You are almost ready to create your EDI application, but first we need
to address the EDI document architecture. The diagram to the left shows
the nested architecture of an X12 EDI document. Working from the outside
in, the envelope contains an interchange header, one or more functional
groups and an interchange trailer. A functional group consists of an
optional header, one or more transaction sets and an optional trailer.
A transaction set consists of a header, detail records and a summary
(a trailer by a different name.) Each of these elements consists of
one or more segments, which can be nested and which typically contain
data elements.
The heirarchical nature of a document is significant insofar as the implementation of OBOE. It's very similar to the DOM approach and involves "walking the tree" in order to create, access and modify the document contents. Since DOM is typically used to parse a received document, it's in some ways the logical inverse of what I am going to describe, namely the creation of an EDI document. XML is also tightly integrated with OBOE so some experience in that area is almost essential for configuring new transaction sets. We'll defer that discussion until we dig down to the transaction set level.
In order to use OBOE to create a new EDI document, you'll have to include the following imports in your Java source:
com.americancoders.edi.* com.americancoders.edi.x12.* |
X12Envelope env = new X12Envelope(); |
Segment seg = Interchange_Control_Header.getInstance(); setElement( seg.getDataElement( 0 ), "0", ZERO_FILL, 0, MIN_LENGTH ); setElement( seg.getDataElement( 1 ), " ", BLANK_FILL, 0, MIN_LENGTH ); setElement( seg.getDataElement( 2 ), "0", ZERO_FILL, 0, MIN_LENGTH ); setElement( seg.getDataElement( 3 ), " ", BLANK_FILL, 0, MIN_LENGTH ); setElement( seg.getDataElement( 4 ), "1", ZERO_FILL, RIGHT_JUSTIFY, MIN_LENGTH ); setElement( seg.getDataElement( 5 ), fromID, BLANK_FILL, 0, MIN_LENGTH ); setElement( seg.getDataElement( 6 ), "1", ZERO_FILL, RIGHT_JUSTIFY, MIN_LENGTH ); setElement( seg.getDataElement( 7 ), toID, BLANK_FILL, 0, MIN_LENGTH ); setElement( seg.getDataElement( 8 ), transDate, 0, 0, 0 ); setElement( seg.getDataElement( 9 ), transTime, 0, 0, 0 ); setElement( seg.getDataElement( 10 ), "U", 0, 0, 0 ); setElement( seg.getDataElement( 11 ), "200", ZERO_FILL, RIGHT_JUSTIFY, MIN_LENGTH ); setElement( seg.getDataElement( 12 ), interchangeControlNumber, ZERO_FILL, RIGHT_JUSTIFY, MIN_LENGTH ); setElement( seg.getDataElement( 13 ), "0", 0, 0, 0 ); setElement( seg.getDataElement( 14 ), "T", 0, 0, 0 ); setElement( seg.getDataElement( 15 ), ":", 0, 0, 0 ); env.setInterchange_Header( seg ); |
for( int i = 0; i < seg.getDataElementSize(); i++ ) {
DataElement elem = seg.getDataElement( i );
System.out.println( i + ": " + elem.getID() + " (" +
elem.getDescription() + "): " + elem.getMinLength() + "/" +
elem.getMaxLength() + " = " + elem.get() );
}
|
Now that we've specified the interchange header fields and added the header to the envelope (env.setInterchange_Header,) we need to create a functional group. I use the following code:
FunctionalGroup fg = null;
try {
fg = X12FunctionalGroup.getInstance();
}
catch( Exception e ) {
e.printStackTrace();
System.exit( 12 );
}
|
As I mentioned, the functional group is pre-populated. The next code snippet gets a handle to the header and sets the value of some of the data elements:
seg = fg.getHeader(); setElement( seg.getDataElement( 0 ), "PT", 0, 0, 0 ); setElement( seg.getDataElement( 1 ), fromID, 0, 0, 0 ); setElement( seg.getDataElement( 2 ), toID, 0, 0, 0 ); setElement( seg.getDataElement( 3 ), transDate, 0, 0, 0 ); setElement( seg.getDataElement( 4 ), transTime, 0, 0, 0 ); seg.getDataElement( 5 ).setMinLength( 1 ); setElement( seg.getDataElement( 5 ), functionalGroupControlNumber, 0, 0, DATA_LENGTH ); setElement( seg.getDataElement( 6 ), "T", 0, 0, 0 ); setElement( seg.getDataElement( 7 ), "004010", 0, 0, 0 ); |
Before getting into the description of the transaction set, let's have a quick look at populating the functional group trailer. Here's the code:
seg = fg.getTrailer(); setElement( seg.getDataElement( 0 ), "1", 0, 0, DATA_LENGTH ); seg.getDataElement( 1 ).setMinLength( functionalGroupControlNumber.length() ); setElement( seg.getDataElement( 1 ), functionalGroupControlNumber, 0, 0, DATA_LENGTH ); env.addFunctionalGroup( fg ); |
seg = Interchange_Control_Trailer.getInstance(); setElement( seg.getDataElement( 0 ), "1", 0, 0, DATA_LENGTH ); setElement( seg.getDataElement( 1 ), interchangeControlNumber, ZERO_FILL, RIGHT_JUSTIFY, MIN_LENGTH ); env.setInterchange_Trailer( seg ); |
System.out.print( env.getFormattedText( Envelope.X12_FORMAT ) ); |
Here's the code for creating a new (empty) transaction set:
TransactionSet ts = null;
try {
ts = TransactionSetFactory.buildTransactionSet( "867" );
}
catch( Exception e ) {
e.printStackTrace();
System.exit( 12 );
}
|
I chose the second option, primarily due to time constraints. In some ways, it's fortunate that the document type 840 XML file was provided since it contains just about every segment type imaginable. I was able to cut and paste the 840 file and generate a functional 867 file in a matter of hours. I had to perform some minor structural alterations in order to support both of the files we needed to generate, and it's certainly not a complete implementation, but then we didn't have to spend $500 for the standard or $150 for the file from American Coders.
I believe that this is one of the trickiest parts of the project. So many different skills are involved. It's not just familiarity with XML but the ability to visually parse structure and make intelligent guesses as to the meaning of tags without ever seeing their specification. Being able to infer the meaning of the sequence and occurs attributes, determining that a value of -1 for the occurs attribute implies infinity. Simply being able to read the definition in order to know how to structure the application is quite difficult.
That being said, let's look at how we go about building the transaction set. We need to obtain a reference to the header table (a form of a segment container) and start to create segments. The code follows:
tbl = ts.getHeaderTable(); seg = tbl.createSegment( "ST" ); elem = (DataElement) seg.buildDE( 0 ); elem.set( "867" ); seg.addDataElement( elem ); elem = (DataElement) seg.buildDE( 1 ); elem.set( transactionSetControlNumber ); seg.addDataElement( elem ); tbl.addSegment( seg ); |
<segment name="Transaction Set Header" id="ST"
sequence="10"
occurs="1"
required='M'
xmlTag="TransactionSetHeader">
<dataElement name="Transaction Set Identifier Code" id="143"
sequence="1"
description="Code uniquely identifying a Transaction Set"
type="ID" required="M"
minLength="3" maxLength="3"
xmlTag="transactionSetIdentifierCode"/>
<dataElement name="Transaction Set Control Number" id="329"
sequence="2"
description="Identifying control number that must be unique within the transaction set functional group assigned by the originator for a transaction set"
type="AN" required="M"
minLength="4" maxLength="9"
xmlTag="transactionSetControlNumber"/>
</segment>
|
Within the ST segment are two data elements definitions. The attributes extend those in the segment tag. The additions are the type (ID=identifier, AN=alphanumeric) and minimum and maximum length. All of these attributes, data elements and segments, including subsegments, are defined in the standards for the common document types. Returning to the Java source code, we can now see that the code is setting the transaction code and sequence number. The transaction code is used by communicating partners to indicate the purpose of the transaction set. In our project, we had different codes for the two different purposes.
This single example demonstrates most of what you need to know to code your application! The steps can be reduced to the following:
Table tbl = ts.getDetailTable(); Segment detail = tbl.createSegment( "PTD" ); ... Segment names = detail.createSegment( "N1" ); ... detail.addSegment( names ); ... tbl.addSegment( detail ); |
<table section="detail">
<segment name="Product Transfer and Resale Detail" id="PTD"
description="To specify identifying information"
sequence="10"
occurs="-1"
required='M'
xmlTag="ReferenceIdentification">
<dataElement name="Product Transfer Type Code" id="521"
sequence="1"
description="To indicate the start of detail information relating to the transfer/resale of a product and provide identifying data"
type="ID" required="M"
minLength="2" maxLength="2"
xmlTag="code"/>
...
<segment name="Name" id="N1"
description="To identify a party by type of organization, name and code"
sequence="40"
occurs="5"
required='O'
xmlTag="name">
<dataElement name="Entity Identifier Code" id="98"
sequence="1"
description="Code identifying an organizational entity, a physical location, property or an individual"
type="ID" required="M"
minLength="2" maxLength="3"
xmlTag="entityID"/>
...
|
I've made a couple of complete examples available: