Brought to you by the Web Experience Factory (formerly WebSphere Portlet Factory) Development Team
© Copyright International Business Machines Corporation 2006, 2009, 2014. All rights reserved.
This document is a collection of best practices, tips, and suggestions from the WebSphere Portlet Factory development team. It is a work-in-progress and will be updated periodically. A section on Selecting Builders provides some guidance in navigating the Builder palette.
The intended audience is developers using or expecting to use Portlet Factory. You should have a working understanding of the basic concepts of Portlet Factory (Builders, Models, and Profiles), but no Java programming knowledge is needed for this article.
Applications created using IBM WebSphere Portlet Factory (hereafter called Portlet Factory) need to be architected for performance just as for any other Java or Web application and they should be implemented using standard coding and modeling best practices. Some of the factors that developers should be aware of when architecting for performance are:
Memory usage (minimize use of session data and large XML variables)
Performance of any back end systems that are invoked
Size of returned data
Sizing of the platform (number and speed of CPUs)
JVM memory and performance settings
Standard debugging and analysis techniques for the Server runtime environment
All other application development best practices you know
While Portlet Factory makes it easy for non-architects to create complex portlets and applications, that does not replace the need for an architect or consultant to plan and review the application and deployment configuration to ensure good performance. Except for the way they are created, Portlet Factory Applications, Portlets and Portlet WAR's are similar to those created using hand-crafted or other development methodologies; they need the same considerations with respect to architecture and design, performance, debugging, testing and runtime analysis.
Model development tips
Use a service provider/consumer architecture (SOA).
The Service Definition and Service Operation Builders make it easy to create a service that's based on integration Builders such as SQL Call, SAP Function Call, Web Service Call, and others. If any data transformation is needed, make this a part of the service Model, too. For the presentation Model, the View & Form Builder can be used to easily build input, results, details, and update pages from a service. This architecture helps keep the presentation Model simpler, it gives you a way to separately test your back end functionality, and it lets you build your front-end presentation Model using stub data to work disconnected from the back end.
Use stateless Service Providers and transform unnecessary data out of the structures in service providers rather than exposing more structure and data to the UI consumer model(s) than necessary, both for ease of development of UIs and to avoid inefficient and unnecessary work and data manipulation in UI models and session state.
Bring back only the data necessary for the application UI and logic from backends and only the data necessary for the UI to the UI consumer models.
Whenever possible, avoid bringing back more data than necessary for the application, from back end systems, to avoid inefficient applications that may have unnecessary scalability limitations. It's better to let the back end do as much of the data manipulation, sorting, transformation and filtering as possible rather than using memory and CPU in the portlet to do so in a less efficient manner, that the portal could otherwise use to serve the application user interfaces to more users.
Use the highest-level Builder available for the job.
Whenever possible, use a single high-level Builder instead of a number of lower-level Builders. This keeps your Models simpler and easier to understand and maintain. For example, the View & Form Builder can often do the work of one or more Imported Page, Data Page, Form Submit Action, Button, and Action List Builders.
Use a single Web Service Multi Operation builder call rather than multiple Web Service Call builders in a provider model, when the operations all come from the same WSDL..
Group related services into one Service Provider Model.
Before you begin developing your data services, consider how to break out data services into Service Provider Models. You do not need a one-to-one relations hip of Service Provider to a Service Consumer (portlet). A good way structure your services is to break the Service Provider Models up by type of data. For example, one Provider Model could provide all services related to Orders Data (e.g. getRecentOrders, getOpenOrders, etc.), and another Provider Model could includes all the services for Customer Data. By taking this approach, you can test all these related services together. In addition, you can often reuse some artifacts across the services, like schemas.
Use pre and post execute features to perform common validation functions.
It is very likely that many of the services you define will have inputs in common, such as a date range or geography hierarchy. There will be common code that should be used by each service to format or validate these inputs (e.g. checking for a valid dat e and formatting it for the back end system). A good way to do this is to use the "Pre-execute" method option available in the Service Operation Builder. You can create a single method that is called from each service operation, which can check for the existence of certain inputs (e.g. START_DATE or END_DATE. If the inputs are found, the method will apply the formatting. Also, the "Post-Execute" and "Error Handler" method can be used to process the results and handle any errors.
Prototype with sample data.
Depending on the nature of the backend systems you are using, creating the data services can be extremely easy or extremely challenging. If the latter is true, you may want to consider prototyping the data service using sample data, using either sample XML files or an Excel Spreadsheet. This allows you to get the service interface stubbed, which enables two benefits. First. the portlet developers can get started without waiting for the backend to be finished. Second, since you can build up the user interface using the sample data, you can show these portlets to your customer or business owner sooner (rather than later), potentially reducing the number of iterations with the backend systems.
Don't write lots of code in Method Builders - use Linked Java Object instead.
If you need significant amounts of hand-coded Java, write a Java class and bring it into the Model with a Linked Java Object Builder instead of using Method Builders. This makes the Model easier to manage, and the code assistance is better in the Eclipse Java editor. Generally, Method Builders in your Model should only contain small amounts of "glue" code that don’t naturally fit into a Java class.
Use only the documented Builder helper Linked Java Object methods.
Many builders add a Linked Java Object to the webapp, that provide methods that you and/or the runtime may call to perform operations at runtime. When you need to call a method on a helper Linked Java Object directly, stick to the ones documented in the API help for the class associated with that LJO. The undocumented methods are typically not supported for external consumption and may be subject to change between versions.
Keep Model size under 50 Builders when possible.
While this isn't a fixed or physical limit, models get hard to maintain when they get much bigger than this. To keep Model size down, you can use the Model Container (for containment of visual elements), Linked Model, or Imported Model Builders. You can also use other best practices referenced here, such as Rich Data Definition and service provider/consumer Models. In some cases you may also want to consider creating custom Builders to automate common design patterns in your application. Custom Builders can be created from a Java Class and BDEF file or from a Model that implements the functionality desired. There are no performance or other significant diff erences between these two methods for Builder creation.
Use Rich Data Definition to simplify and centralize field formatting and validation.
If you find yourself using a number of Builders to control formatting, validation, and UI definitions for data fields on a page, you can often simply the Model by using Rich Data Definition instead. This Builder gives you the ability to encode and reuse detailed field behaviors. You can enable reuse at the schema level by creating an RDD file for a particular schema, and you can also reuse a single shared library of common field types, with the "base" data definition functionality.
Use Imported Page instead of Page.
This makes it easier to change the look and feel of pages and to allow UI designers to create and manipulate the html and style markup, and you can share imported pages across Models.
Use "submit form" actions when updating server state and "link" events for navigation.
The various page action Builders (Button, Link, Image Button, etc.) give you a choice in how the action behaves when clicked by a user. When the action is associated with submitting an input form, configure the Builder’s action type to be "Submit form and invoke action" or "Submit form and invoke URL". For page-to-page navigation set the action type to be "Link to Action" or "Link to URL".
Use "Link to action" or "Submit form and invoke action" to navigate to Portlet Factory pages.
The URL action types in all of the page action Builders allow a user to navigate to a page outside the control of Portlet Factory. If you want users to navigate to another page or action within your Models, you must select "Link to action" or "Submit form and invoke action."
Group declarations of common events in a single Model.
Put the Builders that declare common events in your application into an otherwise empty Model, and then use the Imported Model Builder to pull the declarations into the Models that need to fire and catch these events. This ensures that events are declared and managed consistently across your various Models.
Put a Localized Resource Builder into every top-level Model of your application.
Always use the Lo calized Resource Builder in Models that need to support users with different preferred locales. Configure the Builder with the Custom Variable Names turned off, and profile the Language and Country Builder inputs with the corresponding entries from the standard com.bowstreet.profileset.SimpleLocaleValues profile set. This configuration will cause the Builder to fetch the locale from the request and make it available to other Localized Resource Builders that are in the Model directly or indirectly.
Use Model Container to visually include another model on a page
To combine pages from multiple models, use Model Container instead of Linked Models.
Please refer to the WPF Wiki article on Techniques for working with multiple models in WebSphere Portlet Factory for more details.
The following article and sample describes Techniques for building a controller model using Model Container and events.
Use one Model per Model Container Builder.
Avoid "swapping" Models in and out of a single container. The preferred method is to have one container for each Model and use different pages to display the content of each contained Model. If Model swapping is necessary, be sure to encode your application’s structural state in any URLs to the page if bookmarks and/or use of the browser’s back button are required.
Repeated Region can handle source data other than XML.
The "Source Data" input of the Repeated Region Builder takes an indire ct reference which often has an execution-time type of IXml. However, it may be more efficient to use other supported types, including java.util.Iterator, java.util.Collection, java.util.Map, java.util.Enumeration, or a Java array. These extended types are also available in the Select and Radio Group Builders.
Use the Simple Schema Generator or Variable builder's Generate Schema, to jumpstart the creation of your XML schemas.
This Builder can be used to construct a basic schema using sample data that conforms to the desired schema. Once the schema definition has been finalized you can convert the Simple Schema Generator Builder into a Schema Builder by right-clicking on the Simple Schema Generator in the Builder list, selecting "Convert to...", and choosing "Schema".
As of Web Experience Factory 8.5, you may now generate a schema directly from a Variable Builder call of type XML, alleviating the need to add a separate Simple Schema Generator builder for such XML variables.
Use Data Field Modifier or Data Column Modifier to implement paging.
Paging of data by the DataPage Builder can be enabled in various ways. The best approach is to add a Data Field Modifier or a Data Column Modifier Builder to your Model and configure them to perform paging. The Paging Assistant Builder operates at a low level (and is actually called by the Data Field Modifier and Column Modifier Builders) and when invoked directly, it may not correctly handle paging in all possible configurations of DataPage.
Use of Session Data for Scalability and performance
For scalability and performance, avoid putting any more than is absolutely necessary into sessions. This includes minimizing the use of large, non-readonly XML Variables in Models when possible. When it comes to session data, less is better; use it only for what is required.
Packaging portlets into WAR files.
In general you may package portlets into WAR’s in arbitrary ways. However, portlets that need to use the Portlet Factory Event capability, which relies on sessions, must all be in the same WAR. Portlets that share session data must all be in the same WAR. For absolute maximum performance, one WAR is at least theoretically best, although it may not be possible to actually measure the differences, and it can be far less convenient for multiple groups working on different portlets or for deploying sections of an application independently. If the deployment WAR(s) is to be built using a scripted build process (for example, one that extracts code from version control, and runs ant scripts against it), then the single-WAR approach is viable. There is no "right number" for the number of portlets in a WAR, but we think that 50 may be too many and 5-10 is easily okay. One approach might be to organize portlets that are logically related, such as by function. Another approach is to create WARS based on the teams creating the portlets, or along the lines of your project organization. By default, Portlet Factory Projects will make one WAR that includes all portlets contained in that Project. You may of course create your own process for generating WAR’s and include or exclude whatever you want.
Minimize the number of Portlet WARs.
It is possible, and actually more efficient to have many portlets in a single portlet WAR, as opposed to a portlet per portlet WAR. Each portlet WAR you deploy is going to increase the overhead necessary to run your portal application. There is the overhead of the application server itself hosting yet another application WAR, including storing the application WAR and its metadata and loadi ng/starting the unique applications, and there is overhead for duplicate class definitions in memory, etc. One or two portlet WARs hosting several or many portlets each, will likely use less memory and CPU than if you were to deploy each portlet in a separate portlet WAR. Typically customers have multiple developers work on different portlet models within the same overall project, but if you must have multiple projects then one option to help support a multiple developer portal application project is to use a nightly build script process to combine portlet models built by individual developers into a single portlet WAR for production deployment.
Portlet WARS tend to be large, although recent Portlet Factory releases have improved minimum size and future Portlet Factory versions will continue that effort. The default behavior is that everything in the project gets included in the War unless it is excluded. Portlet Factory automatically excludes many p roject artifacts that are needed only for Design-time use (such as WEB-INF/work/source/... ) from production deployment WARs. There are also two mechanisms that give you more control over what gets included (or excluded) in a WAR:
Use folders named ".../nodeploy/" for content that you need to place in your project but you never want included in a standalone WAR or a portlet WAR.
Add entries (single path/file per line) to the file called .excludeFromServer located in your project root to exclude specific items from being included in generated WARs.
Restart Performance Test and Production Servers after redeploying WARs.
When you redeploy a WAR, the server stops the application, replaces the deployed WAR with the new one, and thus when it starts up you have a NEW copy of all the WAR's necessary classes loaded into memory. JVMs don't immediately garbage collect "classes" (as opposed to object instances), and some classes may have static references to the old (pre-redeployment) caches, which can then keep references to old cache data object instances in memory. To avoid unnecessary memory use by those old classes, it is a best practice to restart performance test servers and production servers after redeploying your portlet WARs, to immediately remove any old classes from memory.
Create directories for and establish standards for naming.
Underneath the WEB-INF/Models directory, consider creating a customer or application specific subfolder and structuring your Models into the following folders below that:
common - Common Models that you will import into your portlet Models
data - Service Provider Models
portlets - Service Consumer (portlet UI) Models
customizers - Customizer Models
admin - Models for administering common settings or testing features
Create Profile sets to handle common configurations.
For example, you might want to profile every template that appears in your higher level Builders, tying the Builder inputs to the same ‘template’ profile entry in a formatting profile set. You could then create a nice Customizer that allows an administrator to make global changes to all the portlets by changing the template.
You should consider the following profile sets for handling common configurations:
Data Source - This should include data source features, such as the data source name. This is useful to change the data source when moving from test to another environment, such as production.
Formatting - Look and feel related, e.g. Data Page Templates, CSS files.
Role-based - Driven by portal groups, etc.
Locale-based - Driven by language setting in portal or other setting
Create folders for your HTML, CSS,and image files
If you are creating new files and not leveraging the defaults shipped with the product, it might be helpful to create a folder at the root of your project, with subfolders for:
HTML - Contains HTML and CSS files for portlets
Images - Contains images and other media (e.g., SWF files)
templates - Data Page HTML templates
Create a folder for your data files
Create a subfolder for your data files, located under WEB-INF in a folder with the same name as your project. In this folder place the following types of files:
Sample XML Data
Sample Cloudscape database, if used (be sure to exclude from your WAR’s)
SQL Script to create and populate database (This also does not need to be deployed with the portlet WAR.)
Create a folder for your Rich Data Definition (RDD) files.
This folder should be at the same level as the data file folder referenced above. Within this folder, you should place a base RDD file, which can be leveraged by all portlet Models. All other portlet-specific RDD files should also be placed in this folder.
Create a folder for your XML Access Scripts
If you are using XML Access scripts to deploy a related set of portlets. (Note- These scripts do not need to be deployed in your Portlet War file.) The XML Access folder should be at the same level as the data, and RDD folders, for example:
Create a folder for your configuration files, such as SAP, Domino, etc. configuration information. You might want to place this folder underneath the WEB-INF/Config folder.
Create a folder for your resource bundles.
Even if you do not have requirements to localize your application, it still makes a lot of sense to externalize the text in your application, for the following key reasons:
These are the types of resource bundles:
Resource bundles will save you a lot of time in development since you won’t have to retype the same text all the time.
You can send the resource files to someone to edit or translate.
Resource bundles ensure consistency across portlets in terms of what the columns are named.
Common for all portlets - Features used in all portlets. Also, consider localizable data format patterns, such as numbers and dates. You can reference these from Rich Data Definition files.
Data-specific - Common elements such as "Customer Name" and "Employee ID" that will be used in many portlets. You want to define these once for consistency.
Portlet-specific - This includes the title for portlet pages and any overrides to common or data-specific labels, such as abbreviation of tags, such as "Emp. #" in place of "Employee ID". Note- If these tags are used in multiple places, consider defining short and long versions of the tags in a common resource bundle. Consider putting all of these labels in one resource bundle that is used in all portlets. You can import this resource bundle into a "common" Model linked in to all your portlets. Make use of the "pre fix" feature in the localization settings section of Data Page to avoid collisions of common tag names.
Portlet and Page titles - for use in XML Access scripts
Create a sample data service, portlet, and Customizer
Before you begin a team development project, you should create a sample data service, a consumer portlet, and optionally a Customizer that use all of the naming conventions described above and test as many of the common files as possible (like base data definition, templates, resource bundles, etc.) A good start would be to create:
A Service Provider Model
. Within this Model, make sure to:
Use conventions for naming service Models and service inputs
Consider naming conventions for schema elements (casing of tags, etc).
Implement at least one service that takes arguments of different types, such as String, Date, Integer, IXml.
Consider the format for each input and whether you want to do any translation. The format you expose in your web services does not have to match that of the backend system. For example, the Date format for the backend system might be YYYY-MM-DD or an awkward DateTime format, but those consuming the web service may find it easier to work with a more readable MM-DD-YYYY format. If this is the case, then you can implement formatting code to handle the translation.
Profile any appropriate settings to the data source, such as Data Source name, or point to your connection properties files for SAP, Domino, etc.
Enable the test harness in Service Definition and supply default inputs.
Create a stub Model for offline development (if required) and setup the service mapping file.
A Service Consumer Model (i.e., Portlet Model). Within this Model, make sure to:
Consume one or 2 sample data services
Have all text come from the resource bundles you defined (page title, alt text for print and excel icons, field labels, etc).
Have all formatting parameters such as data page template, CSS file, etc., come from your common formatting profile set.
Use a Data Column Modifier to set the order and default visibility of each column (Show/Hide). If you wish to expose a table for configuration or personalization, profile the table columns configuration input as a single XML input in a new profile set for your sample portlet.
Use Rich Data Definition to apply base types for each field in the data and test the formatting.
Test out role-based profiling and attach the profile set to the appropriate portal groups.
A Sample Portlet Edit Model using a Portlet Customizer that will test some of the personalization settings for the sample portlet, if appropriate. Within this Model, make sure to:
Use a Table Customizer Builder to edit the table configuration, if desired.
Pull in a resource bundle to localize the prompts and field labels
Stub out all of your portlets
It is a simple but really helpful step to create a placeholder portlet for each of the portlets you plan to build.
By creating stub Models, you will have a constant visual status as to how much you have built and how many portlets you have left to develop. This approach forces you to think about how you will lay out the portlets on your pages. You will be challenged by how much you can fit on a page, and this may make you reconsider your page hierarchy. For example, you might be forced to break one page into two portlets, so the information isn’t lost below the fold. You will need to setup and test user permissions for your portlets. You will save time by not hav ing to redeploy as you add each portlet
For each portlet that you plan to build, create a simple Model that contains:
A simple page Builder and a main Action List. Put some text on the page that says " here". Optionally, add a basic image with something like a screenshot from a requirements doc or even just a spacer image. This will help you think about spacing and real estate needs.
Add a Portlet Adapter Builder and fill out the Portlet Name and Title.
Set up your Portal deployment scripts prior to starting development.
Taking a little time up front to create deployment scripts will save you a tremendous amount of time as you develop your Portal application. By creating XML Access scripts, you capture all of the information about how portlets are configured, laid out on pages, and authorized to users. Once you have captured this information in XML Access scripts you can:
The best XML Access reference is the WebSphere Portal Information Center, which includes several sample XML Access files. If you cannot figure out how to do something like capture a specific layout, you can use the portal admin tools to get a page the way you want it and then use an "export" XML Access script to see how the layout is represented in the script. It may be tempting to use the portal admin tools to do all your configuration and then export the configuration to edit, but you will not end up with a very manageable output. As a result, it is better to create the scripts manually.
Although you can segment the scripts in any way you want, it is more efficient and manageable if you break the scripts into smaller files (since XML is so verbose.) We recommend creating the files to do the tasks listed below, in addition to creating scripts that will run the files. Note: these files are listed in the order that they would be run, since for example, the user groups should already be created in order to permission the portlets and pages, and portlets should be created before the page layout can be done.
Quickly deploy your whole configuration on another machine.
Manage the configuration in a simple file rather than a list of instructions.
Easily revert back to a previous (working state.) So, if something goes wrong, and you need to delete your portlet war, you can rerun your scripts and be back in business in a few short minutes.
Easily package your work and send it to someone else.
Quickly correct mistakes and recover your layouts.
Speed development. For many things like changing a page title or giving a user access, it is much faster to change a script and rerun the script that it is to log into portal and use the administration tools.
Create users and groups. Your actual users and groups may be created elsewhere but you may want at least some test users.
Set permissions for user groups.
Clone portlets, e.g. create several versions of a portlet to be used in different contexts.
Set default configuration data. If you have configuration parameters that you are exposing in a portlet configure screen, you can set defaults through XML Access. This is particularly useful if you need to clone a portlet and use it in several places, and it avoids having to manually go to the portlet configure page and set those values.
Set locale-specific portlet titles via resource bundles. The default portlet titles come from your Portlet Adapter Builders, but it is highly recommended that you use a resource bundle to set the portlet and page titles. For examples of this look, at the XML Access samples in the Portal Information Center.
Create page hierarchy.
Set page permissions.
Configure portlet layout.
Apply any themes for page and skin s for portlets.
Set locale-specific page titles using resource bundles.
Create common Models that include elements needed by other Models
As you build out a set of Models, there will be features that you want to add or make available to every Model. You should put these features/files into a common Model and then import this common Model into each of your Service Provider or Service Consumer (Portlet UI) Models.
Typically, you will have one common Model to be used by Portlets and their Customizers, and a separate common Model for the Service Providers. This keeps an appropriate separation between the two layers.
Common Models often include:
Resource bundles. You will likely have at least one common resource bundle that has common text strings to be used across all portlets.
Event declarations. If you have portlet events that you want to use in multiple portlets, add them to a common Model used in all your portlets. This prevents copying and pasting them into each Model that will use them.
Common Java code. A project will often include a set of methods that are used in several places. For example, a method that translates date formats may be used in all Service Provider Models.
Error pages and error handling code.
Look for opportunities to create Builders
Although Portlet Factory provides a wide variety of Builders, chances are you will have specific, custom patterns that are common to many Models. These common patterns can be made into Builders, helping not only to automate the construction of the UI, but also to give it consistency. Look for opportunities to create things like a ‘base’ Builder if there is common functionality such as error handling, SSO, logging, access to shared data, etc. that all portlets will share. A base Builder will standardize the implementation.
Take advantage of the fine grained performance tuning features in the SQL set of Builders
The SQL Call and SQL Statement Builders support a paged view of a result set and provide a fairly rich set of options for optimizing how paging is done at runtime. For example, using SQL Statement, you can specify the Concurrency, Scroll Type, Fetch Direction, and Fetch Size for a result set. These inputs are independent from the "page" size you specify for display in the browser. For example, you can configure SQL Statement to create results that are read-only, scroll insensitive, fetched primarily in a forward direction, and fetched in 20 row chunks. Visually, you can take this paged result set and specify that you want only 5 rows to be visible to a user at a time. Thus, four forward/reverse paging actions from a user would translate into roughly one actual fetch from the database. SQL Statement also allows you to throttle down a result set by setting upper limits of the field size, max rows, and statement execution time. If you are using the Service Builders (Service Definition and Service Operation) to wrap your SQL as a service, and you want to leverage the data paging features specified above, you will need to do some additional work to get things going. On the Service Consumer side, you will need a DataRetriever implementation that handles fetching of additional rows through the service. To handle data paging on the Producer side, there are two different general approaches that could be taken:
The first approach is to write a Java method that includes inputs like fetch size and start row and that calls the getData method. Then, instead of exposing the execute method as the Action to Call in your Service Operation Builder, expose the method that you created.
The other approach is to use the "Context Variable" support (within the Service Operation Builder) to communicate the information about fetch size and start row, without having explicit arguments for the operation.
Avoid "Big Bang" data access patterns.
This typically occurs when you have implemented a service producer/consumer architecture and the service producers access back-end systems, but don’t perform the access in efficient ways or at appropriate times. This leads to degraded performance of your service consumer Models when handling user requests. As a general rule your service producers should only access back-end systems and load data in direct response to being invoked by a consumer. In this way only the data that’s needed by that consumer is loaded. If the data is sharable across multiple users (or doesn’t change very often per user), then implementing a caching strategy in the producer can significantly improve your application’s overall performance. Note that "shared data" scenarios are often good candidates for a producer’s initialization code, but only if you are reasonably sure that the data will be used in common flows through your application.
Summarizing and sorting of data is best performed on the back end
vOften times you want to create a portlet that displays summarized or sorted data. In most scenarios involving summarizing or sorting of data, it is usually more efficient to let the back end (such as the database) do the summarizing, rather than to write code to do this in the Model layer. See tip about avoiding large data sets. Obviously, if you have a very small data set, you can do some manipulation and tallying on the front end. For example, if are displaying a small table of data, and you want to show a row at the bottom that summarizes the results, you should use the Summary Row Builder. But for the most part, especially with large data sets, leverage your database, or back-end system to do this work for you.
The WEF/WPF Getting Started Guide has a section dedicated to "Selecting builders for common tasks".