Geomajas Community Documentation

Chapter 16. Writing your own commands

A Geomajas command usually consist of three classes, the actual command (which implements the Command interface), and two data transfer objects, one to pass the request parameters (extending CommandRequest, LayerIdCommandRequest or LayerIdsCommandRequest), and one which carries the response (extending CommandResponse).

It is important to assure your request object extends from LayerIdCommandRequest or LayerIdsRequest when one of the parameters is the layer id (or a list thereof). This can be used by the command dispatcher to assure the layer specific (transaction) interceptors are called.

To create a new command we recommend you use a similar package structure as we used in the geomajas-extension-command module. That is to create a "command" package with under that a "dto" package which contains all the request and response objects, and to put the actual commands in sub packages based on some kind of grouping. This helps to automatically determine a sensible command name.

The basic command implementation looks like this:

package com.my.program.command.mysuper;

import com.my.program.command.dto.MySuperDoItRequest;
import com.my.program.command.dto.MySuperDoItResponse;
import org.geomajas.command.Command;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;

/**
 * Simple example command.
 *
 * @author Joachim Van der Auwera
 */
@Component()
public class MySuperDoItCommand implements Command<MySuperDoItRequest, MySuperDoItResponse> {

    private final Logger log = LoggerFactory.getLogger(MySuperDoItCommand.class);

    public MySuperDoItResponse getEmptyCommandResponse() {
        return new MySuperDoItResponse();
    }

    public void execute(MySuperDoItRequest request, MySuperDoItResponse response) throws Exception {
        log.debug("called");
        // ..... perform the actual command
    }

}

Example 16.1. Example command template


Note the presence of the "@Component" annotation which assures the command is registered. You could add the name under which the command needs to be registered in the annotation, but when that is omitted, the default command name is derived from the fully qualified class name. In the example given here this results in command name "command.mysuper.DoIt".

The default way to determine the command name assumes there is a package named "command" in the fully qualified name of the implementing class. It will remove everything before that. It will then remove a "Command" suffix if any. Lastly, it will remove duplication between the intermediate package (between "command" and the class name) and the class name itself. Some examples:

Fully qualified class name Command name
my.app.command.DoIt command.DoIt
my.app.command.super.DoIt command.super.DoIt
my.app.command.super.DoItCommand command.super.DoIt
my.app.command.super.SuperDoItCommand command.super.DoIt
my.app.command.super.DoItSuperCommand command.super.DoIt
my.app.command.super.CommandDoIt command.super.CommandDoIt
my.app.command.super.CommandSuperDoIt command.super.CommandSuperDoIt
my.app.command.super.CommandDoItSuper command.super.CommandDoIt

Table 16.1. Samples of command name resolution


You have to include a line in your Spring configuration to scan class files for annotation to make the components available. For the case above, this could be done by including the following XML fragment in one of your Spring configuration files.

<context:component-scan base-package="com.my.program" name-generator="org.geomajas.spring.GeomajasBeanNameGenerator" />

Example 16.2. Scan to assure command is available


The command will be executed using a singleton. The use of object variables is not recommended. Any object variables will be shared amongst all command invocation, which can be coming from multiple threads at the same time.

Note that it is not mandatory to create your own request and response object classes. If you don't require any parameters you can use EmptyCommandRequest as request class. If you only require a layer id, then use LayerIdCommandRequest. If you only return a success code, you could use the SuccessCommandResponse class.

You have to take care that all objects which are referenced by your request and response objects are actually serializable for the faces in which the commands need to be used. For the dojo face this may require the use of the "@Json" annotation to exclude fields. For GWT you have to assure the no-arguments constructor exists and that the class can be compiled by GWT (no Hibernate enhanced classes, no use of "super.clone()",...).

When the commands are included in a separate module, you should assure the sources are available as these are needed for GWT compilation. This can easily be done using the Maven source plugin.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-source-plugin</artifactId>
    <version>2.1.2</version>
    <executions>
        <execution>
            <goals>
                <goal>jar</goal>
            </goals>
            <configuration>
                <includePom>true</includePom>
            </configuration>
        </execution>
    </executions>
</plugin>

Example 16.3. Maven source plugin


Actually including the sources can then be done using a dependency like the following (this includes the staticsecurity module, both the actual code and the sources). You could set "provided" scope on the source dependency to exclude it from the war file. However, this may prevent use of GWT development mode.

<dependency>
    <groupId>org.geomajas.plugin</groupId>
    <artifactId>geomajas-plugin-staticsecurity</artifactId>
    <version>${geomajas-plugin-staticsecurity-version}</version>
</dependency>
<dependency>
    <groupId>org.geomajas.plugin</groupId>
    <artifactId>geomajas-plugin-staticsecurity</artifactId>
    <version>${geomajas-plugin-staticsecurity-version}</version>
    <classifier>sources</classifier>
</dependency>
<dependency>
    <groupId>org.geomajas.plugin</groupId>
    <artifactId>geomajas-plugin-staticsecurity-gwt</artifactId>
    <version>${geomajas-plugin-staticsecurity-version}</version>
</dependency>
<dependency>
    <groupId>org.geomajas.plugin</groupId>
    <artifactId>geomajas-plugin-staticsecurity-gwt</artifactId>
    <version>${geomajas-plugin-staticsecurity-version}</version>
    <classifier>sources</classifier>
</dependency>

Example 16.4. staticsecurity source plugin - including source