de.zib.gndms.gndmc.gorfx.AbstractTaskFlowExecClient.java Source code

Java tutorial

Introduction

Here is the source code for de.zib.gndms.gndmc.gorfx.AbstractTaskFlowExecClient.java

Source

package de.zib.gndms.gndmc.gorfx;
/*
 * Copyright 2008-2011 Zuse Institute Berlin (ZIB)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import de.zib.gndms.common.model.gorfx.types.*;
import de.zib.gndms.common.rest.Facets;
import de.zib.gndms.common.rest.GNDMSResponseHeader;
import de.zib.gndms.common.rest.Specifier;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;

import java.util.List;
import java.util.UUID;

/**
 * @author try ma ik jo rr a zib
 * @date 14.03.11  11:38
 * @brief Performs all requests necessary for taskflow execution.
 *
 * To put the caller in control over the taskflow this class provides
 * handler methods for the result of imported calls. 
 *
 * \note The handler methods are only called if the preceding server response
 * was positive.
 */
public abstract class AbstractTaskFlowExecClient implements TaskStatusHandler {

    private GORFXClient gorfxClient; ///< A ready to uses instance of the gorfx client.
    private TaskFlowClient tfClient; ///< A ready to uses instance of the taskflow client.
    private TaskClient taskClient; ///< A ready to uses instance of the task client.
    private long pollingDelay = 1000; ///< delay in ms to poll the task status, once the task is running.

    /**
     * @brief Executes a complete task flow.
     *
     * @param order The order of the taskflow.
     * @param dn    The DN of the user calling the task flow.
     * \note here the workflow id is generated on the fly.
     */
    public void execTF(Order order, String dn) {

        execTF(order, dn, true, null, UUID.randomUUID().toString());
    }

    /**
     *
     * @brief Executes a complete task flow.
     *
     * This method is imported when you want to understand the
     * Taskflow protocol.
     *
     * @param order The order of the taskflow.
     * @param dn    The DN of the user calling the task flow.
     *
     * @param withQuote Activates co-scheduling
     * @param desiredQuote A quote holding desired time values for
     * the tasflow execution.
     *
     * \note for now the workflow id is generated on the fly.
     */
    public void execTF(Order order, String dn, boolean withQuote, final Quote desiredQuote) {
        execTF(order, dn, withQuote, desiredQuote, UUID.randomUUID().toString());
    }

    /**
     *
     * @brief Executes a complete task flow.
     *
     * This method is imported when you want to understand the
     * Taskflow protocol.
     *
     * @param order The order of the taskflow.
     * @param dn    The DN of the user calling the task flow.
     *
     * @param withQuote Activates co-scheduling
     * @param desiredQuote A quote holding desired time values for
     * the tasflow execution.
     * @param wid the workflow id
     */
    public void execTF(Order order, String dn, boolean withQuote, final Quote desiredQuote, final String wid) {

        GNDMSResponseHeader context = setupContext(new GNDMSResponseHeader());

        if (null == gorfxClient) {
            throw new IllegalStateException("You need to set gorfxClient before executing a TaskFlow!");
        }

        /**
         * \code this is important
         */

        // sends the order and creates the task flow
        ResponseEntity<Specifier<Facets>> res = gorfxClient.createTaskFlow(order.getTaskFlowType(), order, dn, wid,
                context);

        if (!HttpStatus.CREATED.equals(res.getStatusCode())) {
            throw new RuntimeException("createTaskFlow failed " + res.getStatusCode().name() + " ("
                    + res.getStatusCode() + ")" + " on URL " + gorfxClient.getServiceURL());
        }

        // the taskflow id is stored under "id" in the urlmap
        String tid = res.getBody().getUriMap().get("id");

        Integer q = null;
        if (withQuote) {
            if (null == tfClient) {
                throw new IllegalStateException("No TaskFlowClient set.");
            }

            if (desiredQuote != null) {
                tfClient.setQuote(order.getTaskFlowType(), tid, desiredQuote, dn, wid);
            }
            // queries the quotes for the task flow
            ResponseEntity<List<Specifier<Quote>>> res2 = tfClient.getQuotes(order.getTaskFlowType(), tid, dn, wid);

            if (!HttpStatus.OK.equals(res2.getStatusCode()))
                throw new RuntimeException("getQuotes failed " + res2.getStatusCode().name());

            // lets the implementors of this class choose a quote
            q = selectQuote(res2.getBody());
        }

        //
        // 'til here it is valid to change the order and request new quotes
        // 

        // accepts quote q and triggers task creation
        ResponseEntity<Specifier<Facets>> res3 = tfClient.createTask(order.getTaskFlowType(), tid, q, dn, wid);

        if (!HttpStatus.CREATED.equals(res3.getStatusCode()))
            throw new RuntimeException("createTask failed " + res3.getStatusCode().name());

        final Specifier<Facets> taskSpecifier = res3.getBody();

        // let the implementor do smart things with the task specifier
        handleTaskSpecifier(taskSpecifier);

        // the task id is stored under "taskId" in the specifiers urlmap
        waitForFinishOrFail(taskSpecifier, this, taskClient, pollingDelay, dn, wid);

        /**
         * \endcode
         */
    }

    /**
     * Polls a running task, until its either finished or failed.
     *
     * @param taskSpecifier The specifier of the task.
     * @param statusHandler The handler for the task status, can update some sort of UI.
     * @param taskClient  The task client, which should be used for polling.
     * @param pollingDelay The pollingDelay, its the delay between polling.
     * @param dn  The user DN.
     * @param wid The workflow id.
     *
     * @return The final task status, finished or failed.
     */
    public static TaskStatus waitForFinishOrFail(final Specifier<Facets> taskSpecifier,
            final TaskStatusHandler statusHandler, final TaskClient taskClient, final long pollingDelay,
            final String dn, final String wid) {

        TaskStatus ts;
        String taskId = taskSpecifier.getUriMap().get("taskId");

        ResponseEntity<TaskStatus> stat;
        boolean done = false;
        do {
            // queries the status of the task execution
            stat = taskClient.getStatus(taskId, dn, wid);
            if (!HttpStatus.OK.equals(stat.getStatusCode()))
                throw new RuntimeException("Task::getStatus failed " + stat.getStatusCode().name());
            ts = stat.getBody();

            // allows the implementor to do something with the task status
            statusHandler.handleStatus(ts);
            try {
                Thread.sleep(pollingDelay);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            // finished without an error, good(?)
            if (finished(ts)) {
                // collect the result
                ResponseEntity<TaskResult> tr = null;
                try {
                    tr = taskClient.getResult(taskId, dn, wid);
                } catch (HttpClientErrorException e) {
                    if (404 == e.getStatusCode().value())
                        continue;
                }

                if (!HttpStatus.OK.equals(tr.getStatusCode()))
                    throw new RuntimeException("Failed to obtain task result " + tr.getStatusCode().name());

                // do something with it
                statusHandler.handleResult(tr.getBody());

                done = true;
            } else if (failed(ts)) { // must be failed, not so good
                // find out way
                ResponseEntity<TaskFailure> tf = taskClient.getErrors(taskId, dn, wid);
                if (!HttpStatus.OK.equals(tf.getStatusCode()))
                    throw new RuntimeException("Failed to obtain task errors " + tf.getStatusCode().name());

                // handle the failure
                statusHandler.handleFailure(tf.getBody());

                done = true;
            }
        } while (!done); // run 'til the task hits a final state

        return ts;
    }

    /**
     * Same as the above method, but without a status handler.
     *
     * @param taskSpecifier The specifier of the task.
     * @param taskClient  The task client, which should be used for polling.
     * @param pollingDelay The pollingDelay, its the delay between polling.
     * @param dn  The user DN.
     * @param wid The workflow id.
     *
     * @return The final task status, finished or failed.
     */
    public static TaskStatus waitForFinishOrFail(final Specifier<Facets> taskSpecifier, final TaskClient taskClient,
            final long pollingDelay, final String dn, final String wid) {
        return waitForFinishOrFail(taskSpecifier, new LazyStatusHandler(), taskClient, pollingDelay, dn, wid);
    }

    /**
     * Offers implementing clients the possibility to add values to the request context.
     *
     * The request context is used to create taskflows and the right place to provide
     * myProxyTokens.
     *
     * @param context The create request context.
     * @return The augmented context
     */
    protected GNDMSResponseHeader setupContext(final GNDMSResponseHeader context) {

        // example: context.addMyProxyToken( "c3grid", "foo", "bar" );
        return context;
    }

    /**
     * @brief Allows the caller to select a quote.
     * 
     * @param quotes All available quotes.
     * 
     * @return The index of the accepted quote. \c null will disable
     *         quote usage.
     */
    protected abstract Integer selectQuote(List<Specifier<Quote>> quotes);

    /** 
     * @brief Allows additional handling for the task specifier.
     * 
     * @param ts The task specifier, including all task facets as
     * payload.
     */
    protected abstract void handleTaskSpecifier(Specifier<Facets> ts);

    /** 
     * @brief Handler for the task result.
     *
     * Override this method to gain access to the task(flow) result an
     * send it to the user, post process it or store it for later
     * usage.
     *@param res The result object.
     */
    public abstract void handleResult(TaskResult res);

    /** 
     * @brief Handler for task failures.
     * 
     * Override this method to gain access to the task(flow) error
     * object, e.g. to send an error-report to someone who cares.
     *
     * @param fail The failure object.
     */
    public abstract void handleFailure(TaskFailure fail);

    /** 
     * @brief Checks if ts is FINISHED.
     * 
     * @param ts The current task state.
     * 
     * @return \c true if ts is FINISHED
     */
    private static boolean finished(TaskStatus ts) {
        return TaskStatus.Status.FINISHED.equals(ts.getStatus());
    }

    /** 
     * @brief Checks if ts is FINISHED.
     * 
     * @param ts The current task state.
     * 
     * @return \c true if ts is FINISHED
     */
    private static boolean failed(TaskStatus ts) {
        return TaskStatus.Status.FAILED.equals(ts.getStatus());
    }

    /**
     * @brief Delivers the value of ::gorfxClient.
     * 
     * @return The value of ::gorfxClient.
     */
    public GORFXClient getGorfxClient() {
        return gorfxClient;
    }

    /**
     * @brief Sets the value of ::gorfxClient to \e gorfxClient.
     * 
     * @param gorfxClient The new value of ::gorfxClient.
     */
    public void setGorfxClient(GORFXClient gorfxClient) {
        this.gorfxClient = gorfxClient;
    }

    /**
     * @brief Delivers the value of ::tfClient.
     * 
     * @return The value of ::tfClient.
     */
    public TaskFlowClient getTfClient() {
        return tfClient;
    }

    /**
     * @brief Sets the value of ::tfClient to \e tfClient.
     * 
     * @param tfClient The new value of ::tfClient.
     */
    public void setTfClient(TaskFlowClient tfClient) {
        this.tfClient = tfClient;
    }

    /**
     * @brief Delivers the value of ::taskClient.
     * 
     * @return The value of ::taskClient.
     */
    public TaskClient getTaskClient() {
        return taskClient;
    }

    /**
     * @brief Sets the value of ::taskClient to \e taskClient.
     * 
     * @param taskClient The new value of ::taskClient.
     */
    public void setTaskClient(TaskClient taskClient) {
        this.taskClient = taskClient;
    }

    /**
     * @brief Delivers the value of ::pollingDelay.
     * 
     * @return The value of ::pollingDelay.
     */
    public long getPollingDelay() {
        return pollingDelay;
    }

    /**
     * @brief Sets the value of ::pollingDelay to \e pollingDelay.
     * 
     * @param pollingDelay The new value of ::pollingDelay.
     */
    public void setPollingDelay(long pollingDelay) {
        this.pollingDelay = pollingDelay;
    }

    public static class LazyStatusHandler implements TaskStatusHandler {

        @Override
        public void handleStatus(final TaskStatus stat) {
            // this handler is lazy, it does nothing
        }

        @Override
        public void handleResult(final TaskResult body) {
            // not required here
        }

        @Override
        public void handleFailure(final TaskFailure body) {
            // not required here
        }
    }
}