Package azkaban.flow

Source Code of azkaban.flow.ComposedExecutableFlow$DependerCallback

/*
* Copyright 2010 LinkedIn, Inc
*
* 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.
*/

package azkaban.flow;

import azkaban.common.utils.Props;
import azkaban.jobs.Status;

import org.joda.time.DateTime;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* A "composition" of executable flows.  This is composition in the functional sense.
*
* That is, the composition of two functions f(x) and g(x) is equivalent to f(g(x)).
* Similarly, the composition of two ExecutableFlows A and B will result in a dependency
* graph A -> B, meaning that B will be executed and complete before A.
*
* If B fails, A never runs.
*
* You should never really have to create one of these directly.  Try to use MultipleDependencyExecutableFlow
* instead.
*/
public class ComposedExecutableFlow implements ExecutableFlow
{

    private final Object sync = new Object();
    private final String id;
    private final ExecutableFlow depender;
    private final ExecutableFlow dependee;

    private volatile DateTime startTime;
    private volatile DateTime endTime;
    private volatile Status jobState;
    private volatile Map<String, Throwable> exceptions = new HashMap<String, Throwable>();
    private volatile List<FlowCallback> callbacksToCall = new ArrayList<FlowCallback>();
    private volatile Props parentProps;


    public ComposedExecutableFlow(String id, ExecutableFlow depender, ExecutableFlow dependee)
    {
        this.id = id;
        this.depender = depender;
        this.dependee = dependee;

        final Status dependerState = depender.getStatus();
        switch (dependerState) {
          case IGNORED:
            case READY:
                final Status dependeeState = dependee.getStatus();
                switch (dependeeState) {
                  case IGNORED:
                    case READY:
                        jobState = Status.READY;
                        startTime = null;
                        endTime = null;
                        break;
                    case RUNNING:
                        jobState = Status.RUNNING;
                        startTime = dependee.getStartTime();
                        endTime = null;
                        parentProps = dependee.getParentProps();
                        dependee.execute(parentProps, new DependeeCallback());
                        break;
                    case COMPLETED:
                    case SUCCEEDED:
                        jobState = Status.READY;
                        startTime = dependee.getStartTime();
                        parentProps = dependee.getParentProps();
                        endTime = null;
                        break;
                    case FAILED:
                        jobState = Status.FAILED;
                        startTime = dependee.getStartTime();
                        endTime = dependee.getEndTime();
                        parentProps = dependee.getParentProps();
                }
                break;
            case RUNNING:
                jobState = Status.RUNNING;
                startTime = dependee.getStartTime() == null ? depender.getStartTime() : dependee.getStartTime();
                endTime = null;

                parentProps = dependee.getParentProps();

                depender.execute(new Props(parentProps, dependee.getReturnProps()), new DependerCallback());

                break;
            case COMPLETED:
            case SUCCEEDED:
            case FAILED:
                jobState = dependerState;
                startTime = dependee.getStartTime() == null ? depender.getStartTime() : dependee.getStartTime();
                endTime = depender.getEndTime();
                parentProps = dependee.getParentProps();
        }
    }

    @Override
    public String getId()
    {
        return id;
    }

    @Override
    public String getName()
    {
        return depender.getName();
    }

    @Override
    public void execute(Props parentProps, final FlowCallback callback)
    {
        if (parentProps == null) {
            parentProps = new Props();
        }

        synchronized (sync) {
            if (this.parentProps == null) {
                this.parentProps = parentProps;
            }
            else if (jobState != Status.COMPLETED && ! this.parentProps.equalsProps(parentProps)) {
                throw new IllegalArgumentException(
                        String.format(
                                "%s.execute() called with multiple differing parentProps objects.  " +
                                "Call reset() before executing again with a different Props object. this.parentProps[%s], parentProps[%s]",
                                getClass().getSimpleName(),
                                this.parentProps,
                                parentProps
                        )
                );
            }

            switch (jobState) {
                case READY:
                    jobState = Status.RUNNING;
                    callbacksToCall.add(callback);
                    break;
                case RUNNING:
                    callbacksToCall.add(callback);
                    return;
                case COMPLETED:
                case SUCCEEDED:
                    callback.completed(Status.SUCCEEDED);
                    return;
                case FAILED:
                    callback.completed(Status.FAILED);
                default:
                    return;
            }
        }

        if (startTime == null) {
            startTime = new DateTime();
        }

        try {
            dependee.execute(parentProps, new DependeeCallback());
        }
        catch (RuntimeException e) {
            final List<FlowCallback> callbacks;
            synchronized (sync) {
                jobState = Status.FAILED;
                callbacks = callbacksToCall;
            }

            callCallbacks(callbacks, Status.FAILED);

            throw e;
        }
    }

    @Override
    public boolean cancel()
    {
        final boolean dependerCanceled = depender.cancel();
        final boolean dependeeCanceled = dependee.cancel();

        return dependerCanceled && dependeeCanceled;
    }

    @Override
    public Status getStatus()
    {
        return jobState;
    }

    @Override
    public boolean reset()
    {
        boolean retVal;

        synchronized (sync) {
            switch (jobState) {
                case RUNNING:
                    return false;
                default:
                    jobState = Status.READY;
                    retVal = depender.reset();
                    callbacksToCall = new ArrayList<FlowCallback>();
                    parentProps = null;
                    startTime = null;
                    endTime = null;
                    exceptions.clear();
            }
        }

        return retVal;
    }
   
    @Override
    public boolean markCompleted()
    {
        synchronized (sync) {
            switch (jobState) {
                case RUNNING:
                    return false;
                default:
                    jobState = Status.COMPLETED;
                    parentProps = new Props();
            }
        }

        return true;
    }

    @Override
    public boolean hasChildren()
    {
        return true;
    }

    @Override
    public List<ExecutableFlow> getChildren()
    {
        return Arrays.asList(dependee);
    }

    @Override
    public String toString()
    {
        return "ComposedExecutableFlow{" +
               "depender=" + depender +
               ", dependee=" + dependee +
               ", jobState=" + jobState +
               '}';
    }

    @Override
    public DateTime getStartTime()
    {
        return startTime;
    }

    @Override
    public DateTime getEndTime()
    {
        return endTime;
    }

    @Override
    public Props getParentProps() {
        return parentProps;
    }

    @Override
    public Props getReturnProps() {
        return new Props(dependee.getReturnProps(), depender.getReturnProps());
    }

    @Override
    public Map<String,Throwable> getExceptions()
    {
        return exceptions;
    }

    public ExecutableFlow getDepender()
    {
        return depender;
    }

    public ExecutableFlow getDependee()
    {
        return dependee;
    }

    private void callCallbacks(final List<FlowCallback> callbackList, final Status status)
    {
        if (endTime == null) {
            endTime = new DateTime();
        }

        for (FlowCallback callback : callbackList) {
            try {
                callback.completed(status);
            }
            catch (RuntimeException t) {
                // TODO: Figure out how to use the logger to log that a callback threw an exception.
            }
        }
    }

    private class DependerCallback implements FlowCallback
    {
        @Override
        public void progressMade()
        {
            final List<FlowCallback> callbackList;
            synchronized (sync) {
                callbackList = callbacksToCall;
            }

            for (FlowCallback flowCallback : callbackList) {
                flowCallback.progressMade();
            }
        }

        @Override
        public void completed(Status status)
        {
            final List<FlowCallback> callbackList;

            synchronized (sync) {
                jobState = status;
                exceptions.putAll(depender.getExceptions());
                callbackList = callbacksToCall;
            }

            callCallbacks(callbackList, status);
        }
    }

    private class DependeeCallback implements FlowCallback
    {
        @Override
        public void progressMade()
        {
            final List<FlowCallback> callbackList;
            synchronized (sync) {
                callbackList = callbacksToCall;
            }

            for (FlowCallback flowCallback : callbackList) {
                flowCallback.progressMade();
            }
        }

        @Override
        public void completed(Status status)
        {
            final List<FlowCallback> callbackList;


            switch (status) {
                case SUCCEEDED:
                    synchronized(sync) {
                        callbackList = callbacksToCall;
                    }
                    for (FlowCallback flowCallback : callbackList) {
                        flowCallback.progressMade();
                    }
                   
                    depender.execute(new Props(parentProps, dependee.getReturnProps()), new DependerCallback());
                    break;
                case FAILED:
                    synchronized (sync) {
                        jobState = status;
                         exceptions.putAll(dependee.getExceptions());
                        callbackList = callbacksToCall;
                    }

                    callCallbacks(callbackList, status);
                    break;
                default:
                    throw new IllegalStateException(String.format("Got unexpected status[%s] back in a callback.", status));
            }
        }
    }
}
TOP

Related Classes of azkaban.flow.ComposedExecutableFlow$DependerCallback

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.