Package com.persistit

Source Code of com.persistit.Task

/**
* Copyright © 2005-2012 Akiban Technologies, Inc.  All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This program may also be available under different license terms.
* For more information, see www.akiban.com or contact licensing@akiban.com.
*
* Contributors:
* Akiban Technologies, Inc.
*/

package com.persistit;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import com.persistit.exception.PersistitException;
import com.persistit.exception.PersistitInterruptedException;
import com.persistit.exception.TaskEndedException;
import com.persistit.util.Util;

/**
* Abstract superclass of classes that perform long-running utility operations,
* such as export, import and integrity check. Concrete classes should
* frequently call the {@link #poll()} method to allow users to stop, suspend or view
* progress and should post all progress messages to the message
* log by calling {@link #postMessage(String, int)}.
*
* @author peter
* @version 1.0
*/
public abstract class Task implements Runnable {
    /**
     * Status value indicates this <code>Task</code> has been set up but not yet
     * started.
     */
    public final static int STATE_NOT_STARTED = 0;
    /**
     * Status value indicates this <code>Task</code> is running.
     */
    public final static int STATE_RUNNING = 1;
    /**
     * Status value indicates this <code>Task</code> is suspended.
     */
    public final static int STATE_SUSPENDED = 2;
    /**
     * Status value indicates this <code>Task</code> finished normally.
     */
    public final static int STATE_DONE = 3;
    /**
     * Status value indicates this <code>Task</code> failed with an Exception.
     */
    public final static int STATE_FAILED = 4;
    /**
     * Status value indicates this <code>Task</code> was stopped by user
     * intervention.
     */
    public final static int STATE_ENDED = 5;
    /**
     * Status value indicates this <code>Task</code>'s maximum elapsed time
     * expired.
     */
    public final static int STATE_EXPIRED = 6;

    public final static String[] STATE_NAMES = { "notStarted", "running", "suspended", "done", "failed", "ended",
            "expired", };

    /**
     * Indicates normal level of message logging
     */
    public final static int LOG_NORMAL = 0;
    /**
     * Indicates a more verbose level of message logging.
     */
    public final static int LOG_VERBOSE = 1;
    /**
     * Default maximum number of messages held in the message log
     */
    public final static int DEFAULT_MAX_MESSAGE_LOG_SIZE = 500;

    /**
     * The Persistit instance this Task runs against.
     */
    protected Persistit _persistit;
    /**
     * Task ID for this task
     */
    protected long _taskId;
    /**
     * Description of this task
     */
    protected String _description;
    /**
     * Identifies the initiator or owne of this task
     */
    protected String _owner;
    /**
     * System time when started
     */
    protected long _startTime;
    /**
     * System time when finished
     */
    protected long _finishTime;
    /**
     * System time at which Task should terminate if not finished
     */
    protected long _expirationTime;
    /**
     * Total time during which this Task was suspended
     */
    protected long _suspendedDuration;
    /**
     * By default, the {@link #stop} method sets this flag, and the
     * {@link #poll} method throws a {@link TaskEndedException} to stop a
     * running task.
     */
    protected AtomicBoolean _stop = new AtomicBoolean();
    /**
     * When set, the {@link #poll} method waits.
     */
    protected AtomicBoolean _suspend = new AtomicBoolean();
    /**
     * Most recently thrown Exception
     */
    protected Exception _lastException;
    /**
     * State of the task.
     */
    protected int _state = STATE_NOT_STARTED;
    /**
     * Maximum number of messages to hold in the message log before automaticlly
     * culling.
     */
    protected int _maxMessageLogSize = DEFAULT_MAX_MESSAGE_LOG_SIZE;
    /**
     * Number of messages removed from the message log.
     */
    protected int _culledMessageCount;
    /**
     * Degree of verbosity
     */
    protected int _messageLogVerbosity;
    /**
     * Collection of messages posted by the task. Note we are using JDK1.1
     * ArrayList to help port to J2ME.
     */
    protected final ArrayList<String> _messageLog = new ArrayList<String>();
    /**
     * An optional PrintWriter - if not null, {@link #postMessage(String, int)}
     * writes messages to it.
     */
    protected PrintWriter _messageWriter;

    /**
     * The Thread created to run this task.
     */
    protected Thread _thread;

    static boolean isFinalStatus(final int statusCode) {
        return statusCode == STATE_DONE || statusCode == STATE_ENDED || statusCode == STATE_EXPIRED
                || statusCode == STATE_FAILED;
    }

    protected Task() {

    }

    protected Task(final Persistit persistit) {
        setPersistit(persistit);
    }

    /**
     * Set the Persistit instance to be accessed by this Task.
     *
     * @param persistit
     */
    public void setPersistit(final Persistit persistit) {
        _persistit = persistit;
    }

    /**
     * Called by a newly created <code>Thread</code> to perform the task.
     *
     * @throws Exception
     */
    protected abstract void runTask() throws Exception;

    /**
     * Tests whether this <code>Task</code> should stop or suspend. The concrete
     * task implementation should call <code>poll</code> frequently. The default
     * mechanism for stopping a task does not use the deprecated
     * {@link Thread#stop()} method because it is unsafe. Instead, the task
     * should call poll().
     */
    protected void poll() {
        long now = System.currentTimeMillis();
        if (_startTime == 0)
            _startTime = now;
        if (_expirationTime == 0)
            _expirationTime = Long.MAX_VALUE;

        if (now - _suspendedDuration > _expirationTime) {
            _state = STATE_EXPIRED;
            throw new TaskEndedException("Expired");
        }
        if (_stop.get()) {
            _state = STATE_ENDED;
            throw new TaskEndedException("Stopped");
        }
        if (_suspend.get()) {
            while (_suspend.get()) {
                if (_stop.get()) {
                    _state = STATE_ENDED;
                    throw new TaskEndedException("Stopped");
                }
                _state = STATE_SUSPENDED;
                try {
                    Util.sleep(Persistit.SHORT_DELAY);
                } catch (PersistitInterruptedException ie) {
                    throw new TaskEndedException("Interrupted");
                }
            }
            _suspendedDuration += (System.currentTimeMillis() - now);
            _state = STATE_RUNNING;
        }
    }

    /**
     * Parses a String that represents a list of Volume/Tree pairs. Format:
     * volumeName1,treeName1,treeName2;volumeName2,treeName1,treeName2,..
     *
     * @param specification
     *            The list of Volume/Tree pairs, specified as a String
     * @return Array of Trees specified by the list.
     */
    protected Tree[] parseTreeList(String specification) throws PersistitException {
        List<Tree> list = new ArrayList<Tree>();
        StringBuilder sb = new StringBuilder();
        Volume volume = null;
        int end = specification.length();
        for (int index = 0; index <= end; index++) {
            int c = index < end ? specification.charAt(index) : -1;
            if (c == '\\') {
                if (index++ < specification.length()) {
                    sb.append(specification.charAt(index));
                }
            } else if (c == ';' || c == ',' || c == -1) {
                String name = sb.toString();
                sb.setLength(0);
                if (volume == null) {
                    volume = _persistit.getVolume(name);
                    list.add(volume.getDirectoryTree());
                } else {
                    Tree tree = volume.getTree(name, false);
                    if (tree != null) {
                        list.add(tree);
                    }
                }
                if (c != ',')
                    volume = null;
            } else
                sb.append((char) c);
        }

        Tree[] result = list.toArray(new Tree[list.size()]);
        return result;
    }

    /**
     * Abbreviation for <code>System.currentTimeMillis()</code>.
     *
     * @return Current system time
     */
    protected long now() {
        return System.currentTimeMillis();
    }

    /**
     * Sets up a <code>Task</code>.
     *
     * @param taskId
     *            unique identifier for this task invocation
     * @param description
     *            Description of this task
     * @param owner
     *            Hostname and/or username
     * @param maxTime
     *            Maximum wall-clock duration, in milliseconds, that Task will
     *            be allowed to run
     * @param verbosity
     *            Level at which messages posted by the running task will be
     *            retained in the message log.
     * @throws Exception
     */
    public void setup(long taskId, String description, String owner, long maxTime, int verbosity) throws Exception {
        _taskId = taskId;
        _description = description;
        _owner = owner;
        _messageLogVerbosity = verbosity;
        _expirationTime = maxTime > 0 ? now() + maxTime : Long.MAX_VALUE;
    }

    /**
     * Start this Task
     *
     * @throws IllegalStateException
     *             If this task has already been started
     */
    public void start() {
        if (_thread != null) {
            throw new IllegalStateException("Already started");
        }
        _thread = new Thread(this);
        _thread.start();
    }

    /**
     * Request this task to stop. Note: a subclass could reimplement this using
     * <code>_thread.stop()</code> if necessary.
     *
     */
    public void stop() {
        _stop.set(true);
    }

    /**
     * Request this task to suspend. Note: a subclass could reimplement this
     * using <code>_thread.suspend()</code> if necessary.
     *
     */
    public void suspend() {
        _suspend.set(true);
    }

    /**
     * Request this task to resume. Note: a subclass could reimplement this
     * using <code>_thread.resume()</code> if necessary.
     *
     */
    public void resume() {
        _suspend.set(false);
    }

    /**
     * Set the maximum amount of wall-clock time this <code>Task</code> will be
     * permitted to run. If the <code>Task</code> is suspended, the amount of
     * time spend in the suspended state is not counted toward this maximum.
     *
     * @param maxTime
     *            The time, in milliseconds
     */
    public void setMaximumTime(long maxTime) {
        long now = now();
        _expirationTime = now + maxTime;
        if (_expirationTime < now)
            _expirationTime = Long.MAX_VALUE;
    }

    /**
     * Set verbosity level for selecting posted messages. Current available
     * values are {@link #LOG_NORMAL} and {@link #LOG_VERBOSE}.
     *
     * @param verbosity
     *            Verbosity
     */
    public void setMessageLogVerbosity(int verbosity) {
        _messageLogVerbosity = verbosity;
    }

    /**
     * Returns the current verbosity level
     *
     * @return Current verbosity
     */
    public int getMessageLogVerbosity() {
        return _messageLogVerbosity;
    }

    /**
     * Sets a <code>PrintWriter</code> to receive posted messages.
     *
     * @param pw
     *            The <code>PrintWriter</code>
     */
    public void setMessageWriter(PrintWriter pw) {
        _messageWriter = pw;
    }

    /**
     * Returns the current <code>PrintWriter</code>, or <code>null</code> if
     * there is none.
     *
     * @return Current <code>PrintWriter</code>
     */
    public PrintWriter getMessageWriter() {
        return _messageWriter;
    }

    /**
     * Returns a short String message describing the current state of this
     * <code>Task</code>. It should convey a measurement of progress to the
     * end-user.
     *
     * @return A description of this <code>Task</code>'s current state
     */
    public abstract String getStatus();

    /**
     * Returns a String message describing the current state of this
     * <code>Task<code>, possibly in greater detail than {@link #getStatus}. The
     * default implementation returns the same description as <code>getStatus</code>
     * .
     *
     * @return A detailed description of this <code>Task</code>'s current state.
     */
    public String getStatusDetail() {
        return getStatus();
    }

    /**
     * Indicates how much time remains (in milliseconds) until this
     * <code>Task</code>'s maximum time has expired.
     */
    public long getRemainingTime() {
        if (_expirationTime == 0 || _expirationTime == Long.MAX_VALUE) {
            return Long.MAX_VALUE;
        }
        return _expirationTime - now() + _suspendedDuration;
    }

    /**
     * Posts a message (typically to denote progress, error or other interim
     * information) to the message log.
     *
     * @param message
     *            The message
     * @param level
     *            Indicates the verbosity level. The message is posted only if
     *            the level of the message is below the current verbosity
     *            threshhold set through {@link #setMessageLogVerbosity(int)}.
     */
    protected void postMessage(String message, int level) {
        if (level <= _messageLogVerbosity) {
            synchronized (_messageLog) {
                if (_messageLog.size() >= _maxMessageLogSize) {
                    _messageLog.remove(0);
                }
                _messageLog.add(message);
            }
            if (_messageWriter != null) {
                _messageWriter.println();
                _messageWriter.print(message);
                _messageWriter.flush();
            }
        }
    }

    protected void endMessage(int level) {
        if (level <= _messageLogVerbosity && _messageWriter != null) {
            _messageWriter.println();
        }
    }

    /**
     * Appends a String to the final message of the log.
     *
     * @param fragment
     *            The message fragment to append
     * @param level
     *            Indicates the verbosity level. The message is posted only if
     *            the level of the message is below the current verbosity
     *            threshold set through {@link #setMessageLogVerbosity(int)}.
     */
    protected void appendMessage(String fragment, int level) {
        if (level <= _messageLogVerbosity) {
            synchronized (_messageLog) {
                int index = _messageLog.size() - 1;
                if (index >= 0) {
                    String s = _messageLog.get(index) + fragment;
                    _messageLog.set(index, s);
                }
            }
            if (_messageWriter != null) {
                _messageWriter.print(fragment);
                _messageWriter.flush();
            }
        }
    }

    /**
     * Returns the number of messages available in the message log.
     *
     * @return Number of messages
     */
    public int getMessageLogSize() {
        synchronized (_messageLog) {
            return _messageLog.size();
        }
    }

    /**
     * Get all the messages, starting from a specified index.
     *
     * @param from
     *            The index
     * @return The messages
     */
    public String[] getMessages(int from) {
        synchronized (_messageLog) {
            from -= _culledMessageCount;
            if (from < 0)
                from = 0;
            int size = _messageLog.size() - from;
            if (size < 0)
                size = 0;
            String[] results = new String[size];
            for (int index = 0; index < size; index++) {
                results[index] = _messageLog.get(index + from);
            }
            return results;
        }
    }

    /**
     * Remove all the messages up to, but not including the specified index.
     *
     * @param to
     *            Index of first message not to remove.
     */
    public void cullMessages(int to) {
        synchronized (_messageLog) {
            to -= _culledMessageCount;
            if (to >= _messageLog.size())
                _messageLog.clear();
            else {
                for (int index = to; --index >= 0;) {
                    _messageLog.remove(index);
                    _culledMessageCount++;
                }
            }
        }
    }

    /**
     * Implementation of <code>Runnable</code>.
     */
    @Override
    public void run() {
        _startTime = now();
        try {
            _state = STATE_RUNNING;
            runTask();
            _state = STATE_DONE;
        } catch (Exception e) {
            _lastException = e;
            if (e instanceof TaskEndedException) {
                _state = STATE_ENDED;
            } else {
                _state = STATE_FAILED;
            }
        }
        _finishTime = now();
    }

    /**
     * Copy status of this <code>Task</code> to a {@link Management.TaskStatus}.
     *
     * @param ts
     *            The <code>TaskStatus</code>
     * @param details
     *            <code>true</code> to include messages and status detail
     * @param clearMessages
     *            <code>true</code> to cull the messages being returned
     */
    public void populateTaskStatus(Management.TaskStatus ts, boolean details, boolean clearMessages) {
        ts.taskId = _taskId;
        ts.state = _state;
        ts.stateName = STATE_NAMES[_state];
        ts.description = _description;
        ts.owner = _owner;
        ts.startTime = _startTime;
        ts.finishTime = _finishTime;
        ts.expirationTime = _expirationTime;
        ts.lastException = _lastException == null ? "none" : _lastException.toString();
        ts.statusSummary = getStatus();
        if (details) {
            synchronized (_messageLog) {
                ts.newMessages = getMessages(0);
                ts.statusDetail = getStatusDetail();
                if (clearMessages)
                    cullMessages(Integer.MAX_VALUE);
            }
        }
    }

    public boolean isImmediate() {
        return false;
    }
}
TOP

Related Classes of com.persistit.Task

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.