Search code examples
javarestletrestlet-2.0

Restlet Client :: how to add filters?


I suffering of a lack of documentation on the use of Restlet at the client side. I am getting a resource on server via a ClientResource:

new ClientResource(url).get();

But the server can return an ETag header. To handle this I want to save the ETag when returned and send it back to the server when using the same url. Currently I am doing it like this:

ClientResource clientResource = new ClientResource(url);
addEtag(url, clientResource); // add the cached ETag to the query if any
clientResource.get();
saveEtag(url, clientResource); // cache the ETag if any

I would like to do this using the Restlet framework. I am searching for days wihtout understanding the missing link. I can extend an application, overwrite the createOutboundRoot() method and return a filter:

public class RestLetClient extends Application {

    private Client client;

    // instantiation of the client and other things here

    @Override
    public Restlet createOutboundRoot() {
        return new Filter(getContext(), client){

            @Override
            protected int beforeHandle(Request request, Response response) {
                addEtag(request);
                return super.doHandle(request, response);
            }

            @Override
            protected void afterHandle(Request request, Response response) {
                saveEtag(request, reponse);
                return super.afterHandle(request, response);
            }
        };
    }
}

BUT how can I use this filtering around the Restlet client from my business code?

EDIT

The best I could get to work until now is this:

Request request = new Request(Method.GET, uri);
//the filter created in original post
filter.handle(request).getEntity();

This works but it is not integrated in the framework. What I am achieving to do is at the client side what is only documented for the server side. On the server you would do:

public class ServerApplication extends Application {

    @Override
    public Restlet createInboundRoot() {
        Router router = new Router(getContext());
        router.attach(GET_URL, GetResource.class);
        return router;
    }
}

and then start the server. The application will the be triggered on the reception of a GET request on the url.
What is the equivalent on the client side? How can I trigger a Client Application? If I have an Application running at the client side I can nicely add filters where they belong in a REST application

EDIT 2

When trying to run my client within an Application I get the error: The filter org.restlet.engine.application.RangeFilter@f372a7a was executed without a next Restlet attached to it.

Here is how I am getting the error. I have a class extending Application that is called from a JUnit test:

public class RestLetClient extends Application {

    private final Client client;

    Logger logger = LoggerFactory.getLogger(getClass());

    public RestLetClient() {
        this.client = new Client(Protocol.HTTP);
    }

    public Representation get(final String uri) throws Exception {

        Request request = new Request(Method.GET, uri);
        Response response = handle(request);
        return response.getEntity();
    }

    @Override
    public Restlet createOutboundRoot() {
        return new Filter(getContext(), client) {
            @Override
            protected int beforeHandle(Request request, Response response) {
                addEtagFilter(request);
                return super.beforeHandle(request, response);
            }

            @Override
            protected void afterHandle(Request request, Response response) {
                saveEtagFilter(request, response);
                super.afterHandle(request, response);
            }
        };
    }

    private void saveEtagFilter(Request request, Response response) {
        logger.debug("saving etag");
    }

    private void addEtagFilter(Request request) {
        logger.debug("adding etag");
    }
}

and the unit with a single test method:

public class RestLetClientTest {

    public static final String URL = "http://localhost:8123/resource";

    private RestLetClient instance;

    private Server server;

    @Before
    public void setUp() throws Exception {
        server = new Server(Protocol.HTTP, 8123, new TestApplication());

        server.start();

        instance = new RestLetClient();
        instance.start();
    }

    @After
    public void tearDown() throws Exception {
        instance.stop();
    }

    @Test
    public void testGet() throws Exception {
        Representation representation = instance.get(URL);
        System.out.println(representation.getText());
    }

    private class TestApplication extends Application {
        @Override
        public Restlet createInboundRoot() {
            return new Router().attach(RestLetClientTest.URL, GetResource.class);
        }
    }

    private class GetResource extends ServerResource {
        @Get
        public Representation getResource() {
            return new StringRepresentation("hello world");
        }
    }
}

What am I doing wrong?


Solution

  • I had a much nicer answer from a colleague. I post it here for the documentation.

    The solution is to use a ClientResource, a Filter and a Client. The Filter becomes the next() of the ClientResource and the Client the next() of the Filter.

    public class ETagFilter extends Filter {
    
        @Override
        protected int beforeHandle(Request request, Response response) {
            addEtag(request);
            return super.beforeHandle(request, response);
        }
    
        @Override
        protected void afterHandle(Request request, Response response) {
            saveEtag(request, reponse);
            super.afterHandle(request, response);
        }
    }
    
    public class RestLetClient extends Application {
    
        public Representation get(final String uri) throws Exception {
    
            Client client = new Client(Protocol.HTTP);
            ETagFilter eTagFilter = new ETagFilter();
            clientResource = new ClientResource(uri);
    
            clientResource.setNext(eTagFilter);
            eTagFilter.setNext(client);
    
            return clientResource.get(halMediaType);
        }
    }
    

    For info. In my OP I was trying to transform code meant for server side into client side. That approach was wrong. My colleague pointed that the approach is much more like the use Apache HttpClient for similar needs