Command framework
Command beans follow a specific design pattern. Every command includes both an interface class (for example, CategoryDisplayCmd) and an implementation class (for example, CategoryDisplayCmdImpl). From a caller's perspective, the invocation logic involves setting input properties, invoking an execute() method, and retrieving output properties.
From the perspective of the command implementer, commands follow the WebSphere command framework, which implements the standard command design pattern allowing a level of indirection between the caller and the implementation. The key mechanisms enabled within this level of indirection include:
- The ability to invoke an access control policy manager that determines if the user is allowed to invoke the command.
- The ability to execute a different command implementation for different stores, based upon the store identifier.
Command factory
In order to create new command objects, the caller of the command uses the command factory. The command factory is a bean that is used to instantiate commands. It is based on the factory design pattern, which defers instantiation of an object away from the invoking class, to the factory class that understands which implementation class to instantiate.
The factory provides a smart way to instantiate new objects. In this case, the command factory provides a way to determine the correct implementation class when creating a new command object, based upon the individual store. The command interface name and the particular store identifier are passed into the new command object, upon instantiation.
There
are two ways for the implementation class of a command to be specified.
A default implementation class can be specified directly in the code
for the command interface, using the defaultCommandClassName
variable.
For example, the following code exists in the CategoryDisplayCmd interface:
String defaultCommandClassName =
"com.ibm.commerce.catalog.commands.CategoryDisplayCmdImpl"
The second way to specify the implementation class is to use WebSphere Commerce command registry. The command registry should always be used when the implementation class varies from one store to another. More information about the command registry can be found in the Command registration framework section.
In the case where a default implementation class is specified in the code for the interface and a different implementation class is specified in the command registry, the command registry takes precedence.
The syntax for using the command factory is as follows:
cmd = CommandFactory.createCommand(interfaceName, storeId)
where interfaceName
is
the interface name for the new command bean and storeId
is
identifier of the store for which the command should be implemented.
Typically, the store ID can be retrieved by using the commandContext.getStoreId()
method.
Nested controller commands
You will most often use the command factory to create instances of task commands, however, it can also be used within one controller command to create an instance of another controller command.
The syntax for instantiating task commands and controller commands is the same. That is, you specify the name of the command's interface and the store ID in both scenarios.
If you nest one controller command within another, note the following points:
- Once you have instantiated the nested command, call its setCommandContext method and pass in the current command context. Note that if you are passing a different set of request properties to the nested command and these parameters will affect the command context, you should clone the command context before instantiating the nested command. This will preserve the command context information for the outer command.
- Ideally, call the setRequestProperties method of the nested command and pass a TypedProperties object containing input properties. Otherwise, you can use the individual setter methods defined on the interface of the command to set the required properties.
- After input properties have been set, call the execute method of the nested command.
- Since all controller commands must return a view when processing has completed, the outer command can do nothing with the view returned by the nested command.
- The nested command will be executed within the transaction scope of the outer command.
Consider the following code snippet as an example of a nested controller command. This example shows a method in the outer command and how it can use the command factory to instantiate a second controller command.
YourControllerCmd ctrlCmd = null;
public void processAndCallOtherCommand()
throws ECException
{
YourControllerCmd ctrlCmd = (YourControllerCmd)CommandFactory.createCommand(
com.yourcompany.commands.yourControllerCmd, this.getStoreId());
ctrlCmd.setCommandContext(this.getCommandContext());
ctrlCmd.setRequestProperties(this.getRequestProperties());
ctrlCmd.execute();
}
As another example, consider the case where the nested command is being executed for a different store than the first. In this case, the command context from the outer command must be preserved so that it does not get overwritten by the inner command.
public void processAndCallOtherCommandForOtherStore(int aStoreId)
throws ECException
{
// Make a clone to preserve the command context of the outer command
CommandContext cloneCmdCtx = (CommandContext)this.getCommandContext().clone();
//Now pass in a new set of request properties to the cloned command context
cloneCmdCtx.setRequestProperties(reqProp);
YourControllerCmd ctrlCmd = (YourControllerCmd)CommandFactory.createCommand(
com.yourcompany.commands.yourControllerCmd, aStoreId;
ctrlCmd.setCommandContext(cloneCmdCtx);
ctrlCmd.setRequestProperties(reqProp);
ctrlCmd.execute();
}
Command flow
When the solution controller receives a request, it determines whether the request requires the invocation of a controller command or a view. In either case, the solution controller also determines the implementation class for the command, and then invokes it.
Since controller commands encapsulate the logic for a business process, they frequently invoke individual task commands to perform specific units of work in the business process. Access beans are invoked when information in the database must be retrieved or updated. Either a task or controller command can invoke access beans. Requests then flow from access beans to entity beans that can read from, and write to, the WebSphere Commerce database.
A view command is invoked by the solution controller, either when a controller command has completed processing and it returns the name of a view command to invoke, or when an error occurs and an error view must be displayed.
View commands typically invoke a JSP page to display the response to the client. Within the JSP page, data beans are used to populate dynamic information aboutto the page. Data beans are activated by the data bean manager. The data bean (which extends from an access bean) invokes its corresponding entity bean. When accessed indirectly from a JSP page, an entity bean should typically retrieve information from the database, rather than writing information to the database.
Command registration framework
WebSphere Commerce controller and task commands are registered with the command registration framework. The command registry is implemented by means of the CMDREG database table.
The command registry provides a mechanism for mapping the command interface to its implementation class. Multiple implementations of an interface allow for command customization on a per store basis.
The following table describes information contained in the command registry.
Property | Description | Comments |
---|---|---|
STOREENT_ID | Store entity identifier | This can be set to 0 to use the command for
all stores, or to a unique store identifier to indicate that the command
is used only for a particular store. |
INTERFACENAME | Command interface name | This defines the interface; use the same name as you did in the URL registry. |
DESCRIPTION | Description of this command | For example, This command is used for testing purposes . |
CLASSNAME | Command implementation class name | Typically the interface name with "Impl" appended to end. |
PROPERTIES | Default name-value pairs set as input properties to the command | Format is same as URL query string. For example "parm1=val1&parm2=val2" |
In general, when you create a new controller or task command,
you should create corresponding entry in the command registry. For
example, the following SQL statement creates an entry for MyNewCommand
which is used by a particular store (whose store identifier is 5
):insert
into CMDREG (STOREENT_ID, INTERFACENAME, DESCRIPTION, CLASSNAME, PROPERTIES,
LASTUPDATE, TARGET) values (5,'com.mycompany.commands.MyNewCommand',
'This is a test command', 'com.mycompany.commands.MyNewCommandImpl',
'myDefaulParm1=myDefaultVal1', '0000-12-01', 'Local')
String
values should be enclosed in single quotes.
If the command you are writing always uses the same implementation class, you do not necessarily have to register the command in the command registry. In this case, you can use the defaultCommandClassName attribute in the interface to specify the implementation class. For example, in the code for the interface, you would include the following:
String defaultCommandClassName =
"com.ibm.commerce.command.MyNewCommandImpl"
If you specify the implementation class in this manner, you cannot pass default properties to the implementation class and the same implementation class must be used for all stores.
Example of a registered controller command
Consider a scenario in which your site has two stores: StoreA and StoreB. Each store has different security requirements for the MyUrl controller command as well as different implementations of the command. This section shows how the command registry is used to enable this customization.
The MyUrl will execute the business logic that is associated with the com.ibm.commerce.mycommands.MyUrl interface. The interface is shared between the two stores however the implementation for the business logic will be controlled by the store specific configuration found in the Command Registry. Even though the URL shares the same business logic interface, the HTTP security consideration can differ. The following table shows the security requirements for StoreA and StoreB for the MyUrl URL:
Property | Entry for StoreA | Entry for StoreB |
---|---|---|
STOREENT_ID | 11 | 22 |
HTTPS | 1 | 1 |
AUTHENTICATED | 1 | 0 |
Based upon entries in the URL configuration, the solution controller determines that the interface name for the MyUrl URI is com.ibm.commerce.mycommands.MyUrl. It also determines that StoreA requires the command to be executed using both HTTPS and authentication, but StoreB requires HTTPS only. The values for HTTPS and authentication are used by the solution controller for web requests, not by the interface.
Property | Entry for StoreA | Entry for StoreB |
---|---|---|
STOREENT_ID | 11 | 22 |
INTERFACENAME | com.ibm.commerce. mycommands.myUrl | com.ibm.commerce. mycommands.myUrl |
CLASSNAME | com.ibm.commerce. mycommands. myUrlStoreAImpl | com.ibm.commerce. mycommands.myUrlStoreBImpl |
Based upon entries in the command registry, the solution controller determines that for StoreA, the implementation class for the com.ibm.commerce.mycommands.MyUrl interface is com.ibm.commerce.mycommands.MyUrlStoreAImpl. It also determines for StoreB, the implementation class for the same interface is com.ibm.commerce.mycommands.MyUrlStoreBImpl. The following diagram shows this flow: