SDSU CS 580 Client-Server Programming
Fall Semester, 2002
Some Parsing & States
Previous    Lecture Notes Index    Next    
© 2002, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 29-Oct-02

Contents of Doc 20, Some Parsing & States


References

Design Patterns: Elements of Reusable Object-Oriented Software , Gamma, Helm, Johnson, Vlissides, Addison-Wesley, 1995

Past CS580 lecture notes


Doc 20, Some Parsing & States Slide # 2

Server Structure


Servers perform a number of standard operations


How some operations are done are



Isolate these operations to make it easier to



Doc 20, Some Parsing & States Slide # 3
Parsing & Messages

A client sends a request to a server

The server



Yet we have:

InputStream in = aSocket.getInputStream();
Stringbuffer message = new StringBuffer();
while (some end condition)
   { 
   int c = in.read();
   if (c != -1)
      message.append( (char) c );
   }
Why not

PopReadStream in = new PopStream( aSocket);
PopRequest clientRequest = in.read();


Doc 20, Some Parsing & States Slide # 4

Request & Response Classes


Request & Response classes for your protocol:

Isolate the syntactic details of the protocol

Allow client & server to deal with higher-level structures

aResponse.toString()
aResponse printString
Format the response according to the protocol

Given a message string in the protocol create correct object

   new PopResponse( aPopMessageString );
   PopResponse fromString: aPopMessageString)

What other operations should they have?


Doc 20, Some Parsing & States Slide # 5

High-level Streams


Create Stream classes that read/write messages not chars

So client does:

PopWriteStream out = new PopWriteStream( aSocket);
PopReadStream in = new PopReadStream( aSocket);
PopRequest user = PopRequest.user( “whitney”);
out.write( user);
PopResponse result = in.read();
If (result.isSuccess() )
   {
   out.write( PopRequest.pass( “I forget”));
   }
Server does

clientIO := PopReadWriteStream on: aSocket
request := clientIO next.
request isPassword 
   ifFalse: [clientIO nextPut: (PopResponse error)]



Doc 20, Some Parsing & States Slide # 6
POP & Parsing POP Requests

Very regular & easy to parse

keyword blank argument 1 [ blank argument k ] CRLF

crlf := String with: Character cr with: Character lf.
mesageString := inputStream upToAndSkipThroughAll: crlf.
messageParts := messageString tokensBasedOn: Character cr.
messageString = aBufferedReader.readline();
messageParts = messageString.split( “ “); 

POP Responses

Two different formats

Status  keyword  additionalInfo CRLF

Status  keyword  additionalInfo CRLF
Line1 CRLF
Line2 CRLF
etc
LineN CRLF
.CRLF
How do you know which format you are reading?


Doc 20, Some Parsing & States Slide # 7

States


Some Servers are stateful

Each connection has different states

Some commands are only legal in some states

How to deal with states?



Doc 20, Some Parsing & States Slide # 8

Finite Automata - State Machines


A better way of looking at all of this is using a picture.


Naming the states


We will use the following names for the states:

0
NoAuth
1
HaveUser
2
Process
3
Invalid
4
Quit


Doc 20, Some Parsing & States Slide # 9

Implementing a State Machine: switch


int state = 0;
while (true) {
   command = input.read();
   switch (state) {
      case 0:
         if (command.isUser()) {
            username = command.argument();
            state = 1;
         }
         else if (command.isQuit())
            state = 4;
         else
            error("Illegal command: " + command);
         break;
      case 1:
         if (command.isPassword()) {
            if (valid(username, command.argument()))
               state = 2;
            else {
               error("Unauthorized User");
               state = 3;
            }
         }
         else
            error("Unknown: " + command);
         break;
...


Doc 20, Some Parsing & States Slide # 10
More Readable version
int state = 0;
while (true) {
   command = input.read();
   switch (state) {
      case NO_AUTH:
         noAuthorizationStateHandle( command );
         break;
      case HAVE_USER:
         haveUserStateHandle( command );
         break;
      case PROCESS:
         processStateHandle( command );
         break;
      case INVALID:
         invalidStateHandle( command );
         break;
      case QUIT:
         quitStateHandle( command );
         break;
   }


Doc 20, Some Parsing & States Slide # 11
Example Continued

void noAuthorizationStateHandle(PopCommand a Command) {
   if (command.isUser()) {
      username = command.argument();
      state = 1;
   }
   else if (command.isQuit())
      state = 4;
   else
      error("Illegal command: " + command);
}


Doc 20, Some Parsing & States Slide # 12
Switch Method Analysis

Disadvantages


Need the state machine picture to understand what is going on.


If the protocol (and therefore the state machine) changes, the code will most likely have to be rewritten.




Advantages:



Doc 20, Some Parsing & States Slide # 13

Implementing a State Machine: Objects

The Basic Idea



Each method (pass, user, etc.) performs the proper action for the given state and returns the next state

SPopState is abstract state with the default behavior for each method

Server is done with client when we reach the Quit state, so I did not add methods to that state

Doc 20, Some Parsing & States Slide # 14

Strawman Driver Program

class SPopServer 
   {
   public void processRequest(InputStream in, OutputStream out, 
      InetAddress clientAddress) throws IOException 
      {
   
      SPopState currentState = new NoAuth();
      do
         {
         ProtocolParser requestData = new ProtocolParser( in );
         String request = requestData.getCommand();
         if ( request.isPassword() )
            currentState = currentState.pass( request, this);
   
         else if ( request.isUser())
            currentState = currentState.user(this);
         etc.
            
            send response to client
         }
      while ( ! currentState instanceof Quit  );
      }
   }

Doc 20, Some Parsing & States Slide # 15
SPopState Implements Default Behavior


public class SPopState {
   public SPopState quit( SPopServer parent) {
      return new Quit();
      }
   
   public SPopState pass( PopCommand clientRequest, SPopServer parent) 
      throws IllegalCommand {
      throw new IllegalCommand();
      }
   
   public SPopState user( PopCommand clientRequest, SPopServer parent) 
      throws IllegalCommand {
      throw new IllegalCommand();
      }
   
   public SPopState list( PopCommand clientRequest, SPopServer parent) 
      throws IllegalCommand {
      throw new IllegalCommand();
      }


Doc 20, Some Parsing & States Slide # 16
Subclasses Implement Correct behavior for that State

public class NoAuth extends SPopState {
   public SPopState user( PopCommand clientRequest, SPopServer parent) {
      parent.setUser( clientRequest.getArgument() );
      parent.sendOKResponse();
      return new HaveUser();
   }
}
   
public class HaveUser extends SPopState {
   public SPopState pass( PopCommand clientRequest, SPopServer parent) {
      parent.setPassword( clientRequest.getArgument() );
      if ( parent.user&PasswordValid() ) {
         parent.sendOKResponse();
         return new Process();
      }
      else {
         parent.sendErrorResponse();
         return new NoAuth();
      }
   }
}

Doc 20, Some Parsing & States Slide # 17

Smalltalk Example From VW 7 POP3 Client


Client has abstract state class Pop3State

Concrete states

Pop3Client that does the work


Doc 20, Some Parsing & States Slide # 18
Pop3State

Smalltalk.Net defineClass: #Pop3State
   superclass: #{Core.Object}
   private: false
   instanceVariableNames: ''
   classInstanceVariableNames: ''
   category: 'Net-Pop Rocks'
   
Net.Pop3State methodsFor: 'commands'
   
delete: message for: connection
   Pop3StateError raiseSignal
   
list: number for: connection 
   Pop3StateError raiseSignal
   
pass: aConnection 
   Pop3StateError raiseSignal
   
quit: aConnection 
   aConnection sendQuit
   
retrieveMessage: number for: connection 
   Pop3StateError raiseSignal
   
stat: aConnection 
   Pop3StateError raiseSignal
   
user: aConnection 
   Pop3StateError raiseSignal 


Doc 20, Some Parsing & States Slide # 19
Pop3AuthorizationState

Smalltalk.Net defineClass: #Pop3AuthorizationState
   superclass: #{Net.Pop3State}
   indexedType: #none
   private: false
   instanceVariableNames: ''
   classInstanceVariableNames: ''
   imports: ''
   category: 'Net-Pop Rocks'
   
Net.Pop3AuthorizationState methodsFor: 'commands' 
   
pass: aConnection 
   aConnection sendPassword
   
user: aConnection 
   aConnection sendUser 

Doc 20, Some Parsing & States Slide # 20
Pop3TransactionState

Smalltalk.Net defineClass: #Pop3TransactionState
   superclass: #{Net.Pop3State}
   indexedType: #none
   private: false
   instanceVariableNames: ''
   classInstanceVariableNames: ''
   imports: ''
   category: 'Net-Pop Rocks'
   
   
Net.Pop3TransactionState methodsFor: 'commands'
   
delete: message for: connection
   ^connection sendDeleteMessage: message
   
list: aConnection
   ^aConnection sendList
   
list: number for: connection 
   ^connection sendList: number
   
retrieveMessage: number for: connection 
   ^connection sendRetrieveMessage: number
   
stat: aConnection
   ^aConnection sendStat


Doc 20, Some Parsing & States Slide # 21
Some Pop3Client MethodsUsing State

delete: message
   ^self state delete: message for: self
   
list
   ^self state list: self
   
list: messageNumber 
   ^self state list: messageNumber for: self
   
quit
   self disconnect
   
retrieveMessage: number
   ^self state retrieveMessage: number for: self
   
status
   ^self state stat: self
   
login
   self state user: self.
   self hasPositiveResponse ifFalse: [^false].
   self state pass: self.
   self hasPositiveResponse ifFalse: [^false].
   self state: Pop3TransactionState new. 
   ^true


Doc 20, Some Parsing & States Slide # 22
Called By State

sendUser
   self sendCommand: ('USER <1s>' expandMacrosWith: self user username).
   self waitForResponse.
   self hasPositiveResponse 
      ifFalse: [^NetClientError signalWith: #login message: self serverResponse].
   
sendPassword
   self sendCommand: ('PASS <1s>' expandMacrosWith: self user password).
   self waitForResponse.
   self hasPositiveResponse 
      ifFalse: [^NetClientError signalWith: #login message: self serverResponse].


Doc 20, Some Parsing & States Slide # 23
State Object Analysis

Problems




Advantages:





Doc 20, Some Parsing & States Slide # 24
The Interesting Questions

Who does the actual work?

checking the password and user name
getting the mail messages


Who does the mapping from strings to functions?


How does all this fit together?


Does anyone understand this?



Special Bonus Question

Can you determine how to eliminate the need for mapping from strings to functions without using if statement (or switch statements)?


Doc 20, Some Parsing & States Slide # 25

Implementing a State Machine with a Table



Commands
States
NoAuth
HaveUser
Process
Invalid
Quit
USER





PASS





LIST





RETR





QUIT






Each cell needs:




Doc 20, Some Parsing & States Slide # 26
The State Table


Commands
States
NoAuth
HaveUser
Process
Invalid
Quit
USER
actionUser
actionNull
actionNull


HaveUser
Invalid
Invalid
Quit
Quit
Invalid
Invalid
Invalid
Quit
Quit
PASS
actionNull
actionPass
actionNull


Invalid
Process
Invalid
Quit
Quit
Invalid
Invalid
Invalid
Quit
Quit
LIST
actionNull
actionNull
actionList


Invalid
Invalid
Process
Quit
Quit
Invalid
Invalid
Invalid?
Quit
Quit
RETR
actionNull
actionNull
actionRetr


Invalid
Invalid
Process
Quit
Quit
Invalid
Invalid
Invalid?
Quit
Quit
QUIT
actionQuit
actionQuit
actionQuit


Quit
Quit
Quit
Quit
Quit
Quit
Quit
Quit
Quit
Quit

Key
Function to process request
Next State on success
Next State on failure


Doc 20, Some Parsing & States Slide # 27
Basic Operation

Get request from user

Use current state and new request to find in table operation to perform

Perform the operation

Change state based on table and result of operation



Doc 20, Some Parsing & States Slide # 28
How to place Operation in a Table

C/C++


Smalltalk



Java




Doc 20, Some Parsing & States Slide # 29

Function Pointers in C/C++


void quickSort( int* array, int LowBound, int HighBound)
{
   // source code to sort array from LowBound to HighBound
   // using quicksort has been removed to save room on page
}
   
void mergeSort(int* array, int LowBound, int HighBound)
{   // same here}
   
void insertionSort(int* array, int LowBound, int HighBound)
{   // ditto }
   
void main()
{
   void (*sort) (int*, int, int);
   int size;
   int data[100];
   
   // pretend data and Size are initialized
   
   if (size < 25)
      sort = insertionSort;
   
   else if (size > 100) 
      sort = quickSort;
   
   else
      sort = mergeSort;
   
   sort(data, 0, 99);
}

Doc 20, Some Parsing & States Slide # 30
SPOP State table: C/C++

In C/C++ we can define the following:

struct
{
   int      currentState;
   char      *command;
   int      stateIfSucceed;
   int      stateIfFailed;
   int      (*action)(char **);
} actionTable[] =
{
   {0, "USER", 1, 3, actionUser},
   {0, "QUIT", 4, 4, actionQuit},
   {1, "PASS", 2, 3, actionPass},
   {1, "QUIT", 4, 4, actionQuit},
   {2, "LIST", 2, 2, actionList},
   {2, "RETR", 2, 2, actionList},
   {2, "QUIT", 4, 4, actionList},
   {0, 0, 0, 0, 0}
};

Advantages:
Even easier if the states are given names.



Doc 20, Some Parsing & States Slide # 31

Smalltalk - Symbols & Reflection


Direct method execution

   3 squared
   2 + 3 
   'A cat in the hat' copyFrom: 3 to: 5

Method execution via reflection

   3 perform: #squared 
   2 perform: #+ with: 3
   'A cat in the hat' perform: #copyFrom:to: with: 3 with: 5

The method to execute is an argument of perform:


So store the symbol (#squared) of the method in the table


Doc 20, Some Parsing & States Slide # 32

Function Pointers in Java

Use Reflection

Class.getMethod maps strings to methods

public Method getMethod(String name,Class parameterTypes[])
      throws NoSuchMethodException, SecurityException


Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object. The name parameter is a String specifying the simple name the desired method, and the parameterTypes parameter is an array of Class objects that identify the method's formal parameter types, in declared order.


The method to reflect is located by searching all the member methods of the class or interface represented by this Class object for a public method with the specified name and exactly the same formal parameter types.


Throws: NoSuchMethodException 
   if a matching method is not found. 
Throws: SecurityException 
   if access to the information is denied. 


Doc 20, Some Parsing & States Slide # 33
Simple Class for Example

class Example
   {
   public void getLunch()
      {
      System.out.println( "Lunch Time!");
      }
   
   public void getLunch( String day)
      {
      System.out.println( "Lunch Time for " + day);
      }
      
   public void eatOut( String where)
      {
      System.out.println( "MacDonalds? ");
      }
   
   public void eatOut( int where)
      {
      System.out.println( "PizzaHut? " + where );
      }
   }



Doc 20, Some Parsing & States Slide # 34
Using Class.getMethodSimple Example

import java.lang.reflect.Method;
   
class Test
   {
   public  static  void  main( String  args[] ) throws Exception
      {
      Example a = new Example();
   
      Class[] stringType = { Class.forName( "java.lang.String" ) };
   
      Object[] stringParameter = { "Monday" };
   
      Method tryMe;
   
      tryMe = a.getClass().getMethod( "getLunch", stringType );
   
      tryMe.invoke( a, stringParameter );
   
      }
   }

Output
Lunch Time for Monday



Doc 20, Some Parsing & States Slide # 35
State Table Analysis

Advantages





Disadvantages



Copyright ©, All rights reserved.
2002 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.

Previous    visitors since 29-Oct-02    Next