GWT SyncProxy-0.1.1New!           Bean Properties Editor for JDeveloper 11gNew!. -->
  gwtXPDownloadsBlog

Testing GWT RPC services

Posted in GWT on January 10th, 2010 7 Comments

You may know that testing with GWTTestCase are very slow. And there are many posts about testing GWT applications without using GWTTestCase.

However, it is still hard to test our RPC remote services. That is the reason I decided to develop SyncProxy which can run directly from pure Java (non JSNI) code.

Simple Test case

For example, we have an helloApp application and we want to test our GreetingService

@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
  String greetServer(String name);
}

Assume the server side of application is running (whether in DevMode or deployed to an web server) and the servlet URL is configured at http://localhost/helloApp/greet

The test case

public class GreetingServiceTest extends TestCase{
  private static GreetingService rpcService =
    SyncProxy.newProxyInstance(GreetingService.class,
          "http://localhost/helloApp", "greet");

  public void testGreeting() {
    String result = rpcService.greetServer("SyncProxy");
    assertTrue((result != null) && (result.startsWith("Hello, SyncProxy")))
  }
}

Explanation

SyncProxy.newProxyInstance() method requires a service interface class, a base URL and a relative servlet path.

SyncProxy will search for RPC policy files (*.gwt.rpc files) to determine appropriate policy name for the service interface. Thus, we copy gwt.rpc files from war/helloApp directory to our test case classpath.

SyncProxy.newProxyInstance() will return a new proxy instance which implements our GreetingService interface.

Simulating Async

By design SyncProxy is synchronous, e.g it invoke the remote service and wait for the result. However, our GWT logic code invokes the remote service asynchronously. SyncProxy can simulate the ‘Async’ mode too.

GreetingServiceAsync rpcServiceAsync =
SyncProxy.newProxyInstance(GreetingServiceAsync.class,
          "http://localhost/helloApp", "greet");

The code is almost the same as section above, except we use the Async version of the remote service interface.

rpcService.greetServer("SyncProxy", new AsyncCallback<String>(){
  public void onFailure(Throwable caught) {
    ...
  }
  public void onSuccess(String result) {
    ...
  }
});

Download SyncProxy

Get SyncProxy (with source code) at our Downloads page

SyncProxy includes test suite (see the Java source file com.gdevelop.gwt.syncrpc.test.RPCSyncSuite) to test against the standard GTW RPC test.

Updated on 2010/03/03: Version 0.1.1 is released with cookie supports

Share and Enjoy:

  • Google Bookmarks
  • Twitter
  • Facebook
  • Digg
  • Technorati
  • Live
  • DZone
  • Reddit
  • del.icio.us
  • Mixx
  • Netvibes
  • StumbleUpon
  • Yahoo! Bookmarks
  • Yahoo! Buzz
  • email

GWT Map Generator

Posted in GWT on January 5th, 2010 Be the first to comment

When working with GWT History, we usually need a Map for view transitions.

Traditional approach

For example, our EntryPoint class

public class MyEntryPoint implements EntryPoint, ValueChangeHandler{
  ...

  public void onModuleLoad() {
    History.addValueChangeHandler(this);
  }

  public void onValueChange(ValueChangeEvent event) {
    String token = event.getValue();

    if (token != null) {
      Panel panel = null;

      if (token.equals("list")) {
        panel = new ContactsListPanel();
      } else if (token.equals("add")) {
        panel = new EditContactPanel();
      }else if (token.equals("edit")) {
        panel = new EditContactpanel();
      }

      if (panel != null) {
        container.setWidget(panel);
      }
    }
  }
}

Generator

We can archive this by a simple generator

public class MapGenerator extends Generator{
  public String generate(TreeLogger logger, GeneratorContext context,
                         String requestedClass) throws UnableToCompleteException{
    TypeOracle typeOracle = context.getTypeOracle();
    assert(typeOracle != null);

    JClassType baseType = typeOracle.findType(requestedClass);
    if (baseType == null){
      logger.log(TreeLogger.ERROR, "Unable find type " + requestedClass, null);
      throw new UnableToCompleteException();
    }
    String packageName = baseType.getPackage().getName();
    String baseName = baseType.getSimpleSourceName();
    String simpleName = baseName + "Map";
    JClassType[] types = baseType.getSubtypes();

    // Generate the source file
    PrintWriter printWriter = context.tryCreate(logger, packageName, simpleName);
    ClassSourceFileComposerFactory composer =
      new ClassSourceFileComposerFactory(packageName, simpleName);
    composer.setSuperclass("HashMap");
    composer.addImport("java.util.*");
    SourceWriter out = composer.createSourceWriter(context, printWriter);
    out.println("public " + simpleName + "(){");
    out.indent();
    for (JClassType type : types) {
      String key = type.getSimpleSourceName();
      if (key.endsWith(baseName)){
        key = key.substring(0, key.length() - baseName.length());
      }
      out.println("put(\"" + key + "\", new " + type.getQualifiedSourceName() + "());");
    }
    out.outdent();;
    out.println("}");
    out.commit(logger);

    return packageName + "." + simpleName;
  }
}

The Generator will look for all sub-class of requestedClass, and put an instance of each these classes to a HashMap.

Setting up your module

<module>
  ...
  <generate-with class="com.example.rebind.MapGenerator">
    <when-type-assignable class="com.example.client.MyBasePanel" />
  </generate-with>
</module>

Using the generated Map

public class MyEntryPoint implements EntryPoint, ValueChangeHandler{
  private static final Map<String, MyBasePanel> TOKEN_MAP = GWT.create(MyBasePanel.class);
  ...

  public void onModuleLoad() {
    History.addValueChangeHandler(this);
  }

  public void onValueChange(ValueChangeEvent event) {
    String token = event.getValue();

    if (token != null) {
      Panel panel = TOKEN_MAP.get(token);

      if (panel != null) {
        container.setWidget(panel);
      }
    }
  }
}
Share and Enjoy:

  • Google Bookmarks
  • Twitter
  • Facebook
  • Digg
  • Technorati
  • Live
  • DZone
  • Reddit
  • del.icio.us
  • Mixx
  • Netvibes
  • StumbleUpon
  • Yahoo! Bookmarks
  • Yahoo! Buzz
  • email