OpenBaseMovil
Welcome at » Using the networking library openbasemovil-net (II)

Using the networking library openbasemovil-net (II)

We will now address how to create a servlet to handle RemoteCalls from our device.

The first piece of code is a very generic servlet that takes the binary data, handles it to another class, and sends back the result. That second class is the one that will do the parsing. This way is easier to perform unit testing, for example.

Here is the code for the servlet:

/*
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see .
*/
 
public class RPCServlet
    extends HttpServlet
{
    private String infoPage;
 
    public void init( final ServletConfig config )
    {
        infoPage = config.getInitParameter( "informationPage" );
    }
 
    protected void doGet(
            final HttpServletRequest    request,
            final HttpServletResponse response
    )
            throws ServletException,
                   IOException
    {
        request.getRequestDispatcher( infoPage ).forward( request, response );
    }
 
    protected void doPost(
            final HttpServletRequest     request,
            final HttpServletResponse   response
    )
            throws ServletException,
                   IOException
    {
        processRequest( request, response );
    }
 
    private void processRequest(
            final HttpServletRequest request,
            final HttpServletResponse   response
    )
            throws ServletException
    {
        try
        {
            // Read all the data into a byte array
            final BufferedInputStream bis = new BufferedInputStream(
                    request.getInputStream()
            );
            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int c = bis.read();
            while( c != -1 )
            {
                baos.write( c );
                c = bis.read();
            }
            final byte[] data = baos.toByteArray();
 
            // Handle the data to the RPCBroker class
            final byte[] result = RPCBroker.handle( data );
 
            // Return the result to the device
            response.setContentType( "application/octet-stream" );
            response.setContentLength( result.length );
            response.getOutputStream().write( result );
        }
        catch( Exception e )
        {
            throw new ServletException( e );
        }
    }
}
 

Now the RPCBroker has to parse the data and make the actual call. We will not be doing here the call, you can do what suits you better: from a simple if-else structure to calling objects using reflection. If you use reflection, please be careful that you allow calls to the objects you want to be called and not a generic approach, since that could lead to big security problems.

 
/*
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see .
*/
public class RPCBroker
{
    private static Log log = LogFactory.getLog( RPCBroker.class );
 
    public static final int RC_SUCCESS              = 0;
    public static final int RC_INVALID_OBJECT       = 1;
    public static final int RC_INVALID_METHOD       = 2;
    public static final int RC_NOT_ALLOWED          = 3;
    public static final int RC_INVALID_ARGUMENTS    = 4;
    public static final int RC_INVOKATION_EXCEPTION = 5;
    public static final int RC_INVALID_CRC          = 6;
 
    public static byte[] handle( final byte[] data )
        throws IOException,
               SerializationException
    {
        if( data == null )
        {
            return sendError( RC_INVALID_CRC, "Null data" );
        }
        try
        {
            final ByteArrayInputStream bais = new ByteArrayInputStream( data );
            final ChecksumedInputStream cis = new ChecksumedInputStream( bais );
            final SerializerInputStream in = new SerializerInputStream( cis );
            final byte      version     = in.readByte();
            final byte      encryption  = in.readByte();
            final String    deviceGuid  = in.readString();
            final long      deviceId    = in.readLong();
            final String    objectName  = in.readString();
            final String    methodName  = in.readString();
            final Object[]  parameters;
            try
            {
                parameters = (Object[]) in.readNullableObject();
            }
            catch( SerializationException e )
            {
                throw new IOException( e.getMessage() );
            }
            final long crc = cis.getCRC();
            final String    endOfData   = in.readString();
            if( endOfData.equals( "END_OF_DATA" ) )
            {
                final long originalCRC = in.readLong();
                if( originalCRC != crc )
                {
                    return sendError( RC_INVALID_CRC, "Invalid CRC" );
                }
                else
                {
                    return invoke(
                            version,
                            deviceGuid,
                            deviceId,
                            objectName,
                            methodName,
                            parameters
                    );
                }
            }
            else
            {
                return sendError( RC_INVALID_ARGUMENTS, "Wrong signature" );
            }
        }
        catch( SerializationException e )
        {
            return sendError( RC_INVALID_CRC, e.getMessage() );
        }
    }
 
    private static byte[] invoke(
            final byte      version,
            final String    deviceGuid,
            final long      deviceId,
            final String    objectName,
            final String    methodName,
            final Object[]  parameters
    )
            throws IOException,
                   SerializationException
    {
        // Here is where you actually look up the object
        // make the call and return the value
 
        Object retval;
        /*
        1. Lookup object
 
        if object is not found: return sendError( RC_INVALID_OBJECT, "error message" );
 
        2. Check security, if applicable
 
        if not allowed to call: return sendError( RC_NOT_ALLOWED, "error message" );
 
        3. Make the call
 
        store the return value in the retval variable.
        if an error raises: return sendError( RC_INVOKATION_EXCEPTION, "error message" );
        */
 
        // You should perform the appropriate try-catch
        // The following code should be placed into a try block,
        // when the call has been successful
       final ByteArrayOutputStream baos = new ByteArrayOutputStream();
       final SerializerOutputStream out = new SerializerOutputStream( baos );
       out.writeInt( RC_SUCCESS );
       out.writeString( "OK" );
       out.writeNullableObject( retval );
       out.flush();
       return baos.toByteArray();
    }
 
    private static byte[] sendError( final int code, final String message )
            throws IOException,
                   SerializationException
    {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        final SerializerOutputStream out = new SerializerOutputStream( baos );
        out.writeInt( code );
        out.writeString( message != null ? message : "" );
        out.flush();
        return baos.toByteArray();
    }
}
 

With this second article you should have almost everything you need to perform remote calls.

Please, leave your comments and suggestions.

BlogLines del.icio.us Digg Facebook Google Google Reader Yahoo! MyWeb Newsgator Newsvine reddit SlashDot StumbleUpon Technorati

Leave a Reply

You must be logged in to post a comment.