Techniques for Working With Multiple Models in WebSphere Portlet Factory
When creating complex portlets with Portlet Factory, it is recommended that the number of builder calls in a model be kept to a reasonable number. There is no hard and fast rule on an exact number of builder calls that qualifies as too many. Instead, both the number and relative complexity of the builder calls should be taken into account.
Here are some rough guidelines to follow.
For some models using mostly simple builder calls (Text builders, Page builders, Button builders, etc), it would be a good idea to keep the number of builder calls to under 50. In this case, the manageability of the model generally becomes the limiting factor, as the frequently used builder call list (Outline view) becomes increasingly difficult to manage. As the number of builder calls in the model increases, it becomes more difficult to maintain unique display names in the builder call list and if the builder call list is not well organized, you will have increasing difficulty in locating the desired builder in it. One technique to help with this is the use of Comment builders to group and categorize the builder call list.
In a model employing some rather complex builder calls (View & Form builders, Data Page builders, etc), it may be a good idea to keep the number of builder calls to even fewer than 50. In this case, the limiting factor generally becomes the system performance in handling the model regenerations and lookups necessary to function in the Portlet Factory Designer.
It may help to understand a little more about what Portlet Factory does with a model. A Portlet Factory model is a series of builder calls, and is stored as an XML file. In order to run a model in Portlet Factory, the Portlet Factory runtime code must convert the model XML into something executable in a JVM. This process is called generation (or regeneration). The first step in the process of generation converts a model into a WebApp. The WebApp is a collection of the components generated by the builder calls in the model. The next step in the process of generation converts the WebApp into executable units. The WebApp is converted into one or more .jsp pages and a single Java class.
For example, an Image builder will contribute an img tag to the page(s) on which it is located. This will result in a fragment of .jsp code being inserted into one or more .jsp files. Other builder types will contribute other components to the WebApp. The model class is a single Java class that gets generated from the code in the WebApp. The amount and complexity of the code in the model will affect the complexity of the Java class that gets generated.
Within the Portlet Factory Designer, interactions in the Application Tree view (formerly called WebApp Tree) and the Outline view are synchronized with the generated .jsp and .java files for the model. As you select a builder call from the Outline view and/or a component in the Application Tree view, its contributions to the generated .jsp and .java files are located in the Application Tree view. Portions of the .jsp and .java files are shown in the Source and Design views next to the Application Tree view. As the number and complexity of the builders in a model increases, the impact on the system performance to keep these views synchronized also increases.
Each time you click OK or Apply in the Builder Call Editor, the Designer will go through a generation cycle and re-generate the artifacts. This process involves the creation of the .jsp pages, the creation of the .java file and the compilation of the .java file into a .class file.
There are several techniques available to allow you to work with multiple models and split them effectively. These techniques can provide benefits from a both a usability perspective and a performance perspective. Another benefit to splitting up models is that the smalle r models make it ea sier to share development effort and responsibility across a team. Some of the techniques described here may also allow the individual models to be tested as independent units. The smaller models may also become more reusable, and you may find that some of them could even be converted into model-based builders.
In the diagrams provided below, the red border indicates the independent runtime units created by the models in each scenario.
Service Provider Models
One technique to split models which you are probably already familiar with is the use of Service Provider models. Service Provider models are created through the use of a Service Definition builder and one or more Service Operation builders. The Service Definition and Service Operation builders work together with the Service Consumer builder to expose methods in the Service Provider model to a consumer model. Service Provider models are very useful in implementing a layered or tiered approach to portlet development. A common pattern is to encapsulate all of the data operations in one or more Service Provider models. It is also feasible to insert additional tiers using Service Provider models between your presentation and data tiers to encapsulate various business logic rules. Service Provider models provide many benefits. They can reduce the complexity of a model by moving and grouping calls to data services in a single model. They can easily be reused across multiple presentation models. They can easily provide a unit test framework through the use of the Testing Support functions in the Service Definition builder.
Recommendation: Use a service provider/consumer model architecture, with all back end data access in the provider layer.
Diagram: Using Service Provider Models
Linked Java Objects
Another technique to reduce complexity in a model is the use of the Linked Java Object builder. If you have a model with many Action Lists or Method builders, consider moving the code from the Action Lists and Method builders into a Java class and referencing the Java class through the Linked Java Object builder. It is also possible to make Linked Java Object implementations that are reusable across multiple different models through the use of the Instantiation Method input to the Linked Java Object builder. Using a Linked Java Object has the effect of reducing the complexity of the Methods class that is generated. Using a Linked Java Object also has the benefit of allowing you to use the standard Java class editor in Eclipse, which provides a rich set of features for editing Java source code.
Recommendation: If you are using much hand-coded Java (e.g. in Method builders), put the code in a separate Java class referenced with Linked Java Object builders.
Rich Data Definitions
Another technique to reduce complexity in a model is through using Rich Data Definition files. Rich Data Definition files can encapsulate logic associated with a schema to be used in a Data Page builder. The Rich Data Definition files can contain additional builder calls. It is recommended that only specific, relevant builder calls be encapsulated in a Rich Data Definition file. That is only builder calls related to the schema, for example, a Lookup Table builder to define a set of labels for a data column would be appropriate, however, a Page builder to define a new page would not.
Recommendation: Utilize Rich Data Definition builder to automate the application of validation, formatting, and other UI characteristics for data fields. Use Rich Data Definition files to share this functionality across models.
Imported Model builder
The Imported Model builder is another technique for working with multiple models. The Imported Model builder imports or includes all of the builder calls from the imported model i nside the importing model. The builder calls in t he imported model are in cluded and tr eated exactly if they were actually present in the imported model. The Imported Model builder, therefore, provides no real benefits to reducing the number of builder calls in a model from a complexity standpoint. It does have the effect of reducing the actual number of builder calls visible in the outline view, but when the importing model is generated, it still generates with all of the builder calls that it defines plus all of the builder calls that the imported model defines. The Imported Model builder can also provide a way to factor out common functions, such as utility functions which may be re-used across multiple other models. The Imported Model builder is very useful for defining things in multiple models where a constant definition is required. For example, an Imported Model builder could be used to define Event Declaration builders or Shared Variable builders where multiple models will need to refer to the same event or shared variable and require that the definition match across those multiple models. The Imported Model builder also allows profiling to be applied to the imported model. This allows the imported model to be varied as it is imported further increasing it's functionality. The Imported Model builder also supports an import once feature. In some complex scenarios, a top level model might import two models, each of which import a common base model. The import once feature allows the top level model to only see a single imported instance of the common base model, which eliminates conflicts that may arise due to importing the same model twice.
Recommendation: Use Imported Model when you want to include some builders in multiple models, such as Event Declarations, Shared Variables, or shared utility functions.
Diagram: Using the Imported Model Builder
Diagram: Using the Imported Model Builder with profiling
Diagram: Using the Imported Model Builder with Import Once
Model Container builder
The Model Container builder is a very powerful builder in regards to facilitating the use of multiple models in Portlet Factory. The Model Container builder allows one model to contain another model. The actions in the contained model are available to be called from the containing model. The contained model, however, has no knowledge that it is being contained by another model. The Model Container builder allows for a page in the containing model to delegate the rendering of a region of the page to another model, the contained model. The Model Container builder also provides the ability to defer the generation of the contained model. This can be a very useful feature if the contained model needs to vary based on runtime data in the containing model. This can allow for more dynamic application of profiling based on runtime data from the containing model. In order for the contained model to communicate back to the containing model, it must use a Portlet Factory event. The contained model and containing model should both define the events for communication between the models, and the containing model should declare an Event Handler to listen for events from the contained model. The Model Container builder, like the Imported Model builder, reduces the complexity of the models involved. The Model Container builder, however, also reduces the complexity of the Methods class that is generated, as the containing and contained models are generated independently. This me ans that unlike the Imported Model builder, the complexity of the genera ted artifacts are truly reduced.
Recommendation: Use Model Container to combine user interface elements from multiple models.
Diagram: Using the Model Container Builder
Linked Model builder
Use the Linked Model builder to create and include modular fu nctional units in an application. Linked models are roughly analogous to Java classes; they contain methods and state data that can be easily accessed by the outer model (the outer model is the model that contains the Linked Model builder). An outer model can directly access any public methods, linked Java objects (LJOs), and action lists defined by the linked model. However, you should not use linked models for inserting pages and other visual components from the linked model into the pages of the outer model. The recommended best practices is to use the Model Container builder for visual embedding.
You should be aware of a runtime difference in how Linked Model behaves when used in a portlet model running inside a portlet container versus the same portlet model running as a stand-alone application. If you are trying to share session data across portlets using a session-singleton Linked Model, then this will not work since the linked model will only be shared within the scope of a portlet because of the way portlet containers create and manage portlet sessions.
Recommendation: The Linked Model builder should rarely be used directly. For most scenarios, consider whether techniques such as Service Provider or Model Container can provide the modularity you need.
The attached sample models illustrate some of the techniques described in this article. Download multi_model_sample_v2.zip and import it into a Portlet Factory project using Import WebSphere Portlet Factory Archive. The sample depends on the Tutorials and Samples/Building Models feature set (formerly the Tutorials and Samples/Applications feature set). Additional comments on the techniques illustrated are provided within the sample models.