Data Streaming

Sometimes you need to present the results from a robot execution in real-time. In these cases you want the API to return the extracted values immediately instead of waiting for the robot to finish its execution and access the RQLResult.

The API offers the possibility to receive a callback every time the API receives a value that was returned by the Robot. This is done through the RobotResponseHandler interface


public class DataStreaming {

    public static void main(String[] args) throws ClusterAlreadyDefinedException {

        RoboServer server = new RoboServer("localhost", 50000);
        Cluster cluster = new Cluster("MyCluster", new RoboServer[] {server}, false);
        Request.registerCluster(cluster);

        try {
            Request request = new Request("Library:/Tutorials/NewsMagazine.robot");
            RobotResponseHandler handler = new AbstractFailFastRobotResponseHandler() {
                public void handleReturnedValue(RobotOutputObjectResponse response, Stoppable stoppable) throws RQLException {
                    RQLObject value = response.getOutputObject();
                    Long personId = (Long) value.get("personId");
                    String name = (String) value.get("name");
                    Long age = (Long) value.get("age");
                    System.out.println(personId + ", " + name + ", " + age);
                    }
                };
            request.execute("MyCluster", handler);
        }
        catch (RQLException e) {
            e.printStackTrace();
        }
    }
}
                        

Response streaming using AbstractFailFastRobotResponseHandler


The above example uses the second execute method of the Request, which expects a RobotResponseHandler in addition to the name of the cluster to execute the robot on. In this example we create a RobotResponseHandler by extending AbstractFailFastRobotResponseHandler, which provides default error handling, so we only need to handle the values returned by the robot.

The handleReturnedValue method is called whenever the API receives a returned value from RoboServer. The AbstractFailFastRobotResponseHandler used in this example, will throw exceptions in the same way as the non-streaming execute method. This means that an exception will be thrown in response to any API exceptions generated by the robot.

The RobotResponseHandler has several methods which can be grouped into 3 categories.

Robot life cycle events

Methods which are called when the robot's execution state change on RoboServer, such as when it starts and finishes its execution.

Robot data events

Methods which are called when the robot returns data or errors to the API.

Additional error handling
Methods which are called either due to an error inside RoboServer or in the API.

Method name Description
void requestSent(RoboServer roboServer, ExecuteRequest request) Called when the RequestExecutor has found the server which will execute the request.
void requestAccepted(String executionId) Called when the found RoboServer has accepted the request and put it into it queue.
void robotStarted(Stoppable stoppable) Called when the RoboServer begins to execute the robot. This usually occurs immediately after the robot has been queued, unless the RoboServer is under heavy load, or used by multiple API clients.
void robotDone(RobotDoneEvent reason) Called when the robot is done executing on RoboServer. The RobotDoneEvent is used to specify if the execution terminated normally, due to an error, or if it was stopped.

RobotResponseHandler - robot life cycle events


Method name Description
void handleReturnedValue(RobotOutputObjectResponse response, Stoppable stoppable) Called when the robot has executed a Return Value action and the value has been returned via the socket to the API.
void handleRobotError(RobotErrorResponse response, Stoppable stoppable) Called when the robot raises an API exception. Under normal circumstances the robot will stop executing after the first API exception. This behavior can be overridden by using Request.setStopRobotOnApiException(false), in which case this method will be called multiple times. This is useful if you want a data streaming robot to continue to execute regardless of any generated errors.
void handleWriteLog(RobotMessageResponse response, Stoppable stoppable) Called if the robot executes the Write Log action. This is useful if you wish to provide additional logging info from within a robot.

RobotResponseHandler - robot data events


Method name Description
void handleServerError(ServerErrorResponse response, Stoppable stoppable)

Called if RoboServer generates an error, for instance if the server too busy to process any requests, or if an error occurs inside RoboServer which prevents it from starting the robot.

handleError(RQLException e, Stoppable stoppable) Called if an error occurs inside the API. Most commonly if the client looses the connection to RoboServer.

RobotResponseHandler - additional error handling


Many of the methods will include a Stoppable object, this object can be used to stop for instance in response to a specific error or value returned.

Some of these methods allow you to throw an RQLException, if you do this you should be aware of the consequences. The thread that calls the handler is the thread the calls Request.execute(), this means that any exceptions thrown will bubble up the call stack and out the execute method. If you throw an exception in response to handleReturnedValue, handleRobotError or handleWriteLog it is your responsibility to invoke Stoppable.stop(), or the robot may continue to execute even though the call to Request.execute() has completed.

Data streaming is most often used in one of the following use cases.


public class DataStreamingCollectErrorsAndValues {

    public static void main(String[] args) throws ClusterAlreadyDefinedException {

        RoboServer server = new RoboServer("localhost", 50000);
        Cluster cluster = new Cluster("MyCluster", new RoboServer[] {server}, false);
        Request.registerCluster(cluster);

        try {
            Request request = new Request("Library:/Tutorials/NewsMagazine.robot");
            request.setStopRobotOnApiException(false);  // IMPORTANT!!
            request.setRobotLibrary(new DefaultRobotLibrary());
            ErrorCollectingRobotResponseHandler handler = new ErrorCollectingRobotResponseHandler();
            request.execute("MyCluster", handler);

            System.out.println("Extracted values:");
            for (RobotOutputObjectResponse response : handler.getOutput()) {
                RQLObject value = response.getOutputObject();
                Long personId = (Long) value.get("personId");
                String name = (String) value.get("name");
                Long age = (Long) value.get("age");
                System.out.println(personId + ", " + name + ", " + age);
            }

            System.out.println("Errors:");
            for (RobotErrorResponse error : handler.getErrors()) {
                System.out.println(error.getErrorLocationCode() + ", " + error.getErrorMessage());
            }
        }
        catch (RQLException e) {
            e.printStackTrace();
        }
    }

    private static class ErrorCollectingRobotResponseHandler extends AbstractFailFastRobotResponseHandler {

        private List<RobotErrorResponse> _errors = new LinkedList<RobotErrorResponse>();
        private List<RobotOutputObjectResponse> _output = new LinkedList<RobotOutputObjectResponse>();
        public void handleReturnedValue(RobotOutputObjectResponse response, Stoppable stoppable) throws RQLException {
            _output.add(response);
        }

        @Override
        public void handleRobotError(RobotErrorResponse response, Stoppable stoppable) throws RQLException {
            // do not call super as this will stop the robot
            _errors.add(response);
        }

        public List<RobotErrorResponse> getErrors() {
            return _errors;
        }

        public List<RobotOutputObjectResponse> getOutput() {
            return _output;
        }
    }
}
                        

Response and error collecting using AbstractFailFastRobotResponseHandler


The example above shows how to use a RobotResponseHandler that collects returned values and errors. This type of handler is useful if the robot should continue to execute even when error are encountered, this can be useful if the website is unstable and occasionally times out. Notice that only robot errors (API exceptions) are collected by the handler, if the connection to RoboServer is lost Request.execute() will still throw an RQLException (and the robot will be stopped by RoboServer).

For more details check the RobotResponseHandler JavaDoc.