By Adam Kewley, Web Experience Factory Lead, Davalen
There are many benefits to creating your own custom builders. Be it setting standards, code reuse, or simply making it easier for other developers to implement your functionality across multiple projects. Writing builders and mastering the creation of them however can be tricky. While IBM provides a lot of documentation and samples, it’s nearly impossible to cover all the areas of builder creation. In this article, we’ll try to address a couple of these lesser-known areas, and provide tips on how to better understand builder creation. This article assumes a basic understanding of the creation of Java-based builders in Web Experience Factory.
We’ll start by generalizing builders into three major areas of interest. The first is the UI itself. The UI mainly consists of the BuilderDefinition (bdef), and the optional Coordinator class. The second area is code Generation. The Regen class generates code and webapp artifacts for the builder. The final area is composed of the Helper class, and other webApp artifacts which assist in the runtime execution of the builder.
The UI of the builder starts with the Bdef or Builder Definition file. You can create your own from scratch, use the builder skeleton to get you started (recommended), or simply clone an existing bdef file. This file contains the information about the builder itself, along with the builder inputs, tooltips, and other metadata. The best way to get a better understanding of how these files work is to open up the WEF builders and view them yourself. Most of the WEF builders can be found by extracting the WEB-INF/builders/webappbuilders.zip file. This archive contains most of the builders used in WEF, and will provide a valuable resource when constructing your own. Additional builders (such as Dojo or data service builders) will be automatically added to your project as you install various featuresets. These builders will not reside in an archive but simply be placed within subfolders of the WEB-INF/builders directory. While writing my own builders, I’ll often refer to these core factory builders to copy out different inputs or Widget types. All of the inputs are made up of Widgets. The widgets are defined in Widget Definition files, which drive the UI.
The BDEF or builder definition files will be your best source of information. Not only can you copy over builder inputs, but you can also view other builders to get information about various parameters that can be passed to customize these inputs.
The coordinator class is used to automate UI interactivity with the end user. They are not always required, but useful for making it easier for users to interact with complex builders. When creating a coordinator class, I usually rely on the builder skeleton to create the initial class for me. The builder skeleton (builder) will generate the class, and populate it with useful code and information. It will also follow the WEF recommended java based builder structure. This can be used as a great starting point for working with your coordinator. Unfortunately there is not much information for creating your own coordinator classes, and we cannot directly gain access to the core builder coordinator classes as we can the Bdef files.
The generation class does the work for the builder. Either by generating content, or manipulating the webApp, this class usually contains the bulk of the code and logic. You’ll be spending most of your time with this class when creating a java-based builder. I recommend using the builder skeleton builder to generate this class for you. It will setup the initial methods as well as define some useful structures for creating your own builder. Once again there is not a lot of information for the end user, so working with IBM samples and documentation is important here.
Most builders Implement the WebAppControlBuilder Class, and override the DoBuilderCall method. This approach gives you a PageLocation object in which to place one or more elements on the page of your choice. However, there’s another lesser-known approach which utilizes the BaseWebAppControlBuilder Class and overrides the handleControl() Method. Instead of a PageLocation object, you’re given the Page object, for the current page you’re operating on, and an IXml object that represents the tag you’ve selected for the builder. This means you can directly manipulate the content as an Xml object. Very useful for modifying existing content or adding elements directly to the page.
The regen class should contain a BuilderStaticValues Interface class, which contains builder input names, and enumerated input values. While updating the builder with new inputs, be sure to add to this interface. If you’re using helper classes or coordinator classes it will be useful for easily recalling various builder inputs.
Most builders also invoke other builders. Very few builders (aside from atomic builders) do all the work themselves. Invoking other builders is an essential part of most builder creation. It can be difficult knowing the proper incantation and set of inputs to assign before invoking a builder. Utilizing the builder API java doc, reading up on samples, and investigating the Model XML (for various builder inputs) will provide a lot of insight into how to invoke these external builders.
The regen class also handles capturing and manipulating of inputs. If you’re adding additional inputs, check the modelXML view of an existing model that uses a similar input. If the input value is XML you’ll want to call the appropriate get method from the BuilderCall class as getString will most likely not return the value in the format that you’re looking for. Setting these values for the helper class may be important as well.
WebApp Objects and Helper Class
The builder helper is a class that assists the builder during runtime. Any builder that contains a helper class will have an LJO associated with it in the webapp. A good example of this is the split pager builder. The pager builders have a pager helper api which control which page is being viewed, along with returning information on the current page, current record and other information. Helper classes are not always needed, but if your builder needs to interact with something across multiple requests, a helper class is a good idea. Furthermore, if you simply need to access static data, a variable can be added instead (to the webapp) which can be set at regen time. Any runtime changes should be handled by a helper class.
Additional WebApp artifacts may exist to aide in the runtime execution of the builder. Variables, Schemas, Methods, Linked Models, and other artifacts may be added by the builder to assist.
There are several areas where a builder can go astray and cause headaches. Most of the time I’m writing a builder for a one-off scenario where it really won’t be much of a use outside its current ecosystem, but it never hurts to think about reusability. There are many snares that can catch up a wayward builder if not properly planned for. Unfortunately, These traps are not always evident either. Here are a few scenarios:
- Multiple instances of a tag: Similar to the datapage issue, there’s nothing stopping a named tag element from being placed on the same page more than once. In fact this is useful sometimes when you need to duplicate functionality on a page. You will run into similar issues as with page automation if your builder cannot handle this.
- Regen issues: These can be tough to diagnose. If your builder is failing at regen time, and the error message is less than useful, try turning on the eclipse debug console. This is accomplished by creating a shortcut to eclipse (or use the WEF shortcut ) and add a “-debug” statement to the shortcut. Just be sure to add it before any –vmArgs flag or it will never work. Restart eclipse, and a second window will appear. This second window should include stack traces during failed model regenerations.
- Confusing inputs: Be sure to add descriptive tooltip and label information to your builders.. especially if you wish to share them with others. Effective documentation is important. Including an additional sample (or two) will be much appreciated as well.
This section focuses on tips and tricks for better understanding and constructing builders.
- Most all builder inputs that have an interactive UI are being driven by a coordinator. There are some exceptions (such as the Page Location input), however. Keep this in mind if you’re expecting to pull in some builder inputs and have them interact with the end user as they were in a previous builder. While we can access the builder definition, unfortunately we don’t have access to the coordinator class, and therefore must reconstruct the UI interaction on our own.
- If you need a certain flavor of a given input (lets say a file and indirect reference picker), try to think of, or locate, a builder that follows the pattern you’re looking for. Open up the BDEF for this builder and look at the optional parameters for the input. This will tell you how to modify your input to gain the desired effect. There are quite a few unique options for various builder inputs.
- If you’re invoking another builder from your builder and are having trouble figuring out which inputs to pass, try working with a sample model. Open up a new model and add the builder you wish to better understand to a new model. Set the inputs as desired, then switch to the Model XML view. Look at the xml generated for that builder. The element names, structures, and values for various inputs should offer some insights into how to invoke this builder. This is especially useful for inputs that take an xml structure.
- If you’re inserting multiple builders that require page location, it can be tricky figuring out how to create your own pageLocation objects. You can create your own using the WEF Page Location Syntax, or by using the PageLocationConstructor class. Example:
//Create a new pagelocation tag relative to the old page location tag.PageLocation newTag = PageLocationConstructor.addInsert( pageLocation,PageLocationConstructor.INSERT_AFTER,name + “_hidden_value” );
- Most all builders (aside from atomic builders) invoke other builders. Relying on existing builders to assist your own custom builder is highly recommended as you can avoid reinventing the wheel. Lastly, most existing builders have had years of bug and performance fixes.
- IDGenerator is invaluable if your builder will be added to a page multiple times. There is also an API for generating unique ID’s for webapp objects. The following example uses concatenated JSP strings to generate dynamic runtime ID’s that are unique. Every time you call “getNextID” you’ll get a new unique ID for the same Key. This is very useful for attaching to ID attributes of elements that are added to the page.
final String NEXT_ID = StringUtil.strcat( “IDGenerator.getNextID(
webAppAccess, \”", name, “\” )%>” );
final String CURRENT_ID = StringUtil.strcat( “IDGenerator.getCurrentID( webAppAccess, \”", name, “\” )%>” );
- Most Callable Builders (API) have a BuilderStaticValues interface that contains constant values. Along with builder input names, they also contain enumerated values for various builder inputs. Sometimes these may have acronyms that are a bit difficult to distinguish, so trial and error can help here. Once again, referring to the ModelXml data for a given builder may provide additional clues to the proper value (or constant) that needs to be applied to a particular builder Input. In the following example, we inspected the Model Xml (using model xml view tab in Designer) to discover the different page location and script source type values.
// “Implicit” here means “add to head tag and don’t rely on page location.”
- Some builders add additional hidden input values (either through bdef, coordinator, or regen class) that are not always evident. Looking at the Model XML view can help determine which additional input values may be set for an existing builder, that were not evident in the builder UI.
- Errors and Warnings can give useful information to an end user while trying to use your builder. Errors will prevent a builder from completing its generation cycle. Warnings will simply inform the user that something may be wrong. These can be added to your builder generation class. Example:
// Cumulative warning message (will not block)final String message = “Negative Values for Offset are not recommended as undesirable effects may occur.“;builderCall.addMessage( BuilderCall.SEVERITY_WARNING, message );// Blocking error messagefinal String message2 = StringUtil.strcat( “Value:”, sysID, ” Out of Range! SysID must be greater than 0 and less than 15” );builderCall.addMessage( BuilderCall.SEVERITY_ERROR, message2 );
- Coordinator classes can be useful for creating interactive interfaces with the end user, as well as handling more complex features such as reading from config files and populating inputs.
- IBM Wiki has quite a bit of information on custom builders. Some of the samples are very useful as well (such as the email builder). From time to time new samples are published.
- When Swapping visibility of inputs in Coordinator, be sure to disable the “required” flag for required inputs. Conversely add the “required” flag when showing the builder again. Hidden inputs that are required cannot be filled by the end user.
- The Builder Skeleton builder is useful for getting started with a new builder. It’s also useful for laying down the framework for writing a proper WEF builder. By utilizing this skeleton framework you can write your builder in an efficient manner.
- Creating Input Definition Groups in the Builder Definition file will allow you to create collapsible/expandable sections. Use these when creating a complex UI to allow the user to hide/show grouped areas of interest.
- If you need to manipulate datapage elements or schema structure for a datapage, consider creating a PageAutomation-based builder. These are a bit different from PageControl builders in that they do not have a page location, and operate (mostly) on datpage itself. For more information, checkout this tutorial.
- Choosing a base interface class for your java-based builder (doBuilderCall vs handleControl)
- Older, but relevant information on creating your own builder
- Sample builder that adds a numerical counter (with icon) to a datapage column.
- Ubituitous Email builder (very useful)
- Custom Data Access builder for interacting with an LDAP backend
I will be discussing some of these topics and more in a technical presentation at the IBM Exceptional Web Experience 2013 at the Hilton Chicago, May 20 – 23, 2013. More details to come.