Monday, January 26, 2015

How to use DTO Factory in Eclipse Che

DTO

Data transfer objects are used in Che to do the communication between client and server. In a code level, this is just an interface annotated with @DTO (com.codenvy.dto.shared.DTO). This interface should contain getters and setters (with bean naming conventions) for each and every fields that we need in this object.

For example, following is a DTO with a single String field.

@DTO
public interface HelloUser {

    String getHelloMessage();

    void setHelloMessage(String message);
}

By convention, we need to put these DTOs to shared package as it will be used by both client and server side.

DTO Factory

DTO Factory is a factory available for both client and server sides, which can be used to serialize/deserialize DTOs. DTO factory internally uses generated DTO implementations (described in next section) to get this job done. Yet, it has a properly encapsulated API and developers can simply use DTOFactoy instance directly.

For client side   : com.codenvy.ide.dto.DtoFactory  
For server side : com.codenvy.dto.server.DtoFactory


HelloUser helloUser = DtoFactory.getInstance().createDto(HelloUser.class);
Initializing a DTO

Above code snippet shows how to initialize a DTO using DTOFactory. As mentioned above, proper DtoFactory classes should be used by client or server sides.

Deserializing in client side

import com.codenvy.ide.rest.AsyncRequestCallback;
import com.codenvy.ide.rest.DtoUnmarshallerFactory;
import com.codenvy.ide.rest.Unmarshallable;
      
Unmarshallable<HelloUser> unmarshaller =
                                unmarshallerFactory.newUnmarshaller(HelloUser.class);
      
helloService.sayHello(sayHello, new AsyncRequestCallback<HelloUser>(unmarshaller) {
           @Override
           protected void onSuccess(HelloUser result) {
                ….
           }

           @Override
           protected void onFailure(Throwable exception) {
                ….
           }
});
Deserializing in client side

When invoking a service that returns a DTO, client side should register a callback created using relevant unmarshaller factory. Then, the on success method will be called with a deserialized DTO.

Deserializing in server side

      @Path("world")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces("application/json")
    @POST
    public ... sayHello(SayHello sayHello){
            ….
             sayHello.getHelloMessage() ...
            ….
    }
Deserializing in server side     
                                               
Everest (JAX-RS implementation of Che) implementation automatically deserialize DTOs when they are used as parameters in rest services. It will identify serialized DTO with marked type -  @Consumes(MediaType.APPLICATION_JSON) - and use generated DTO implementations to deserialize DTO.

DTO maven plugin

As mentioned earlier, for DtoFactoy to function properly, it needs some generated code that will contain concrete logic to serialize/deserialize DTOs. GWT compiler should be able to access generated code for client side and generated code for server side should go in jar file.

Che uses a special maven plugin called “codenvy-dto-maven-plugin” to generate these codes. Following figure illustrates a sample configuration of this plugin. It contains separate executions for client and server sides.
We have to input correct package structures accordingly and file paths to which these generated files should be copied.

<groupId>com.codenvy.platform-api</groupId>
<artifactId>codenvy-dto-maven-plugin</artifactId>
<version>${codenvy.platform-api.version}</version>
<executions>
    <execution>
       <id>generate-client</id>
       <phase>process-sources</phase>
       <goals>
           <goal>generate</goal>
       </goals>
       <configuration>
           <dtoPackages>
                   <package>org.wso2.dss.shared.dto</package>
           </dtoPackages>
           <outputDirectory>${dto-generator-out-directory}</outputDirectory>
           <genClassName>org.wso2.dss.client.DtoClientImpls</genClassName>
           <impl>client</impl>
       </configuration>
    </execution>
    <execution>
       <id>generate-server</id>
       <phase>process-sources</phase>
       <goals>
           <goal>generate</goal>
       </goals>
       <configuration>
           <dtoPackages>
                   <package>org.wso2.dss.shared.dto</package>
           </dtoPackages>
           <outputDirectory>${dto-generator-out-directory}</outputDirectory>
           <genClassName>org.wso2.dss.server.DtoServerImpls</genClassName>
           <impl>server</impl>
       </configuration>
    </execution>
</executions>
<dependencies>
    <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>${project.artifactId}</artifactId>
       <version>${project.version}</version>
    </dependency>
    <!--
    Other dependencies if DTOs from current project need them.
    -->
</dependencies>

package - package, in which, DTO interfaces resides
outputDirectory -  directory, to which, generated files should be copied
genClassName - class name for the generated class

You should also configure your maven build to use these generated classes as a resource when compiling and packaging. Just add following line in resources in build section.

<resource>
           <directory>${dto-generator-out-directory}</directory>
</resource>