pl.umk.mat.zawodyweb.compiler.classes.LanguageUVA.java Source code

Java tutorial

Introduction

Here is the source code for pl.umk.mat.zawodyweb.compiler.classes.LanguageUVA.java

Source

/*
 * Copyright (c) 2009-2014, ZawodyWeb Team
 * All rights reserved.
 *
 * This file is distributable under the Simplified BSD license. See the terms
 * of the Simplified BSD license in the documentation provided with this file.
 */
package pl.umk.mat.zawodyweb.compiler.classes;

import java.io.*;
import java.util.*;
import java.util.concurrent.TimeoutException;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import pl.umk.mat.zawodyweb.judge.commons.TestInput;
import pl.umk.mat.zawodyweb.judge.commons.TestOutput;
import pl.umk.mat.zawodyweb.judge.commons.CompilerInterface;
import pl.umk.mat.zawodyweb.database.ResultsStatusEnum;

/**
 * Copied from LanguageLA, acmSite =
 * http://uva.onlinejudge.org/
 *
 * @author faramir
 */
public class LanguageUVA implements CompilerInterface {

    public static final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(LanguageUVA.class);
    private Properties properties;
    private HttpClient client;
    private final String acmSite = "http://uva.onlinejudge.org/";

    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    //http://livearchive.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=19 - last 50 submissions
    private int checkInQueueStatus(String problemId) throws HttpException, IOException {
        List<Map<String, String>> results = getResults(50);

        int inQueue = 0;
        for (Map<String, String> result : results) {
            if (problemId.equals(result.get("problemsid"))) {
                String status = result.get("status");
                if ("".equals(status) || "Sent to judge".equals(status) || "In judge queue".equals(status)) {
                    ++inQueue;
                }
            }
        }

        return inQueue;
    }

    private void logIn(String login, String password) throws HttpException, IOException {
        ArrayList<NameValuePair> vectorLoginData;
        GetMethod get = new GetMethod(acmSite);

        try {
            client.executeMethod(get);
            InputStream firstGet = get.getResponseBodyAsStream();
            BufferedReader br = null;

            try {
                br = new BufferedReader(new InputStreamReader(firstGet, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
            }

            String line, name, value;
            vectorLoginData = new ArrayList<NameValuePair>();
            vectorLoginData.add(new NameValuePair("username", login));
            vectorLoginData.add(new NameValuePair("passwd", password));

            line = br.readLine();
            while (line != null && !line.matches(".*class=\"mod_login\".*")) {
                line = br.readLine();
            }
            while (line != null && !line.matches("(?i).*submit.*login.*")) {
                if (line.matches(".*hidden.*name=\".*value=\".*")) {
                    name = line.split("name=\"")[1].split("\"")[0];
                    value = line.split("value=\"")[1].split("\"")[0];
                    vectorLoginData.add(new NameValuePair(name, value)); // FIXME: check if it's neccesary: URLDecoder.decode(value, "UTF-8"));
                }
                line = br.readLine();
            }
        } finally {

            get.releaseConnection();
        }
        vectorLoginData.add(new NameValuePair("remember", "yes"));
        vectorLoginData.add(new NameValuePair("Submit", "Login"));

        PostMethod post = new PostMethod(acmSite + "index.php?option=com_comprofiler&task=login");
        post.setRequestHeader("Referer", acmSite);
        NameValuePair[] loginData = new NameValuePair[0];
        loginData = vectorLoginData.toArray(loginData);
        post.setRequestBody(loginData);

        client.executeMethod(post);

        post.releaseConnection();
    }

    private String sendSolution(String code, TestInput input) throws HttpException, IOException {
        PostMethod post = new PostMethod(
                acmSite + "index.php?option=com_onlinejudge&Itemid=25&page=save_submission");
        try {

            String langId = properties.getProperty("uva.languageId");
            if (langId != null) {
                if (langId.equals("1") || langId.equalsIgnoreCase("C")) {
                    langId = "1";
                } else if (langId.equals("2") || langId.equalsIgnoreCase("JAVA")) {
                    langId = "2";
                } else if (langId.equals("3") || langId.equalsIgnoreCase("C++")) {
                    langId = "3";
                } else if (langId.equals("4") || langId.equalsIgnoreCase("PASCAL")) {
                    langId = "4";
                } else if (langId.equals("5") || langId.equalsIgnoreCase("C++11")) {
                    langId = "5";
                } else {
                    langId = null;
                }
            }

            if (langId == null) {
                String fileExt = properties.getProperty("CODEFILE_EXTENSION");
                if (fileExt.equals("c")) {
                    langId = "1";
                } else if (fileExt.equals("java")) {
                    langId = "2";
                } else if (fileExt.equals("cpp")) {
                    langId = "3";
                } else if (fileExt.equals("pas")) {
                    langId = "4";
                } else if (fileExt.equals("cpp11")) {
                    langId = "5";
                }
            }
            NameValuePair[] dataSendAnswer = { new NameValuePair("problemid", ""),
                    new NameValuePair("category", ""), new NameValuePair("localid", input.getInputText()),
                    new NameValuePair("language", langId), new NameValuePair("code", code),
                    new NameValuePair("submit", "Submit") };
            post.setRequestBody(dataSendAnswer);

            client.executeMethod(post);
            String location = post.getResponseHeader("Location").getValue();

            String msg = location.substring(location.lastIndexOf("msg=") + 4);
            if (msg.contains("Submission+received")) {
                return msg.substring(msg.lastIndexOf("+") + 1);
            } else {
                return java.net.URLDecoder.decode(msg, "UTF-8");
            }
        } finally {
            post.releaseConnection();
        }
    }

    private String getCompilationError(int id) throws HttpException, IOException {
        GetMethod get = new GetMethod(
                acmSite + "index.php?option=com_onlinejudge&Itemid=9&page=show_compilationerror&submission=" + id);

        try {
            client.executeMethod(get);
            InputStream firstGet = get.getResponseBodyAsStream();
            BufferedReader br = null;

            try {
                br = new BufferedReader(new InputStreamReader(firstGet, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
            }

            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
                sb.append("\n");
            }
            String[] split = sb.toString().split("(<pre>)|(</pre>)");
            if (split.length == 3) {
                return split[1].trim();
            }
        } finally {
            get.releaseConnection();
        }
        return "";
    }

    /**
     *
     * @param br BufferedReader
     * @return List of Map (id, status, time)
     * @throws IOException
     */
    private List<Map<String, String>> processResults(BufferedReader br) throws IOException {
        String line;
        StringBuilder sb;
        List<Map<String, String>> results = new ArrayList<Map<String, String>>();

        while ((line = br.readLine()) != null) {
            if (line.matches(".*<td>[0-9]+</td>.*")) {
                sb = new StringBuilder(line);
                line = br.readLine();
                while (!line.matches(".*</tr>.*")) {
                    sb.append(line.trim());
                    line = br.readLine();
                }
                String[] split = sb.toString().split("(<td[^>]*>)|(</td>)");

                Map<String, String> result = new HashMap<String, String>();
                result.put("id", split[1].trim());
                result.put("problemid", (split[3].replaceAll("<[^>]*>", "")).trim());
                result.put("status", (split[7].replaceAll("<[^>]*>", "")).trim());
                result.put("time", split[11].replace(".", "").trim());

                results.add(result);
            }
        }
        return results;
    }

    private List<Map<String, String>> getResults(int limitOnPage) throws HttpException, IOException {
        BufferedReader br = null;
        GetMethod get = new GetMethod(
                acmSite + "index.php?option=com_onlinejudge&Itemid=9&limit=" + limitOnPage + "&limitstart=0");
        try {
            client.executeMethod(get);
            InputStream firstGet = get.getResponseBodyAsStream();

            try {
                br = new BufferedReader(new InputStreamReader(firstGet, "UTF-8"));
            } catch (UnsupportedEncodingException ex) {
            }

            return processResults(br);
        } finally {
            get.releaseConnection();
        }
    }

    private void checkResults(int id, long maxTime, TestInput input, TestOutput result)
            throws InterruptedException, TimeoutException, HttpException, IOException {

        int limitRise = 50;
        int limitOnPage = 50;

        Random random = new Random();

        Thread.sleep(7000 + (Math.abs(random.nextInt()) % 3000));

        long start_time = System.currentTimeMillis();
        do {
            if (System.currentTimeMillis() - start_time > maxTime * 1000L) {
                logger.info(String.format("%.1f minutes without answer. Destroy!", maxTime / 60.));
                throw new TimeoutException("Too slow to answer.. destroy");
            }

            logger.info("Checking answer on UVa-ACM");
            List<Map<String, String>> results = getResults(limitOnPage);

            String sid = String.valueOf(id);
            Map<String, String> map = null;
            for (Map<String, String> m : results) {
                if (sid.equals(m.get("id"))) {
                    map = m;
                    break;
                }
            }

            if (map != null) {
                String status = map.get("status");
                String time = map.get("time");
                result.setPoints(0);
                if ("".equals(status) || "Received".equals(status) || "Running".equals(status)
                        || "Sent to judge".equals(status) || "In judge queue".equals(status)
                        || "Compiling".equals(status) || "Linking".equals(status)) {
                    Thread.sleep(7000);
                } else if ("Accepted".equals(status)) {
                    result.setStatus(ResultsStatusEnum.ACC.getCode());
                    result.setPoints(input.getMaxPoints());
                    result.setRuntime(Integer.parseInt(time));
                    break;
                } else if ("Compilation error".equals(status)) {
                    result.setStatus(ResultsStatusEnum.CE.getCode());
                    result.setNotes(getCompilationError(id));
                    result.setRuntime(Integer.parseInt(time));
                    break;
                } else if ("Presentation error".equals(status)) {
                    result.setStatus(ResultsStatusEnum.ACC.getCode());
                    result.setPoints(input.getMaxPoints());
                    result.setRuntime(Integer.parseInt(time));
                    break;
                } else if ("Wrong answer".equals(status)) {
                    result.setStatus(ResultsStatusEnum.WA.getCode());
                    result.setRuntime(Integer.parseInt(time));
                    break;
                } else if ("Time limit exceeded".equals(status)) {
                    result.setStatus(ResultsStatusEnum.TLE.getCode());
                    result.setRuntime(Integer.parseInt(time));
                    break;
                } else if ("Memory limit exceeded".equals(status)) {
                    result.setStatus(ResultsStatusEnum.MLE.getCode());
                    result.setRuntime(Integer.parseInt(time));
                    break;
                } else if ("Runtime error".equals(status)) {
                    result.setStatus(ResultsStatusEnum.RE.getCode());
                    result.setRuntime(Integer.parseInt(time));
                    break;
                } else {
                    result.setStatus(ResultsStatusEnum.UNKNOWN.getCode());
                    result.setNotes("Unknown status: \"" + status + "\"");
                    logger.info("Unknown status: \"" + status + "\"");
                    break;
                }
            } else {
                limitOnPage += limitRise;
            }
            Thread.sleep(3000 + (Math.abs(random.nextInt()) % 3000));
        } while (true);
    }

    private void logOut() throws HttpException, IOException {
        PostMethod post = new PostMethod(acmSite + "index.php?option=logout");
        try {
            NameValuePair[] logout = { new NameValuePair("op2", "logout"), new NameValuePair("return", acmSite),
                    new NameValuePair("lang", "english"), new NameValuePair("message", "0"),
                    new NameValuePair("Submit", "Logout") };
            post.setRequestBody(logout);

            client.executeMethod(post);
        } finally {
            post.releaseConnection();
        }
    }

    @Override
    public TestOutput runTest(String path, TestInput input) {
        TestOutput result = new TestOutput(null);

        Random random = new Random();

        String login = properties.getProperty("uva.login");
        String password = properties.getProperty("uva.password");

        int inQueue;
        try {
            inQueue = Integer.parseInt(properties.getProperty("uva.inqueue"));
        } catch (NumberFormatException e) {
            inQueue = 4;
        }

        long maxTime;
        try {
            maxTime = Long.parseLong(properties.getProperty("uva.max_time"));
        } catch (NumberFormatException e) {
            maxTime = 10L * 60;
        }

        prepareHttpClient();

        try {
            logIn(login, password);
            logger.info("Logged to UVa-ACM");

            for (int i = 3; i >= 0; --i) {
                if (checkInQueueStatus(input.getInputText()) > inQueue) {
                    if (i == 0) {
                        result.setStatus(ResultsStatusEnum.UNDEF.getCode());
                        result.setNotes(
                                "More than " + inQueue + " submissions of " + input.getInputText() + " in queue.");
                        result.setOutputText("UVa-ACM judge broken?");
                        logger.info(result.getNotes());
                        return result;
                    } else {
                        Thread.sleep(10000 + (Math.abs(random.nextInt()) % 5000));
                        continue;
                    }
                }
                break;
            }

            String msg = sendSolution(path, input);
            int id = 0;
            try {
                id = Integer.parseInt(msg);
            } catch (NumberFormatException ex) {
                logger.info("Unable to get UVa-ACM Submit id. Message: " + msg);
            }

            if (id == 0) {
                result.setStatus(ResultsStatusEnum.RV.getCode());
                result.setNotes(msg);
                return result;
            } else {
                logger.info("UVa-ACM Submit id = " + id);

                checkResults(id, maxTime, input, result);
            }
            logOut();
            logger.info("Logged out from UVa-ACM");
        } catch (Exception e) {
            logger.info("Exception: ", e);
            result.setStatus(ResultsStatusEnum.UNDEF.getCode());
            result.setNotes(e.getMessage());
            result.setOutputText(e.getClass().getName());
            return result;
        }

        return result;
    }

    private void prepareHttpClient() {
        client = new HttpClient();
        HttpClientParams params = client.getParams();
        params.setParameter("http.useragent", "Opera/9.80 (X11; Linux x86_64; U) Presto/2.12.388 Version/12.11");
        client.setParams(params);
    }

    @Override
    public byte[] precompile(byte[] code) {
        return code;
    }

    @Override
    public String compile(byte[] code) {
        return new String(code);
    }

    @Override
    public String postcompile(String path) {
        return path;
    }

    @Override
    public void closeProgram(String path) {
    }
}