This is how we currently handle our client / server communication. We are using a stateless JavaEE architecture and provide everything as REST services. This allows us to scale horizontally by adding more servers and simply adding them to the cluster entry in the Glassfish config.
However due to missing reflection or a working JSON lib, it forces us to implement a toJson()method in every data transfer object BY HAND (be carfull with case sensitivity). We added our server project as a module to the PlayN metaproject and added the core as a dependency to the server. So you can place every DTO on the core project (which is quite awesome). Placing them on the server results in people trying to annotate the classes as entities, which will result in failure during the html build. I am currently thinking about adding a shared project to the build, like in GWT projects.
This is how we send data to the server (for this example without any abstraction, do yourself a favor and implement one):
private void loadMapFromServer() {
SessionDto session = this.main.getSessionCtrl().getSessionMdl();
PlayN.net().post("http://localhost:8080/server/rest/map/mapMdl",
session.toJson(),
new Callback<String>() {
@Override
public void onSuccess(String result) {
mapMdl = new MapDto();
}
@Override
public void onFailure(Throwable cause) {
PlayN.log().error("fail " + cause.getMessage());
}
});
}
Notice how we bastardize the POST argument with session.toJson(). This is because of the missing MIME type feature and passing JSON strings via GWT will fail.
And this is how it looks on the server:
package com.fact.server;
//I also posted the imports, for clarity.
import com.fact.core.map.MapDto;
import com.fact.core.user.SessionDto;
import com.fact.server.game.map.MapCtrl;
import com.google.gson.Gson;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.log4j.Logger;
@Stateless
@Path("/map")
public class MapSrvs {
@Inject
Logger logger;
@Inject
MapCtrl mapCtrl;
Gson gson = new Gson();
@POST
@Path("/mapMdl/")
@Produces( MediaType.APPLICATION_JSON )
public MapDto getMapMdl(String sessionDtoStr) {
//logger.info("map was requested: " + sessionDtoStr);
// Use gson to deserialize the class. Jersey would do this, if PlayN
// would allow to set a correct MIME type (and @Consumes was set).
// We could simply write: getMapMdl(SessionDto sessionDto)
SessionDto sessionDto = gson.fromJson(sessionDtoStr, SessionDto.class);
if(sessionDto == null) {
return null;
}
// [...] check for a valid session
MapDto mapMdl = new MapDto();
mapMdl.foo = "message from server";
return mapMdl;
}
}
This would be a bit nicer, if PlayN would allow you to set the MIME type, so that you won't have to cast the String using Gson. However this works quite well. We use Jersey on the server side to handle all the REST stuff. To get it running I had to put the following into the web.xml:
<servlet>
<!-- We need jersey for the REST stuff -->
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.fact.server</param-value>
</init-param>
<init-param>
<!-- This took me hours to find :(. It is needed to automatically
map POJOs to JSON code. -->
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Also Jersey is already included into the Java-EE-Api, so it works out of the box. I hope this helps. If anyone knows how to do client side JSON parsing , please look at this question: How do I convert a POJO to JSON in PlayN?