33
33
import net.codescore.dbo.Team;
34
34
import net.codescore.dbo.TeamSubmission;
35
35
import net.codescore.exe.ExecutionResult;
36
import net.codescore.exe.GradingMode;
37
import net.codescore.exe.GradingThread;
38
import net.codescore.exe.ScoringMode;
36
39
import net.codescore.ui.SubUpdateListener;
37
import net.codescore.ui.admin.CompHome;
38
40
import net.codescore.ui.client.TeamHome;
43
* Manages a competition, providing methods to get competitions from the
44
* database, and facilities for processing submissions, such as a grading queue
45
* and accessor methods to the competition settings.
47
* @author Adam Cornett
40
49
public class CompetitionManager implements SubUpdateListener {
51
* The last competition to be created
41
55
private static CompetitionManager activeCompetition;
56
private static DataContext dc;
58
* A map of linking a competition to its associated manager, used to prevent
59
* a competition from having more than one associated manager
61
private static HashMap<Competition, CompetitionManager> managers =
62
new HashMap<Competition, CompetitionManager>();
65
* @return A list of all competitions, using the internal
68
public static List<Competition> getAllComps() {
70
dc = DataContext.createDataContext();
71
return getAllComps(dc);
76
* The context used to execute the query
77
* @return A List of all competitions
79
public static List<Competition> getAllComps(ObjectContext oc) {
80
NamedQuery nq = new NamedQuery("competitions_all");
81
return oc.performQuery(nq);
86
* The context used to search for the competition
88
* The name of the competition
89
* @return The competition with the associated name
43
91
public static Competition getCompetitionByName(ObjectContext c, String name) {
44
92
HashMap<String, Object> params = new HashMap<String, Object>();
45
93
params.put("n", name);
53
101
return (Competition) l.get(0);
105
* Uses the internal context to search for a competition
108
* The name of the competition to lookup
109
* @return The competition with the given name
111
public static Competition getCompetitionByName(String name) {
113
dc = DataContext.createDataContext();
114
return getCompetitionByName(dc, name);
118
* @return The last manager associated with the most recently created
56
122
public static CompetitionManager getCurrent() {
57
123
if (activeCompetition == null) {
58
124
ObjectContext oc = DataContext.createDataContext();
59
125
new CompetitionManager(getCompetitionByName(oc, "Test Competition"));
61
128
return activeCompetition;
133
* The context used to lookup a competition
134
* @return The last registered competition
64
137
public static Competition getCurrent(ObjectContext oc) {
65
138
if (activeCompetition == null)
66
139
activeCompetition =
73
146
activeCompetition.getCompetition().getObjectId());
150
* Get all currently running competitions using the internal context
152
* @return A list of all competitions that are currently 'running': The
153
* competitions start time is before the current time, and the end
154
* time is after the current time.
156
public static List<Competition> getCurrentComps() {
157
return getCurrentComps(DataContext.createDataContext());
161
* A list of all currently running competitions using the supplied context
164
* The context to use when executing the query
165
* @return A list of all competitions where the competition's start time is
166
* before the current time and its end time is after the current
169
public static List<Competition> getCurrentComps(ObjectContext oc) {
170
NamedQuery nq = new NamedQuery("competitions_current");
171
return oc.performQuery(nq);
175
* Get the associated manager for a competition. A new manager will be
176
* created if one does not already exist for the given competition.
180
* @return The manager associated with the competition.
182
public static CompetitionManager getManager(Competition c) {
183
if (managers.containsKey(c))
184
return managers.get(c);
185
return new CompetitionManager(c);
190
* Lookup a submission for a given submission id (sid)
193
* The context to use for executing the query
195
* The sid of the submission
196
* @return The Team Submission with the associated sid
76
198
public static TeamSubmission getSubmission(ObjectContext c, int sid) {
77
199
HashMap<String, Object> params = new HashMap<String, Object>();
78
200
params.put("sid", sid);
85
207
return (TeamSubmission) l.get(0);
88
private CompHome competitionHome;
90
210
private volatile boolean compIsRunning;
91
211
private Competition currentComp;
92
212
private ConcurrentHashMap<Submission, ExecutionResult> gradeResults;
93
private ConcurrentLinkedQueue<Submission> gradingQueue;
213
private ConcurrentLinkedQueue<Submission> gradingQueue, practiceQueue;
94
214
private List<GradingThread> gThreads;
95
215
private ConcurrentLinkedQueue<Submission> judgingQueue;
96
216
private Log log = LogFactory.getLog(CompetitionManager.class);
97
217
private int numGradingThreads;
98
218
private Map<Team, TeamHome> teamHomes;
219
private GradingThread testThread;
222
* Create a new manager.
225
* The competition the manager is associated with
226
* @throws IllegalArgumentException
227
* If a manager already exists for the competition. Use
228
* {@link CompetitionManager#getManager(Competition)} to safely
229
* get a manager for a competition
100
231
public CompetitionManager(Competition c) {
232
if (managers.containsKey(c)) {
233
throw new IllegalArgumentException("A manager for " + c
234
+ " already exists, please use getManager(Competition)");
236
managers.put(c, this);
101
237
if (activeCompetition == null) {
102
238
CompetitionManager.activeCompetition = this;
104
240
gradingQueue = new ConcurrentLinkedQueue<Submission>();
241
practiceQueue = new ConcurrentLinkedQueue<Submission>();
105
242
judgingQueue = new ConcurrentLinkedQueue<Submission>();
106
243
gradeResults = new ConcurrentHashMap<Submission, ExecutionResult>();
107
244
gThreads = new LinkedList<GradingThread>();
108
245
teamHomes = new HashMap<Team, TeamHome>();
109
246
this.setCurrentComp(c);
110
competitionHome = new CompHome();
111
247
compIsRunning = currentComp.getActive();
112
248
SubmissionListener.registerCallback(this);
113
249
if (getCompetition().getProp("num_graders") == null) {
116
252
Integer i = (Integer) getCompetition().getProp("num_graders");
118
numGradingThreads = 10;
254
numGradingThreads = 2;
120
256
numGradingThreads = i;
258
testThread = new GradingThread(this, GradingMode.PracticeSubmission);
260
log.debug("Loaded:" + currentComp);
264
* When a submission has been graded, the result of the Grader is returned
265
* here so that it can be reviewed by a judge.
268
* The submission which was graded
270
* The result of the grading
272
public void addGradeResult(Submission s, ExecutionResult er) {
273
// If it is not a Judge's Solution, add the result to the list
274
if (s instanceof TeamSubmission)
275
gradeResults.put(s, er);
276
// If the status is still pending, send it to a judge
277
if (s.getStatus() == null) {
280
s.getObjectContext().commitChanges();
302
* Add a submission to the practice queue.<br />
303
* If the queue already contains s, it will not be added again. The queue is
306
* @see ConcurrentLinkedQueue
308
* The submission to be added.
310
public void addSubmissionToPracticeQueue(Submission s) {
311
// Don't add a submission if its already in the queue
312
if (practiceQueue.contains(s))
314
log.info("Adding Submission to practice queue:" + s.getObjectId());
315
practiceQueue.add(s);
143
319
* Removes dead graders from the list and ensures that the actual number of
144
320
* active grading threads matches the expected number.
146
322
public void checkGThreads() {
147
Iterator<GradingThread> gti = gThreads.iterator();
148
while (gti.hasNext())
149
if (!gti.next().isAlive())
151
int expNum = numGradingThreads;
152
numGradingThreads = gThreads.size();
153
setNumGraders(expNum);
327
* Dump debugging information to the log
329
public void debug() {
330
log.debug(getCompetition());
331
getCompetition().debug();
406
* The context used to get the ProblemStatus.
407
* @return The problem status object signifying a compiler error for a
409
* @see #getCompile_error_status()
221
411
public ProblemStatus getCompileError(ObjectContext oc) {
222
412
return DataObjectUtils.objectForPK(oc, ProblemStatus.class,
223
413
getCompile_error_status());
418
* The context used to get the ProblemStatus.
419
* @return The problem status object signifying a correct submission.
420
* @see #getCorrect_status()
226
422
public ProblemStatus getCorrect(ObjectContext oc) {
227
423
return DataObjectUtils.objectForPK(oc, ProblemStatus.class,
228
424
getCorrect_status());
232
* @return the correct_status
428
* @return The <code>correct_status</code> property of the competition
429
* @see #getCorrect(ObjectContext)
234
431
public int getCorrect_status() {
235
432
Integer cs = (Integer) getCompetition().getProp("correct_status");
520
* The context used to get the ProblemStatus.
521
* @return The problem status object signifying a runtime error in a
523
* @see #getRuntime_error_status()
305
525
public ProblemStatus getRuntimeError(ObjectContext oc) {
306
526
return DataObjectUtils.objectForPK(oc, ProblemStatus.class,
307
527
getRuntime_error_status());
531
* Get the scoring mode for this competition.<br />
532
* Stored in the <code>scoring_mode</code> competition property
534
* @return The scoring mode for the associated competition
536
public ScoringMode getScoringMode() {
537
return (ScoringMode) getCompetition().getProp("scoring_mode");
541
* @return The <code>test_status</code> property of the competition.
542
* @see #getTestStatus(ObjectContext)
544
public int getTest_status() {
545
Integer tes = (Integer) getCompetition().getProp("test_status");
551
* The context used to get the ProblemStatus.
552
* @return The problem status signifying a problem should not be graded
553
* normally, but instead is a test submission.
554
* @see #getTest_status()
556
public ProblemStatus getTestStatus(ObjectContext oc) {
557
return DataObjectUtils.objectForPK(oc, ProblemStatus.class,
562
* @return The <code>timelimit_error_status</code> property of the
564
* @see #getTimelimitError(ObjectContext)
310
566
public int getTimelimit_error_status() {
312
568
(Integer) getCompetition().getProp("timelimit_error_status");
574
* The context used to get the ProblemStatus.
575
* @return The problem status signifying that a submission has exceeded the
576
* maximum time limit.
577
* @see #getTimelimit_error_status()
578
* @see #getExeTimeout()
316
580
public ProblemStatus getTimelimitError(ObjectContext oc) {
317
581
return DataObjectUtils.objectForPK(oc, ProblemStatus.class,
318
582
getTimelimit_error_status());
586
* Tells the grading system if it should use the extra security precautions
587
* while executing a submission.<br />
588
* <b>This will cause the grader to fail currently as the security system
589
* has not been implemented</b>
591
* @return The <code>use_security</code> property of the competition
321
593
public boolean getUseSecurity() {
322
594
return (Boolean) getCompetition().getProp("use_security");
326
* @return the wrong_output_status
598
* @return The <code>wrong_output_status</code> property of the
600
* @see #getWrongOutput(ObjectContext)
328
602
public int getWrong_output_status() {
329
603
Integer wos = (Integer) getCompetition().getProp("wrong_output_status");
609
* The context used to get the ProblemStatus.
610
* @return The problem status object signifying that a submission has the
612
* @see #getWrong_output_status()
333
614
public ProblemStatus getWrongOutput(ObjectContext oc) {
334
615
return DataObjectUtils.objectForPK(oc, ProblemStatus.class,
335
616
getWrong_output_status());
643
* Called by the grading thread to check if any submissions are waiting to
646
* @return A submission, or null if the queue is empty.
648
public Submission pollGradeQueue() {
649
return gradingQueue.poll();
653
* Called by the grading thread to check if any submissions are waiting to
654
* be run. This polls the practice queue, which is for submissions that are
655
* not scored and are only fed the sample input.
657
* @return A submission, or null if the queue is empty.
659
public Submission pollPracticeQueue() {
660
return practiceQueue.poll();
664
* Registers a team home with the competition manager
667
* The team home to register
358
670
public void registerTeamHome(TeamHome th) {
359
671
Team t = th.getController().getTeam();
360
672
if (teamHomes.containsKey(t)) {
377
689
subs.addAll(p.getSubmissions());
379
691
for (TeamSubmission s : subs) {
692
if (s.getStatus() != null
693
&& s.getStatus().equals(getTestStatus(s.getObjectContext())))
694
addSubmissionToPracticeQueue(s);
696
addSubmissionToGradeQueue(s);
380
697
s.setStatus(null);
383
700
currentComp.getObjectContext().commitChanges();
384
for (TeamSubmission s : subs)
385
addSubmissionToGradeQueue(s);
832
* Set the scoring mode to be used by the competition
835
* The new scoring mode
837
public void setScoringMode(ScoringMode mode) {
838
getCompetition().setProp("scoring_mode", mode);
843
* the test_status to set
845
public void setTest_status(int test_status) {
846
getCompetition().setProp("test_status", test_status);
513
850
* @param timelimit_error_status
514
851
* the timelimit_error_status to set
535
878
teamHomes.remove(t);
881
private void checkGThreads(boolean fix) {
882
Iterator<GradingThread> gti = gThreads.iterator();
883
while (gti.hasNext())
884
if (!gti.next().isAlive())
886
if (fix && numGradingThreads != gThreads.size()) {
887
int expNum = numGradingThreads;
888
numGradingThreads = gThreads.size();
889
setNumGraders(expNum);
891
numGradingThreads = gThreads.size();
538
894
private void initGThreads() {
539
895
GradingThread gt;
540
896
for (int i = 0; i < numGradingThreads; i++) {
548
* When a submission has been graded, the result of the Grader is returned
549
* here so that it can be reviewed by a judge.
552
* The submission which was graded
554
* The result of the grading
556
protected void addGradeResult(Submission s, ExecutionResult er) {
557
// If it is not a Judge's Solution, add the result to the list
558
if (s instanceof TeamSubmission)
559
gradeResults.put(s, er);
560
// If the status is still pending, send it to a judge
561
if (s.getStatus() == null) {
564
s.getObjectContext().commitChanges();
568
904
* We need to make sure that we stop the grading thread for this competition
569
905
* when we clean up this object, also deregisters the listeners.