Monday, June 17, 2019

Oracle B2B Java Callout to intercept incoming ebXML wire messages (hacking folder) BLOGGER editing text sucks....

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
  1. ------=_Part_11_1995803378.1472136462013
  2. Content-Type: text/xml;charset=UTF-8
  3. Content-ID: <0>
  4. <ebXML envelop…..>
  5. ------=_Part_11_1995803378.1472136462013
  6. Content-Type: application/octet-stream
  7. Content-Transfer-Encoding: binary
  8. Content-ID: <Payload-1>
  9. <gzipped file containing payload>
  10. ------=_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
  1. package nl.qualogy.b2bcallout;
  2. import oracle.tip.b2b.callout.Callout;
  3. import oracle.tip.b2b.callout.CalloutContext;
  4. import oracle.tip.b2b.callout.CalloutMessage;
  5. import oracle.tip.b2b.callout.exception.CalloutDomainException;
  6. import oracle.tip.b2b.callout.exception.CalloutSystemException;
  7. import java.util.*;
  8. import java.io.*;
  9. public class MyCallout implements Callout {
  10. public void execute(CalloutContext arg0, List input,
  11. List output) throws CalloutDomainException,
  12. CalloutSystemException {
  13. try {
  14. CalloutMessage cm1 = (CalloutMessage)input.get(0);
  15. CalloutMessage cmOut = null;
  16. String msg = cm1.getBodyAsString();
  17. byte[] decode = null;
  18. decode = cm1.getBodyAsBytes();
  19. byte[] search = "application/octet-stream".getBytes("UTF-8")
  20. byte[] replacement = "application/gzip".getBytes("UTF-8");
  21. // Find and replace in bytearray stream
  22. InputStream ris = new ReplacingInputStream(bis, search, replacement);
  23. int b;
  24. while (-1 != (b = ris.read()))
  25. bos.write(b);
  26. // Use modified byte array as output
  27. cmOut = new CalloutMessage(bos.toByteArray());
  28. output.add(cmOut);
  29. } catch (Exception e) {
  30. System.out.println("Exception: " + e.getMessage() );
  31. }
  32. }
  33. class ReplacingInputStream extends FilterInputStream {
  34. LinkedList<Integer> inQueue = new LinkedList<Integer>();
  35. LinkedList<Integer> outQueue = new LinkedList<Integer>();
  36. final byte[] search, replacement;
  37. protected ReplacingInputStream(InputStream in, byte[] search
  38. byte[] replacement) {
  39. super(in);
  40. this.search = search
  41. this.replacement = replacement;
  42. }
  43. private boolean isMatchFound() {
  44. Iterator<Integer> inIter = inQueue.iterator();
  45. for (int i = 0; i < search.length; i++
  46. if (!inIter.hasNext() || search[i] != inIter.next())
  47. return false;
  48. return true;
  49. }
  50. private void readAhead() throws IOException {
  51. // Work up some look-ahead.
  52. while (inQueue.size() < search.length) {
  53. int next = super.read();
  54. inQueue.offer(next);
  55. if (next == -1)
  56. break;
  57. }
  58. }
  59. @Override
  60. public int read() throws IOException {
  61. // Next byte already determined.
  62. if (outQueue.isEmpty())
  63. readAhead();
  64. if (isMatchFound()) {
  65. for (int i = 0; i < search.length; i++)
  66. inQueue.remove();
  67. for (byte b : replacement)
  68. outQueue.offer((int) b);
  69. } else
  70. outQueue.add(inQueue.remove());
  71. }
  72. return outQueue.remove();
  73. }
  74. }

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"