/*==========================================================================*\ | $Id: Step.java,v 1.5 2011/12/25 21:11:41 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2011 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT 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 General Public License for more details. | | You should have received a copy of the GNU Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.grader; import er.extensions.eof.ERXConstant; import java.io.*; import org.apache.log4j.Logger; // ------------------------------------------------------------------------- /** * Represents a single step (plug-in entry) in the grading pipeline or * processing sequence for handling a given assignment. * * @author Stephen Edwards * @author Last changed by $Author: stedwar2 $ * @version $Revision: 1.5 $, $Date: 2011/12/25 21:11:41 $ */ public class Step extends _Step { //~ Constructors .......................................................... // ---------------------------------------------------------- /** * Creates a new Step object. */ public Step() { super(); } //~ Methods ............................................................... // ---------------------------------------------------------- /** * Get a short (no longer than 60 characters) description of this * step, which currently returns {@link #toString()}. * @return the description */ public String userPresentableDescription() { return "(" + order() + "): " + gradingPlugin(); } // ---------------------------------------------------------- public static int maxTimeout() { return maxTimeout; } // ---------------------------------------------------------- public static int defaultTimeout() { return defaultTimeout; } // ---------------------------------------------------------- public static boolean timeoutIsWithinLimits( int value ) { return value > 0 && value <= maxTimeout; } // ---------------------------------------------------------- public static boolean timeoutIsWithinLimits( Number value ) { return value == null || timeoutIsWithinLimits( value.intValue() ); } // ---------------------------------------------------------- public void setTimeout( int value ) { if ( !timeoutIsWithinLimits( value ) ) { value = maxTimeout; } super.setTimeout( value ); } // ---------------------------------------------------------- public void setTimeoutRaw( Integer value ) { if ( value != null && !timeoutIsWithinLimits( value.intValue() ) ) { value = ERXConstant.integerForInt( maxTimeout ); } super.setTimeoutRaw( value ); } // ---------------------------------------------------------- public int effectiveTimeoutForOneRun() { int value = timeout(); return ( value == 0 ) ? defaultTimeout : value; } // ---------------------------------------------------------- public int effectiveEndToEndTimeout() { int timeoutOneRun = effectiveTimeoutForOneRun(); return timeoutOneRun * gradingPlugin().timeoutMultiplier() + gradingPlugin().timeoutInternalPadding(); } // ---------------------------------------------------------- /** * Deprecated; may be removed in the future once all bindings in WOD files * have been renamed. Use {@link #gradingPlugin()} instead. * * @return the grading plugin associated with this step */ @Deprecated public GradingPlugin script() { return gradingPlugin(); } // ---------------------------------------------------------- /** * Execute this step with the given command line argument(s). * * @param args the arguments to pass to the script * @param cwd the working directory to use * @param stdout the file where the script's standard output should * be stored * @param stderr the file where the script's standard error output * should be stored * @return true if the execution script exceeded the time limit * @throws IOException if one occurs */ public boolean execute( String args, File cwd, File stdout, File stderr ) throws IOException { String finalArgs = args; if ( finalArgs == null ) { finalArgs = ""; } if ( stdout != null ) { finalArgs = finalArgs + " 1> " + stdout.getPath(); } if ( stderr != null ) { finalArgs = finalArgs + " 2> " + stderr.getPath(); } ExecThread exeThread = new ExecThread( Thread.currentThread(), finalArgs, cwd); boolean timedOut = false; try { exeThread.start(); Thread.sleep( effectiveEndToEndTimeout() * 1000 ); timedOut = true; exeThread.interrupt(); } catch ( InterruptedException e ) { timedOut = false; } if ( exeThread.exception != null ) { throw exeThread.exception; } return timedOut; } // ---------------------------------------------------------- /** * An internal class used as the thread of execution when this * step is executed. */ private class ExecThread extends Thread { public ExecThread(Thread parent, String argList, File dir) { super("Step.ExecThread"); parentThread = parent; args = argList; cwd = dir; } public void run() { try { gradingPlugin().execute( args, cwd ); } catch ( IOException e ) { // Error creating process, so record it log.error( "Exception executing " + gradingPlugin().mainFilePath(), e ); exception = e; } catch ( InterruptedException e ) { // Stopped by timeout log.info( "Plug-in process was interrupted due to " + "grace period timeout" ); } catch ( Throwable t ) { log.error( "Unhandled exception occurred executing plug-in:", t ); } parentThread.interrupt(); } private Thread parentThread; private String args; private File cwd; public IOException exception = null; } // If you add instance variables to store property values you // should add empty implementions of the Serialization methods // to avoid unnecessary overhead (the properties will be // serialized for you in the superclass). // // ---------------------------------------------------------- // /** // * Serialize this object (an empty implementation, since the // * superclass handles this responsibility). // * @param out the stream to write to // */ // private void writeObject( java.io.ObjectOutputStream out ) // throws java.io.IOException // { // } // // // // ---------------------------------------------------------- // /** // * Read in a serialized object (an empty implementation, since the // * superclass handles this responsibility). // * @param in the stream to read from // */ // private void readObject( java.io.ObjectInputStream in ) // throws java.io.IOException, java.lang.ClassNotFoundException // { // } //~ Instance/static variables ............................................. static final int maxTimeout = org.webcat.core.Application .configurationProperties() .intForKeyWithDefault( "grader.timeout.max", 600 ); static final int defaultTimeout = org.webcat.core.Application .configurationProperties() .intForKeyWithDefault( "grader.timeout.default", 60 ); static Logger log = Logger.getLogger( Step.class ); }