Web Experience Factory is a pretty good tool for rapid development, but its designer lacks automated documentation that can be used to explain an application to a new developer. Models can get extremely complicated, especially when they're assembled by developers who are just starting to use the tool. This article describes how graphs can be generated using eclipse.
Using an Eclipse Plugin to Generate Graphs
I've worked with projects that, in some cases, imported dozens of other models, making it quite challenging to follow a sequence of events. About a year ago I had the idea of generating a graph showing how models are imported into each other, but quickly got lost in the details of graphing within eclipse.
I recently stumbled on an open source application named Graphviz, and when I saw the simplicity of using a DOT file to describe a graph, I was able to quickly develop a plugin to inspect a WEF model and produce a graph of it.
An easy candidate for a graph is tracing how models import other models. The plugin does this by recursively traversing the import builders in each model, producing a graph similar to this example.
Once I figured that out, I was able to add some more to the plugin, producing a graph of service consumer operation calls like the one below. This graph indicates that model_4 contains an action list builder named alTest that invokes the doSomeOperation operation exposed by the service consumer builder named svcConsumer.
The graph of action list calls (builders which invoke action lists) is very similar to the one above, except the target node is an action list instead of a service operation.
After the plugin is installed by following these instructions, you will see the following submenu when right clicking on a model.
Just select the graph you wish to generate and it will appear in an eclipse browser window. Although I developed this tool for my own use, I'm making it available free of charge to anyone.
If the tool doesn't work for you, then please leave me a message on this blog (or email me).
References: Download plugin from http://www.dsixe.com/eclipse
A collection of tips and best practices for developing great applications using Web Experience Factory and Java EE.
Monday, December 31, 2012
Tuesday, December 4, 2012
Using com.bowstreet.builders.webapp.api.Method
I've been writing some more custom builders recently and had a hard time figuring out how to generate a method with arguments.
Below is an example:
and here's the code added to the model on regen:
Below is an example:
Method method = new Method(builderCall, genContext); method.setName("myMethod"); IXml args = null; try { args = XmlUtil.parseXml("<top><Argument><Name>msgValue</Name><Type>String</Type></Argument></top>"); } catch (IOException e) { e.printStackTrace(); } method.setArguments(args); StringBuffer sb = new StringBuffer("{ \n"); sb.append(" webAppAccess.getVariables().getVariable(\"myVar\").setValue(msgValue); \n"); sb.append("}"); method.setBody(sb.toString()); method.invokeBuilder();
and here's the code added to the model on regen:
/** * Generated Method [myMethod] */ public void myMethod(WebAppAccess webAppAccess, String msgValue) { webAppAccess.getVariables().getVariable("myVar").setValue(msgValue); }I figured out the magic XML for method.setArguments() by looking at the model XML for a standard method builder.
Wednesday, November 28, 2012
WEF Profiling Explained
Profiling is a frequently misunderstood concept in web experience factory. This blog entry describes profiling by comparing it to java code, something that more developers are familiar with.
Comparing Profiles to Java Code
Many people struggle with the concept of profiling and profile sets, but it can be closely compared to a conditional statement in java. Suppose that we have a button that needs to be displayed if the application is accessed from an android device. Coding this using java would look something like this (depending on the framework):
The same can be accomplished with WEF profiling except we don't have any code to work with, only a button builder with inputs. Ideally, we would want to apply the results of profiling to the builder enable input, setting it to true or false which would effectively replicate what the java code above does.
Think of a profile set as a collection of profiles and the selection of a given profile is the result of a conditional statement. That conditional statement is controlled by a selection handler. Let's look at the code above again from this perspective
The profile set selection handler is the engine that controls which profile is selected. In this particular case, we're using the mobile selection handler which contains logic that looks at the HTTP request user agent header to determine which profile is selected. Moving one step further, the selection handler doesn't really return a value like "android" per se, it chooses a profile.
Suppose we not only want to control the visibility of a button, but we also want to execute a call to a service provider operation when the application is accessed from an android device. In java, we would add some logic
For the sake of making this explanation clearer, let's also add some code to handle iPhones as well as a handler for the default case
That all seems very obvious if we were using java, but how is it implemented in WEF using profiling?
Here is the detailed profile entry for the button
Similarly, the action list is profiled
and below is the detailed profile entry
Only one profile in any profile set can be active at one time, and profiling is applied when the user session is first initiated. It is not possible to use two different profiles from the same profile set or switch profiles in the same session.
Comparing Profiles to Java Code
Many people struggle with the concept of profiling and profile sets, but it can be closely compared to a conditional statement in java. Suppose that we have a button that needs to be displayed if the application is accessed from an android device. Coding this using java would look something like this (depending on the framework):
if (request.getHeader("User-Agent") == "android"){ button.visible = true; }
The same can be accomplished with WEF profiling except we don't have any code to work with, only a button builder with inputs. Ideally, we would want to apply the results of profiling to the builder enable input, setting it to true or false which would effectively replicate what the java code above does.
Think of a profile set as a collection of profiles and the selection of a given profile is the result of a conditional statement. That conditional statement is controlled by a selection handler. Let's look at the code above again from this perspective
if (profileset.selectionHandler.result == "android"){ button.enable = true; }
The profile set selection handler is the engine that controls which profile is selected. In this particular case, we're using the mobile selection handler which contains logic that looks at the HTTP request user agent header to determine which profile is selected. Moving one step further, the selection handler doesn't really return a value like "android" per se, it chooses a profile.
Suppose we not only want to control the visibility of a button, but we also want to execute a call to a service provider operation when the application is accessed from an android device. In java, we would add some logic
if (profileset.selectionHandler.profile.name == "android"){ button.enable = true; invokeSomeOperation(); }
For the sake of making this explanation clearer, let's also add some code to handle iPhones as well as a handler for the default case
if (profileset.selectionHandler.profile.name == "android"){ button.enable = true; invokeSomeOperation(); } else if (profileset.selectionHandler.profile.name == "iphone"){ button.enable = false; invokeSomeOperationIPhone(); } else{ button.enable = false; // do nothing }
That all seems very obvious if we were using java, but how is it implemented in WEF using profiling?
Here is the detailed profile entry for the button
Similarly, the action list is profiled
and below is the detailed profile entry
Only one profile in any profile set can be active at one time, and profiling is applied when the user session is first initiated. It is not possible to use two different profiles from the same profile set or switch profiles in the same session.
Labels:
profileset
,
profiling
,
web experience factory
,
WEF
Wednesday, November 7, 2012
WEF and Custom HTML
Working with custom HTML is probably one of the most common patterns I've seen when building an application with web experience factory (WEF). Typically this is a situation where I'm given a really nice HTML stub from a professional web designer and I need to build an application around it.
Don't Fight with WEF
A common mistake I see all the time is a web developer trying to untangle or intercept the code generated by WEF. Don't do it, you're going to lose your mind or create an unmaintainable mess (or both). Here are a few tips that should help:
1 - Turn off the theme
2 - Don't let the data page builder create the UI
3 - Learn how to exploit page automation
Turn off the Theme
There are two ways to accomplish this, adding a theme builder to your model and selecting the individual components, or blanking out the theme file name in the project's override.properties.
The theme is great when using WEF's default user interface, but it just gets in the way of custom HTML.
Dumb Down the Data Page Builder
The data page builder is the heart of page automation in WEF, but sometimes it wants to do too much, and again, gets in the way of custom HTML. We want dumb it down a little by turning off the option to make the UI from data.
In many cases we'll also want to set the page type to view only if we're only displaying data, but this depends on the use case.
Exploit Page Automation
Now that we've turned off some of the noise that we don't need, how do we tell the page automation to place data on our custom HTML? This is easily done by adding our own name attributes to the HTML elements. Whenever page automation finds a value for the name attribute that matches the data specified in the data page variable, it inserts a data value. Take for example the following XML data
We can ask page automation to take this custom HTML
This is how page automation sees the HTML name attributes that we added
You may have noticed that we added sorting capability to the first column, this is standard practice using a data column modifier (DCM) builder. The DCM builder needs to be able to identify the location of the table headers, and since we have custom HTML, we have to do this ourselves by adding a suffix Header to the name attribute in the table header rows.
Also note that the table row identified with the name attribute contact refers to the repeating XML data element contact. Page automation will create a new HTML row for each contact element in our data.
The edit button is there to demonstrate that we can easily add builders that refer to named locations in our HTML, just as if the data page builder had done all the work for us.
More information about manually marking up HTML for page automation can be found here.
Don't Fight with WEF
A common mistake I see all the time is a web developer trying to untangle or intercept the code generated by WEF. Don't do it, you're going to lose your mind or create an unmaintainable mess (or both). Here are a few tips that should help:
1 - Turn off the theme
2 - Don't let the data page builder create the UI
3 - Learn how to exploit page automation
Turn off the Theme
There are two ways to accomplish this, adding a theme builder to your model and selecting the individual components, or blanking out the theme file name in the project's override.properties.
The theme is great when using WEF's default user interface, but it just gets in the way of custom HTML.
Dumb Down the Data Page Builder
The data page builder is the heart of page automation in WEF, but sometimes it wants to do too much, and again, gets in the way of custom HTML. We want dumb it down a little by turning off the option to make the UI from data.
In many cases we'll also want to set the page type to view only if we're only displaying data, but this depends on the use case.
Exploit Page Automation
Now that we've turned off some of the noise that we don't need, how do we tell the page automation to place data on our custom HTML? This is easily done by adding our own name attributes to the HTML elements. Whenever page automation finds a value for the name attribute that matches the data specified in the data page variable, it inserts a data value. Take for example the following XML data
<contacts> <contact> <name>n1</name> <phone>p1</phone> </contact> <contact> <name>n2</name> <phone>p2</phone> </contact> </contacts>
We can ask page automation to take this custom HTML
<html> <head> <title>Default Test Page</title> </head> <body> <table name="contacts" width="50%"> <tr align="center"> <th><span name="nameHeader">Name</span></th> <th>Click to update</th> <th><span name="phoneHeader">Phone</span></th> </tr> <tr name="contact" align="center"> <td><span name="name"></span></td> <td><span name="btn"></span></td> <td><span name="phone"></span></td> </tr> </table> </body> </html>to produce a user interface, and this is what we see in the browser when we launch the application
This is how page automation sees the HTML name attributes that we added
You may have noticed that we added sorting capability to the first column, this is standard practice using a data column modifier (DCM) builder. The DCM builder needs to be able to identify the location of the table headers, and since we have custom HTML, we have to do this ourselves by adding a suffix Header to the name attribute in the table header rows.
Also note that the table row identified with the name attribute contact refers to the repeating XML data element contact. Page automation will create a new HTML row for each contact element in our data.
The edit button is there to demonstrate that we can easily add builders that refer to named locations in our HTML, just as if the data page builder had done all the work for us.
More information about manually marking up HTML for page automation can be found here.
Labels:
custom html
,
page automation
,
table headers
,
WEF
,
WPF
Sunday, July 29, 2012
What's in a Portal URL?
I've been working with WebSphere Portal for many years and I always wondered about the format of the URL, but never took the time to track it down. Last week I stumbled on this interesting document that laid it all out.
The short if it is that the typical URL
No need for me to go into details - the document explains it quite well.
EDIT 09/30/2013: Here's a URL that describes REST services which will encode/decode portal URLs:
http://www-10.lotus.com/ldd/portalwiki.nsf/dx/IBM_WebSphere_Portal_Remote_State_Service_and_Fragment_Service
The short if it is that the typical URL
http://localhost:10039/wps/portal/!ut/p/b0/04_Sj9CPykssy0xPLMnMz0vMAfGjzOKd3R09TMx9DAwsTC0MDTwdPULNzYJdjAxczfXD9aPwKnE0hiowwAEcDfT9fPLT01NT_EtL9Auys9McHRUVAdjo3OY!is an XML representation of the portal page compressed and encoded with a proprietary algorithm. It turns out there are several alternate URL schemes for WPS, but this one is the most common variation that everyone is familiar with.
No need for me to go into details - the document explains it quite well.
EDIT 09/30/2013: Here's a URL that describes REST services which will encode/decode portal URLs:
http://www-10.lotus.com/ldd/portalwiki.nsf/dx/IBM_WebSphere_Portal_Remote_State_Service_and_Fragment_Service
Tuesday, July 24, 2012
A Lotus Notes Calendar Widget
Most of my blog entries reflect some challenging task that I helped my client complete, this latest one was particularly interesting because there was little documentation to guide us. In fact I'm going to blog about something that I wouldn't recommend to anyone but sometimes there are no easy ways out.
The Requirement for a Calendar
My client wanted to expose their user's lotus notes calendar entries into a JSR 286 portlet but we couldn't find any easy out of the box solutions for this. Since I don't know anything (at all) about lotus domino I turned to a resident expert for advice on how to get my hands on the calendar data. We appeared to have two options:
After much discussion we decided to pursue the second option since we had little time to roll out the portlet and we didn't want to alter the mailbox database of thousands of users.
Tapping into ReadViewEntries
The domino mailbox supports a URL command named ReadViewEntries that can be used to retrieve calendar entries as JSON data. The URL looks something like this
The result looks pretty good - thanks to Rick Taylor for helping me out with the lotus domino stuff.
The Requirement for a Calendar
My client wanted to expose their user's lotus notes calendar entries into a JSR 286 portlet but we couldn't find any easy out of the box solutions for this. Since I don't know anything (at all) about lotus domino I turned to a resident expert for advice on how to get my hands on the calendar data. We appeared to have two options:
- Develop a custom lotus form that would require replicating every user's mailbox
- Re-purpose an existing form to suit our needs
After much discussion we decided to pursue the second option since we had little time to roll out the portlet and we didn't want to alter the mailbox database of thousands of users.
Tapping into ReadViewEntries
The domino mailbox supports a URL command named ReadViewEntries that can be used to retrieve calendar entries as JSON data. The URL looks something like this
http://yourbox.com/mailServer/mailbox.nsf/Calendar?ReadViewEntries&outputformat=JSONThis request returns calendar entries for a given date range, more details can be found on this cheat sheet. Unfortunately we quickly realized that ReadViewEntries is not really designed for retrieving calendar entries formatted the way we needed. I'm not sure about the real purpose of this form, but I had to reverse engineer the results by creating a test sampling of all the different types of calendar entries (anniversary/meeting/reminder/repeated entries....) and then looking at the result set. Below is the javascript that I created to parse out the data feed
function populateCalendarData(viewentries) { var jqueryData = []; for ( var x = 0; x < viewentries.viewentry.length; x++) { var entry = {}; entry.type = "meeting"; var cday = null; if (viewentries.viewentry[x].entrydata[0].datetime != undefined){ cday = viewentries.viewentry[x].entrydata[0].datetime[0]; } else { // repeating item shows up with datetimelist cday = viewentries.viewentry[x].entrydata[0].datetimelist.datetime[0][0]; } var sd = cday; // all day events don't have start dates if (viewentries.viewentry[x].entrydata[2].datetime != undefined){ sd = viewentries.viewentry[x].entrydata[2].datetime[0]; } else { entry.type = "all-day"; } var ed = sd; if (viewentries.viewentry[x].entrydata[4].datetime != undefined){ ed = viewentries.viewentry[x].entrydata[4].datetime[0]; } else { // appointments don't have end dates entry.type = "appointment"; } var startDate = getDate(sd); var endDate = getDate(ed); var moderator = ""; var subject = ""; var location = ""; if (viewentries.viewentry[x].entrydata[5].textlist == undefined){ // not a meeting type entry. This could be reminder or appointment subject = viewentries.viewentry[x].entrydata[5].text[0]; } else{ // check if a location was provided if (viewentries.viewentry[x].entrydata[5].textlist.text.length > 2){ subject = viewentries.viewentry[x].entrydata[5].textlist.text[0][0]; location = viewentries.viewentry[x].entrydata[5].textlist.text[1][0]; moderator = viewentries.viewentry[x].entrydata[5].textlist.text[2][0]; } else { subject = viewentries.viewentry[x].entrydata[5].textlist.text[0][0]; moderator = viewentries.viewentry[x].entrydata[5].textlist.text[1][0]; } } // create jquery calendar entry entry.date = startDate.getTime().toString(); entry.title = subject; entry.description = "no description available at this time"; entry.url = "no url"; entry.end = endDate.getTime().toString(); entry.location = location; jqueryData.push(entry); } return jqueryData; }The result is neatly populated into an array of javascript objects with the following structure
{"type":"meeting","date":"1343138400000","title":"Project A meeting","description":"no description available at this time","url":"no url","end":"1343142000000","location":"Huddle room 5"}This data construct was then fed into a jquery widget to produce this interface
The result looks pretty good - thanks to Rick Taylor for helping me out with the lotus domino stuff.
Wednesday, May 2, 2012
Integrating Sametime into Portal
I recently worked with a client that wished to integrate Sametime instant messaging into WebSphere Portal. After evaluating the options available, I found that although IBM provides a Sametime portlet, it has one big drawback in that it takes so much space on the screen. User interfaces are already limited by screen size and resolution, it makes sense to use this space well.
Using a Tooltip Dialog
Most native instant messaging clients hide their user interfaces until they're needed - either to send a message or to handle an incoming message. The remainder of the time it just provides a status indicator on a tray at the bottom of the screen. This same concept can be applied to a web interface using a dojo tooltip dialog. Suppose that we've identified a location in the HTML where we want to show the Sametime status:
Conclusion
Using this technique I was able to provide an instant messaging experience that is familiar to the end user while consuming very little screen space.
Using a Tooltip Dialog
Most native instant messaging clients hide their user interfaces until they're needed - either to send a message or to handle an incoming message. The remainder of the time it just provides a status indicator on a tray at the bottom of the screen. This same concept can be applied to a web interface using a dojo tooltip dialog. Suppose that we've identified a location in the HTML where we want to show the Sametime status:
<div id="myClient2" onclick="ShowSTClientInFloatingPane()">Show tooltip dialog</div>Now we can programmatically create a sametime.WebClient dojo widget using javascript:
var dsixe = {}; var tooltipDialog = null; dojo.ready(function() { // Instantiate the sametime client widget dsixe.ST_Client = new sametime.WebClient({ style : "width:200px;height:300px;border:1px solid black;" }); dojo.require("dijit.TooltipDialog"); tooltipDialog = new dijit.TooltipDialog({ content : dsixe.ST_Client, onBlur : function() { dijit.popup.close(tooltipDialog); } }); });and add a script to handle the onClick event that will display the Sametime buddy list:
function ShowSTClientInFloatingPane() { var node = dojo.byId("myClient2"); var coords = dojo.coords("myClient2"); var x = coords.x + 15; var y = coords.y + (coords.h / 2) + 5; dijit.popup.open({ popup : tooltipDialog, x : x, y : y }); dsixe.ST_Client.main.initLayout(); }When we put this all together in a proper HTML page, we get something that looks like this:
Conclusion
Using this technique I was able to provide an instant messaging experience that is familiar to the end user while consuming very little screen space.
Monday, April 9, 2012
Managing CVS Branches with WEF
Today I finally had the time to resolve a problem I had experienced regarding branches on CVS with web experience factory. Sometimes I want to work with a branch that, for some unknown reason, doesn't show up in eclipse drop down lists. I know it exists, but I have no means to select it.
Eclipse and CVS Branches
The solution to the problem is documented in this eclipse CVS FAQ list, but it's worth mentioning in a WEF blog because we typically don't check in the .project file. As the FAQ explains, eclipse looks at this file to determine which branches are available for a project, so we have to change the CVS configuration to look at another file instead.
Checking in the .project file causes problems when later attempting to check out using the new WEF project wizard. Following the instructions in the FAQ above provides an easy work around.
Hope someone finds some use in this tidbit of information.
Eclipse and CVS Branches
The solution to the problem is documented in this eclipse CVS FAQ list, but it's worth mentioning in a WEF blog because we typically don't check in the .project file. As the FAQ explains, eclipse looks at this file to determine which branches are available for a project, so we have to change the CVS configuration to look at another file instead.
Checking in the .project file causes problems when later attempting to check out using the new WEF project wizard. Following the instructions in the FAQ above provides an easy work around.
Hope someone finds some use in this tidbit of information.
Labels:
CVS branches
,
portlet factory
,
web experience factory
,
WEF
,
WPF
Wednesday, March 14, 2012
SOA Development Using Javascript
In the past several months I've been putting some thought into the trends that I see emerging in software development frameworks. Working in the IT industry means that things are always moving and survival requires keeping a close watch on trends.
It looks like application development is quickly moving away from servlets, and towards javascript. After prototyping a pure javascript web application using web services I realized that servlets are an antiquated way of designing applications. This blog entry describes an approach I used that worked well for me.
Overview
Here's a diagram from my slide deck that describes what I aimed to accomplish
Although I used SOAP web services, the design works equally well with REST services. I did find a few frameworks like DWR, but these didn't meet my needs since I work in a corporate environment that prefers to not use open source code. Besides, I already had a rich set of in house web services available that I wanted to leverage.
Creating Javascript Objects from XML
Since most of code I typically work with is in java, I've been spoiled with all kinds of frameworks and annotations that render things like web services really easy to deal with. Working with javascript means that I need to find similar tools to facilitate unpacking XML into javascript objects much like JAX-WS does for java. I decided to roll my own using dojo class framework and the FastCode eclipse code generation plugin.
The idea is to follow what JAXB does for java in a javascript environment, namely that given some XML I want to have something parse it and hand me a collection of javascript objects - and vice versa from objects to XML. I accomplished this using the FastCode plugin.
Generating Javascript Code from Java Beans
I started by coding a dojo class that contained the functions necessary to parse XML to JS and back to XML, then wrapped a FastCode template around it:
The java class that I'm using for this blog:
I invoke the plugin using these steps - select template:
Choose class:
Pick java fields:
And voila - a nice dojo class that is very nearly complete:
Picking Apart the Generated Dojo Class
Let's take a closer look at the code which is almost ready to use, but requires a few tweaks.
The template adds a comment providing an easy way to copy/paste code for a constructor. This seems trivial when there are only two fields, but it can get tiresome when the class contains a dozen or more.
Using the Generated Code
Now that I don't have to get my hands dirty with the XML, I can write some nice clean code against dojo classes. In a nutshell, here's how I invoked a web service operation using ibm_soap:
Nice!
Attachments:
DsixEBlog_WS.war
References:
Fast Code eclipse plugin http://fast-code.sourceforge.net/
It looks like application development is quickly moving away from servlets, and towards javascript. After prototyping a pure javascript web application using web services I realized that servlets are an antiquated way of designing applications. This blog entry describes an approach I used that worked well for me.
Overview
Here's a diagram from my slide deck that describes what I aimed to accomplish
Although I used SOAP web services, the design works equally well with REST services. I did find a few frameworks like DWR, but these didn't meet my needs since I work in a corporate environment that prefers to not use open source code. Besides, I already had a rich set of in house web services available that I wanted to leverage.
Creating Javascript Objects from XML
Since most of code I typically work with is in java, I've been spoiled with all kinds of frameworks and annotations that render things like web services really easy to deal with. Working with javascript means that I need to find similar tools to facilitate unpacking XML into javascript objects much like JAX-WS does for java. I decided to roll my own using dojo class framework and the FastCode eclipse code generation plugin.
The idea is to follow what JAXB does for java in a javascript environment, namely that given some XML I want to have something parse it and hand me a collection of javascript objects - and vice versa from objects to XML. I accomplished this using the FastCode plugin.
Generating Javascript Code from Java Beans
I started by coding a dojo class that contained the functions necessary to parse XML to JS and back to XML, then wrapped a FastCode template around it:
<template type="Dojo declare class from java bean"> <description>Create a dojo class from a java bean.</description> <variation></variation> <class-pattern></class-pattern> <getter-setter>getter-setter</getter-setter> <allowed-file-extensions>js</allowed-file-extensions> <number-required-classes>1</number-required-classes> <allow-multiple-variation>false</allow-multiple-variation> <template-body> <![CDATA[ dojo.provide("${full_class_name}"); dojo.require("com.dsixe._BeanBase"); \n dojo.declare("${full_class_name}", com.dsixe._BeanBase, { \n #foreach ($field in ${fields}) _${field.name} : null, /* ${field.type} */ #end \n /* Copy/paste to save some typing: new ${full_class_name}({ #foreach ($field in ${fields}) \t ${field.name}: null, #end }); */ constructor : function(args) { #foreach ($field in ${fields}) this.setValue(args, "${field.name}"); #end }, \n /* Useful for creating instance after service call */ createFromXML : function(xmlData, elementName) { var list = []; \n if (elementName == undefined){ elementName = "${class_name}"; this is probably the wrong element name } \n var _list = dojo.query(elementName, xmlData); for (var x=0; x < _list.length; x++){ var inst = new ${full_class_name}(); var item = _list[x]; #foreach ($field in ${fields}) #if (${field.type.endsWith("[]")}) \n // Child element contains XML var o_${field.name} = new QUALIFY_YOUR_PACKAGE_NAME_HERE.${field.type}(); fix this line var i_${field.name} = o_${field.name}.createFromXML(xmlData, "${field.name}"); inst.${field.setter}(i_${field.name}); \n #else inst.${field.setter}(this._getValueXML(item, "${field.name}")); #end #end list.push(inst); } return list; }, \n /* Useful for updates/inserts on large objects */ createWire : function(elementName){ var wire = new dojox.wire.ml.XmlElement(elementName); #foreach ($field in ${fields}) #if (${field.type.endsWith("[]")}) \n // Child element contains XML var ${field.name}Wire = []; for (var x=0; x < this._${field.name}.length; x++){ var item = this._${field.name}[x]; ${field.name}Wire.push(item.createWire("${field.name}")); } wire.setPropertyValue("${field.name}", ${field.name}Wire); \n #else wire.setPropertyValue("${field.name}", this._${field.name}); #end #end return wire; }, \n #foreach ($field in ${fields}) ${field.getter} : function (){ return this._${field.name}; }, ${field.setter} : function (arg){ this._${field.name} = arg; }, \n #end }); ]]> </template-body> </template>
The java class that I'm using for this blog:
package com.dsixe.blog; public class ContactBean { private String name; private String phone; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } }
I invoke the plugin using these steps - select template:
Pick java fields:
And voila - a nice dojo class that is very nearly complete:
dojo.provide("com.dsixe.blog.ContactBean"); dojo.require("com.dsixe._BeanBase"); dojo.declare("com.dsixe.blog.ContactBean", com.dsixe._BeanBase, { _name : null, /* String */ _phone : null, /* String */ /* Copy/paste to save some typing: new com.dsixe.blog.ContactBean({ name: null, phone: null, }); */ constructor : function(args) { this.setValue(args, "name"); this.setValue(args, "phone"); }, /* Useful for creating instance after service call */ createFromXML : function(xmlData, elementName) { var list = []; if (elementName == undefined) { elementName = "ContactBean"; // !! this is probably the wrong element name } var _list = dojo.query(elementName, xmlData); for ( var x = 0; x < _list.length; x++) { var inst = new com.dsixe.blog.ContactBean(); var item = _list[x]; inst.setName(this._getValueXML(item, "name")); inst.setPhone(this._getValueXML(item, "phone")); list.push(inst); } return list; }, /* Useful for updates/inserts on large objects */ createWire : function(elementName) { var wire = new dojox.wire.ml.XmlElement(elementName); wire.setPropertyValue("name", this._name); wire.setPropertyValue("phone", this._phone); return wire; }, getName : function() { return this._name; }, setName : function(arg) { this._name = arg; }, getPhone : function() { return this._phone; }, setPhone : function(arg) { this._phone = arg; } });
Picking Apart the Generated Dojo Class
Let's take a closer look at the code which is almost ready to use, but requires a few tweaks.
The template adds a comment providing an easy way to copy/paste code for a constructor. This seems trivial when there are only two fields, but it can get tiresome when the class contains a dozen or more.
/* Copy/paste to save some typing: new com.dsixe.blog.ContactBean({ name: null, phone: null, }); */The constructor delegates parsing of the fields to the superclass _BeanBase. This allows invoking with a subset of parameters if needed.
constructor : function(args) { this.setValue(args, "name"); this.setValue(args, "phone"); },Create from XML is pretty obvious, it provides a way to convert XML to a collection of javascript objects. This code needs to be reviewed to ensure that the element names match the values returned by the service.
createFromXML : function(xmlData, elementName) { var list = []; if (elementName == undefined) { elementName = "ContactBean"; // !! this is probably the wrong element name } var _list = dojo.query(elementName, xmlData); for ( var x = 0; x < _list.length; x++) { var inst = new com.dsixe.blog.ContactBean(); var item = _list[x]; inst.setName(this._getValueXML(item, "name")); inst.setPhone(this._getValueXML(item, "phone")); list.push(inst); } return list; },Create wire will convert a javascript class instance into a dojox.wire.ml.XmlElement object which is needed by the ibm_soap dojo library. If I was using a REST service then I guess I'd have to change this code or add another function to the template.
createWire : function(elementName) { var wire = new dojox.wire.ml.XmlElement(elementName); wire.setPropertyValue("name", this._name); wire.setPropertyValue("phone", this._phone); return wire; },The remainder of the code is standard gettter/setters for the fields. I tried my best to make the code as formal as possible without any kind of javascript magic for a few reasons:
- javascript can get real messy real fast
- this code is intended for use by java developers who aren't javascript experts
Using the Generated Code
Now that I don't have to get my hands dirty with the XML, I can write some nice clean code against dojo classes. In a nutshell, here's how I invoked a web service operation using ibm_soap:
wsdlParser = new ibm_soap.util.WsdlParser(); wsdlParser.parse("contacts.wsdl"); // Create reference to service and set the URL myService = new ibm_soap.rpc.SoapService(wsdlParser.smdObj); myService.serviceUrl = "/axis2/services/ContactService.ContactInfoPort/"; var params = new dojox.wire.ml.XmlElement("getAllContacts"); var deferred = myService.getAllContacts(params); deferred.addCallback(function(xmlData) { var cb = new com.dsixe.blog.ContactBean(); var contacts = cb.createFromXML(xmlData, "contact"); // do something useful with the data }In practice, the code is much shorter if I parse the wsdl into javascript upfront. In addition, the service/url is really only defined once in the application. This leaves me with creating the dojox.wire.ml.XmlElement and invoking the service operation.
Nice!
Attachments:
DsixEBlog_WS.war
References:
Fast Code eclipse plugin http://fast-code.sourceforge.net/
Monday, March 12, 2012
Using the Fast Code Eclipse Plugin
I recently discovered an eclipse plugin that I've been needing since I started coding. On several occasions I started down the path of writing my own but quickly got lost in the world of eclipse and ran out of time. Out of the box, the fast code plugin generates many types of code/xml, but the part that I really like is its support for custom templates.
How to Write Your Own Template
Here are the steps that I used to create my own template which generates a spring RowMapper for a JdbcDaoSupport class.
Start with exporting templates using menu Fast Code | Templates | Export Templates:
Choosing templates-config.xml (or all) creates a new project in my workspace:
Next, I altered the exported templates-config.xml file and added my own custom template:
I did find a few quirks with the plugin - it looks like the custom templates need to be reloaded every time eclipse is restarted, and the import/export mechanism seems to make a service call to localhost so make sure that the eclipse network proxy preference is configured correctly.
In short - I can create a new template whenever I want to generate some code or configuration file based on the definition of a java class. In another blog post, I use it to generate a javascript clone of a java bean.
How to Write Your Own Template
Here are the steps that I used to create my own template which generates a spring RowMapper for a JdbcDaoSupport class.
Start with exporting templates using menu Fast Code | Templates | Export Templates:
Choosing templates-config.xml (or all) creates a new project in my workspace:
<template type="Create Spring Detail Mapper"> <description></description> <variation></variation> <class-pattern></class-pattern> <getter-setter>getter-setter</getter-setter> <allowed-file-extensions>java</allowed-file-extensions> <number-required-classes>1</number-required-classes> <allow-multiple-variation>false</allow-multiple-variation> <template-body> <![CDATA[ private class ${class_name}Mapper implements RowMapper <${class_name}>{ public ${class_name} mapRow(ResultSet rs, int line) throws SQLException { ${class_name} inst = new ${class_name}(); #foreach ($field in ${fields}) #if (${field.type.endsWith("String")}) inst.${field.setter}(rs.getString("")); #else inst.${field.setter}(rs.getInt("")); // type not String, assume int #end #end return inst; } } ]]> </template-body> </template>To test the result I import the template back into fast code using menu Fast Code | Templates | Import Templates, then open up a java class that needs the mapper and execute the template using menu Fast Code | Templates | Create New Snippet. Pick my template, class, fields and presto:
private class ContactBeanMapper implements RowMapper<ContactBean> { public ContactBean mapRow(ResultSet rs, int line) throws SQLException { ContactBean inst = new ContactBean(); inst.setName(rs.getString("")); inst.setPhone(rs.getString("")); return inst; } }This may not seem like a big deal because I only have two fields in class ContactBean. It really pays off when I have a dozen or more fields.
I did find a few quirks with the plugin - it looks like the custom templates need to be reloaded every time eclipse is restarted, and the import/export mechanism seems to make a service call to localhost so make sure that the eclipse network proxy preference is configured correctly.
In short - I can create a new template whenever I want to generate some code or configuration file based on the definition of a java class. In another blog post, I use it to generate a javascript clone of a java bean.
Labels:
eclipse plugin
,
spring
,
template
Thursday, March 8, 2012
Debugging Dojo with Chrome
As a web developer I spend many hours working with javascript libraries like dojo. Unfortunately, in many cases the javascript in these libraries is compressed making it very difficult to trace in a debug session.
Chrome is Your New Best Friend
Google's chrome developer tools has many bells and whistles, some of which I've only discovered in the last week. One feature that I think is worthy of mention is the pretty print button, tucked away at the bottom of the page in the status bar.
Click on the button and dojo.js goes from this:
to this:
Some of the variables are still compressed, but now the code becomes legible and breakpoints can be added with ease.
Yes, it also works with JQuery.
Chrome is Your New Best Friend
Google's chrome developer tools has many bells and whistles, some of which I've only discovered in the last week. One feature that I think is worthy of mention is the pretty print button, tucked away at the bottom of the page in the status bar.
Click on the button and dojo.js goes from this:
to this:
Some of the variables are still compressed, but now the code becomes legible and breakpoints can be added with ease.
Yes, it also works with JQuery.
Tuesday, January 17, 2012
XML to JSON Converter Builder
Today I had the opportunity to use the xml to json converter builder for a project I'm working on and found that the documentation was a little lacking. Searching google for help netted me next to nothing so I figured it was time for a blog entry.
Why is This Not Working?
The key to getting this feature working is to understand that the builder on its own doesn't really do anything useful. It just provides a method that must be invoked somewhere else, some code that produces the JSON notation. The callback builder input field is text which is prepended to the output of this method. For example if I have these values
with the following html in a page builder:
The output html looks like this:
Why is This Not Working?
The key to getting this feature working is to understand that the builder on its own doesn't really do anything useful. It just provides a method that must be invoked somewhere else, some code that produces the JSON notation. The callback builder input field is text which is prepended to the output of this method. For example if I have these values
with the following html in a page builder:
<html> <head><title>Default Test Page</title></head> <body> <script type="text/javascript"> function dumpJson(json){ console.log(json); } </script> <span name="script1"/> </body> </html>all that's missing now (and this is important) is a call to the generated method, in my case using a client javascript builder:
The output html looks like this:
<html> <head> <title>Default Test Page</title> </head> <body> <script type="text/javascript"> function dumpJson(json){ console.log(json); } </script> <script name="script1" type="text/javascript">dumpJson({"evaluationDetail":{....})</script> </body> </html>Hopefully someone will stumble on this blog before they pull their hair out trying to figure out how to use this builder.
Subscribe to:
Posts
(Atom)