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