0

I've been using a simple eclipse plugin to create visual state machines, called statecharts, that also uses Java code to work. My overall goal is to make two state machines communicate with each other through sockets and exchange data and make transitions based on that, such as a client-server communication. In the beginning I was using simple synchronous client-server code, but apparently using a synchronous approach can not help; the correct way is to continuously poll data from a queue. I'm now trying to use Java NIO which seems promising, but unfortunately had no success in the first attempt. It seems like there is a busy loop somewhere that doesn't allow the received value to trigger the change.

The code is pretty simple: I first try to connect to server (it works), send a data (it works), and try to read from input buffer every cycle as a way to receive data as you can see in the picture. The logic so far makes sense. I set the received data to a variable which also is located in a transition expression. So basically whenever it is set true, I should transit to next state. But it doesn't work.

Can someone help me solve this problem? I've seen there are async APIs like Netty and Naga that might make things easier if that is a remedy.

Here is a visual scheme of a state machine: enter image description here

Here is the code for client:

package test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class EchoClient2 {
    String serverHostname = new String("127.0.0.1");
    BufferedReader stdIn;
    Socket echoSocket = null;
    PrintWriter out = null;
    BufferedReader in = null;

    public void open(){
        System.out.println("Attemping to connect to host " + serverHostname
                + " on port 5555.");
        try {
            echoSocket = new Socket(serverHostname, 5555);
            out = new PrintWriter(echoSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(
                    echoSocket.getInputStream()));
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host: " + serverHostname);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for " + "the connection to: "
                    + serverHostname);
        }
    }

    public void send(){
        String userInput = "1";
        out.println(userInput);
    }

    public String receive(){
        String result = "";
        try {
            result = in.readLine();
            if(result==null)
                return "0";
        } catch (IOException e) {
        }
        return result;
    }
}

and here is the code for server:

package test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoServer extends Thread {
    protected Socket clientSocket;

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = null;

        try {
            serverSocket = new ServerSocket(5555);
            System.out.println("Connection Socket Created");
            try {
                while (true) {
                    System.out.println("Waiting for Connection");
                    new EchoServer(serverSocket.accept());
                }
            } catch (IOException e) {
                System.err.println("Accept failed.");
                System.exit(1);
            }
        } catch (IOException e) {
            System.err.println("Could not listen on port: 5555.");
            System.exit(1);
        } finally {
            try {
                serverSocket.close();
            } catch (IOException e) {
                System.err.println("Could not close port: 5555.");
                System.exit(1);
            }
        }
    }

    private EchoServer(Socket clientSoc) {
        clientSocket = clientSoc;
        start();
    }

    public void run() {
        System.out.println("New Communication Thread Started");

        try {
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),
                    true);
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    clientSocket.getInputStream()));

            String inputLine;

            while ((inputLine = in.readLine()) != null) {
                System.out.println("Server: " + inputLine);
                out.println(inputLine);

                if (inputLine.equals("Bye."))
                    break;
            }

            out.close();
            in.close();
            clientSocket.close();
        } catch (IOException e) {
            System.err.println("Problem with Communication Server");
            System.exit(1);
        }
    }
}

And here is the Eclipse project folder that you can simply import if that might be easier.

Tina J
  • 4,983
  • 13
  • 59
  • 125

1 Answers1

1

The math behind what you intend to do is called PI Calculus. It's not the only approach, but it is a great place to start.

Basically what you will model is a relationship where two machines can enter related states and not progress until a shared condition occurs (which is typically a message being passed).

This means that you will have to have both of your state machines on separate threads. Attempting to use a common event queue orders the machines, which can become very problematic if the ordering is not stable (and can be even more problematic if the ordering is not complementary to a certain problem).

Often the shared message is simplified. For example, many systems use a "mailbox" type delivery mechanism, where one state machine delivers a message to the inbound mailbox of the other. Then the delivering state machine blocks until the message clears the mailbox. If you formalize this in the right manner, you effectively create an Actor like solution. Should you decide this is the manner in which you might like to proceed, baking in a mailbox early on will allow you to later replace the class with a persistent message delivery system.

As far as actually making it work with two independent processes, my favorite approach is to have a third process launch the two, where the launching process also creates any needed communication channels. That launching process can then set the configurable elements of it's two children. Doing things this way might require a bit of knowledge of how to create "system services" aka programs without ttys, but it's good knowledge to have. Also I prefer a JMS API and one of the many implementations.

If you really want to "roll your own" then I'd start of with something a bit less than a full blown NIO solution. Remember NIO scales well when you have certain communication patterns, but a single state machine blocking on needed input (or needed delivery confirmation) doesn't need to scale across pools of threads or wait on complex event callbacks. Of course, others might differ in opinion, but certain work flows do benchmark faster with a less scalable solution (and I think this might be a job where scalability is not going to buy you much unless you're really proxying the communications for dozens or hundreds of state machines in the same process).

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138