/*
* Copyright 2007 Xu, Chuan <xuchuan@gmail.com>
*
* This file is part of ZOJ.
*
* ZOJ is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either revision 3 of the License, or (at your option) any later revision.
*
* ZOJ 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 General Public License along with ZOJ. if not, see
* <http://www.gnu.org/licenses/>.
*/
package cn.edu.zju.acm.onlinejudge.judgeservice;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import cn.edu.zju.acm.onlinejudge.bean.Submission;
import cn.edu.zju.acm.onlinejudge.bean.enumeration.JudgeReply;
import cn.edu.zju.acm.onlinejudge.judgeservice.submissionfilter.SubmissionFilter;
import cn.edu.zju.acm.onlinejudge.persistence.PersistenceException;
import cn.edu.zju.acm.onlinejudge.persistence.SubmissionPersistence;
import cn.edu.zju.acm.onlinejudge.util.ConfigManager;
import cn.edu.zju.acm.onlinejudge.util.PersistenceManager;
public class JudgeService extends Thread {
private static JudgeService instance;
static {
try {
JudgeService.instance =
new JudgeService(Integer.parseInt(ConfigManager.getValue("queue_port")),
ConfigManager.getValues("client_ip"),
Integer.parseInt(ConfigManager.getValue("client_max_job")));
JudgeService.instance.start();
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
private ServerSocket serverSocket;
private List<JudgeClient> judgeClientList = new ArrayList<JudgeClient>();
private Logger logger = Logger.getLogger(JudgeService.class);
private JudgingQueue judgingQueue = new JudgingQueue();
private SubmissionQueue submissionQueue = new SubmissionQueue();
private SubmissionFilter submissionFilter = null;
private Set<String> clientHostAddressSet = new HashSet<String>();
private int defaultNumberOfJudgeThreads;
private Set<Long> queuingSubmissionIdSet = new HashSet<Long>();
private Thread rejudgeThread;
public static JudgeService getInstance() {
return JudgeService.instance;
}
private JudgeService(int port, String[] clientHostAddressList, int defaultNumberOfJudgeThreads) throws IOException {
this.serverSocket = new ServerSocket(port);
this.logger.info("Listening on port " + port);
if (clientHostAddressList != null) {
for (String clientHostAddress : clientHostAddressList) {
this.clientHostAddressSet.add(clientHostAddress);
}
}
this.defaultNumberOfJudgeThreads = defaultNumberOfJudgeThreads;
this.rejudgeThread = new Thread() {
public void run() {
SubmissionPersistence submissionPersistence =
PersistenceManager.getInstance().getSubmissionPersistence();
try {
long last = 0;
for (int i = 0;; ++i) {
List<Submission> submissions = submissionPersistence.getQueueingSubmissions(last - 1, 100);
if (submissions.size() == 0) {
break;
}
synchronized (JudgeService.this.queuingSubmissionIdSet) {
for (int j = submissions.size() - 1; j >= 0; --j) {
Submission submission = submissions.get(j);
JudgeService.this.queuingSubmissionIdSet.add(submission.getId());
if (i == 0) {
JudgeService.instance.judge(submission, Priority.NORMAL);
} else {
JudgeService.instance.judge(submission, Priority.LOW);
}
}
JudgeService.this.queuingSubmissionIdSet.wait();
}
last = submissions.get(0).getId();
}
} catch (PersistenceException e) {
e.printStackTrace();
} catch (InterruptedException e) {
}
JudgeService.this.rejudgeThread = null;
JudgeService.this.queuingSubmissionIdSet = null;
}
};
rejudgeThread.start();
}
@Override
public void run() {
while (!this.serverSocket.isClosed()) {
try {
Socket socket = this.serverSocket.accept();
this.logger
.info("Connection from " + socket.getInetAddress().getHostAddress() + ":" + socket.getPort());
if (!socket.getInetAddress().isLoopbackAddress() &&
!this.clientHostAddressSet.contains(socket.getInetAddress().getHostAddress())) {
this.logger.info("Refused");
socket.close();
continue;
}
JudgeClient client = new JudgeClient(this, socket, this.defaultNumberOfJudgeThreads);
client.start();
synchronized (this.judgeClientList) {
this.judgeClientList.add(client);
}
} catch (IOException e) {
this.logger.error(e);
}
}
}
public List<JudgeClient> getJudgeClientList() {
synchronized (this.judgeClientList) {
return new ArrayList<JudgeClient>(this.judgeClientList);
}
}
public JudgingQueueIterator getJudgingQueueIterator() {
return this.judgingQueue.iterator();
}
public SubmissionFilter getSubmissionFilter() {
return this.submissionFilter;
}
public void judge(Submission submission, int priority) {
submission.setJudgeReply(JudgeReply.QUEUING);
this.submissionQueue.push(submission, priority);
}
void judgeStart(Submission submission) {
this.judgingQueue.push(submission);
}
void judgeDone(Submission submission) {
this.judgingQueue.remove(submission);
if (this.queuingSubmissionIdSet != null) {
try {
synchronized (this.queuingSubmissionIdSet) {
this.queuingSubmissionIdSet.remove(submission.getId());
if (this.queuingSubmissionIdSet.size() == 0) {
this.queuingSubmissionIdSet.notify();
}
}
} catch (NullPointerException e) {
// In case queuingSubmissionIdSet is null
}
}
}
public SubmissionQueue getSubmissionQueue() {
return this.submissionQueue;
}
@Override
public void interrupt() {
super.interrupt();
synchronized (this.judgeClientList) {
for (JudgeClient client : this.judgeClientList) {
client.interrupt();
}
}
try {
this.rejudgeThread.interrupt();
} catch (NullPointerException e) {
// In case rejudgeThread is null
}
}
}