~codescore-dev/codescore/version-1.0

« back to all changes in this revision

Viewing changes to src/net/codescore/exe/Grader.java

  • Committer: Adam Cornett
  • Date: 2008-05-16 02:53:17 UTC
  • mfrom: (65.1.52 codescore)
  • Revision ID: adam.cornett@gmail.com-20080516025317-douek1kxjabvb3wu
Merged changes from my branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
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;
20
21
 
 
22
import org.apache.cayenne.ObjectContext;
21
23
import org.apache.commons.logging.Log;
22
24
import org.apache.commons.logging.LogFactory;
23
25
 
37
39
 */
38
40
public class Grader {
39
41
        /**
40
 
         * This setting needs to be moved to the CompetitionManager and handled
41
 
         * there.
42
 
         */
43
 
        public static final int INIT_POINTS = 20;
44
 
        /**
45
42
         * This is the place-holder for the Unix problem name. When it is
46
43
         * encountered in the process commands it is replaced with the Unix name of
47
44
         * the problem.
49
46
         * @see Problem#getUnixName()
50
47
         */
51
48
        public static final String PRB_NAME_VAR = "$ProbName";
 
49
 
52
50
        /**
53
51
         * This is the place-holder for the source file name. When it is encountered
54
52
         * in the process commands, it is replaced with the absolute path name of
58
56
         * @see TeamSubmission#getSrcFile()
59
57
         */
60
58
        public static final String SRC_FILE_VAR = "$SrcFile";
 
59
 
 
60
        /**
 
61
         * Score a submission
 
62
         * 
 
63
         * @param ts
 
64
         *            The submission to score
 
65
         * @param cm
 
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.
 
69
         */
 
70
        public static void scoreSubmission(TeamSubmission ts, CompetitionManager cm) {
 
71
                ScoringMode sm = cm.getScoringMode();
 
72
                int pts = 0;
 
73
                switch (sm) {
 
74
                        case Flat:
 
75
                                pts = 20;
 
76
                                break;
 
77
                        case SubTime:
 
78
                                Date sub = ts.getTime();
 
79
                                Date start = cm.getCompetition().getStart();
 
80
                                long diff = sub.getTime() - start.getTime();
 
81
                                pts = (int) diff / 60000;
 
82
                                break;
 
83
                        case Weighted:
 
84
                                sub = ts.getTime();
 
85
                                start = cm.getCompetition().getStart();
 
86
                                diff = sub.getTime() - start.getTime();
 
87
                                int minutes = (int) diff / 60000;
 
88
                                pts = (minutes * 10) / ts.getProblem().getDifficulty();
 
89
                                break;
 
90
                        case ExeTime:
 
91
                                throw new RuntimeException(
 
92
                                        "Grading mode `ExeTime` not yet implemented");
 
93
                        case Custom:
 
94
                                throw new RuntimeException(
 
95
                                        "Grading mode `Custom` not yet implemented");
 
96
                        default:
 
97
                                pts = 0;
 
98
                }
 
99
                if(pts<0)
 
100
                        throw new RuntimeException("Points cannot be less than zero!");
 
101
                ts.setPoints(pts);
 
102
        }
 
103
 
61
104
        /**
62
105
         * The maximum runtime for a program in milliseconds
63
106
         */
64
107
        public final int MAX_RUNTIME;
65
 
        private CompetitionManager compMan;
66
 
        private Log log = LogFactory.getLog(Grader.class);
 
108
        private final CompetitionManager compMan;
 
109
        /*
 
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
 
112
         * added.
 
113
         */
 
114
        private Log log = LogFactory.getLog(getClass());
67
115
        private ProcessBuilder pb;
68
116
        private final Submission submission;
69
117
 
70
118
        /**
71
 
         * Create a new Grader.
 
119
         * The mode of this grader
 
120
         * 
 
121
         * @see GradingMode
 
122
         */
 
123
        protected final GradingMode mode;
 
124
 
 
125
        /**
 
126
         * Create a new Grader
72
127
         * 
73
128
         * @param s
74
 
         *            The submission to be graded.
 
129
         *            The submission to be graded
 
130
         * @param cm
 
131
         *            The competition manager to pull settings from
 
132
         * @param mode
 
133
         *            The mode for this grader
75
134
         */
76
 
        public Grader(final Submission s, CompetitionManager cm) {
 
135
        public Grader(final Submission s, CompetitionManager cm, GradingMode mode) {
 
136
                this.mode = mode;
77
137
                submission = s;
78
138
                compMan = cm;
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());
83
141
        }
84
142
 
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");
 
162
                        return null;
 
163
                }
101
164
                Collections.sort(cArgs);
102
165
                for (final Compilearg ca : cArgs) {
103
166
                        String val = ca.getVal();
126
189
         */
127
190
        public ExecutionResult execute() throws IOException {
128
191
                final CompileResult cr = compile();
129
 
                if (cr.waitForExit() != 0) {
130
 
                        if (compMan.getAutoCompileError()) {
131
 
                                ProblemStatus ps =
132
 
                                        compMan.getCompileError(submission.getObjectContext());
133
 
                                submission.setStatus(ps);
 
192
                if (cr == null) {
 
193
                        /*
 
194
                         * If cr==null, then no compilation was necessary.
 
195
                         */
 
196
                        log.info("Compilation skipped for:" + submission.getObjectId());
 
197
                } else {
 
198
                        if (cr.waitForExit() != 0) {
 
199
                                if (compMan.getAutoCompileError()) {
 
200
                                        setCompileError();
 
201
                                }
 
202
                                log.info("Compilation failure for:" + submission.getObjectId());
 
203
                                cr.cleanup();
 
204
                                getSubmission().setExeStdErr("<!did not run!>");
 
205
                                getSubmission().setExeStdOut("<!did not run!>");
 
206
                                getSubmission().setStdOutDiff("<!did not run!>");
 
207
                                return returnER(null);
 
208
                        } else {
 
209
                                log
 
210
                                        .info("Finished compilation for:"
 
211
                                                + submission.getObjectId());
 
212
                                cr.cleanup();
134
213
                        }
135
 
                        log.info("Compilation failure for:" + submission.getObjectId());
136
 
                        cr.cleanup();
137
 
                        return null;
138
214
                }
139
215
                log.debug("Starting Execution...");
140
216
                pb = new ProcessBuilder();
141
217
                /*
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.
144
220
                 */
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());
 
230
                }
151
231
                Collections.sort(eArgs);
152
232
                for (final Executearg ea : eArgs) {
153
233
                        String val = ea.getVal();
160
240
                        cmds.add(val);
161
241
                }
162
242
                if (compMan.getUseSecurity()) {
163
 
                        List<String> sArgs = Sandbox.getProcessArgs();
164
 
                        sArgs.addAll(cmds);
165
 
                        Sandbox.secureSubmission(getSubmission());
166
 
                        pb.command(sArgs);
 
243
                        // not implemented, yet :-)
 
244
                        throw new RuntimeException("Security not available");
167
245
                } else {
168
 
                        pb.command(cmds);
 
246
                        pb.command(cmds); // Prep the submission
169
247
                }
170
 
                final ExecutionResult er = new ExecutionResult(pb.start(), submission);
 
248
                // Start the submission
 
249
                final ExecutionResult er =
 
250
                        new ExecutionResult(pb.start(), submission, mode);
171
251
                er.setCr(cr);
172
252
                int exitVal;
 
253
                Tester tester = new Tester(submission, mode, er.myProcess);
173
254
                try {
 
255
                        tester.test();
174
256
                        exitVal = watchTime(er);
175
257
                } catch (final InterruptedException e) {
176
258
                        log.error(
177
259
                                "Interrupted while waiting for program execution to complete",
178
260
                                e);
179
 
                        er.cleanup();
180
 
                        return null;
 
261
                        return returnER(null);
181
262
                }
 
263
 
182
264
                if (exitVal == -1) {
183
 
                        er.cleanup();
184
 
                        return er;
 
265
                        return returnER(er);
185
266
                } else if (exitVal != 0) { // Runtime error
186
267
                        if (compMan.getAutoRuntimeError()) {
187
 
                                ProblemStatus ps =
188
 
                                        compMan.getRuntimeError(submission.getObjectContext());
189
 
                                submission.setStatus(ps);
 
268
                                setRunError();
190
269
                        }
191
270
                        log.info("Runtime error for:" + submission.getObjectId());
192
 
                        er.cleanup();
193
 
                        return er;
 
271
                        return returnER(er);
194
272
                } else { // Normal execution
195
273
                        boolean pDiff = false;
196
274
                        try {
199
277
                                log.error("Interruped while processing diff", e);
200
278
                        }
201
279
                        if (pDiff && compMan.getAutoCorrect()) {
202
 
                                ProblemStatus ps =
203
 
                                        compMan.getCorrect(submission.getObjectContext());
204
 
                                submission.setStatus(ps);
205
 
                        }else if(pDiff) {
 
280
                                setCorrect();
 
281
                                scoreSub();
 
282
                                if (submission instanceof TeamSubmission) {
 
283
                                        TeamSubmission ts = (TeamSubmission) submission;
 
284
                                        log.info("Correct submission:" + submission.getObjectId()
 
285
                                                + "; Points:" + ts.getPoints());
 
286
                                }
 
287
                        } else if (pDiff) {
206
288
                                log.debug("Perfect diff, but auto correct is off...");
207
289
                        }
208
 
                        log.debug("Done grading; result:" + submission.getStatus());
209
 
                        er.cleanup();
210
 
                        return er;
 
290
                        return returnER(er);
211
291
                }
212
292
        }
213
293
 
215
295
                return submission;
216
296
        }
217
297
 
 
298
        private void commitChanges() {
 
299
                submission.getObjectContext().commitChanges();
 
300
        }
 
301
 
 
302
        private ObjectContext getOC() {
 
303
                return submission.getObjectContext();
 
304
        }
 
305
 
 
306
        /**
 
307
         * A helper method for cleaning up after execution
 
308
         * 
 
309
         * @param er
 
310
         *            The {@link ExecutionResult} to be cleaned up and returned
 
311
         * @return The ExecutionResult passed in
 
312
         * @throws IOException
 
313
         *             see {@link ExecutionResult#cleanup()}
 
314
         */
 
315
        private ExecutionResult returnER(ExecutionResult er) throws IOException {
 
316
                log.debug("Done grading. result:" + submission.getStatus());
 
317
                if (er != null)
 
318
                        er.cleanup();
 
319
                commitChanges();
 
320
                return er;
 
321
        }
 
322
 
 
323
        private void scoreSub() {
 
324
                TeamSubmission ts;
 
325
                if (submission instanceof TeamSubmission) {
 
326
                        ts = (TeamSubmission) submission;
 
327
                } else {
 
328
                        return;
 
329
                }
 
330
                switch (mode) {
 
331
                        case GradedSubmission:
 
332
                                scoreSubmission(ts, compMan);
 
333
                        case PracticeSubmission:
 
334
                                ts.setPoints(0);
 
335
                                return;
 
336
                }
 
337
        }
 
338
 
 
339
        private void setCompileError() {
 
340
                ProblemStatus ps;
 
341
                switch (mode) {
 
342
                        case GradedSubmission:
 
343
                                ps = compMan.getCompileError(getOC());
 
344
                                break;
 
345
                        case PracticeSubmission:
 
346
                                ps = compMan.getTestStatus(getOC());
 
347
                                break;
 
348
                        default:
 
349
                                throw new RuntimeException("oops, unknown mode!");
 
350
                }
 
351
                submission.setStatus(ps);
 
352
        }
 
353
 
 
354
        private void setCorrect() {
 
355
                ProblemStatus ps;
 
356
                switch (mode) {
 
357
                        case GradedSubmission:
 
358
                                ps = compMan.getCorrect(getOC());
 
359
                                break;
 
360
                        case PracticeSubmission:
 
361
                                ps = compMan.getTestStatus(getOC());
 
362
                                break;
 
363
                        default:
 
364
                                throw new RuntimeException("oops, unknown mode!");
 
365
                }
 
366
                submission.setStatus(ps);
 
367
        }
 
368
 
 
369
        private void setRunError() {
 
370
                ProblemStatus ps;
 
371
                switch (mode) {
 
372
                        case GradedSubmission:
 
373
                                ps = compMan.getRuntimeError(getOC());
 
374
                                break;
 
375
                        case PracticeSubmission:
 
376
                                ps = compMan.getTestStatus(getOC());
 
377
                                break;
 
378
                        default:
 
379
                                throw new RuntimeException("oops, unknown mode!");
 
380
                }
 
381
                submission.setStatus(ps);
 
382
        }
 
383
 
 
384
        private void setTimeError() {
 
385
                ProblemStatus ps;
 
386
                switch (mode) {
 
387
                        case GradedSubmission:
 
388
                                ps = compMan.getTimelimitError(getOC());
 
389
                                break;
 
390
                        case PracticeSubmission:
 
391
                                ps = compMan.getTestStatus(getOC());
 
392
                                break;
 
393
                        default:
 
394
                                throw new RuntimeException("oops, unknown mode!");
 
395
                }
 
396
                submission.setStatus(ps);
 
397
        }
 
398
 
218
399
        /**
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
240
421
                        } catch (final RuntimeException e) {
241
422
                                Thread.sleep(2000);
242
423
                                cleanExit = false;
 
424
                                final long delta = System.currentTimeMillis() - startTime;
 
425
                                log.debug("Waiting for process to exit, delta=" + delta + "ms");
243
426
                                /*
244
427
                                 * Ignore this exception, it is thrown if the process hasn't
245
428
                                 * exited yet
248
431
                                        log.error("Error while waiting for exit value:", e);
249
432
                        }
250
433
 
251
 
                        if (cleanExit)
 
434
                        if (cleanExit) {
252
435
                                return rv;
 
436
                        }
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();
258
 
                                ProblemStatus ps =
259
 
                                        compMan.getTimelimitError(er.sub.getObjectContext());
260
 
                                er.sub.setStatus(ps);
 
442
                                setTimeError();
261
443
                                return -1;
262
444
                        }
263
445
                }