Client-Server Programming
QWT applications have a client part and a server part. The client part is a translated to JavaScript and executes in the browser, the server part executes in the servlet container on the server.
QWT provides remote method invocation for client-server communication: client objects can invoke methods on server objects and vice versa, passing objects between client and server.
Client-server programming with QWT was heavily inspired by Java RMI: applications use 'normal' Java objects, and the system handles networking and Java/JavaScript issues transparently.
Example
The grep example demonstrates client-server programming with QWT. It defines a grep service to search for files on the server and the corresponding user interface to call grep with an arbitrary browser. Hitting the grep button in your browser invokes the grep method on the server.
This example is used throughout the following sections to explain the respective feature
Value Objects
RMI passes objects (method arguments and results) between client and server. Normally, objects are passed by value. If you send an object to the server, the JavaScript object in the browser is serialized, sent to the server, and de-serialized into a Java object on the server. If you send an object to the client, the Java object on the server is serialized, sent to the client, and de-serialized into a JavaScript object in the browser.
Build-in Java objects like String and Date are value objects. Primitive types like int and boolean are also value objects.
Value Objects should be defined in the common package of your application because they are used on both client and server.
Example
The Match class defines a value object passed from the grep service back to the browser. It is defined in a common package.
Service Objects
If passing objects by value is not suitable, you can use service objects to pass objects by reference: QWT passes all objects by value - unless it's a service object. Passing a service object from server to client creates a proxy object in the browser. The proxy has the same interface as the service object, but a method called on the client is forwarded to the server for execution. Similarly, passing a service object from client to server, creates a proxy on the server which forwards method calls back to the client.
To define a service object Foo:
- write an interface
FooServicedefining the methods you wish to call from remote - write a class
Foothat implements "FooService" - create an instance of "Foo" and pass it to the remote side
- from remote: invoke your methods
QWT uses the interface name to distinguish between service objects and normal objects. When passing an object, QWT checks all interfaces implemented directly or indirectly. If any of them has a name the ends with Service, the object is a service object and passed by reference. Otherwise, it's a normal object and passed by value.
Note: the service class (Foo) should be defined in a server or a client package, depending on where the services executes. The service interface (FooService) should be define in a common package because the interface has to be known on both client and server.
Note 2: make sure to synchronize service objects running on the server, they can be accessed concurrently.
Example
The Grep class defines a service object because it implements the GrepService interface.
Application startup
QWT applications have two main classes, a server main class and a client main class.
The server main class is instantiated when the application starts. It has to extends QWT's Server class and may override various methods. In particular, you can override createClient, which is called whenever a new client starts. The object returned by this method is passed to constructor of the client main class.
The client main class is normal qooxdoo application main class, except that it's written in Java. If the server passes an argument to clients, it's passed to the client main constructor.
Example
The Server Main class overrides createClient to pass a Grep object to every client. Since Grep is a service object, it's passed by reference, thus, clients receive a proxy object. Note that every client has gets a new Grep instance, so there's no need for synchronizing Grep. Alternatively, you could create a single synchronized Grep instance in the constructor and pass this very instance to all clients.
The Client main class has a one-argument constructor receiving the proxy sent by the server.
The proxy is stored in the grep field an used in the notify method to call grep on the server.
Configuration
To run client-server applications, QWT needs to know the server main class, the client main class, and the client classes. All this is specified the pom file of your application, by the lines marked here with '-':
<plugin>
<groupId>org.qooxdoo.toolkit</groupId>
<artifactId>plugin</artifactId>
<version>${toolkit.version}</version>
- <configuration>
- <client>com.foo.bar.client.Main</client>
- <includes>**/*.java</includes>
- <excludes>**/server/**/*.java</excludes>
- <server>com.foo.bar.server.Main</server>
- </configuration>
<executions>
<execution>
<goals>
<goal>war</goal>
</goals>
</execution>
</executions>
</plugin>
clientspecifies the client main class. This field is optional, if not specified, QWT uses the applications group- and artifact id and appendsclient.Main. (Or more precisely: the default value is${project.groupId}.${project.artifactId}.client.Main)
serverspecifies the server main class. This field is optional, if not specified, QWT uses the applications group- and artifact id and appendsserver.Main. (Or more precisely: the default value is${project.groupId}.${project.artifactId}.server.Main)
includesandexcludesspecify the client classes which will be translated to JavaScript. These fields are optional, if not specified, QWT translates all classes except* */server/* */*.java.