IBM Mashup Center comes with a data mashup editor that you can use to combine and transform XML from multiple sources. The data mashup editor is easy to use, but in some scenarios you may find that it is more efficient to use XSLT to perform your XML transforms. So that you can learn how to get the benefit from both types of transforms, this article shows you how to build an IBM Mashup Center plug-in that can perform XSLT transformations.
"Extend the reach of data for IBM Mashup Center" and "An IBM Mashup Center plug-in to convert HTML to XML" are two earlier developerWorks articles that describe how to develop plug-ins to extend the capabilities of the IBM Mashup Center (see the Resources section for links). Those articles are based on Version 1 of the Mashup Center. The emphasis of this article is on the new capabilities in Version 2 of the plug-in API, such as Basic and Form-based authentication support. It only covers the areas where there are significant differences between Version 1 and Version 2 of the plug-in API.
This article assumes that you are already familiar with the basics of writing an IBM Mashup Center plug-in. In particular, you should know how to program in Java™, JSP, JavaScript, and XSLT.
A quick summary of the v2.0 plug-in API changes
Different plug-ins might require different versions of the same Java package.
To provide the necessary isolation,
starting in Version 2, classes for each plug-in are loaded using separate class loaders.
Classes and plug-in specific jars are kept within the plug-in specific folder and are
not
copied to a common location as in Version 1.
Interaction with the framework is now done mostly via interfaces rather than concrete classes.
For example, the Version 1 renderEditor method used to take two
concrete classes: RequestData and Entry as parameters.
But in v2.0, class instances are changed to interfaces, as shown in Listing 1.
Listing 1. Use of interface as parameter
// v1.1 public ViewBean renderEditor(RequestData rdata, Entry entry)
public Object renderEditor(IEditorContext context)
|
The above changes do not have a significant effect on plug-in implementation. Where the v2.0 changes are most noticeable is in the plug-in JSP implementation. Since JSPs are loaded by the Web Container's JSP classloader, they cannot access plug-in specific (ViewBean) classes. Rather than using attribute specific getters, the v2.0 plug-in JSP needs to use generic name / value pairs to retrieve attributes. This is demonstrated in a later section.
As described in Section 6.1 of the Application Programming Interface Reference, Version 2.0 (see Resources for link), during startup, the server searches for ZIP files containing third party plug-ins that have been placed in the special <WebApplication>/WEB-INF/plugins folder. The ZIP archive must have the following folder structure:
- /client/plugins/PLUGIN_DIR — Contains files for browsers, for example, images and JavaScript files.
- /server/plugins/PLUGIN_DIR — Contains the plug-in manifest and files used by the plug-in to render itself (JSP pages).
- /server/plugins/PLUGIN_DIR/classes — Contains the plug-in Java classes. This can be a hierarchy of folders.
- /server/plugins/PLUGIN_DIR/lib — Contains JAR files used by the plug-in (third-party).
If you are familiar with developing v1.1 plug-ins, you may notice that the classes and lib folders are no longer placed under the WEB-INF folder. To simplify the final build and packaging of the plug-in, use your favorite IDE to create a project that has the same directory structure that is required by the final ZIP archive. Figure 1 shows the layout of a sample Eclipse project.
Figure 1. Eclipse project
You can find the complete sample Eclipse project in the Download section. The example, uses the package name sample.mashupcenter.xslt for PLUGIN_DIR. It also contains an extra folder named lib_noship to hold the Version 2 IBM Mashup Center mhubapi.jar file that you need during development. You have to copy the mhubapi.jar file from your server's <installationDir>\Hub\installedApps\Mashup Hub.ear\mashuphub-enterprise.war\client\api folder. You only need this file during development; you should remove it from the final deployment-ready ZIP file.
Listing 2 shows the contents of the file named plugin.xml that is in the server/plugins/PLUGIN_DIR folder.
Listing 2. Package XML file
<?mhub version="2.0"?>
<plugin>
<name>XSLT Transform</name>
<description>Apply XSLT to transform XML</description>
<author>IBM</author>
<version>2.0</version>
<extension id="sample.mashupcenter.xslt.transformeditor"
name="XSLT Transform Editor"
point="com.ibm.mashuphub.core.extension.IEditor">
<editor class="sample.mashupcenter.xslt.TransformEditorPlugin"
type="xslt"
category="departmental"
icon="/plugins/sample.mashupcenter.xslt/icons/btn16_hello.gif"
name="%editor.name"
description="%editor.description" />
</extension>
<extension id="sample.mashupcenter.xslt.transformgenerator"
name="XSLT Transform Generator"
point="com.ibm.mashuphub.core.extension.IGenerator">
<generator class="sample.mashupcenter.xslt.TransformGeneratorPlugin"
type="xslt" />
</extension>
</plugin>
|
Besides providing the basic plug-in information, the plugin.xml file contains names of classes that implement the Editor and Generator component. By default, the plug-in generates feeds and the name of the Editor is presented in a list when users select Create and then New Feed, as shown in Figure 2. Notice also that the Editor name appears under departmental, which is the category specified in the plug-in.xml file.
Figure 2. Feed plug-in selection listbox
To allow for internationalization, you should specify variables for the
the name and description parameters.
For example, in the plugin.xml example shown in Listing 2,
%editor.name and %editor.description are variables.
Their values are specified in a file named plugin.properties that resides in the
same directory as plugin.xml.
The plugin.properties file is loaded by the framework using the standard Java resource bundle loading convention.
For each supported language, place the translated strings in a properties file with the locale appended to
"plugin".
For example, the Japanese version of the file should be named plugin_ja.properties.
As specified in the plugin.xml file, the TransformEditorPlugin class provides the plug-in editing function.
Its implementation extends BaseEditor, a base class that
requires implementation of the renderEditor method.
The renderEditor method is called by the framework when users make the selection to create a new feed using this plug-in,
or when users edit an existing feed previously created by this plug-in.
Listing 3.
TransformEditorPlugin class
public class TransformEditorPlugin extends BaseEditor {
@Override
public Object renderEditor(IEditorContext context)
{
ResourceBundle i18n =
ResourceBundle.getBundle(TransformConstants.I18N_RESFILE, context.getLocale());
IEntry entry = context.currentEntry();
ILog log = context.getLog();
IRequestContext request = context.getRequestContext();
|
The renderEditor method in Version 2 takes a single parameter: IEditorContext.
The parameter is common to all methods invoked by the framework in response to user editing actions.
Recall the earlier discussion on using interfaces versus concrete classes.
From the context, you get the
IRequestContext interface containing information sent from the browser, and
IEntry containing all the information maintained by the framework for this feed instance.
The usage of these interfaces is similar to the usage of
RequestData and Entry classes in
Version 1, so this article does not provide any further information on this topic.
If you would like more details though, you can refer to the
two Version 1 articles mentioned earlier and linked to in the Resources section.
You can find the Javadoc for the plug-in API on your IBM Mashup Center installation by
going to this URL:
http://<yourserver>/mashuphub/client/doc/javadoc/index.html.
Note also that the logging instance is now passed in and accessible through the IEditorContext parameter.
Log messages are interleaved with those from the feed generation framework and are
written to the following file:
<WebApplication>/META-INF/logs/javamashuphub.log.
By default, only
messages with a severity of WARN and above (for example ERROR) are written to the log file.
Listing 4 shows the body of the renderEditor method.
Listing 4.
renderEditor method body
BasicEditorViewBean htViewBean =
new BasicEditorViewBean( TransformConstants.JSPPATH_TRANSFORM );
htViewBean.setEntry(entry);
htViewBean.setI18NProperties( TransformConstants.I18N_RESFILE );
String s = entry.getAttribute(TransformConstants.PARAM_XSLT );
htViewBean.addAttribute( TransformConstants.PARAM_XSLT , escapeXml( s ) );
IParameter param = entry.getParameter( TransformConstants.PARAM_XMLURL );
if ( param != null ) {
htViewBean.addAttribute( TransformConstants.PARAM_XMLURL
, param.getDefaultValue() );
}
AuthHelper helper = AuthHelper.restoreFromEntry(context
, AuthConstants.AUTH_METHOD_BASIC
, null );
ViewBean authViewBean = helper.getViewBean(context);
FormViewBean form = new FormViewBean();
form.setSuffix( SUFFIX_TRANSFORM );
form.addComponent( htViewBean );
form.addComponent( authViewBean );
form.setOnsubmit( PluginHelper.getClientMe(pluginId, entry.getObjectId()) +
".invokeServer('displayPreviewPage',"
+ PluginHelper.getClientId(pluginId, entry.getObjectId()) +
"_" + form.getSuffix() + ");");
|
The renderEditor method returns an instance of the type ViewBean.
The main purpose of the ViewBean is to specify the
JSP used by the feed generation framework to create an HTML fragment for the plug-in specific editor.
In Version 2, JSPs are loaded by the
Web Container's JSP classloader and cannot access plug-in specific classes,
such as a ViewBean.
So instead of creating a plug-in specific ViewBean class, the sample above
uses the generic BasicEditorViewBean class provided by the framework.
The path to the plug-in specific JSP is passed
in through the constructor.
Plug-in specific parameters are passed to the BasicEditorViewBean
instance through the generic addAttribute method rather than using parameter specific getters and setters.
These plug-in specific parameters are used to pre-populate the editing form
with values the user entered in earlier edit sessions.
As explained in one of the earlier articles, the plug-in specific ViewBean is not returned to the framework directly.
Instead, it is added to the FormViewBean.
The latter contains logic to interact with the framework and provide a
wizard-like multi-step editing experience.
The difference is that this example adds two ViewBeans to the FormViewBean.
The FormViewBean can piece together multiple HTML fragments
generated by JSPs and associated with multiple ViewBeans.
Figure 3 shows the form that is rendered from the two ViewBeans.
Figure 3. XSLT plug-in editor
The top two form elements are a text area for entering XSLT and a text entry field for the URL that points to the XML to be transformed. These elements are generated by the plug-in specific JSP. You can find the plug-in specific JSP in the XSLTPlugin.zip file available in the Download section. JSPs and the client side JavaScript framework did not change much between Versions 1 and 2, so please consult the earlier article for details on those subjects.
The second ViewBean is returned by the AuthHelper class by
calling the restoreFromEntry method.
The AuthHelper class is added in Version 2 to make it easy to add
Basic and Form-based authentication support to any third party plug-in.
The AuthHelper ViewBean generates
all the form elements needed to gather credentials for the supported authentication methods.
Later in this article you learn how to use the gathered credentials to access the URL containing the data to be transformed.
Figure 4 shows the generated form elements with Basic authentication selected.
Figure 4. Authentication form
Before leaving this topic, note that the AuthHelper instance is created by calling the static
restoreFromEntry method and the first parameter is the IEditorContext instance.
The AuthHelper uses the IEditorContext instance to automatically retrieve parameter values
saved with the feed during earlier invocation of the editor.
There is nothing for the caller to do!
The design of this example plug-in is similar to the plug-in described in the
"An IBM Mashup Center plug-in to convert HTML to XML" article (see Resources).
Users paste XSLT into the text box,
provide a test URL, and then proceed to the preview page by clicking the Next button.
The method to help generate the follow-on preview page is specified in the earlier renderEditor method
where we have inserted a small chunk of Javascript code using
form.setOnsubmit as shown in Listing 4.
You can retrieve the JavaScript file providing the client side API by going to this URL:
https://<yourserver>/mashuphub/client/scripts/hub/managers/Plugin.js
The inserted JavaScript invokeServer will call the specified
displayPreviewPage method through an AJAX call.
Listing 5 shows the body of the displayPreviewPage method.
Listing 5.
displayPreviewPage method body
entry.addAttribute(TransformConstants.PARAM_XSLT , sXSLT );
IParameter eparam = entry.createParameter();
eparam.setName( TransformConstants.PARAM_XMLURL );
eparam.setDefaultValue( sUrl );
eparam.setType("string");
eparam.setPrompt("Y");
entry.addParameter( eparam );
AuthHelper authHelper = AuthHelper.createFromRequest( context
, AuthConstants.AUTH_METHOD_BASIC
, null );
authHelper.saveParameters(context);
authHelper.saveCredential( context );
|
The main purpose of the displayPreviewPage method is to
return a second ViewBean (therefore JSP) to the framework for rendering
an editor page to display the output of the XSLT transformation.
Before returning the output from the XSLT transform, the method first has to save the parameters supplied by the user.
The code in Listing 5 shows how the different pieces of information are saved.
The XSLT is to be used in every feed generation, so it is saved as an attribute.
The XML to transform can vary from invocation to invocation, so it is saved as a parameter.
Note that Version 2 has a new method named createParameter that
is used to return a parameter interface instance.
For the authentication related information, the example re-instantiates a new instance of AuthHelper by calling
the static createFromRequest method.
This extracts and persists any user specified authentication information from the
HTTP request.
The actual XSLT transform in preview is common to feed generation and is covered next. This wraps up the discussion of the Editor. Again, the complete source code is available in the Download section.
Implementing the feed generation
The TransformGeneratorPlugin class extends the BaseGenerator class
and must implement the abstract method generateFeed.
Similar to the renderEditor method, it takes a single
parameter, which is an IGenerateContext
interface instance.
From the IGeneratorContext instance, you get the
IRequestContext interface instance containing the information sent from the browser,
and an IEntry instance containing all the information maintained by the framework for this feed instance.
Listing 6 contains the
standard boilerplate for the generateFeed method.
Listing 6.
renderEditor method body
public class TransformGeneratorPlugin extends BaseGenerator
{
public IFeedContent generateFeed(IGeneratorContext context) throws GeneratorException
{
ILog log = context.getLog();
IEntry entry = context.currentEntry();
IRequestContext request = context.getRequestContext();
ResourceBundle i18n = ResourceBundle.getBundle( TransformConstants.I18N_RESFILE
, context.getLocale());
|
To generate feeds, you first retrieve attributes containing configuration information
saved during the editing process; for this plug-in, it is the XSLT entered into the text box during edit.
Every invocation uses this same XSLT.
The XML to transform can vary from invocation to invocation and is specified in the feed
generation URL parameter named xmlurl.
These are fairly straightforward operations against the IEntry
and IRequestContext instances, so the relevant code snippet
is not included in this article.
After retrieving the XML to transform, the implementation of the actual transformation is rather anti-climatic. As shown in Listing 7, it is fairly simple to initiate the XSLT transformation using JAXP and the XSLT engine shipped with the JDK.
Listing 7.
renderEditor method body
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(new StreamSource( rdrXSLT ));
// Use the Transformer to apply the XSLT to the XML document
Writer writer = new StringWriter();
transformer.transform( new StreamSource( rdrContent )
, new StreamResult( writer ));
String after = writer.toString();
|
What is of interest and new in this example, is how you retrieve the content from the
caller provided URL.
Instead of instantiating a java.net.URL instance directly, you first instantiate an instance of AuthHelper
for this feed entry by passing in the context.
You then obtain a connection from the AuthHelper instance by
calling the connect method and passing in the URL.
Under the covers, the AuthHelper instance generates the appropriate HTTP handshake according to the specified
authentication method and using the credentials supplied during feed creation.
Also, if the Mashup Center is configured to use a proxy, the request is directed through
the specified proxy.
Listing 8 shows the two lines of code needed to do all this.
Listing 8. renderEditor method body
AuthHelper helper = AuthHelper.restoreFromEntry( context
, AuthConstants.AUTH_METHOD_BASIC
, null );
conn = helper.connect(context, sUrl);
|
This concludes the tour of the implementation, now it is time to see the plug-in in action.
This section shows you how to put the plug-in to use. The first example considers the case where one of the elements in the input xml contains a large number of attributes. The idea is to convert them to elements so they can be displayed using widgets shipped with the product. This could be easily done using the data mashup editor's Transform operator. For each attribute, you would create an element in the output entry and then copy the value of the attribute over. However, if the number of attributes is high, this can become fairly tedious. An alternative would be to use XSLT. As an example, look at the sample attributes.xml file included in the XSLTPlugin.zip download and shown in Listing 9.
Listing 9. attribute.xml
<root>
<entry>
<info product='camera'
price='40.00'
instock='y'/>
</entry>
<entry>
<info product='USB drive'
price='60.00'
instock='n'/>
</entry>
</root>
|
Listing 10 is a fragment of the attribute.xsl sample XSLT included in the XSLTPlugin.zip
download.
This is the part that performs the attributes to elements transformation.
The xsl:template element is used to locate the element of
interest, which in this case is info.
Once found, the xsl:for-each construct loops through all attributes selected using the pattern @*.
For each attribute that matches the pattern, the xsl:element
construct creates an element with the same name as the attribute.
Lastly, the text value of the attribute is placed inside the newly created element.
Listing 10. attribute.xsl fragment
<xsl:template match="info">
<info>
<xsl:for-each select="@*">
<xsl:element name="{name()}">
<xsl:value-of select='.' />
</xsl:element>
</xsl:for-each>
</info>
</xsl:template>
|
The next example exploits the richer conditional capabilities of XSLT to add HTML markup that enhances the display of a feed by using the DataViewer widget that is available "out-of-the-box" with the Mashup Center. Listing 11 shows the stocks.xml sample feed that is also included in the XSLTPlugin.zip download. The file was created by uploading a CSV file containing real times quote from the Yahoo! finance site.
Listing 11. stocks.xml
<entry xmlns="http://www.w3.org/2005/Atom">
.........
<content type="application/xml">
<row xmlns="http://www.ibm.com/xmlns/atom/content/datarow/1.0">
<col_A>BAC</col_A>
<col_B>14.94</col_B>
<col_C>11/6/2009</col_C>
<col_D>11:23am</col_D>
<col_E>-0.19</col_E>
<col_F>14.94</col_F>
<col_G>15.24</col_G>
<col_H>14.84</col_H>
<col_I>68841848</col_I>
</row>
</content>
</entry>
|
In this case, you want to display negative values in red to visually represent a drop in price. Also, the column names generated by the Mashup Center are not meaningful because the csv file does not contain a header row. So you also want to replace what is there with more meaningful names. Listing 12 is fragments from the stockColor.xsl sample XSLT file included in the XSLTPlugin.zip download. These are the parts of the file that make the changes described above and insert the HTML markup.
Listing 12. stockColor.xsl fragment
<xsl:template match="*" mode="rowChild">
<xsl:choose>
<xsl:when test="name()='col_A'" >
<xsl:element name="Ticker">
<xsl:value-of select="." />
</xsl:element>
</xsl:when>
................
<xsl:when test="name()='col_E'" >
<xsl:element name="Change">
<xsl:choose>
<xsl:when test="text() < 0">
<font color="#FF0000" face="courier"><xsl:value-of select="." /></font>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:when>
...............
</xsl:choose>
</xsl:template>
|
The fragment in Listing 12 does not show
the xsl:template statements that are used to locate the elements of interest
(child elements of row).
These child elements
from row
are passed to the above template for further processing.
The xsl:choose and xsl:when statements correspond to the Java switch
and case statements.
There is one case (xsl:when) for each of the child elements of
row.
Non-numeric columns (for example, col_A) are renamed
by creating a new element using xsl:element and copying the text value over.
Numeric columns that could be negative (for example, col_E)
are tested using a xsl:when statement to see if its text value is less than or greater than 0.
If the value is negative, HTML markup to change the font color to red is inserted.
Notice that the inserted markup needs to be escaped for the DataViewer to interpret it correctly.
Note also that the comparison operators for "less than" and "greater or equal to" inside the xsl:if statement are
also escaped to comply with XML rules.
One last thing to note before wrapping up is related to something that was skipped over when
discussing the code in Listing 4.
HTML text box unescapes XML entities < and > when text content is assigned to it programmatically
(not when the user directly enters it into the text box).
To ensure that the above XSLT file is rendered correctly when it is re-edited,
the implementation escapes the XSLT content.
For example, < is changed to &lt; so
that it stays escaped when displayed.
You have just walked through the construction of a fairly complex plug-in involving multiple editor pages using the Mashup Center Version 2 APIs. To try out what you learned in this article, you could extend the current XLST plug-in to add parameter support. You could also start using XSLT transform by downloading the plug-in project and installing it on a Version 2 Mashup Center server.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample plug-in files | XSLTPlugin.zip | 181KB | HTTP |
Information about download methods
Learn
-
To learn the basics of creating an IBM Mashup Center
V1.1
plug-in, read "
Extend the reach of data for IBM Mashup Center" (developerWorks, August 2008).
For IBM Mashup Center Version 2.0 or later, use this article.
-
For a more in-depth discussion on creating an IBM Mashup Center
V1.1
plug-in, read "
An IBM Mashup Center plug-in to convert HTML to XML" (developerWorks, August 2009).
-
Chapter 6 of
IBM Mashup Center Version 2.0 Application Programming
Interface Reference is the official documentation for API of the IBM Mashup Center plug-in .
-
"Manipulating data with XSL" (developerWorks, October 2001)
is a good introductory tutorial to XSLT.
-
To learn about Dojo, see The Official Dojo
Documentation Web page.
- In the
Information Management area on developerWorks,
get the resources you need to advance your skills on IBM Information Management products.
Get products and technologies
-
To get hands-on experience with IBM Mashup Center, visit Lotus greenhouse.
-
Download a free trial version of IBM Mashup Center
from developerWorks.
-
You may also access Mashup Center on the Amazon Elastic Compute
Cloud.
Discuss
- Participate in the discussion forum.
- Check out
My developerWorks
blogs and
get involved in the
developerWorks community.




