Skip to content

Commit

Permalink
RELEASE: 0.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Eldath committed Jul 23, 2017
1 parent 0e2bd35 commit 053d16f
Show file tree
Hide file tree
Showing 18 changed files with 243 additions and 27 deletions.
16 changes: 16 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
sudo: required

language: java

jdk:
- oraclejdk8

script:
- "./gradlew build"

before_install:
- chmod a+x gradlew

notifications:
email:
- ray.eldath@gmail.com
8 changes: 4 additions & 4 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ for Avalon-Executive, API version `v0`

## 编译程序:Compile

- **调用URL:`/avalon/executive/v0/get_all_lang`**
- **调用URL:`/avalon/executive/v0/compile`**

- **调用方法:POST**

Expand All @@ -60,7 +60,8 @@ for Avalon-Executive, API version `v0`
- ```json
{
"id": 1000,
"error": false
"error": false,
"out": "URLEncoded后的编译错误信息,若无错误信息则为空"
}
```

Expand Down Expand Up @@ -88,7 +89,6 @@ for Avalon-Executive, API version `v0`
{
"id": 1000,
"error": false,
"return": "2", // URLEncoded后的错误信息(error为true)或程序返回(error为false)
"result": "Exited Normally\nWIFEXITED - WEXITSTATUS() = 0\n0\n6628"
"return": "2" // URLEncoded后的错误信息(error为true)或程序返回(error为false)
}
```
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Avalon-Executive [![GNU Affero General Public License, version 3](https://www.gnu.org/graphics/agplv3-155x51.png)](https://www.gnu.org/licenses/lgpl.html)
[![Build Status](https://travis-ci.org/ProgramLeague/Avalon-Executive.svg?branch=master)](https://travis-ci.org/ProgramLeague/Avalon-Executive) [![CircleCI](https://circleci.com/gh/Ray-Eldath/Avalon/tree/master.svg?style=svg)](https://circleci.com/gh/Ray-Eldath/Avalon/tree/master) [![](https://jitpack.io/v/ProgramLeague/Avalon-Executive.svg)](https://jitpack.io/#ProgramLeague/Avalon-Executive)

A safe program compiler & executor base on Docker.

## Deployment
Expand Down
25 changes: 24 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group 'tech.ray-eldath'
version 'v0.0.1-alpha'
version '0.0.1'

apply plugin: 'java'
apply plugin: 'scala'
Expand All @@ -12,6 +12,9 @@ targetCompatibility = 1.8
repositories {
jcenter()
mavenCentral()
maven {
url 'https://jitpack.io'
}
}

dependencies {
Expand All @@ -36,6 +39,26 @@ dependencies {
compile group: 'com.spotify', name: 'docker-client', version: '8.8.0'
}

sourceSets {
main {
scala {
srcDirs = ['src/main/java']
}
java {
srcDirs = []
}
}

test {
scala {
srcDirs = ['src/test/java']
}
java {
srcDirs = []
}
}
}

tasks.withType(ScalaCompile) {
options.encoding = "UTF-8"
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/ray/eldath/avalon/executive/core/Compiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public static File compile(String workDir, Submission submission)
String cmd = language.getCompileCmd();
String executableSuffix = getCodeFileSuffix(cmd);
File executableFile = new File(String.format("%s/%s/_file.%s", workDir, submission.getSubmitTime(), executableSuffix));

if (!language.isCompileNeeded())
return executableFile;

String containerId = CompilerContainerPool.instance().getContainerId(language);

ExecPair state = DockerOperator.instance().exec(containerId, cmd);
Expand Down
21 changes: 17 additions & 4 deletions src/main/java/ray/eldath/avalon/executive/core/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import static java.util.stream.Collectors.toList;
import static ray.eldath.avalon.executive.tool.DockerOperator.instance;

public class Runner {
private static final int TIME_LIMIT_MILLISECONDS = 5000;
private static final int _TIME_LIMIT_MILLISECONDS = 1500;

public static ExecPair run(Language language, File executableFile) throws DockerException, InterruptedException, RunErrorException, IOException {
public static ExecPair run(Language language, File executableFile)
throws DockerException, InterruptedException, RunErrorException, IOException {
String image = language.getRunDockerImageName();
ContainerConfig config = ContainerConfig.builder()
.networkDisabled(true)
Expand All @@ -32,7 +36,7 @@ public static ExecPair run(Language language, File executableFile) throws Docker
instance().copyFileIn(containerId, executableFile.getParentFile().toPath(), "/sandbox");
ExecPair state = instance().exec(containerId, language.getRunCmd());

Thread.sleep(TIME_LIMIT_MILLISECONDS);
Thread.sleep(_TIME_LIMIT_MILLISECONDS);

ExecInfoSimple info = instance().inspectExec(state.getExecId());

Expand All @@ -48,7 +52,16 @@ public static ExecPair run(Language language, File executableFile) throws Docker
if (!containerState.running())
throw new RunErrorException("<unknown error>");
instance().killContainer(containerId);
Files.deleteIfExists(executableFile.getParentFile().toPath());

delAll(executableFile.getParentFile().toPath());

return state;
}

private static void delAll(Path path) throws IOException {
List<Path> paths = Files.list(path).collect(toList());
for (Path thisPath : paths)
Files.deleteIfExists(thisPath);
Files.deleteIfExists(path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import ray.eldath.avalon.executive.servlet.Compile;
import ray.eldath.avalon.executive.servlet.GetAllLang;
import ray.eldath.avalon.executive.servlet.Run;

public class MainServer {
public static void main(String[] args) throws Exception {
Expand All @@ -13,6 +15,8 @@ public static void main(String[] args) throws Exception {
server.setHandler(context);
server.setStopAtShutdown(true);

context.addServlet(new ServletHolder(new GetAllLang()), "/get_all_lang");
context.addServlet(new ServletHolder(new Compile()), "/compile");
context.addServlet(new ServletHolder(new Run()), "/run");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ package ray.eldath.avalon.executive.model


class Language(id: String, name: String, buildDockerImageName: String, runDockerImageName: String, compileCmd: String, runCmd: String) {
var compileNeeded = false

def isCompileNeeded: Boolean = compileNeeded

def setDoNotNeedCompile(): Language = {
compileNeeded = true
this
}

def getId: String = id

def getName: String = name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class SafetyOutputStream extends OutputStream {
public void write(int b) throws IOException {
if (full)
return;
if (builder.length() + 1 > ConstantPool.MAX_OUTPUT_STREAM_LENGTH()) {
if (builder.length() + 1 > ConstantPool._MAX_OUTPUT_STREAM_LENGTH()) {
builder.append("...");
full = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import java.io.File

object ConstantPool {

val CURRENT_PATH: String = new File("").getCanonicalPath
val _CURRENT_PATH: String = new File("").getCanonicalPath

val MAX_OUTPUT_STREAM_LENGTH: Int = 255
val _MAX_OUTPUT_STREAM_LENGTH: Int = 255

val _WORK_DIR: String = _CURRENT_PATH + File.separator + "temp"
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ray.eldath.avalon.executive.pool

import java.io.File

object ExecutableFilePool {
private val executableFiles = new java.util.HashMap[Integer, File]

def get(id: Int): File = executableFiles.get(id)

def put(id: Int, executableFile: File): Unit = executableFiles.put(id, executableFile)

def rm(id: Int): Unit = executableFiles.remove(id)
}
14 changes: 12 additions & 2 deletions src/main/java/ray/eldath/avalon/executive/pool/LanguagePool.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,18 @@ object LanguagePool {
"python3",
"python:alpine",
"python:alpine",
"python3 -m py_compile _file.py",
"python3 _file.py")
"python3 _file.py",
"python3 _file.py").setDoNotNeedCompile()
)

idToLanguage.put("lice",
new Language(
"lice",
"lice-lang",
"ray-eldath/lice-alpine",
"ray-eldath/lice-alpine",
"java -jar lice.jar _file.lice",
"java -jar lice.jar _file.lice").setDoNotNeedCompile()
)

def getById(id: String): Language = idToLanguage.get(id)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ray.eldath.avalon.executive.pool

import java.util

import ray.eldath.avalon.executive.model.Submission

object SubmissionPool {
private val submissions = new util.HashMap[Integer, Submission]

def put(submission: Submission): Unit = SubmissionPool.submissions.put(submission.getId, submission)

def rm(id: Int): Unit = SubmissionPool.submissions.remove(id)

def getById(id: Int): Submission = SubmissionPool.submissions.get(id)

def has(id: Int): Boolean = SubmissionPool.submissions.containsKey(id)
}
34 changes: 32 additions & 2 deletions src/main/java/ray/eldath/avalon/executive/servlet/Compile.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,66 @@
package ray.eldath.avalon.executive.servlet;

import com.spotify.docker.client.exceptions.DockerException;
import org.eclipse.jetty.util.UrlEncoded;
import org.json.JSONObject;
import org.json.JSONTokener;
import ray.eldath.avalon.executive.core.Compiler;
import ray.eldath.avalon.executive.exception.CompileErrorException;
import ray.eldath.avalon.executive.model.Language;
import ray.eldath.avalon.executive.model.Submission;
import ray.eldath.avalon.executive.pool.ConstantPool;
import ray.eldath.avalon.executive.pool.ExecutableFilePool;
import ray.eldath.avalon.executive.pool.LanguagePool;
import ray.eldath.avalon.executive.tool.ResponseErrorMessage;
import ray.eldath.avalon.executive.pool.SubmissionPool;
import ray.eldath.avalon.executive.tool.ResponseRequestUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;

public class Compile extends HttpServlet {
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json");
JSONObject object = (JSONObject) new JSONTokener(req.getReader()).nextValue();
int id = object.getInt("id");
String encodedCode = object.getString("code");
String languageId = object.getString("lang");
Language language = LanguagePool.getById(languageId);
if (language == null) {
ResponseErrorMessage.response(
ResponseRequestUtils.response(
resp,
HttpServletResponse.SC_BAD_REQUEST,
"找不到id为" + languageId + "的语言!"
);
return;
}
Submission submission = new Submission(id, language, encodedCode);
SubmissionPool.put(submission);
JSONObject response = new JSONObject();
response.put("id", id);
File executableFile;
try {
executableFile = Compiler.compile(ConstantPool._WORK_DIR(), submission);
} catch (DockerException | InterruptedException e) {
ResponseRequestUtils.responseDockerException(resp, e);
return;
} catch (CompileErrorException e) {
response.put("out", UrlEncoded.encodeString(e.toString()));
response.put("error", true);
resp.getWriter().write(response.toString());
resp.getWriter().close();
return;
}
ExecutableFilePool.put(id, executableFile);
response.put("out", "");
response.put("error", false);
resp.getWriter().write(response.toString());
resp.getWriter().close();
}
}
Loading

0 comments on commit 053d16f

Please sign in to comment.