5

So, in my app, I send the request from http server handler to another server. The thing is the second server is stuck on reading object. I was trying to figure out it myself but I can't see the problem. All streams are closed, maybe the response to the client is in the wrong place? I have no idea why is that..

This is my client:

public class ClientSimulator {

    private Random random;
    private static int clientCounter = 1;

    public static void main(String[] args) throws Exception {
        new ClientSimulator();
        new ClientSimulator();
        new ClientSimulator();
    }

    private ClientSimulator() {

        this.random = new Random();
        RuntimeMXBean rmb = ManagementFactory.getRuntimeMXBean();
        long arrivalTime = rmb.getUptime();

        System.out.println("thread no. " + clientCounter++ + " arrival time: " + arrivalTime);
        try {
            String myurl= "http://localhost:8080/sender";
            String serverResponse = createClient(myurl);
            System.out.println(serverResponse);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String createClient(String myurl) throws Exception {
        URL url;
        BufferedReader reader = null;
        StringBuilder stringBuilder;

        try {
            //Standard HTTP connection
            url = new URL(schedulerUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoInput(true);
            connection.setDoOutput(true);

            int[] arr = {
                    random.nextInt(10-1)+1,
                    random.nextInt(10-1)+1,
                    random.nextInt(10-1)+1,
                    random.nextInt(10-1)+1,
                    random.nextInt(10-1)+1,
            };
            Task t = new Task(arr);

            ObjectOutputStream oos = new ObjectOutputStream(connection.getOutputStream());
            oos.writeObject(t);
            oos.close();

            // read the output from the server
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            stringBuilder = new StringBuilder();
            //print the response
            String line = reader.readLine();
            stringBuilder.append(line);

            return stringBuilder.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
        }
    }

    private boolean getRandBool(){
        return this.random.nextBoolean();
    }
}

This is how I send the request from the main server to another server:

@Override
    public void handle(HttpExchange httpExchange) throws IOException {

        String template = "\nClient no. %s connected!";

        //Getting task
        Task t;
        ObjectInputStream ois = new ObjectInputStream(httpExchange.getRequestBody());
        try {
            System.out.print("Recieved object:");
            t = (Task) ois.readObject();
            t.setDeadline(deadline);
            t.setHard(isHard);
            System.out.print(" not sorted array: ");
            int[] arr = (int[]) t.getData();
            for (int anArr : arr) {
                System.out.print(anArr + " ");
            }
            ois.close();

           String response = "response for client no. " + clientCounter;
           httpExchange.sendResponseHeaders(200, response.length());
           OutputStream os = httpExchange.getResponseBody();
           os.write(response.getBytes());
           os.close();
           clientCounter++;

            HttpURLConnection test = (HttpURLConnection) new URL(fogServ1URL).openConnection();
            test.setDoOutput(true);
            System.out.println("test__1");
            ObjectOutputStream stream = new ObjectOutputStream(test.getOutputStream());
            stream.flush();
            System.out.println("test__2");
            stream.writeObject(t);
            System.out.println("test__3");
            stream.close();
            System.out.println("test__4");
            test.getResponseCode();
            System.out.println("test__5"); //this doesn't print
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

This is handler from the second server:

class RootHandler implements HttpHandler{
private static int clientCounter = 1;

@Override
public void handle(HttpExchange exchange) throws IOException {



    System.out.println("\nRoot handler; \n\tclient no. " + clientCounter++);

    Task t;
    ObjectInputStream ois = new ObjectInputStream(exchange.getRequestBody());
    try {
        System.out.println("Recieved object:"); //only this is on the console
        t = (Task) ois.readObject();
        ois.close();
        System.out.println("Array not sorted:");
        int[] arr = (int[]) t.getData();
        for (int anArr : arr) {
            System.out.print(anArr + " ");
        }
        TaskSolver.solve(t);
        System.out.println("\nArray sorted!");
        for (int anArr : (int[])t.getData()) {
            System.out.print(anArr + " ");
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

    String response = "Server up!";
    exchange.sendResponseHeaders(200, response.getBytes().length);
    OutputStream os = exchange.getResponseBody();
    os.write(response.getBytes());
    os.close();
}
}

I don't get it because I send the Task from client to the main server in the same way and its working. I just can't read the output in the next servers. What am I doing wrong?

Btw. If someone is curious why I'm sending the same object to another server: I plan to create more servers, the main server will be sending requests to them depending on type/containing headers.

shurrok
  • 795
  • 2
  • 13
  • 43
  • There is nothing wrong with the code you have posted. I have created an MCVE from the code in your question and it works. I won't promise it is free of race conditions and robust but it is not encountering the problem you describe. You can check it out [here](https://gist.github.com/anonymous/09148676e67144a8841ee83cf9429473). – Pace Jan 29 '18 at 18:51
  • Also, there are third party libraries that solve this problem ([jesque](https://github.com/gresrun/jesque), [quartz](http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/)) and better libraries for building HTTP APIs ([Jersey](https://jersey.github.io/), [CXF](http://cxf.apache.org/)) and that using ObjectOutputStream/ObjectInputStream is quite brittle when there are such easy to use JSON/XML libraries ([jackson](https://github.com/FasterXML/jackson), [JAX-B](https://docs.oracle.com/javase/tutorial/jaxb/intro/index.html)). If this is mostly for learning then go for it. – Pace Jan 29 '18 at 19:02
  • @Pace in your MCVE can you also mention what will be the composition of the `Task` class – absin Jan 30 '18 at 07:27
  • If `handle()` method initializes `InputStream` before `OutputStream`, infinite block is possible, see this answer: https://stackoverflow.com/a/14111047/5784273 – jihor Jan 30 '18 at 09:36
  • Any exception that is not a subclass of `ClassNotFoundException` will be swallowed by the server. Try changing exception class in the `catch` block of `RootHandler` from `ClassNotFoundException` to `Throwable` or at least `IOException`. – jihor Jan 30 '18 at 15:13
  • Why are you writing a success response back to the client before you've passed the request to the next server? This is madness. What happens if the upstream request fails? You must complete the request before you can possibly know what the correct response is. – user207421 Feb 02 '18 at 05:44
  • Be very cautious when using `readline` with streams. If you do not receive a newline it will block and wait forever. – Hannes Feb 04 '18 at 20:12

2 Answers2

0

In Second server while reading the object there is some IOException occurred, since IOException in handle method of RootHandler class is passed to invoking method instead of handling it. So error like Internal Server Error response status is sent to client ( first server ).

Since there is exception in second server the test.getResponseCode() throws IOException since it is not handled in first server handle method

System.out.println("test__5");

is not invoked.

If you want to know what is happening catch IOException in handle method of the both server and print Stack trace.

0

Try following below sequence in your first server -

  1. Receive object by reading the InputStream
  2. Connect with the second server.
  3. Send object by writing on OutputStream of second server.
  4. Get the response from second server
  5. Send response to client by writing on OutputStream of first server.

The reason behind current behavior is related to closing of Exchange. As per the documentation -

Exchanges are terminated when both the request InputStream and response OutputStream are closed.

Currently, in your first server code, you are writing the response on OutputStream and then closing it. After that, your first server start communicating with second server.

When first server sent request to second server and wait for the response, at the time, underlying HttpExchange gets closed, thus connection and associated thread are released. Till this time, second server hasn't read the request.

In this situation, some IOException must occur in second server for indicating the error or second server will keep waiting for reading the InputStream and eventually get timeout.

Closing of Exchange and releasing of associated thread is not immediate step after closing OutputStream, but it can terminate anytime and subsequent code lines are not guaranteed to be executed. In your case, connection terminated while waiting for the response from second server but it can terminate before that also.

Vikas Sachdeva
  • 5,633
  • 2
  • 17
  • 26