/* * 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.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import cn.edu.zju.acm.onlinejudge.bean.enumeration.JudgeReply; import cn.edu.zju.acm.onlinejudge.bean.enumeration.Language; import cn.edu.zju.acm.onlinejudge.judgeservice.submissionfilter.SubmissionFilter; import cn.edu.zju.acm.onlinejudge.util.Utility; public class JudgeClient extends Thread { public static final int CONNECTION_TIMEOUT = 30000; public static final int READ_TIMEOUT = 60000; public static final int HEART_BEAT_INTERVAL = 30000; private String host; private List<Language> supportedLanguages = new ArrayList<Language>(); private int port; private List<JudgeClientJudgeThread> judgeThreads = new ArrayList<JudgeClientJudgeThread>(); private Logger logger = Logger.getLogger(JudgeClient.class); private int defaultNumberOfJudgeThreads; private DataInputStream in; private DataOutputStream out; private Socket socket; private boolean initialized; private JudgeService service; private SubmissionFilter submissionFilter = null; private int[] pingCounter = new int[] {0}; private Object pingBarrier = new Object(); public JudgeClient(JudgeService service, Socket socket, int defaultNumberOfJudgeThreads) throws IOException { this.service = service; this.host = socket.getInetAddress().getHostAddress(); this.socket = socket; socket.setKeepAlive(true); socket.setSoTimeout(JudgeClient.READ_TIMEOUT); this.defaultNumberOfJudgeThreads = defaultNumberOfJudgeThreads; this.in = new DataInputStream(socket.getInputStream()); this.out = new DataOutputStream(socket.getOutputStream()); this.initialized = false; } public List<JudgeClientJudgeThread> getJudgeThreads() { synchronized (this.judgeThreads) { return new ArrayList<JudgeClientJudgeThread>(this.judgeThreads); } } @Override public void run() { try { this.logger.info("Start to get information"); try { this.sendInfoCommand(); this.port = this.in.readInt(); this.logger.info("port=" + this.port); this.supportedLanguages.clear(); int n = this.in.readInt(); if (n < 0 || n > LanguageManager.getNumberOfLanguages()) { this.logger.error("Invalid number of supported languages:" + n); return; } for (int i = 0; i < n; ++i) { int id = this.in.readInt(); Language language = LanguageManager.getLanguage(id); if (language == null) { this.logger.error("Invalid language id:" + id); return; } this.supportedLanguages.add(language); this.logger.info("Supported language:" + language.getName()); } this.initialized = true; for (int i = 0; i < this.defaultNumberOfJudgeThreads; ++i) { this.addJudgeThread(); } } catch (IOException e) { this.logger.error("Fail to get information", e); return; } for (;;) { synchronized (this.pingCounter) { if (this.pingCounter[0] == 0) { this.pingCounter.wait(JudgeClient.HEART_BEAT_INTERVAL); } } try { if (this.sendPingCommand() != JudgeReply.READY.getId()) { break; } synchronized (this.pingBarrier) { this.pingCounter[0] = 0; this.pingBarrier.notifyAll(); } } catch (IOException e) { this.logger.error("Send ping command failure", e); break; } } } catch (InterruptedException e) { this.logger.info("Interrupted"); } finally { synchronized (this.pingBarrier) { this.pingCounter[0] = 0; Utility.closeSocket(this.socket); this.pingBarrier.notifyAll(); } for (JudgeClientJudgeThread judgeThread : this.judgeThreads) { judgeThread.interrupt(); } } } @Override public void interrupt() { super.interrupt(); synchronized (this.judgeThreads) { for (JudgeClientJudgeThread judgeThread : this.judgeThreads) { judgeThread.interrupt(); } } } public boolean isInitialized() { return this.initialized; } public String getHost() { return this.host; } public int getPort() { return this.port; } public List<Language> getSupportedLanguages() { return this.supportedLanguages; } public JudgeService getService() { return this.service; } public SubmissionFilter getSubmissionFilter() { return this.submissionFilter; } void setSubmissionFilter(SubmissionFilter submissionFilter) { this.submissionFilter = submissionFilter; } public JudgeClientJudgeThread addJudgeThread() { synchronized (this.judgeThreads) { JudgeClientJudgeThread judgeThread = new JudgeClientJudgeThread(this); judgeThread.start(); this.judgeThreads.add(judgeThread); return judgeThread; } } public void removeJudgeThread(int index) { synchronized (this.judgeThreads) { this.judgeThreads.remove(index).interrupt(); } } boolean ping() throws InterruptedException { synchronized (this.pingBarrier) { if (this.socket.isClosed()) { return false; } else { synchronized (this.pingCounter) { ++this.pingCounter[0]; this.pingCounter.notify(); } this.pingBarrier.wait(); return !this.socket.isClosed(); } } } private void sendInfoCommand() throws IOException { this.out.write(JudgeClientCommandsFactory.createInfoCommand()); } private int sendPingCommand() throws IOException { this.out.write(JudgeClientCommandsFactory.createPingCommand()); return this.in.readInt(); } }