/*
* 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.specification.semantic;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import org.ourgrid.common.specification.CompilerMessages;
import org.ourgrid.common.specification.exception.JobSpecificationException;
import org.ourgrid.common.specification.exception.TaskSpecificationException;
import org.ourgrid.common.specification.job.IOBlock;
import org.ourgrid.common.specification.job.IOEntry;
import org.ourgrid.common.specification.job.JobSpecification;
import org.ourgrid.common.specification.job.TaskSpecification;
import org.ourgrid.common.specification.main.CommonCompiler;
import org.ourgrid.common.specification.semantic.exception.SemanticException;
import org.ourgrid.common.specification.syntactical.CommonSyntacticalAnalyzer;
import org.ourgrid.common.specification.token.Token;
/**
* This entity is the set of actions that the JOB grammar uses to build a answer
* to the compilation of sources wrote in this language. Created on 15/06/2004
*
* @see JDFSemanticActionsTest
*/
public class JDFSemanticActions implements SemanticActions {
private static transient final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
.getLogger( JDFSemanticActions.class );
private Stack<String> stack;
private Token actualToken;
private int mode = CommonSyntacticalAnalyzer.MODE_NORMAL;
// Needed variables
private boolean isJobAttrib = true;
private IOBlock transferEntries = null;
private List<TaskSpecification> tasksSpecs = new ArrayList<TaskSpecification>();
// Job (default) attributes
private String jobRemoteScript = null;
private IOBlock jobInputEntries, jobOutputEntries = null;
// Actual Task attributes
private String remoteScript = null;
private IOBlock inputEntries, outputEntries = null;
private String condition = null;
private JobSpecification theJob = null;
/* The last action that will be executed has to set it */
private List<JobSpecification> result;
private String sabotageCheckCommand;
private String jobSabotageCheckCommand;
/**
* The constructor
*/
public JDFSemanticActions() {
this.stack = new Stack<String>();
}
/**
* @see org.ourgrid.common.specification.semantic.SemanticActions#performAction(java.lang.String,
* org.ourgrid.common.specification.token.Token)
*/
public void performAction( String action, Token token ) throws SemanticException {
this.actualToken = token;
try {
Class semantic = Class.forName( this.getClass().getName() );
Method method = semantic.getMethod( action );
method.invoke( this );
} catch ( NoSuchMethodException nsmex ) {
throw new SemanticException( CompilerMessages.SEMANTIC_ACTION_NOT_FOUND, nsmex );
} catch ( ClassNotFoundException cnfex ) {
throw new SemanticException( CompilerMessages.SEMANTIC_CLASS_NOT_FOUND, cnfex );
} catch ( InvocationTargetException itex ) {
if ( itex.getCause() instanceof SemanticException ) {
throw (SemanticException) itex.getCause();
}
throw new SemanticException( CompilerMessages.SEMANTIC_FATAL_ERROR(), itex.getCause() );
} catch ( IllegalAccessException iaex ) {
throw new SemanticException( CompilerMessages.SEMANTIC_FATAL_ILLEGAL_ACCESS, iaex );
}
}
/**
* @see org.ourgrid.common.specification.semantic.SemanticActions#getOperationalMode()
*/
public int getOperationalMode() {
return this.mode;
}
/**
* @see org.ourgrid.common.specification.semantic.SemanticActions#getResult()
*/
public List<JobSpecification> getResult() {
return this.result;
}
/**
* This action: Sets the actual script of the remote script. Actual can be
* jobs (default) script if this.isJobAttrib == true It happens only when
* the first tag "task:" was not found yet.
*/
public void action4() {
if ( this.isJobAttrib == true ) {
this.jobRemoteScript = actualToken.getSymbol();
} else {
this.remoteScript = actualToken.getSymbol();
}
}
/**
* This action: Tells to this entity that the job (default) attributes
* reading was finished. That means that will begin to read tasks.
*/
public void action8() {
this.isJobAttrib = false;
}
/**
* This action: Closes a Task and put it at "this.tasksSpec" list.
*
* @throws SemanticException When the task could not be validated then the
* TaskSpecificationException is wrapped into this one.
*/
public void action9() throws SemanticException {
checkTaskEntries();
TaskSpecification task;
try {
task = new TaskSpecification( this.inputEntries, this.remoteScript, this.outputEntries, this.sabotageCheckCommand );
tasksSpecs.add( task );
nullTaskEntries();
} catch ( TaskSpecificationException tsex ) {
LOG.error( "A task could not be validated! - Interrupting the compilation process." );
throw new SemanticException( CompilerMessages.BAD_TASK_DEFINITION( (tasksSpecs.size() + 1), tsex.getCause()
.getMessage() ) );
}
}
/**
* This action: Initializes the "condition" string for the actual block of
* I/O commands or job expression.
*/
public void action10() {
this.condition = new String();
}
/**
* This action: Concatenates the symbol of the Token at the condition string
* Statement.
*/
public void action11() {
this.condition = condition + " " + this.actualToken.getSymbol();
}
/**
* This action: Sets the condition string to null, it means that the
* condition read will not be used anymore.
*/
public void action12() {
this.condition = null;
}
/**
* This action: Mounts the condition for the ELSE block and push it at the
* stack.
*/
public void action13() {
this.condition = "! ( " + condition.trim() + " )";
this.stack.push( condition );
}
/**
* This action: Closes and set the job expression.
*/
public void action14() {
this.theJob.setRequirements( condition.trim() );
}
/**
* This action: initializes the IOBlock to receive a new one.
*/
public void action15() {
this.transferEntries = new IOBlock();
}
/**
* This action: Puts the I/O block condition statement at the stack.
*/
public void action16() {
this.stack.push( this.condition.trim() );
}
/**
* This action: Will push a empty String object at the stack. It happens
* when the I/O entries have no conditions to be transfered.
*/
public void action17() {
this.condition = "";
this.stack.push( condition );
}
/**
* This action: Will push the other parts ( command, file, path ) of the I/O
* commands at the stack.
*/
public void action18() {
this.stack.push( this.actualToken.getSymbol() );
}
/**
* This action: Builds a IOEntry and insert it at the actual IOBlock.
*
* @throws SemanticException If the user did not define a part of the I/O
* command.
*/
public void action19() throws SemanticException {
String place = stack.pop();
String filePath = stack.pop();
String command = stack.pop();
String condition = stack.peek(); // do not remove it because
// it can be necessary for
// other entries.
IOEntry entry = buildEntry( command, filePath, place );
this.transferEntries.putEntry( condition, entry );
}
/**
* This action:Pops the "condition" string that remains at the stack top.
*/
public void action20() {
stack.pop();
}
/**
* This action: Sets the IOBlock built as the input entry for the
* actualTask.
*/
public void action21() {
if ( this.isJobAttrib == true ) {
this.jobInputEntries = this.transferEntries;
} else {
this.inputEntries = this.transferEntries;
}
}
/**
* This action: Sets the final result LIST object.
*
* @throws SemanticException
*/
public void action22() throws SemanticException {
try {
this.theJob.setTaskSpecs( this.tasksSpecs );
} catch ( JobSpecificationException e ) {
throw new SemanticException( "Tried to contruct a Job Spec based on a problematic list of Tasks Specs. " );
}
this.result = new LinkedList<JobSpecification>();
result.add( theJob );
}
/**
* This action: Sets the IOBlock built as the output entry for the
* actualTask.
*/
public void action23() {
if ( this.isJobAttrib == true ) {
this.jobOutputEntries = this.transferEntries;
} else {
this.outputEntries = this.transferEntries;
}
}
/**
* This action: initializes the object JobSpec with the found label.
*/
public void action24() {
theJob = new JobSpecification( this.actualToken.getSymbol() );
mode = CommonSyntacticalAnalyzer.MODE_NORMAL;
}
/**
* This action: initializes the object JobSpec with a empty string because
* any label was defined.
*/
public void action25() {
theJob = new JobSpecification( "" );
}
/**
* This action: sets the reading mode to readstring
*/
public void action26() {
mode = CommonSyntacticalAnalyzer.MODE_READSTRING;
}
/**
* This action: sets the reading mode to normal
*/
public void action27() {
mode = CommonSyntacticalAnalyzer.MODE_NORMAL;
}
/**
* This action: sets the reading mode to readline
*/
public void action28() {
mode = CommonSyntacticalAnalyzer.MODE_READLINE;
}
/**
* This action: sets the sabotage check command
*/
public void action29() {
if ( this.isJobAttrib == true ) {
this.jobSabotageCheckCommand = actualToken.getSymbol();
} else {
this.sabotageCheckCommand = actualToken.getSymbol();
}
}
/**
* This action: Puts the value string for a attribute at the top of the
* stack.
*
* @throws SemanticException
*/
public void action30() throws SemanticException {
this.stack.push( actualToken.getSymbol() );
}
/**
* This action: Puts the value string for a attribute at the top of the
* stack.
*
* @throws SemanticException
*/
public void action31() throws SemanticException {
String tempAttValue = actualToken.getSymbol();
if ( tempAttValue.equals( "" ) ) {
throw new SemanticException( CompilerMessages.SEMANTIC_EMPTY_ATTRIBUTE_VALUE( stack.pop(), actualToken
.getLine() ) );
}
this.stack.push( tempAttValue );
}
/**
* This action: Inserts a new attribute at the actual worker specification
* or at the default map depending of the type of the attribute (worker or
* default).
*/
public void action32() {
String attValue = stack.pop();
String attName = stack.pop();
theJob.getAnnotations().put( attName, attValue );
}
// /////////// AUXILIAR METHODS /////////////////////
/*
* Make all the entries for a task point to null.
*/
private void nullTaskEntries() {
inputEntries = null;
remoteScript = null;
outputEntries = null;
sabotageCheckCommand = null;
}
/*
* This method checks if a task has any non defined entry and if exists any
* defauld value (from Job) to insert at that.
*/
private void checkTaskEntries() {
if ( inputEntries == null ) {
if ( jobInputEntries != null )
inputEntries = jobInputEntries;
else
inputEntries = new IOBlock();
}
if ( remoteScript == null && jobRemoteScript != null ) {
remoteScript = jobRemoteScript;
}
if ( outputEntries == null ) {
if ( jobOutputEntries != null )
outputEntries = jobOutputEntries;
else
outputEntries = new IOBlock();
}
if ( sabotageCheckCommand == null && jobSabotageCheckCommand != null ) {
sabotageCheckCommand = jobSabotageCheckCommand;
}
}
/*
* Builds a IOEntry object making the local file paths absolute ones. @param
* command the command of the I/O operation, that can be - GET, PUT or STORE
* @param filePath the file path of the source @param place the file path of
* the destiny @return a IOEntry object referente of the attributes but,
* including a absolute path for the local ( that will be found at the home
* machine ) file paths using the description file parent. @throws
* SemanticException if any of the paramethers is a empty string
*/
private IOEntry buildEntry( String command, String filePath, String place ) throws SemanticException {
if ( command.equals( "" ) || filePath.equals( "" ) || place.equals( "" ) ) {
throw new SemanticException( CompilerMessages.SEMANTIC_MALFORMED_IO_COMMAND );
}
String localParentDir = CommonCompiler.getSourceParentDir();
if ( command.equalsIgnoreCase( "GET" ) ) {
// To insert the JDF parent directory in relative path
File temp = new File( place );
if ( !temp.isAbsolute() )
place = localParentDir + File.separator + place;
} else { // command is PUT or STORE
// To insert the JDF parent directory in relative path
File temp = new File( filePath );
if ( !temp.isAbsolute() )
filePath = localParentDir + File.separator + filePath;
}
return new IOEntry( command, filePath, place );
}
}