在线OJ系统的项目解析
当我们在设计一个OJ系统时我们首先考虑的几个点就是:
1.如何对输入的指令进行操作
2.通过指令实现对代码的编译及运行
3.怎样建立数据库去保存题目的相关信息(名称,简介,测试用例,代码模板~)
4.怎样实现调用数据库信息实现对题目的抽取(JDBC)。
5.如何实现前后端交互,将前端信息传输到后台服务器进行校验,并将校验结果传回前端反馈给用户。
6.前端页面的设计。
1.对输入的指令进行操作
1.1分析实现过程
1).当我们对代码进行操作时通常会执行两个命令
1.javac命令:将代码编译成java文件。
2.java命令:将编译好的代码进行运行。
2).而到我们想要实现这个命令就用到了这个语句
Runtime.getRuntime().exec(“String”);
通过这个语句创建一个进程来执行exec()内的命令。
3).我们在执行相关操作时会给我们反馈一些信息(相关的输出信息,以及报错信息)而我们就需要这些信息来判断我们的校验是否正确,所以我们就采用了重定向的方式将命令运行后的相关信息放到两个文件里面,这样方便我们对信息进行提取和查询。
4).所以我们便将其整合成一个“run”方法
cmd表示我们需要操作的命令符
stdoutFile保存命令的输出内容
stderrFile保存命令的错误内容
返回值为int是返回其新进程的退出码,通过退出码判断进程是否有问题。通常我们用0来表示无问题,非0表示有问题
我们通常用process.waitFor()来获取退出码。
public static int run(String cmd,String stdoutFile,String stderrFile){}
1.2代码示例
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
//创建一个类用于保存其校验方法。
public class CommandUtil {
//run方法就用于进行创建进程并执行命令
//cmd 表示要执行的名领,比如javac
//stdoutFile制定标准输出写道哪个文件
//stderrFile表示标准错误写到那个文件
public static int run(String cmd,String stdoutFile,
String stderrFile) throws IOException, InterruptedException {
//使用runtime这样的类完成进程的创建
//runtime的实例只有唯一一个,是单例模式
//这个process对是新创建出来的这个进程
Process process=Runtime.getRuntime().exec(cmd);
//通过exec这个方法生成的进程就是新进程
//正在执行exec这个方法的进程就是旧进程
//当新的进程跑起来之后,就需要获取新的进程的输出结果
//对新进程输入结果进行重定向,将其放入文件中
if (stdoutFile !=null) {
//getInputStream 得到的是新进程的标准输出
InputStream stdoutFrom = process.getInputStream();
//创建一个输入流将进程的标准输出写入文件中
FileOutputStream stdoutTo=new FileOutputStream(stdoutFile);
//接下来就从新进程这边一次读取每个字节,把stdoutFrom放入stdoutTo
while (true){
int ch= stdoutFrom.read();
if (ch==-1){
break;
}
stdoutTo.write(ch);
}
//文件读写完毕
stdoutFrom.close();
stdoutTo.close();
}
//针对标准错误进行重定向
if (stderrFile!=null){
//得到的是标准错误
InputStream stderrFrom = process.getErrorStream();
FileOutputStream stderrTo = new FileOutputStream(stderrFile);
//写入
while (true){
int ch= stderrFrom.read();
if (ch==-1){
break;
}
stderrTo.write(ch);
}
//文件读写完毕
stderrFrom.close();
stderrTo.close();
}
//等待新进程结束,并获取到新进程的退出码
int exitCode=process.waitFor();
return exitCode;
}
}
2.通过指令实现对代码的编译及运行
2.1分析实现过程
1).当我们已经实现了run方法后,我们就需要将我们的代码填充进去,于是我便建立了两个类:Question类用于存放前端发送过来的需要检测的代码片段,Answer类来存放反馈结果(代码运行是否有问题,反馈错误原因等)
2).因为考虑到我们后续的输入过程中因为存在很多用户及很多次输入。为方便区分于是我们便将这些信息(用户输入的代码,编译错误结果,运行错误信息,运行输出信息,编译后的java文件等)都保存成不同的文件,通过字节输入流(fileinputstream)及字节输出流(fileoutputstream)对文件进行操作。
3).为了方便我们进行文件操作。我们用一个类(FileUtil)封装读,写两个方法。
(1)readFile()方法
从文件名为filePath文件中一次把所有的内容读出来
public static String readFile(String filePath) {};
(2)writeFile()方法
把 content 中的内容一次写入到 filePath 对应的文件中
public static void writeFile(String filePath, String content) {};
4).当这些准备工作完成后,我们就开始对我们的代码进行编译及运行了,我们创建一个Task类设计了一个compileAndRun方法通过输入question里面代码返回一个answer的信息。
public Answer compileAndRun (Question question){};
5).compileAndRun方法的具体实现过程
(1).给这些临时文件准备一个目录(因为为了区分每次输入的结果,所以将每次操作进行都放到一个文件夹里)
(2).先要准备好需要用的的临时文件:
要编译源代码的文件,编译出错的文件
最终运行的标准输出&#