/*
* Copyright (C) 2008 Universidade Federal de Campina Grande
*
* This file is part of OurGrid.
*
* OurGrid is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.ourgrid.common.job;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.TreeSet;
import org.ourgrid.broker.business.dao.Request;
import org.ourgrid.broker.business.dao.WorkerEntry;
import org.ourgrid.common.interfaces.to.GridProcessExecutionResult;
import org.ourgrid.common.interfaces.to.GridProcessHandle;
import org.ourgrid.common.interfaces.to.GridProcessPhase;
import org.ourgrid.common.interfaces.to.GridProcessState;
import org.ourgrid.common.interfaces.to.RequestSpecification;
import org.ourgrid.common.specification.job.JobSpecification;
import org.ourgrid.common.specification.job.TaskSpecification;
import org.ourgrid.common.specification.worker.WorkerSpecification;
import org.ourgrid.common.util.CommonUtils;
/**
* The <code>Job</code> is an abstraction representing a set of
* <code>Task</code>s that the user requested to be executed.
*
* @see Task
* @see GridProcess
*/
public class Job implements Serializable, Comparable<Job> {
private static final long serialVersionUID = 40L;
private final Set<WorkerEntry> blackListedWorkers;
private Map<Long, Request> requests;
/**
* ID of this job.
*/
private int jobid;
/**
* Specification for this Job.
*/
private JobSpecification jobSpec;
/**
* Collection of <code>Task</code>s that compose this job.
*/
private ArrayList<Task> tasks;
/**
* Current state of this job.
*/
private GridProcessState state;
/**
* Creation time of this Job
*/
private long creationTime;
/**
* Time when this Job finished its execution
*/
private long finalizationTime;
/**
* Constant used to indicated that this task has not been finalized.
*/
public static final int TIME_NOT_FINALIZED = -1;
/**
* Creates a new <code>Job</code>.
*
* @param jobid ID of the <code>Job</code>.
* @param jobSpec Specification that defines this <code>Job</code>.
* @param maxFails Maximum number of <code>Replica</code>s that may fail
* on <code>Task</code>s that compose this <code>Job</code>.
* @param maxReplicas Maximum number of replicas that may run simultaneously
* for <code>Tasks</code> of this job
*/
public Job( int jobid, JobSpecification jobSpec) {
this.jobid = jobid;
this.jobSpec = jobSpec;
this.tasks = new ArrayList<Task>();
this.state = GridProcessState.UNSTARTED;
this.creationTime = System.currentTimeMillis();
this.finalizationTime = TIME_NOT_FINALIZED;
this.requests = CommonUtils.createSerializableMap();
this.blackListedWorkers = new LinkedHashSet<WorkerEntry>();
int taskid = 1;
for ( TaskSpecification taskSpec : jobSpec.getTaskSpecs() ) {
this.tasks.add( new Task( this, taskid++, taskSpec) );
}
}
/**
* Informs the <code>Job</code> the result of a <code>Replica</code>.
*
* @param replicaResult Result of the <code>Replica</code>.
* @param newState New state of the <code>Replica</code>.
* @param maxFailed
*/
public void newReplicaResult( GridProcessExecutionResult replicaResult, GridProcessState newState, boolean maxFailed,
boolean canReplicate) {
Task task = getTaskByID( replicaResult.getReplicaHandle().getTaskID() );
if ( task != null ) {
task.newReplicaResult( replicaResult, newState, maxFailed, canReplicate );
if ( !GridProcessState.CANCELLED.equals( state ) ) {
updateState( task );
if (GridProcessState.FINISHED.equals(state) || GridProcessState.FAILED.equals(state)) {
this.finalizationTime = System.currentTimeMillis();
}
}
}
}
/**
* Updates the state of this <code>Job</code> according to the last
* <code>Task</code> that had it's state changed.
*
* @param task Last <code>Task</code> to change state.
*/
private void updateState( Task task ) {
boolean taskRunning = hasTaskInExecution();
if ( GridProcessState.FINISHED.equals( task.getState() ) ) {
if ( taskRunning ) {
state = GridProcessState.RUNNING;
} else {
state = hasAnyTaskFailed() ? GridProcessState.FAILED : GridProcessState.FINISHED;
}
} else if ( GridProcessState.FAILED.equals( task.getState() ) ) {
state = taskRunning ? GridProcessState.RUNNING : GridProcessState.FAILED;
}
}
/**
* Searches for any task that is either in state
* <code>ExecutionState.UNSTARTED</code> or
* <code>ExecutionState.RUNNING</code>.
*
* @return True if any tasks are on either state.
*/
private boolean hasTaskInExecution() {
for ( Task task : tasks ) {
if ( GridProcessState.RUNNING.equals( task.getState() ) || GridProcessState.UNSTARTED.equals( task.getState() ) ) {
return true;
}
}
return false;
}
/**
* Searches for any task that is in <code>ExecutionState.FAILED</code>
* state.
*
* @return True if any tasks are on that state.
*/
private boolean hasAnyTaskFailed() {
for ( Task task : tasks ) {
if ( GridProcessState.FAILED.equals( task.getState() ) ) {
return true;
}
}
return false;
}
/**
* Sets this <code>Job</code> as cancelled meaning that no more
* <code>Replica</code>s can be created from <code>Task</code>s that
* compose this <code>Job</code>.
*/
public void setAsCanceled() {
if ( isRunning() ) {
this.state = GridProcessState.CANCELLED;
this.finalizationTime = System.currentTimeMillis();
}
}
/**
* Get's <code>Task</code> according to the given taskid.
*
* @param taskid ID of the task.
* @return The <code>Task</code> that has such id or null.
* @see Task
*/
public Task getTaskByID( int taskid ) {
return tasks.get( taskid - 1 );
}
/**
* Get's the id for this <code>Job</code>.
*
* @return ID of this <code>Job</code>.
*/
public int getJobId() {
return jobid;
}
/**
* Get's the specification of this <code>Job</code>.
*
* @return <code>JobSpec</code> determining the specification for this
* <code>Job</code>.
*/
public JobSpecification getSpec() {
return jobSpec;
}
/**
* Returns the current state of this <code>Job</code>.
*
* @return State of this <code>Job</code>.
*/
public GridProcessState getState() {
return state;
}
/**
* Return the timestamp on which the job was added
*
* @return timestamp in milliseconds since epoch
* @see System#currentTimeMillis()
*/
public long getCreationTime() {
return this.creationTime;
}
/**
* Return the timestamp on which job was finished
*
* @return timestamp in milliseconds since epoch
* @see System#currentTimeMillis()
*/
public long getFinalizationTime() {
return finalizationTime;
}
/**
* Create's a new <code>Replica</code> of the <code>Task</code>
* corresponding to the given id.
*
* @param taskid ID of the <code>Task</code>.
* @param canReplicate
* @return Newly created <code>Replica</code>.
*/
private GridProcess createNewExecution( int taskid ) {
Task task = getTaskByID( taskid );
if ( task != null ) {
if ( isRunning() ) {
this.state = GridProcessState.RUNNING;
return task.createNewReplica();
}
}
return null;
}
/**
* Verifies if this <code>Job</code> is in state
* <code>ExecutionState.RUNNING</code> or
* <code>ExecutionState.UNSTARTED</code>.
*
* @return <code>true</code> if it is.
*/
public boolean isRunning() {
return GridProcessState.RUNNING.equals( state ) || GridProcessState.UNSTARTED.equals( state );
}
public List<Task> getTasks() {
Set<Task> availableTasks = null;
availableTasks = new TreeSet<Task>(new Comparator<Task>(){
public int compare(Task t1, Task t2) {
return new Integer(t1.getTaskid()).compareTo(new Integer(t2.getTaskid()));
}
});
if (this.tasks != null) {
availableTasks.addAll(this.tasks);
return new ArrayList<Task>(availableTasks);
}
return null;
}
@Override
public String toString() {
return "" + getJobId();
}
public void replicaPhaseUpdate( GridProcessHandle replicaHandle, GridProcessPhase newPhase ) {
Task task = getTaskByID( replicaHandle.getTaskID() );
if ( task != null ) {
task.replicaPhaseUpdate( replicaHandle, newPhase );
}
}
public Collection<WorkerEntry> getAvailableWorkers() {
return getAvailableWorkers(null);
}
public Collection<WorkerEntry> getAvailableWorkers(Comparator<WorkerEntry> comparator) {
Collection<WorkerEntry> returnValue = new LinkedHashSet<WorkerEntry>();
if(comparator == null){
returnValue = new LinkedHashSet<WorkerEntry>();
}
else{
returnValue = new PriorityQueue<WorkerEntry>(tasks.size(), comparator);
}
for (Request request : this.requests.values()) {
for (WorkerEntry entry : request.getWorkers()) {
//TODO id UP ?/if (entry.isUp() && !entry.allocated() && !blackListedWorkers.contains(entry)) {
if (entry.isUp() && !entry.allocated() && !blackListedWorkers.contains(entry)) {
returnValue.add(entry);
}
}
}
return returnValue;
}
public GridProcess createAndAllocateExecution(int taskid, WorkerEntry chosenWorker) {
if ( isRunning() ) {
GridProcess execution = createNewExecution( taskid );
if (execution != null) {
execution.allocate( chosenWorker );
return execution;
}
}
return null;
}
public Collection<WorkerEntry> getDeallocatedWorkerEntries() {
Set<WorkerEntry> returnValue = new LinkedHashSet<WorkerEntry>();
for (Request request : requests.values()) {
for (WorkerEntry entry : request.getWorkers()) {
if (!entry.allocated()) {
returnValue.add(entry);
}
}
}
return returnValue;
}
public void addRequest(RequestSpecification requestSpec, String peerID) {
Request request = new Request(requestSpec, peerID);
this.requests.put(requestSpec.getRequestId(), request);
}
/**
* Remove the requests made on a peer
* @param peerEntry
*/
public void removeRequests(String peerID) {
List<Request> values = new ArrayList<Request>(this.requests.values());
for (Request request : values) {
if (request.getPeerID().equals(peerID)) {
this.requests.remove(request.getSpecification().getRequestId());
}
}
}
public void removeRequest(Long idRequest) {
this.requests.remove(idRequest);
}
public Collection<Request> getRequests() {
return this.requests.values();
}
public void finishRequests() {
this.requests = CommonUtils.createSerializableMap();
}
public WorkerEntry addWorker(long id, WorkerSpecification workerSpec, String workerID) {
Request request = requests.get(id);
WorkerEntry workerEntry = new WorkerEntry(workerSpec, request, workerID);
request.addWorker(workerEntry);
return workerEntry;
}
public void unwantWorker(WorkerEntry workerEntry) {
this.blackListedWorkers.add(workerEntry);
}
public Request getRequest(long requestId) {
return requests.get(requestId);
}
public boolean hasUnallocatedTasks() {
for (Task task : this.tasks) {
if (task.getState().equals(GridProcessState.UNSTARTED) || (!task.getState().equals(GridProcessState.FINISHED)
&& !task.hasRunnigGridProcess())) {
return true;
}
}
return false;
}
public Set<WorkerEntry> getBlackListedWorkers() {
return blackListedWorkers;
}
public int compareTo(Job o) {
return jobid - o.jobid;
}
}