Package com.asakusafw.yaess.basic

Source Code of com.asakusafw.yaess.basic.AbstractJobScheduler$Engine

/**
* Copyright 2011-2014 Asakusa Framework Team.
*
* 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 com.asakusafw.yaess.basic;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.asakusafw.yaess.basic.JobExecutor.Executing;
import com.asakusafw.yaess.core.ExecutionContext;
import com.asakusafw.yaess.core.Job;
import com.asakusafw.yaess.core.JobScheduler;
import com.asakusafw.yaess.core.PhaseMonitor;
import com.asakusafw.yaess.core.PhaseMonitor.JobStatus;
import com.asakusafw.yaess.core.YaessLogger;

/**
* An abstract implementation of {@link JobScheduler}.
* @since 0.2.3
*/
public abstract class AbstractJobScheduler extends JobScheduler {

    static final YaessLogger YSLOG = new YaessBasicLogger(AbstractJobScheduler.class);

    static final Logger LOG = LoggerFactory.getLogger(AbstractJobScheduler.class);

    /**
     * Returns a {@link JobExecutor} for this scheduler.
     * @return the {@link JobExecutor}
     */
    protected abstract JobExecutor getJobExecutor();

    @Override
    public final void execute(
            PhaseMonitor monitor,
            ExecutionContext context,
            List<? extends Job> jobs,
            ErrorHandler errorHandler) throws InterruptedException, IOException {
        if (monitor == null) {
            throw new IllegalArgumentException("monitor must not be null"); //$NON-NLS-1$
        }
        if (context == null) {
            throw new IllegalArgumentException("context must not be null"); //$NON-NLS-1$
        }
        if (jobs == null) {
            throw new IllegalArgumentException("jobs must not be null"); //$NON-NLS-1$
        }
        if (errorHandler == null) {
            throw new IllegalArgumentException("errorHandler must not be null"); //$NON-NLS-1$
        }
        monitor.open(jobs.size());
        try {
            monitor.checkCancelled();
            Engine engine = new Engine(getJobExecutor(), monitor, context, errorHandler, jobs);
            engine.run();
        } finally {
            monitor.close();
        }
    }

    private static final class Engine {

        private final JobExecutor executor;

        final PhaseMonitor monitor;

        final ExecutionContext context;

        private final ErrorHandler handler;

        private final LinkedList<Job> waiting;

        private final Map<String, Executing> executing;

        private final BlockingQueue<Executing> doneQueue;

        private final Set<String> blockers;

        private boolean sawError;

        Engine(
                JobExecutor executor,
                PhaseMonitor monitor,
                ExecutionContext context,
                ErrorHandler handler,
                List<? extends Job> waiting) {
            assert executor != null;
            assert monitor != null;
            assert context != null;
            assert handler != null;
            assert waiting != null;
            this.executor = executor;
            this.monitor = monitor;
            this.context = context;
            this.handler = handler;
            this.waiting = new LinkedList<Job>(waiting);
            this.executing = new HashMap<String, Executing>();
            this.doneQueue = new LinkedBlockingQueue<Executing>();
            this.blockers = new TreeSet<String>();
            for (Job job : waiting) {
                blockers.add(job.getId());
            }
            this.sawError = false;
        }

        void run() throws IOException, InterruptedException {
            while (waiting.isEmpty() == false) {
                boolean submitted = submitAllWaiting();
                if (submitted == false && executing.isEmpty()) {
                    assert waiting.isEmpty() == false;
                    if (sawError) {
                        waiting.clear();
                    } else {
                        throw new IOException(MessageFormat.format(
                                "Jobnet execution was deadlocked: {4} (batch={0}, flow={1}, phase={2}, execution={3})",
                                context.getBatchId(),
                                context.getFlowId(),
                                context.getPhase(),
                                context.getExecutionId(),
                                blockers));
                    }
                } else {
                    waitForDone();
                }
            }
            while (executing.isEmpty() == false) {
                waitForDone();
            }
            if (sawError) {
                throw new IOException(MessageFormat.format(
                        "Jobnet was failed by preceding errors (batch={0}, flow={1}, phase={2}, execution={3})",
                        context.getBatchId(),
                        context.getFlowId(),
                        context.getPhase(),
                        context.getExecutionId()));
            }
        }

        private boolean submitAllWaiting() throws IOException, InterruptedException {
            boolean sawSubmit = false;
            for (Iterator<Job> iter = waiting.iterator(); iter.hasNext();) {
                Job next = iter.next();
                LOG.debug("Attemps to submit job: {}", next.getId());
                if (isBlocked(next)) {
                    LOG.debug("Job is currently blocked: {}", next.getId());
                    continue;
                }
                iter.remove();
                if (submit(next)) {
                    sawSubmit = true;
                }
            }
            return sawSubmit;
        }

        private void waitForDone() throws InterruptedException, IOException {
            assert executing.isEmpty() == false;
            monitor.checkCancelled();
            Executing done = doneQueue.take();
            assert done.isDone();
            handleDone(done);
            while (true) {
                monitor.checkCancelled();
                Executing rest = doneQueue.poll();
                if (rest == null) {
                    break;
                }
                assert rest.isDone();
                handleDone(rest);
            }
        }

        private void handleDone(Executing done) throws InterruptedException, IOException {
            assert done != null;
            assert done.isDone();
            Executing removed = executing.remove(done.getJob().getId());
            assert removed != null;
            try {
                done.get();
                done(done.getJob());
                monitor.reportJobStatus(done.getJob().getId(), JobStatus.SUCCESS, null);
            } catch (CancellationException e) {
                sawError = true;
                monitor.reportJobStatus(done.getJob().getId(), JobStatus.CANCELLED, e);
            } catch (ExecutionException e) {
                sawError = true;
                Throwable cause = e.getCause();
                if (cause instanceof InterruptedException) {
                    monitor.reportJobStatus(done.getJob().getId(), JobStatus.CANCELLED, cause);
                    cancelExecution();
                    throw (InterruptedException) cause;
                } else if (cause instanceof IOException) {
                    monitor.reportJobStatus(done.getJob().getId(), JobStatus.FAILED, cause);
                    handleException(done.getJob(), (IOException) cause);
                } else if (cause instanceof Error) {
                    monitor.reportJobStatus(done.getJob().getId(), JobStatus.FAILED, cause);
                    cancelExecution();
                    throw (Error) cause;
                } else if (cause instanceof RuntimeException) {
                    monitor.reportJobStatus(done.getJob().getId(), JobStatus.FAILED, cause);
                    cancelExecution();
                    throw (RuntimeException) cause;
                } else {
                    monitor.reportJobStatus(done.getJob().getId(), JobStatus.FAILED, cause);
                    cancelExecution();
                    throw new AssertionError(cause);
                }
            }
        }

        private boolean submit(Job job) throws InterruptedException, IOException {
            assert job != null;
            monitor.checkCancelled();
            try {
                Executing execution = executor.submit(
                        monitor.createJobMonitor(job.getId(), 1), context, job, doneQueue);
                executing.put(execution.getJob().getId(), execution);
                return true;
            } catch (IOException e) {
                sawError = true;
                handleException(job, e);
                return false;
            }
        }

        private void handleException(Job job, IOException exception) throws IOException {
            assert job != null;
            assert exception != null;
            if (handler.handle(context, exception) == false) {
                cancelExecution();
                throw exception;
            }
        }

        private void cancelExecution() {
            for (Executing exec : executing.values()) {
                exec.cancel(true);
            }
        }

        private boolean isBlocked(Job job) {
            assert job != null;
            for (String blocker : job.getBlockerIds()) {
                if (blockers.contains(blocker)) {
                    return true;
                }
            }
            return false;
        }

        private void done(Job job) {
            assert job != null;
            blockers.remove(job.getId());
        }
    }
}
TOP

Related Classes of com.asakusafw.yaess.basic.AbstractJobScheduler$Engine

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.