Zest™
Introduction
Tutorials
Javadoc
Samples
Core
Libraries
Extensions
Tools
Glossary 

ReST Client

code

docs

tests

Rickard sent a very interesting HATEOAS Primer to the mailing list in October 2011, as the starting point for the renovation of the ReST Client Library. You should read that to get the full background on the choices made in this library.

Table 41. Artifact

Group IDArtifact IDVersion

org.qi4j.library

org.qi4j.library.rest-client

2.1


Usage

This library leverages the Restlet library, so keep its documentation nearby as well.

This library expects the client code to build up handlers on how to react to resources and errors. It is a more declarative approach than a typical ReST client application, which often isn’t HATEOAS at all, and Roy Fielding is upset that ReST now means something else than it was originally intended. We try to be true to Dr. Fielding’s intentions.

Establish Client

The first thing that must be done is to create a ContextResourceClient. Let’s walk through the different steps typically needed.

Client client =   new Client( Protocol.HTTP );

ContextResourceClientFactory contextResourceClientFactory = module.newObject( ContextResourceClientFactory.class, client );
contextResourceClientFactory.setAcceptedMediaTypes( MediaType.APPLICATION_JSON );

Above we create the Client instance and a ContextResourceClientFactory, which takes a client via @Uses annotation. We also set the accepted media type to JSON.

We then create the global handler, which will be set to all ContextResourceClient instances that this factory creates.

contextResourceClientFactory.setErrorHandler( new ErrorHandler().onError( ErrorHandler.AUTHENTICATION_REQUIRED, new ResponseHandler()
{
    boolean tried = false;

    @Override
    public HandlerCommand handleResponse( Response response, ContextResourceClient client )
    {
            if (tried)
                throw new ResourceException( response.getStatus() );

            tried = true;
            client.getContextResourceClientFactory().getInfo().setUser( new User("rickard", "secret") );

            // Try again
            return refresh();
    }
} ).onError( ErrorHandler.RECOVERABLE_ERROR, new ResponseHandler()
{
    @Override
    public HandlerCommand handleResponse( Response response, ContextResourceClient client )
    {
        // Try to restart
        return refresh();
    }
} ) );

Above, we try to handle that autheorization is required by setting user credentials and then try again. The client could do a pop-up box instead, have its own cached entries, contact a credentials server or many other things.

We also added another handler that does a refresh() on any recoverable error.

Note that the ErrorHandler.AUTHENTICATION_REQUIRED and ErrorHandler.RECOVERABLE_ERROR are not enums or constants, but Specifications and it is possible to implement your own.

We then simply proceed to create the ContextResourceClient, by giving the factory the bookmarkable reference of the ReST API.

Reference ref = new Reference( "http://localhost:8888/" );
crc = contextResourceClientFactory.newClient( ref );
Using ContextClientResource

Once we have the ContextResourceClient, we can proceed with using it. The general approach is to register handlers for potential results when invoking the method on the ReST resource.

Query without Value
crc.onResource( new ResultHandler<Resource>()
{
    @Override
    public HandlerCommand handleResult( Resource result, ContextResourceClient client )
    {
        return query( "querywithoutvalue" );
    }
} ).
onQuery( "querywithoutvalue", new ResultHandler<TestResult>()
{
    @Override
    public HandlerCommand handleResult( TestResult result, ContextResourceClient client )
    {
        Assert.assertThat( result.xyz().get(), CoreMatchers.equalTo( "bar" ) );
        return null;
    }
} );

crc.start();
Query and Command
crc.onResource( new ResultHandler<Resource>()
{
    @Override
    public HandlerCommand handleResult( Resource result, ContextResourceClient client )
    {
        return query( "querywithvalue", null );
    }
} ).onProcessingError( "querywithvalue", new ResultHandler<TestQuery>()
{
    @Override
    public HandlerCommand handleResult( TestQuery result, ContextResourceClient client )
    {
        ValueBuilder<TestQuery> builder = module.newValueBuilderWithPrototype( result );

        builder.prototype().abc().set( "abc" + builder.prototype().abc().get() );

        return query( "querywithvalue", builder.newInstance() );
    }
} ).onQuery( "querywithvalue", new ResultHandler<TestResult>()
{
    @Override
    public HandlerCommand handleResult( TestResult result, ContextResourceClient client )
    {
        return command( "commandwithvalue", null );
    }
} ).onProcessingError( "commandwithvalue", new ResultHandler<Form>()
{
    @Override
    public HandlerCommand handleResult( Form result, ContextResourceClient client )
    {
        result.set( "abc", "right" );

        return command( "commandwithvalue", result );
    }
} );

crc.start();
Query List and Command
crc.onResource( new ResultHandler<Resource>()
{
    @Override
    public HandlerCommand handleResult( Resource result, ContextResourceClient client )
    {
        return query( "commandwithvalue" );
    }
} ).onQuery( "commandwithvalue", new ResultHandler<Links>()
{
    @Override
    public HandlerCommand handleResult( Links result, ContextResourceClient client )
    {
        Link link = LinksUtil.withId( "right", result );

        return command( link );
    }
} ).onCommand( "commandwithvalue", new ResponseHandler()
{
    @Override
    public HandlerCommand handleResponse( Response response, ContextResourceClient client )
    {
        System.out.println( "Done" );
        return null;
    }
} );

crc.start();
  [...snip...]

crc.onResource( new ResultHandler<Resource>()
{
    @Override
    public HandlerCommand handleResult( Resource result, ContextResourceClient client )
    {
        return query( "commandwithvalue" ).onSuccess( new ResultHandler<Links>()
        {
            @Override
            public HandlerCommand handleResult( Links result, ContextResourceClient client )
            {
                Link link = LinksUtil.withId( "right", result );

                return command( link ).onSuccess( new ResponseHandler()
                {
                    @Override
                    public HandlerCommand handleResponse( Response response, ContextResourceClient client )
                    {
                        System.out.println( "Done" );
                        return null;
                    }
                } );
            }
        } );
    }
} );

crc.start();
Query List and Command Progressive
crc.onResource( new ResultHandler<Resource>()
{
    @Override
    public HandlerCommand handleResult( Resource result, ContextResourceClient client )
    {
        return query( "commandwithvalue" ).onSuccess( new ResultHandler<Links>()
        {
            @Override
            public HandlerCommand handleResult( Links result, ContextResourceClient client )
            {
                Link link = LinksUtil.withId( "right", result );

                return command( link ).onSuccess( new ResponseHandler()
                {
                    @Override
                    public HandlerCommand handleResponse( Response response, ContextResourceClient client )
                    {
                        System.out.println( "Done" );
                        return null;
                    }
                } );
            }
        } );
    }
} );

crc.start();