SeMa4g

Description

A light-weighted, client-sided framework to manage asynchronous calls in GWT.

In many cases it is neccessary to do several server calls, before a GWT application starts or a view can be displayed. SeMa4g will help you to get this calls managed.

The idea of SeMa4g is to use a callback proxy. The asyncronized server call will use the callback classes of SeMa4g. This allows SeMa4g to get informed if a server call has finished.

Features

Using

Configure the Module Descriptor

Configure the GWT project to inherit the SeMa4g module.

Create a SeMa4g Context

To use SeMa4g it is neccessary to create a SeMa4g Builder, add all commands, build the context and call run.

Create a SeMa4g context:

SeMa4g.Builder sema4gContext = SeMa4g.builder();

Create a InitCommand and add it to the SeMa4g context

Add a InitCommanmd to the SeMa4g context:

sema4gContext.addInitCommand(new InitCommand() {
                                   @Override
                                   public void onStart() {
                                     // Enter here your code, that
                                     // should be executed when
                                     // the context is started
                                   }
                                 });

It is possible to add more then one InitCommand command to the SeMa4g context.

Create a FinalCommand and add it to the SeMa4g context

Add a FinalCommanmd to the SeMa4g context:

sema4gContext.addFinalCommand(new FinalCommand() {
                                    @Override
                                    public void onSuccess() {
                                      // Enter here the code, that will
                                      // be executed in case the context
                                      // ended without error
                                    }

                                    @Override
                                    public void onFailure() {
                                      // Enter here the code, that will
                                      // be executed in case the context
                                      // ended with an error
                                    });

It is possible to add more then one FinalCommand command to the SeMa4g context. At least you need to add one FinalCommand.

Create a command and add it to the SeMa4g context

Adding a command to the SeMa4g context is quite easy:

sema4gContext.add(new SyncCommand() {
                        @Override
                        public void execute() {
                          // Enter here the code, that will
                          // be executed, in case the SeMa4g
                          // context will execute the command.
                        }
                      });

Type of commands

SeMa4g knows two types of commands.

SyncCommands

A SyncComannd is a command that contains code which will be executed synchronously. That means, SeMa4g will call the execute-method of the command and execute the next command. Do not implement server actions in this command!

Example:

private SeMa4gCommand createSyncCommand() {
  return new SyncCommand() {
               @Override
               public void execute() {
                 // Enter here the code, that will
                 // be executed, in case the SeMa4g
                 // context will execute the command.
               }
             };
}

AsyncCommands

A AsyncCommand is a command that contains asynchronously executed code. In this case you have to use one of the SeMa4g callback classes. This callback classes will inform SeMa4g when a server call returns from the server.

Example:

private SeMa4gCommand createAsyncCommand() {
  return new AsyncCommand() {
      // create callback
      MethodCallbackProxy<String> proxy = new MethodCallbackProxy<String>(this) {
        @Override
        protected void onProxyFailure(Method method,
                                      Throwable caught) {
          // Enter here the code, that will
          // be executed in case of failure
        }

        @Override
        protected void onProxySuccess(Method method,
                                      String response) {
          // Enter here the code, that will
          // be executed in case of success
        }
      };

      @Override
      public void execute() {
        // That's the place for the server call ...
        greetingService.greet(name, proxy);
      }
    };
  }
}

In case that one AsyncCommad ends in error, SeMa4g stops the excecution, waits for running commands and calls the onFailure-method of the FinalCommand.

In case that all commands finished without errors the onSuccess-method of the FinalCommand is called.

Conditional commands

In case the SeMa4g context is started by calling the run-method, the execute-method of all commands will be called. In some cases it might be necessary that one command needs the response of another command. To handle such things, SeMa4g offers the possibility to add depending commands to a command.

If a command depends on another command (or maybe more than one command), the execution of this command will wait until all depending commands have been successfully finished.

Example:

// the execution of comand03 will stsrt, in case that command01 and command02
// successfully finished.
SeMa4gCommand command01 = createAsyncCommand01();
SeMa4gCommand command02 = createAsyncCommand02();
SeMa4gCommand command03 = createAsyncCommand03();

sema4gContext.add(command01)
             .add(command02)
             .add(command03.dependingOn(command01, command02);

Execute the context

To execute the context, you have to build the context by calling the build-method and then call the run-method to run the context.

Example:

sema4gContext.build()
             .run();

Order of Execution

Executing the context will:

That means:

The order of executions of the InitCommands and FinalCommands is not defined!

Manually stop execution

SeMa4g offers two commands, that will interrupt the execution of a running SeMa4gContext.

Complex example

private void doServerCalls() {
  // the execution of comand03 will stsrt, in case that command01 and command02
  // successfully finished.
  SeMa4gCommand command01 = createAsyncCommand01();
  SeMa4gCommand command02 = createAsyncCommand02();
  SeMa4gCommand command03 = createAsyncCommand03();

  try {
    SeMa4g.builder()
          .addInitCommand(new InitCommand() {
                               @Override
                               public void onStart() {
                                 // Enter here your code, that
                                 // should be executed when
                                 // the context is started
                               })
    .addFinalCommand(new FinalCommand() {
                           @Override
                           public void onSuccess() {
                             // Enter here the code, that will
                             // be executed in case the context
                             // ended without error
                           }

                           @Override
                           public void onFailure() {
                             // Enter here the code, that will
                             // be executed in case the context
                             // ended with an error
                           })
    .add(command01)
    .add(command02)
    .add(command03.dependingOn(command01, command02)
    .build()
    .run();
  } catch (SeMa4gException e) {
    // Ups, something wrong with the context ...
  }
}

private SeMa4gCommand createAsyncCommand01() {
  return new AsyncCommand() {
      // create callback
      MethodCallbackProxy<String> proxy = new MethodCallbackProxy<String>(this) {
        @Override
        protected void onProxyFailure(Method method,
                                      Throwable caught) {
          // Enter here the code, that will
          // be executed in case of failure
          // ended with an error
        }

        @Override
        protected void onProxySuccess(Method method,
                                      String response) {
          // Enter here the code, that will
          // be executed in case of success
          // ended without error
       }
      };

      @Override
      public void execute() {
        // That's the place to do the server call ...
        greetingService.greet(name, proxy);
      }
    };
  }
}

private SeMa4gCommand createAsyncCommand02() {
  return new AsyncCommand() {
      // create callback
      MethodCallbackProxy<String> proxy = new MethodCallbackProxy<String>(this) {
        @Override
        protected void onProxyFailure(Method method,
                                      Throwable caught) {
          // Enter here the code, that will
          // be executed in case of failure
           // ended with an error
       }

        @Override
        protected void onProxySuccess(Method method,
                                      String response) {
          // Enter here the code, that will
          // be executed in case of success
          // ended without error
        }
      };

      @Override
      public void execute() {
        // That's the place to do the server call ...
        greetingService.greet(name, proxy);
      }
    };
  }
}

private SeMa4gCommand createAsyncCommand02() {
  return new AsyncCommand() {
      // create callback
      MethodCallbackProxy<String> proxy = new MethodCallbackProxy<String>(this) {
        @Override
        protected void onProxyFailure(Method method,
                                      Throwable caught) {
          // Enter here the code, that will
          // be executed in case of failure
          // ended with an error
       }

        @Override
        protected void onProxySuccess(Method method,
                                      String response) {
          // Enter here the code, that will
          // be executed in case of success
          // ended without error
        }
      };

      @Override
      public void execute() {
        // That's the place to do the server call ...
        greetingService.greet(name, proxy);
      }
    };
  }
}

Downloading

Use the “Clone or download” button at the top right of this page to get the source. You can get a pre-built JAR (usable in JRE 1.7 or later) from Sonatype (in progress), download the jar form here , or add the following Maven Central dependency:

<dependency>
    <groupId>de.gishmo.gwt</groupId>
    <artifactId>sema4g</artifactId>
    <version>LATEST</version>
</dependency>

Example

SeMa4g provides an example web application to show some common use cases.

To run the example use:

mvn install

from the pom to install SeMa4g in your local repository (as long as SeMa4g is not provided via Maven Central.)

To run the example web application, use

mvn gwt:devmode

from the pom inside the sema4g-example directory.

In case the web applicaiton is running, select a test and press the ‘Run’-Button.

Test Case Description

For all test cases:

Test Case 01

A single service call with a InitCommand and a FinishCommand.

The context will end successfully!

Flow Test Case 03

Test Case 02

Several service calls with no dependencies and a InitCommand and a FinishCommand.

The context will successfully end!

Flow Test Case 03

Test Case 03

Several service calls with a InitCommand and a FinishCommand and dependencies.

The context will end successfully.

Flow Test Case 03

Test Case 04

Several service calls with different duration on the server. This test case chows the behaviour in case of a cycle dependencies. Exception expected. No service will be called.

Test Case 05

A single service call (service no. 13) which will fail and throw an exception.

The context will end in error because of a server exception!

Test Case 06

Several service calls with a InitCommand and a FinishCommand and dependencies. One service (service no. 13) will fail and throw an exception.

The context will end in error. Waiting commands, because of the dependencies to command ‘thirteen’, will not be started!

Test Case 07

Several asynchronous and one synchronous service calls with a InitCommand and a FinishCommand and dependencies.

The context will end successfully.

License

Copyright (c) 2015 - 2017 Frank Hossfeld

Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0