Custom Protocol Handling
Resin 3.0

Features
Installation
Configuration
Web Applications
IOC/AOP
Resources
JSP
Quercus
Servlets and Filters
Databases
Admin (JMX)
CMP
EJB
Amber
EJB 3.0
Security
XML and XSLT
XTP
JMS
Performance
Protocols
Third-party
Troubleshooting/FAQ

Hessian
Hessian Messaging
Hessian 1.0 spec
Hessian 2.0 draft spec
Java Binding
Burlap
Burlap 1.0 Spec
Burlap Design Notes
Tutorials

Hessian Addition
Service Addition
Hessian with DI
Burlap Addition
custom-protocol
Burlap Addition
Tutorials
Third-party

Find this tutorial in: /usr/local/resin/webapps/resin-doc/protocols/tutorial/custom-protocol

This tutorial shows the usage of the Resin server architecture to handle a custom protocol. Resin handles the TCP connections, multi-threading, and the request object pooling. The application implements a class that reads from a stream and writes to a stream.

Professor Trelawny once got a student to make a Magic8Ball, used for prophecy. Originally it was used with a simple web interface. Now Trelawny wants to provide a magic8ball protocol server on the Hogwart's public web server. The magic8ball protocol is at the same level as http or smtp, it sit's directly on top of TCP/IP.

  1. Files in this tutorial
  2. The advantage of using Resin's server architecture
  3. com.caucho.server.port.Protocol
  4. com.caucho.server.port.ServerRequest
  5. Deployment and Configuration
  6. Demo
    1. 1. find the tutorial
    2. 2. add the entry to resin.conf
    3. 3. build and deploy the library
    4. 4. start Resin or wait for a restart
    5. 5. test using telnet
  7. Compatibility

Files in this tutorial

src/example/Magic8Ball.java The orignal Magic8Ball class, it knows nothing about the code that uses it.
src/example/Magic8BallProtocol.java The implementation of class com.caucho.server.port.Protocol that Resin uses to create a Request object.
src/example/Magic8BallRequest.java The implementation of interface class com.caucho.server.port.ServerRequest that represents a single request.
src/example/Parser.java protocol-specific parsing class to parse the ReadStream and determine commands.
src/example/AbstractCommand.java protocol-specific abstract base class for commands.
src/example/SetProphetCommand.java protocol-specific Command to set the prophet to use for the magic 8 ball.
src/example/AskCommand.java protocol-specific Command to ask the magic 8 ball for a prophecy.
build.xml Ant build file

The advantage of using Resin's server architecture

Implementation of a protocol at the level that magic8ball is at normally requires a large amount of bug-prone code, such as code for handling tcp/ip connections, multi-threading, and request object reuse. Resin provides a simple way to handle protocols without requiring the implementation of that code.

Applications take advantage of Resin's highly tested and configurable server architecture and only need to implement some simple objects. Two classes are required, one which extends the abstract class class com.caucho.server.port.Protocol and one which implements the interface class com.caucho.server.port.ServerRequest

.

com.caucho.server.port.Protocol

A custom class derived from class com.caucho.server.port.Protocol is a factory that produces custom Request objects.

The Protocol passes a class com.caucho.server.connection.Connection object to the Request object it creates. The Request object uses the Connection to obtain a read stream and a write stream.

The example in this tutorial is simple, it's main purpose is to override the createRequest method to return an instance of the example.Magic8BallRequest request handling object.

See it in: src/example/Magic8BallProtocol.java
  ...

  /**
   * Create a Magic8BallRequest object for the new thread.
   */
  public ServerRequest createRequest(Connection conn)
  {
    return new Magic8BallRequest(this, conn);
  }

  ...

It also stores and returns the protocol name.

See it in: src/example/Magic8BallProtocol.java
  private String _protocolName = "magic8ball";

  ...

  /**
   * Return the protocol name.
   */
  public String getProtocolName()
  {
    return _protocolName;
  }
  
  /**
   * Set the protocol name.
   */
  public void setProtocolName(String name)
  {
    _protocolName = name;
  }

com.caucho.server.port.ServerRequest

class com.caucho.server.port.ServerRequest is the interface used for implementing the class that handles a request. This is where the bulk of the work is done.

The method handleRequest() is called for each request. Implementations should not assume that a new request object is created for each new connection from a client. Resin may reuse a request object once it has finished handling a request. It is guaranteed, however, that the handleRequest() method of a given instance will only be handling a single request at a time.

This means that any member variables must be initialized at the beginning of the code in handleRequest().

The first step in handleRequest() is usually to obtain a class com.caucho.vfs.ReadStream and a class com.caucho.vfs.WriteStream from the Connection that was stored with the constructor. These streams are used to read raw data from the client and write raw data to the client.

This tutorial then goes on to initialize a parser and perform appropriate commands, depending on what is submitted by the client. The readStream is used to determine what to do, and the writeStream is used to send the response. The implementation here depends on the protocol that is being supported.

See it in: src/example/Magic8BallRequest.java
  ...

  /**
   * Handle a new connection.  The controlling Server may call
   * handleRequest again after the connection completes, so the
   * implementation must initialize any variables for each connection.
   */
  public boolean handleRequest() throws IOException
  {
    ReadStream readStream = _conn.getReadStream();
    WriteStream writeStream = _conn.getWriteStream();

    try {
      _parser.init(readStream);

    ...

        if (error != null) {
          writeStream.print("ERROR: ");
          writeStream.println(_parser.getError());
          break;
        }
        else if (result != null) {
          writeStream.print("RESULT: ");
          writeStream.println(result);
        }

    ...

    } catch (Throwable e) {
      log.log(Level.WARNING, e.toString(), e);
    }

    return false;
  }

Deployment and Configuration

The protocol is handled at the server level in the Resin Environment hierarchy. This means that the code has to be available to the appropriate classloader. A jar with your code in it can be placed in $RESIN_HOME/lib/, for example $RESIN_HOME/lib/magic8ball.jar.

The use of the Protocol class is configured with <port> and <protocol> .

Custom protocol configuration in resin.conf
  <server>

    ...

    <!-- The magic8ball port -->
    <port id='' host='*' port='8888'>
      <protocol resin:type="example.Magic8BallProtocol"/>
    </port>

Demo

This tutorial has to be deployed by hand in your installation of Resin.

1. find the tutorial

The filesystem location of this tutorial is at the top of this page.

2. add the entry to resin.conf

Custom protocol configuration in resin.conf
  <server>

    ...

    <!-- The magic8ball port -->
    <port id='' host='*' port='8888'>
      <protocol resin:type="example.Magic8BallProtocol"/>
    </port>

3. build and deploy the library

To run the demo, you can find the build/magic8ball.jar file and copy it to $RESIN_HOME/lib/.

An ant build file is provided. Calling "ant deploy" will build the jar and deploy it in $RESIN_HOME/lib/magic8ball.jar

4. start Resin or wait for a restart

Start Resin, or if it is already running wait for it to notice the new library and restart. The default Resin configuration will notice the new jar file (eventually) and restart itself. If you are impatient you can stop and start the server yourself.

Example output from Resin showing the magic8ball protocol port
Starting Resin on Tue, 23 Sep 2003 15:40:16 -0500 (GMT-05:00)
[15:40:17.920] Loaded Socket JNI library.
[15:40:38.871] http listening to *:8080
[15:40:59.831] magic8ball listening to *:8888
...

5. test using telnet

The simplest way to test a protocol like the magic8ball is to use telnet.

Telnet test of the magic8ball protocol
$ telnet localhost 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
ASK
RESULT: trelawney says "The future for you is very dire"
ASK
RESULT: trelawney says "You must not leave the room"
SET-PROPHET classic
RESULT: prophet set to `classic'
ASK
RESULT: classic says "It is certain"
SET-PROPHET kingwu
RESULT: prophet set to `kingwu'
ASK
RESULT: kingwu says "THE CREATIVE works sublime success, Furthering through perseverance."
SET-PROPHET xxxxx
RESULT: prophet set to `xxxxx'
ASK
RESULT: xxxxx says "a false prophet is never wise"
QUIT
ERROR: Unknown command `QUIT'
Connection closed by foreign host.

Compatibility

The class com.caucho.server.port.Protocol and class com.caucho.server.port.ServerRequest classes are of course Resin specific. The class com.caucho.vfs.ReadStream and class com.caucho.vfs.ReadStream are as well.

If portability is a concern, implement a class that is similar in functionality to class com.caucho.server.port.ServerRequest . Code that class to get passed a class java.io.InputStream and a class java.io.OutputStream . A bare-bones Resin-specific Protocol and ServerRequest can be used as a thin wrapper to your custom Request class, and the Resin's ReadStream and WriteStream can be passed as the InputStream and OutputStream. When porting to another application server (if they provide this kind of functionality) you can code a thin-wrapper specific to that app-server that uses your custom Request class.


Burlap Addition
Tutorials
Third-party
Copyright © 1998-2006 Caucho Technology, Inc. All rights reserved.
Resin® is a registered trademark, and HardCoretm and Quercustm are trademarks of Caucho Technology, Inc.