Tag Archives: javascript

First communication between Java and node.js

My first feature communication between the Java client and node.js server project focused on bringing data of CHARACTER_SPRITE table. Until then, attributes such as “number of steps”, “current step”, “speed” were obtained from properties files. Now they are coming directly from the database through a socket. I’ll explain here how it is built.

Plan to maintain only two node.js scripts: server.js and controller.js. The first will receive the socket connections, open communication with MySQL and encapsulate all in a domain (or try/catch) to handle errors. In future it will also have a TLS (SSL) layer on the socket to encrypt the data, but so far not implemented this. See below server.js code.

var net = require('net');
var mysql =  require('mysql');
var d = require('domain').create();
var controller = require('./controller');

var HOST = '127.0.0.1';
var PORT = 8000;
var TERMINAL_CHAR = '\n';
var SEPARATOR_CHAR = '#';

var pool =  mysql.createPool({
    host : '127.0.0.1',
      user : 'myadmin',
      password: 'mypass',
    database: 'vectortown'
  });

process.on('uncaughtException', function(err) {
    // handle the error safely
    console.log(err);
    console.log('Server listening on ' + HOST +':'+ PORT);
});

d.on('error', function(err){
    console.log(err);
    console.log('Server listening on ' + HOST +':'+ PORT);
});


// catch the uncaught errors in this asynchronous or synchronous code block
d.run(function(){

    // Create a server instance
    net.createServer(function(sock) {
    
        // We have a connection - a socket object is assigned to the connection automatically
        console.log('CONNECTED: ' + sock.remoteAddress +':'+ sock.remotePort);
        
        // Add a 'data' event handler to this instance of socket
        sock.on('data', function(data) {
            
            console.log('Data received by ' + sock.remoteAddress );
            
            var full_msg = data.toString();
            var array_msg = full_msg.split( SEPARATOR_CHAR );
            
            var command = array_msg[0];
            var msg = array_msg[1];
                    
            // Run controller
            controller[ command ]( pool , msg , function( result ) {
                sock.write( result + TERMINAL_CHAR );
            });
                        
        });
        
        // Add a 'close' event handler to this instance of socket
        sock.on('close', function(data) {
            console.log('CLOSED');
        });
    
    }).listen(PORT, HOST);

    console.log('Server listening on ' + HOST +':'+ PORT);

});

Note that I do not write here (hard coded) the name of the controller methods. They will be referenced by a string that comes from the Java application. The command will be divided into two elements separated by a #: the code to indicate the command and any parameters. Like this:

C0001#5

In this example, I separate them by # and call the C0001 method with the parameter 5. The code controller.js keep the declaration of all methods of communication with MySQL. See the code below:

module.exports =
{
    C0001: function ( pool , msg , callback ) {
        
        var query = 'SELECT FLOOR.HEIGHT as \'height\', SPRITE.CODE as \'code\', ' + 
            'CHARACTER_SPRITE.DIRECTION as \'direction\', CHARACTER_SPRITE.STEP as \'step\', ' + 
            'CHARACTER_SPRITE.NUMBER_STEPS as \'numberSteps\', CHARACTER_SPRITE.SPEED as \'speed\', ' + 
            'SPRITE.FLOOR_CODE as \'floorCode\', SPRITE.TILE_CODE as \'tileCode\', SPRITE.X as \'x\', ' + 
            'SPRITE.Y as \'y\', SPRITE.IMAGE_CODE as \'imageCode\', SPRITE.BLOCKED as \'blocked\' ' + 
            'FROM CHARACTER_SPRITE INNER JOIN SPRITE ' + 
            'ON CHARACTER_SPRITE.SPRITE_CODE = SPRITE.CODE ' + 
            'INNER JOIN POSITION_TILE ' +
            'ON SPRITE.FLOOR_CODE = POSITION_TILE.FLOOR_CODE AND ' +
            'SPRITE.TILE_CODE = POSITION_TILE.TILE_CODE AND ' +
            'SPRITE.X = POSITION_TILE.X AND SPRITE.Y = POSITION_TILE.Y ' +
            'INNER JOIN FLOOR ' +
            'ON POSITION_TILE.FLOOR_CODE = FLOOR.CODE ' +
            'WHERE SPRITE.CODE = ' + msg;
        
        pool.getConnection(function(err, connection){
            connection.query( query ,  function( err , rows , fields ){
                if(err)    {
                    throw err;
                } else {
                    if ( rows.length == 1 ) {
                        callback( JSON.stringify( rows[0] ) );
                    } else {
                        callback( null );
                    }
                }
            });
            connection.release();
        });
    }
    
}

Ok, the query is big. Do not worry, it simply loads the data needed for the Java model.

The application models are on the base project (there are three Java projects: base, client and server) and will be consumed by other projectsMeanwhile I kept hibernate layer for communication with MySQL. This layer is formed only by anemic classes annotated with @Entity. As this layer will only be used in server project, the performance requirement is not as essential as the client project. 

There is also a model that will make communication with the Super-Server node.js. As the node.js returns a JSON, I preferred to create model classes (like DTOs) who will receive the JSON data through a conversion by the framework Jackson JSON Processor. Below is the class that receives the JSON implemented in C0001 method described above.

package br.com.linu.vectortown.base.model.json;
import java.io.Serializable;
import org.codehaus.jackson.annotate.JsonIgnore;
import br.com.linu.vectortown.base.model.enumeration.DirectionEnum;

public class SpriteJson implements Serializable {

 @JsonIgnore
 private static final long serialVersionUID = -1421845826361208213L;

 private int height;
 private Long code;
 private DirectionEnum direction;
 private int step;
 private int numberSteps;
 private int speed;
 private Long floorCode;
 private Long tileCode;
 private int x;
 private int y;
 private Long imageCode;
 private int blocked;
 
 public SpriteJson() {}
 
 public int getHeight() { return height; }
 public void setHeight(int height) { this.height = height; }
 public Long getCode() { return code; }
 public void setCode(Long code) { this.code = code; }
 public DirectionEnum getDirection() { return direction; }
 public void setDirection(DirectionEnum direction) { this.direction = direction; }
 public int getStep() { return step; }
 public void setStep(int step) { this.step = step; }
 public int getNumberSteps() { return numberSteps; }
 public void setNumberSteps(int numberSteps) { this.numberSteps = numberSteps; }
 public int getSpeed() { return speed; }
 public void setSpeed(int speed) { this.speed = speed; }
 public Long getFloorCode() { return floorCode; }
 public void setFloorCode(Long floorCode) { this.floorCode = floorCode; }
 public Long getTileCode() { return tileCode; }
 public void setTileCode(Long tileCode) { this.tileCode = tileCode; }
 public int getX() { return x; }
 public void setX(int x) { this.x = x; }
 public int getY() { return y; }
 public void setY(int y) { this.y = y; }
 public Long getImageCode() { return imageCode; }
 public void setImageCode(Long imageCode) { this.imageCode = imageCode; }
 public int getBlocked() { return blocked; }
 public void setBlocked(int blocked) { this.blocked = blocked; }   

}

The JSON returned from node.js will be transformed into SpriteJson class with a command like this:

SpriteJson spriteJson = (SpriteJson) ConverterJson.INSTANCE.from(json).to( SpriteJson.class );

I encapsulated the logic conversion org.codehaus.jackson in a class with “fluent interface”, but we will not discuss the features of the Jackson JSON Processor here. Now I will show the class that create and maintain the socket with node.js.

package br.com.linu.vectortown.client.infrastructure;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.net.Socket;
import org.apache.log4j.Logger;
import br.com.linu.vectortown.base.model.json.FloorAndTileJson;
import br.com.linu.vectortown.base.util.ConverterJson;
import br.com.linu.vectortown.base.util.PropertiesUtil;
import br.com.linu.vectortown.client.infrastructure.enumeration.TransCodeEnum;
import br.com.linu.vectortown.client.infrastructure.exception.CommunicationServerException;

public class SuperClient implements Serializable {
 
 private static final long serialVersionUID = -6376764477013637988L;

 private static final Logger LOGGER = Logger.getLogger(SuperClient.class);
 
 public static final SuperClient INSTANCE = new SuperClient();
 
 private int port;
 private String host;
 private Socket socket;
 
 private BufferedWriter writer;
 private BufferedReader reader;
 
 private SuperClient() {
   port = PropertiesUtil.INSTANCE.inteiroPor("game.server.port");
   host = PropertiesUtil.INSTANCE.valorPor("game.server.host");
 }
 
 public void open() { 
   try {
     socket = new Socket( this.host , this.port );
     writer = new BufferedWriter( new OutputStreamWriter( socket.getOutputStream() ) );
     reader = new BufferedReader( new InputStreamReader( socket.getInputStream() ) );
     LOGGER.info( "Open server connection." );
   } catch (Exception e) {
     LOGGER.error( e.getMessage() , e );
     throw new CommunicationServerException( e );
   }
 }
 
 public void close() { 
   try {
     socket.close();
     LOGGER.info( "Close server connection." );
   } catch (Exception e) {
     LOGGER.error( e.getMessage() , e );
     throw new CommunicationServerException( e );
   }
 }
 
 private void send(String message) {
   try {
     writer.write(message);
     writer.flush();
   } catch (Exception e) {
     LOGGER.error( e.getMessage() , e );
     throw new CommunicationServerException( e );
   }
 }
 
 private String recieve() {
   try {
     return reader.readLine(); 
   } catch (IOException e) {
     LOGGER.error( e.getMessage() , e );
     throw new CommunicationServerException( e );
   }
 }
 
 public String recieve(String command) {
   this.send( command ); 
   return recieve();
 }
 
}

To receive data from node.js server, we can run something like:

 SuperClient.INSTANCE.open();
 // Get sprite with code 5
 String json = SuperClient.INSTANCE.recieve( TransCodeEnum.C0001 + TransCodeEnum.getSeparatorChar() + 5 );
 System.out.println( json );
 SpriteJson spriteJson = (SpriteJson) ConverterJson.INSTANCE.from(json).to( SpriteJson.class );
 System.out.println( spriteJson.getCode() );
 SuperClient.INSTANCE.close();

This code will generate the following text at the prompt:

{"height":1,"code":5,"direction":"SOUTH_EAST","step":0,"numberSteps":7,"speed":100,"floorCode":1200000001000000000,"tileCode":504,"x":1,"y":9,"imageCode":500000000,"blocked":1}
5

The node.js server will log:

C:\java\workspace\vectortown\super-server>node server.js
Server listening on 127.0.0.1:8000
CONNECTED: 127.0.0.1:51541
Data received by 127.0.0.1
CLOSED

Besides the anemic domain JSON and Hibernate, I have a rich model with well-defined attributes and behaviors. In the server project, the data will be read by the MySQL layer and Hibernate entities to be loaded later in the rich model. In the client project data will be read from node.js converting JSON in anemic model and then be loaded in the rich model. You may be finding it a bit complex, but actually I prefer to keep my rich model exclusively to treat the rules of the game without spoiling them with annotations or getters and setters required for a reading from the database.

Good staff, that is. While I am writing this post my SuperServer node.js has other implemented methods, but these are basically similar to what I showed you. In the future there will be a more interesting method in which the node.js script will execute a stored procedure MySQL with various actions directly in the database. This will help me to reduce the access time between the node.js server and the database server, but I’ll leave those details for another conversation.

Thanks for reading. Bye!