Repeated calls by Restlet client to Restlet server hangs
Asked Answered
S

4

6

I am using Restlet to implement a web service. The client (also uses Restlet) makes many consecutive calls to the server, but after a small number of calls complete successfully, further calls hang the server, which shows the message:

INFO: Stop accepting new connections and transactions. Consider increasing the maximum number of threads.

I tried:

getContext().getParameters().add("maxThreads", "200");

but that does not help. In any case, it seems like the client should be able to make an unlimited number of calls, and increasing maxThreads just ups the limit. It looks like I am not freeing up some resource or disconnecting after each client call, but I do not know how to do so.

The following (small as I could make it) stand alone program demonstrates the problem. It starts a simple server and then a client calls it a bunch of times:

/** You may copy, modify, and re-use this code as you see fit - Jim Irrer */
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.restlet.Application;
import org.restlet.Component;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.Server;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Status;
import org.restlet.representation.InputRepresentation;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
import org.restlet.resource.Directory;

public class SimpleServerPut extends Component implements Runnable {
    private static final int PORT = 8080;

    private static int readToByteArray(InputStream inputStream, byte[] buf) throws IOException {
        int length = 0;
        int b;
        while ((b = inputStream.read()) != -1) {
            buf[length++] = (byte)b;
        }
        return length;
    }

    @Override
    public void run() {
        getContext().getParameters().add("maxThreads", "200");

        // Create the HTTP server and listen on port PORT
        SimpleServerPut simpleServer = new SimpleServerPut();
        Server server = new Server(Protocol.HTTP, PORT, simpleServer);
        simpleServer.getClients().add(Protocol.FILE);

        // Create an application  
        Application application = new Application(simpleServer.getContext()) {  
            @Override  
            public Restlet createRoot() {  
                return new Directory(getContext(), "C:");  
            }  
        };

        // Attach the application to the component and start it  
        simpleServer.getDefaultHost().attach("/stuff/", application);
        try {
            server.start();
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override  
    public void handle(Request request, Response response) {  
        // assume the worst
        response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED);
        response.setEntity("No no - Bad client!  Only do PUTs.", MediaType.TEXT_PLAIN);

        try {
            if (request.getMethod() == Method.PUT) {
                InputStream inputStream = request.getEntity().getStream();
                byte[] buf = new byte[64*1024];
                int totalLength = readToByteArray(inputStream, buf);
                response.setStatus(Status.SUCCESS_OK);
                String msg = "Number of bytes received: " + totalLength;
                response.setEntity(msg, MediaType.TEXT_PLAIN);
                System.out.println("server: " + msg);
                return;
            }
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static String callServer() throws IOException {
        String urlText = "http://localhost:" + PORT + "/";
        ClientResource clientResource = new ClientResource(urlText);
        clientResource.setReferrerRef(urlText);

        byte[] buf = new byte[1000];
        for (int i = 0; i < buf.length; i++) {
            buf[i] = (byte)((int)'a' + (i%26));
        }
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buf);
        Representation representation = new InputRepresentation(byteArrayInputStream, MediaType.APPLICATION_OCTET_STREAM);
        Representation representation2 = clientResource.put(representation);
        byte[] responseBuf = new byte[16*1024];
        int length = readToByteArray(representation2.getStream(), responseBuf);
        Response response = clientResource.getResponse();
        Status status = response.getStatus();
        return "status: " + status + "    message: " + new String(responseBuf, 0, length);
    }

    // Start server and call it a bunch of times
    public static void main(String[] args) throws Exception {
        SimpleServerPut simpleServer = new SimpleServerPut();
        new Thread(simpleServer).start();
        Thread.sleep(200);  // cheap trick to make sure that server is running
        // make a bunch of client calls
        for (int t = 0; t < 100; t++) {
            System.out.println("client count: " + (t+1) + "    " + callServer());
        }
        System.exit(0);
    }
}
Selfsustaining answered 23/11, 2011 at 19:19 Comment(1)
The maxThreads key value pair parameter needs to be set in org.restlet.Server, not in the org.restlet.Component. Like this: Server server = mycomponent.getServers().add(Protocol.HTTP, "localhost", 9090); server.getContext().getParameters().add("maxThreads", "20");Metaphor
B
5

We were only able to solve the problem by stopping the ClientResource's associated client directly (using Restlet version 2.0.15):

Client c = (Client)clientResource.getNext();
try {
    c.stop();
} catch (Exception e) {
   //handle exception
}   
Blinding answered 29/11, 2012 at 12:27 Comment(3)
It seems I have also had to do this in order to get the threads to die promptly, but perhaps this is a problem with the specific version of Restlet? (see @kassius-vargas-prestes's answer above) - Which version are you using?Tights
The version we used was 2.0.15, the latest stable version at this time (and still it is)Blinding
Thanks, we're using the same version, and so I'll be leaving this explicit .stop() in our codebase for now.Tights
S
4

Add a line so that the client releases the resource:

    Response response = clientResource.getResponse();
    Status status = response.getStatus();
    clientResource.release();   // add this line

to the client and everything works. Eventually the server times out if the client dies, but that takes a while.

Selfsustaining answered 28/11, 2011 at 16:2 Comment(0)
U
1

I've solved my problem downloading the last stable release of the Restlet API

Apparently the .jar the I've been using were an old version where the release() command doesn't make any effect.

Before the update the client log only outputs the start of the client:

Sep 05, 2012 9:50:19 AM org.restlet.engine.http.connector.HttpClientHelper start
INFO: Starting the default HTTP client

Now it are outputing the stop too:

Sep 05, 2012 9:50:19 AM org.restlet.engine.http.connector.HttpClientHelper stop
INFO: Stopping the default HTTP client
Unearthly answered 5/9, 2012 at 12:51 Comment(1)
Would you mind being explicit about which version you were using and which version fixed the problem? It would help others (including me!) to debug the issues.Tights
A
0

In addition to calling ClientResource.release(), you may want to call exhaust() on the representation.

Representation responseRepresentation = response.getEntity();
if (responseRepresentation != null) {
    try {
        responseRepresentation.exhaust();
    } catch (IOException e) {
        // handle exception
    }
    responseRepresentation.release();
}

Related discussion in this thread.

Authorize answered 17/1, 2012 at 22:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.