16
16
import java.io.IOException;
17
17
import java.util.ArrayList;
18
18
import java.util.Collections;
19
import java.util.Date;
19
20
import java.util.List;
22
import org.apache.cayenne.ObjectContext;
21
23
import org.apache.commons.logging.Log;
22
24
import org.apache.commons.logging.LogFactory;
58
56
* @see TeamSubmission#getSrcFile()
60
58
public static final String SRC_FILE_VAR = "$SrcFile";
64
* The submission to score
66
* Needed to get the scoring mode and the competition start time
67
* if using a time based scoring method. <br />
68
* See {@link ScoringMode} for details on each mode.
70
public static void scoreSubmission(TeamSubmission ts, CompetitionManager cm) {
71
ScoringMode sm = cm.getScoringMode();
78
Date sub = ts.getTime();
79
Date start = cm.getCompetition().getStart();
80
long diff = sub.getTime() - start.getTime();
81
pts = (int) diff / 60000;
85
start = cm.getCompetition().getStart();
86
diff = sub.getTime() - start.getTime();
87
int minutes = (int) diff / 60000;
88
pts = (minutes * 10) / ts.getProblem().getDifficulty();
91
throw new RuntimeException(
92
"Grading mode `ExeTime` not yet implemented");
94
throw new RuntimeException(
95
"Grading mode `Custom` not yet implemented");
100
throw new RuntimeException("Points cannot be less than zero!");
62
105
* The maximum runtime for a program in milliseconds
64
107
public final int MAX_RUNTIME;
65
private CompetitionManager compMan;
66
private Log log = LogFactory.getLog(Grader.class);
108
private final CompetitionManager compMan;
110
* Normally Commons Logging is used (which is using Log4J anyway) but here
111
* we need to directly use Log4J so that an additional appender can be
114
private Log log = LogFactory.getLog(getClass());
67
115
private ProcessBuilder pb;
68
116
private final Submission submission;
71
* Create a new Grader.
119
* The mode of this grader
123
protected final GradingMode mode;
126
* Create a new Grader
74
* The submission to be graded.
129
* The submission to be graded
131
* The competition manager to pull settings from
133
* The mode for this grader
76
public Grader(final Submission s, CompetitionManager cm) {
135
public Grader(final Submission s, CompetitionManager cm, GradingMode mode) {
79
139
MAX_RUNTIME = cm.getExeTimeout();
80
if (s instanceof TeamSubmission)
81
((TeamSubmission) s).setPoints(Grader.INIT_POINTS);
82
140
log.debug("Creating Grader for " + s.getObjectId());
97
155
pb.directory(submission.getTempDir());
98
156
final List<String> cmds = new ArrayList<String>();
99
157
final List<Compilearg> cArgs =
100
submission.getLanguage().getCompilerArgs();
158
submission.getLanguage().getCompileArgs();
159
if (cArgs.size() == 0) {
160
log.info("No compiler args for language:"
161
+ submission.getLanguage().getName() + " not compiling");
101
164
Collections.sort(cArgs);
102
165
for (final Compilearg ca : cArgs) {
103
166
String val = ca.getVal();
127
190
public ExecutionResult execute() throws IOException {
128
191
final CompileResult cr = compile();
129
if (cr.waitForExit() != 0) {
130
if (compMan.getAutoCompileError()) {
132
compMan.getCompileError(submission.getObjectContext());
133
submission.setStatus(ps);
194
* If cr==null, then no compilation was necessary.
196
log.info("Compilation skipped for:" + submission.getObjectId());
198
if (cr.waitForExit() != 0) {
199
if (compMan.getAutoCompileError()) {
202
log.info("Compilation failure for:" + submission.getObjectId());
204
getSubmission().setExeStdErr("<!did not run!>");
205
getSubmission().setExeStdOut("<!did not run!>");
206
getSubmission().setStdOutDiff("<!did not run!>");
207
return returnER(null);
210
.info("Finished compilation for:"
211
+ submission.getObjectId());
135
log.info("Compilation failure for:" + submission.getObjectId());
139
215
log.debug("Starting Execution...");
140
216
pb = new ProcessBuilder();
142
218
* Clear out the environment to prevent any config, such as extra memory
143
* allocation, being passed to the child process.
219
* allocation for java, being passed to the child process.
145
221
pb.environment().clear();
146
222
/* Set the execution directory */
148
224
final List<String> cmds = new ArrayList<String>();
149
225
final List<Executearg> eArgs =
150
226
submission.getLanguage().getExecuteArgs();
227
if (eArgs.size() == 0) {
228
throw new RuntimeException("No execution args for language "
229
+ submission.getLanguage().getName());
151
231
Collections.sort(eArgs);
152
232
for (final Executearg ea : eArgs) {
153
233
String val = ea.getVal();
162
242
if (compMan.getUseSecurity()) {
163
List<String> sArgs = Sandbox.getProcessArgs();
165
Sandbox.secureSubmission(getSubmission());
243
// not implemented, yet :-)
244
throw new RuntimeException("Security not available");
246
pb.command(cmds); // Prep the submission
170
final ExecutionResult er = new ExecutionResult(pb.start(), submission);
248
// Start the submission
249
final ExecutionResult er =
250
new ExecutionResult(pb.start(), submission, mode);
253
Tester tester = new Tester(submission, mode, er.myProcess);
174
256
exitVal = watchTime(er);
175
257
} catch (final InterruptedException e) {
177
259
"Interrupted while waiting for program execution to complete",
261
return returnER(null);
182
264
if (exitVal == -1) {
185
266
} else if (exitVal != 0) { // Runtime error
186
267
if (compMan.getAutoRuntimeError()) {
188
compMan.getRuntimeError(submission.getObjectContext());
189
submission.setStatus(ps);
191
270
log.info("Runtime error for:" + submission.getObjectId());
194
272
} else { // Normal execution
195
273
boolean pDiff = false;
199
277
log.error("Interruped while processing diff", e);
201
279
if (pDiff && compMan.getAutoCorrect()) {
203
compMan.getCorrect(submission.getObjectContext());
204
submission.setStatus(ps);
282
if (submission instanceof TeamSubmission) {
283
TeamSubmission ts = (TeamSubmission) submission;
284
log.info("Correct submission:" + submission.getObjectId()
285
+ "; Points:" + ts.getPoints());
206
288
log.debug("Perfect diff, but auto correct is off...");
208
log.debug("Done grading; result:" + submission.getStatus());
215
295
return submission;
298
private void commitChanges() {
299
submission.getObjectContext().commitChanges();
302
private ObjectContext getOC() {
303
return submission.getObjectContext();
307
* A helper method for cleaning up after execution
310
* The {@link ExecutionResult} to be cleaned up and returned
311
* @return The ExecutionResult passed in
312
* @throws IOException
313
* see {@link ExecutionResult#cleanup()}
315
private ExecutionResult returnER(ExecutionResult er) throws IOException {
316
log.debug("Done grading. result:" + submission.getStatus());
323
private void scoreSub() {
325
if (submission instanceof TeamSubmission) {
326
ts = (TeamSubmission) submission;
331
case GradedSubmission:
332
scoreSubmission(ts, compMan);
333
case PracticeSubmission:
339
private void setCompileError() {
342
case GradedSubmission:
343
ps = compMan.getCompileError(getOC());
345
case PracticeSubmission:
346
ps = compMan.getTestStatus(getOC());
349
throw new RuntimeException("oops, unknown mode!");
351
submission.setStatus(ps);
354
private void setCorrect() {
357
case GradedSubmission:
358
ps = compMan.getCorrect(getOC());
360
case PracticeSubmission:
361
ps = compMan.getTestStatus(getOC());
364
throw new RuntimeException("oops, unknown mode!");
366
submission.setStatus(ps);
369
private void setRunError() {
372
case GradedSubmission:
373
ps = compMan.getRuntimeError(getOC());
375
case PracticeSubmission:
376
ps = compMan.getTestStatus(getOC());
379
throw new RuntimeException("oops, unknown mode!");
381
submission.setStatus(ps);
384
private void setTimeError() {
387
case GradedSubmission:
388
ps = compMan.getTimelimitError(getOC());
390
case PracticeSubmission:
391
ps = compMan.getTestStatus(getOC());
394
throw new RuntimeException("oops, unknown mode!");
396
submission.setStatus(ps);
219
400
* This method watches an execution and if it exceeds {@link #MAX_RUNTIME}
220
401
* it will kill the process and set the status of the submission to indicate
248
431
log.error("Error while waiting for exit value:", e);
253
437
final long delta = System.currentTimeMillis() - startTime;
254
438
if (delta > MAX_RUNTIME) {
255
439
log.warn("Execution exceeded max runtime(" + MAX_RUNTIME
256
440
+ "ms) for submission:" + submission.getObjectId());
257
441
er.myProcess.destroy();
259
compMan.getTimelimitError(er.sub.getObjectContext());
260
er.sub.setStatus(ps);