XI Java Mapping Helper (DOM)
The longer I write object oriented stuff, the more I learn and realize: as a matter of fact, the approach I am suggesting here won’t sound any newer for object oriented gurus, but does for me, who too often has a very empiric approach (mercy on me!), and hopefully will be useful to other people on SDN.
When I write a Java mapping for SAP XI, I usually use DOM, which is my favourite technique, no doubts. Please be aware that what I’m going to show you here is no way the formula that will resolve any situation, but rather a suggested approach. An approach that, truly, can be applied in a number of scenarios: the same stuff can be done if you prefer Java mapping using SAX, JDOM, or whatever. And more, as ABAP mapping is also based on an object oriented paradigm, this can be easily applied to the ABAP world as well! (I don’t have time to show this now, too, but I can guarantee it’ll work.)
The Helper Class
The driver question is: what are the main tasks of a Java mapping (using DOM in this case)?
1. parse input document
2. getting a new document for output result
3. working on root element
4. serializing output document to stream
Starting from these basic tasks, we can build an abstract class that implements them as methods. Why abstract? Well, an interface would be useless enough, as we’re aging to write some real code inside it. Let’s say that the abstract class in this case is a good middle course between an interface and a real class. First fo all, it would make no sense to instantiate and use this class directly. Second, we’ll be leveraging on nice features in NWDS (NetWeaver Developer Studio) based on this.
Take a look at the code of my DOMTranformation class, and get an idea. The class name comes from the basic idea that this class is a derivation of the too generic StreamTransformation SAP standard interface which we must always implement to get a workin’ Java mapping.
What do we have, then?
Of course we have the promised methods to perform our afore-said basic tasks, plus the always-needed
setParameter(Map param)
method. Following this very easy way of thinking, you can embed in this java mapping superclass any other frequently used methods.
What’s next?
Well, now you probably want to build the “real” mapping. So proceed as follows:
– create a new Java project
– extend your build path adding the usual aii_map_api.jar
– extend your build path again, adding the project in which you put our DOMTransformation class. At the time of “installing” the whole thing on XI, you’ll have to create two Imported Archives: one .jar for the DOMTransformation class, in a base SWCV/namespace, and onw .jar for the real java mapping(s).
– create your java mapping class: as in the picture below, you don’t need anymore to declare the StreamTranformation interface, but rather to extend our DOMTranformation.
You can search the superclass (DOMTransformation) by using the Browse button (highlighted with lime green) which will popup a class search window (highlighted with lime green as well). Be sure to flag “Inherited abstract methods”: this is no great help actually, but at least we’ll have the core
execute(InputStream input, OutputStream output)
method ready to implement in the new java class (and that’s one of the reason why we used abstract class!).
Now the real java mapping code follows, in which you’ll have to perform only the real xml message transformation, as basic tasks will be accomplished by the superclass DOMTransformation. Also, please find in the code below relevant comments!
Bottom line
As already said, this will be no news for object oriented minded people, but at least I hope it will make some people life easier. And more, that it could serve as a basis for many other weblogs/implementations (ABAP, JDOM, SAX, etc).
Can the super class DomTransformation be a bit more clever so as to detect if it is being run inside XI runtime or inside NWDS...guess the trace method as it is not going to work....I generally make use of a boolean variable which would otherwise be set to true with the help of command line arguments when inside NWDS...and in the XI mapping runtime will be untouched default false...and based on the value trace related statements will be executed / or not.
Anyways...great stuff as usual!!
Rgds,
Amol
I used to make dynamical stuff like that using:
String sapsys = System.getProperty("SAPSYSTEMNAME");
if it is not null, then you're probably on an SAP instance (XI box), otherwise you're testing locally.
Cheers,
Alex
Quite a tiny little thing actually :p
Take care,
Alex
As I cannot get back my old S-user (got with my former employer...), I cannot change weblogs that don't belong to me anymore... 😐 too bad... will write to SAP more "strongly" on this sometime maybe.
Anyway, I've just run into adapting the helper for PI 7.1
Should you need it, here's the code.
Cheers,
Alex
package guarneri.whatever.helpers;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import com.sap.aii.mapping.api.AbstractTrace;
import com.sap.aii.mapping.api.AbstractTransformation;
import com.sap.aii.mapping.api.StreamTransformationException;
import com.sap.aii.mapping.api.TransformationInput;
import com.sap.aii.mapping.api.TransformationOutput;
import com.sap.engine.lib.xml.parser.DOMParser;
import com.sap.engine.lib.xml.util.DOMSerializer;
public abstract class DOMTransformation extends AbstractTransformation {
public Map param = null;
public AbstractTrace trace = null;
private Document doc = null;
private DocumentBuilder builder = null;
public abstract void transform(TransformationInput in, TransformationOutput out) throws StreamTransformationException;
/**
* @param in
* @return
* @throws StreamTransformationException
*/
public Document parse(InputStream in) throws StreamTransformationException {
// Initialize trace object
trace = getTrace();
// Initialize factory and builder
/* DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(false);
try {
builder = factory.newDocumentBuilder();
} catch (Exception e) {
trace.addWarning("Couldn't create DocumentBuilder - " + e);
return null;
}*/
DOMParser dp = null;
try {
dp = new DOMParser();
} catch (Exception e) {
trace.addWarning("Couldn't create DocumentBuilder - " + e);
return null;
}
// Create input DOM tree
try {
doc = dp.parse(in);
} catch (Exception e) {
trace.addWarning("Couldn't parse input XML doc - " + e);
return null;
}
return doc;
}
/**
* @return
*/
public Document newDoc() {
return builder.newDocument();
}
/**
* @param _doc
* @return
*/
public Element getRoot(Document _doc) {
return _doc.getDocumentElement();
}
/**
* @param doc
* @return
*/
public void serialize(Document doc, OutputStream out) {
try {
DOMSerializer dser = new DOMSerializer();
dser.setOutputProperty("indent", "no");
dser.write(doc, out);
} catch (Exception e) {
trace.addWarning("Couldn't write on output stream - " + e);
return;
}
}
public void serialize(Node node, OutputStream out) {
try {
DOMSerializer dser = new DOMSerializer();
dser.setOutputProperty("indent", "no");
dser.write(node, out);
} catch (Exception e) {
trace.addWarning("Couldn't write on output stream - " + e);
return;
}
}
// public AbstractTrace getTrace() {
// return (AbstractTrace) param.get(StreamTransformationConstants.MAPPING_TRACE);
// }
/* (non-Javadoc)
* @see com.sap.aii.mapping.api.StreamTransformation#setParameter(java.util.Map)
*/
public void setParameter(Map param) {
this.param = param;
if (param == null) {
this.param = new HashMap();
}
}
}
Cheers,
Alex
public class MapJmsVendorData2Rfc extends DOMTransformation {
@Override
public void transform(TransformationInput in, TransformationOutput out) throws StreamTransformationException {
// How to get a trace object
AbstractTrace trace = super.getTrace();
// How to parse your input stream
Document docIn = super.parse(in.getInputPayload().getInputStream());
// If you mean to use the trace object only after the parsing, you can also do...
super.trace.addInfo("My info message");
// Get a new document for the result
Document docOut = super.newDoc();
// Wanna work on the root element...?
Element root = super.getRoot(docIn);
// DO YOUR REAL TRANSFORMATION HERE !!!
// ...
// If you want to get mapping runtime constants...
String rcv = (String) super.param.get(StreamTransformationConstants.RECEIVER_NAME);
// And, finally, serialize your resulting document
super.serialize(docOut, out.getOutputPayload().getOutputStream());
}
}