/**
 * Copyright 2011 The Open Source Research Group,
 *                University of Erlangen-Nürnberg
 *
 * 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 org.sweble.wikitext.articlecruncher;

import java.util.List;

import org.sweble.wikitext.articlecruncher.JobTrace.Signer;

/**
 * A job with a history and a trace.
 * 
 * A job is one unit of work that is generated by a job generator (see also
 * {@link JobGeneratorFactory}). It is then processed in a processing node (see
 * also {@link ProcessingNodeFactory}) and finally its result is stored by a
 * storer (see also {@link StorerFactory}).
 * 
 * To generate a job first inherit from this class and add a payload to your job
 * class. This payload is then processed by a processing node.
 * 
 * A job starts with a trace (and a job ID stored in that trace), no result or
 * exception and in the UNPROCESSED state.
 * 
 * After processing a job can have a result if processing was successful or an
 * exception can be associated with the job in case of failure. It will then be
 * in the HAS_RESULT or FAILED state. It will further have a single history
 * element in its history chain. The state of a job as well as its result or an
 * exception are actually stored in that history element. The Job class offers
 * delegating getters for convenience.
 * 
 * Further history elements can be present if a job failed multiple times and
 * was retried.
 */
public abstract class Job
{
	private final JobTrace trace;

	private JobHistory history;

	// =========================================================================

	public Job()
	{
		this.trace = new JobTrace();
	}

	public Job(JobTrace trace)
	{
		this.trace = trace;
	}

	// =========================================================================

	public JobTrace getTrace()
	{
		return trace;
	}

	public long getJobId()
	{
		return trace.getJobId();
	}

	public List<Signer> getSigners()
	{
		return trace.getSigners();
	}

	public void signOff(Class<?> signer, String signature)
	{
		trace.signOff(signer, signature);
	}

	// =========================================================================

	/**
	 * Retrieve the latest history element of the job. The history of a job
	 * forms a chain. The head element (the element returned by this getter)
	 * represents the latest processing attempt.
	 * 
	 * @return Return the latest history element.
	 */
	public JobHistory getHistory()
	{
		return history;
	}

	/**
	 * Set the result of a successfully processed job.
	 */
	public synchronized void processed(Object result)
	{
		history = new JobHistory(history, result);
	}

	/**
	 * Set the exception of a job for which processing failed.
	 * 
	 * Such a job can be re-submitted.
	 */
	public synchronized void failed(Exception exception)
	{
		history = new JobHistory(history, exception);
	}

	// =========================================================================

	/**
	 * Returns the state of this job.
	 */
	public synchronized JobProcessingState getState()
	{
		return history != null ? history.getState() : JobProcessingState.UNPROCESSED;
	}

	/**
	 * Returns the result produced in the last processing run.
	 * 
	 * @return The result produced in the last processing run or
	 *         <code>null</code> if the last processing run failed or if the job
	 *         has not yet been processed.
	 */
	public synchronized Object getResult()
	{
		return (getState() == JobProcessingState.HAS_RESULT) ? history.getResult() : null;
	}

	/**
	 * Returns the exception that occurred in the last processing run.
	 * 
	 * @return The exception that occurred in the last processing run or
	 *         <code>null</code> if that last processing run succeeded or if the
	 *         job has not yet been processed.
	 */
	public synchronized Exception getException()
	{
		return (getState() == JobProcessingState.FAILED) ? history.getException() : null;
	}
}
