For our use case we need to intercept an incoming ebXML wire message, and change some part of the wire message itself before processing it in B2B.
Use case description
In this use case we are receiving binary payloads, which are processed incorrectly in Oracle B2B. The incoming ebXML message has the following structure (see figure 1).
Figure 1: ebXML message example
------=_Part_11_1995803378.1472136462013 Content-Type: text/xml;charset=UTF-8 Content-ID: <0> <ebXML envelop…..> ------=_Part_11_1995803378.1472136462013 Content-Type: application/octet-stream Content-Transfer-Encoding: binary Content-ID: <Payload-1> <gzipped file containing payload> ------=_Part_11_1995803378.1472136462013--
Oracle B2B seems to be having problems processing the GZIP payload in the second MIME part of the message. Changing the Content-Type from application/octet-stream to application/gzip will solve the issue, but our trading partner was not planning to perform this change.
Solution
We need to intercept the wire message in B2B and modify the Content-Type value to ‘application/gzip’
Quickly summarized: the message body is retrieved from B2B as a byte array. This ensures the binary stream is processed correctly. Converting to String will cause a character-set conversion which will wreck the binary stream. The ReplacingInputStream private class is used to find and replace a string value within a large bytearray [4].
Figure 9: Java source code for Callout
package nl.qualogy.b2bcallout; import oracle.tip.b2b.callout.Callout; import oracle.tip.b2b.callout.CalloutContext; import oracle.tip.b2b.callout.CalloutMessage; import oracle.tip.b2b.callout.exception.CalloutDomainException; import oracle.tip.b2b.callout.exception.CalloutSystemException; import java.util.*; import java.io.*; public class MyCallout implements Callout { public void execute(CalloutContext arg0, List input, List output) throws CalloutDomainException, CalloutSystemException { try { CalloutMessage cm1 = (CalloutMessage)input.get(0); CalloutMessage cmOut = null; String msg = cm1.getBodyAsString(); byte[] decode = null; decode = cm1.getBodyAsBytes(); byte[] search = "application/octet-stream".getBytes("UTF-8") byte[] replacement = "application/gzip".getBytes("UTF-8"); // Find and replace in bytearray stream ByteArrayInputStream bis = new ByteArrayInputStream(decode ); InputStream ris = new ReplacingInputStream(bis, search, replacement); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int b; while (-1 != (b = ris.read())) bos.write(b); // Use modified byte array as output cmOut = new CalloutMessage(bos.toByteArray()); output.add(cmOut); } catch (Exception e) { System.out.println("Exception: " + e.getMessage() ); } } class ReplacingInputStream extends FilterInputStream { LinkedList<Integer> inQueue = new LinkedList<Integer>(); LinkedList<Integer> outQueue = new LinkedList<Integer>(); final byte[] search, replacement; protected ReplacingInputStream(InputStream in, byte[] search byte[] replacement) { super(in); this.search = search this.replacement = replacement; } private boolean isMatchFound() { Iterator<Integer> inIter = inQueue.iterator(); for (int i = 0; i < search.length; i++ if (!inIter.hasNext() || search[i] != inIter.next()) return false; return true; } private void readAhead() throws IOException { // Work up some look-ahead. while (inQueue.size() < search.length) { int next = super.read(); inQueue.offer(next); if (next == -1) break; } } @Override public int read() throws IOException { // Next byte already determined. if (outQueue.isEmpty()) readAhead(); if (isMatchFound()) { for (int i = 0; i < search.length; i++) inQueue.remove(); for (byte b : replacement) outQueue.offer((int) b); } else outQueue.add(inQueue.remove()); } return outQueue.remove(); } }
Compile, package and load Callout
For compiling the Java callout we need to include the b2b.jar library. For some utility classes you could also consider including the utils.jar.
1) Compiling can be done as shown here:
javac –classpath $MW_HOME/modules/com.bea.core.utils_1.10.0.0.jar: $MW_HOME/soa/soa/modules/oracle.soa.b2b_11.1.1/b2b.jar nl/qualogy/b2bcallout/MyCallout.java
2) Packaging it into a jar file is simply done using:
jar cf MyCallout.jar nl
3) To update Oracle B2B on the fly to use this new callout you can use the following command:
ant -f $MW_HOME/soa/bin/ant-b2b-util.xml b2bupdatecalloutjars -Dpath="/home/oracle/callouts" -Dlibraryname=" MyCallout.jar"