Hi all,

As per the previous post I've been playing with getting a REST client to be pretty much a workflow engine, with each step leading to a decision point. With the latest changes this workflow can now work indefinitely, without causing StackOverFlow due to the processing stack increasing in each step.

I'll show you two examples of how to write code for this. Both examples first does a query to "/", which returns a representation about that use case. It includes a command called "commandwithvalue", meaning it's a command that requires some input. Upon reading "/" the client then does a query to it. Doing a query on a command is a bit like first getting an HTML page with a form, before filling it in and submitting it. Same here. In this case it returns a list of links, the client then picks one of them, and invokes it as a command. Once that finishes I just do a System.out.println and the whole thing stops.

The first way to do this looks like this:
crc.onResource( new ResultHandler<Resource>()
{
    @Override
public HandlerCommand handleResult( Resource result, ContextResourceClient client )
    {
        return query( "commandwithvalue" );
    }
} ).onQuery( "commandwithvalue", Links.class, 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();
---
First I build up the handlers required for the different possible states in the workflow. There are also generic error handlers registered elsewhere, one of which handles authentication. When crc.start() is called the workflow kicks off by first doing a refresh, and then follows the registered rules for what to do next, one step at a time, until no more steps are required (the returned HandlerCommand is null).

start() looks like this:
public void start()
{
    HandlerCommand command = refresh();
    while (command != null)
        command = command.execute( this );
}
---
Basically, kick off the workflow by doing a refresh (like a browser) on the initial resource, and then let the handlers determine what to do next (the onResource-handler above in this case). Loop while the result of the commands return a new command to be executed.

The second way to do the same thing is to progressively register the handlers, as the workflow progresses:
crc.onResource( new ResultHandler<Resource>()
{
    @Override
public HandlerCommand handleResult( Resource result, ContextResourceClient client )
    {
return query( "commandwithvalue" ).onSuccess( Links.class, 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();
---
It's the exact same code, but instead of registering the handlers upfront they are provided in each step. In the above I instantiate new anonymous classes for each step, so there is a practical limit to how many steps you could do this way. If they instead were defined as classes there is no limit on how many steps the workflow could have, as each step could progressively create the next handler, and provide it with whatever state is necessary to make it understand what to do with the result. Looping back to the start is also entirely possible, if needed.

And that's about it! I think this, or something like this, is what true REST clients should look like, which use hypermedia as the way to proceed towards some determined goal.

Any thoughts on this? I know the syntax is horrible due to lack of closures in Java, but right now it's the principle I'm after. The code code be easily ported to other languages which are prettier for closures, like Scala or Javascript.

/Rickard

_______________________________________________
qi4j-dev mailing list
[email protected]
http://lists.ops4j.org/mailman/listinfo/qi4j-dev

Reply via email to