Friday, August 29, 2008

XSLT in SAP Portal's Knowledge Management

One of the features of SAP's Portal application is a Knowledge Management library. Think of it as a JSR-170 application that's not JSR-170 compliant.

One of the challenges of working with this library is the lack of meaningful documentation. It's difficult to parse exactly how it works by just looking at the javadocs. There are some examples of what you can do, but they require strange configurations and occasionally bouncing the Portal. Considering a bounce can take 20-30 minutes rather than seconds, that's not an ideal situation.

Let's examine an idea that the UI/Usability designer has had on my current project. He wanted to simply drop XML into the Portal and use XSLT to give the look and feel he was looking for on individual pages.

Seems like a reasonable request. After much searching, I found this document on how to do that within SAP's KM. Go ahead, give it a read. Seems straight forward except for the bouncing of the server and the fact that it is focused more on XML documents rather than XML for the sake of having HTML look proper within the portal. If you've spend any time with Firebug looking at Portal output, you'll understand where I'm coming from.

Needless to say, this seemed highly difficult to actually implement. I don't want to have to bounce a server for each page we develop or each mistake we might make with the XSLT. Velocity of development would be far too slow.

Therefore I set off on a journey to figure out just how the KM APIs work. I ended up with the following code:


import com.sapportals.portal.prt.component.*;
import com.sapportals.wcm.repository.*;
import com.sapportals.wcm.util.uri.*;
import com.sapportals.wcm.util.usermanagement.*;


import org.jdom.*;
import org.jdom.input.*;
import org.jdom.output.*;
import org.jdom.transform.*;

import javax.xml.transform.stream.*;
import javax.xml.transform.*;

public class KmXmlTransformer extends AbstractPortalComponent {

public void doContent(IPortalComponentRequest request, IPortalComponentResponse response) {
IPortalComponentProfile profile = request.getComponentContext().getProfile();
String xmlDocument = profile.getProperty("XmlDocumentPath");
String xsltDocument = profile.getProperty("XsltDocumentPath");

try {
com.sap.security.api.IUser user=(com.sap.security.api.IUser)request.getUser();
com.sapportals.portal.security.usermanagement.IUser epUser = WPUMFactory.getUserFactory().getEP5User(user);
ResourceContext ctx= new ResourceContext(epUser);

RID xmlRid=RID.getRID(xmlDocument);
IResource xmlResource = (ResourceFactory.getInstance().getResource(xmlRid, ctx));

RID xslRid=RID.getRID(xsltDocument);
IResource xslResource = (ResourceFactory.getInstance().getResource(xslRid, ctx));

SAXBuilder builder = new SAXBuilder();

Document docXml = builder.build(xmlResource.getContent().getInputStream());
Document resultDoc = null;

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Templates stylesheet =
transformerFactory.newTemplates(new StreamSource(xslResource.getContent().getInputStream()));
Transformer xslTransformer = stylesheet.newTransformer();

JDOMResult jdRes = new JDOMResult();
JDOMSource jdSrc = new JDOMSource(docXml);
xslTransformer.transform(jdSrc, jdRes);

resultDoc = jdRes.getDocument();

XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
outputter.output(resultDoc, response.getWriter());

} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
So what does this do? It uses JDOM and an XSLT engine to take an XML file in the repository and transform it with an XSLT file in the repository. It uses properties (XmlDocumentPath, XsltDocumentPath) to define where in the KM those files are. These are configurable so that you can simply reuse this object and just modify the properties to choose different files.

There are some issues with the code. Obviously, it's limited to a single transform in its current form. It also uses a deprecated API in the first three lines of the try block. com.sapportals.portal.security.usermanagement.IUser is a deprecated class. Unfortunately you can't create a ResourceContext without one. Nice professionalism by SAP to not offer an alternative.

Other than those limitations, it works pretty darn well. The only question left to analyze is how well this scales.

No comments:

ShareThis