仿照leetcode来写一个在线判题系统的项目,用户可以实现通过url在浏览器访问试题列表、选中相关题目并编写代码、提交到服务器编译运行、将结果反馈给浏览器展示在页面等相关操作。
目标:给定以Java代码文件(这里只考虑单个文件的情况,不考虑多文件复杂工程的情况),能够通过代码并并控制jdk进行编译和运行
Runtime类和Process类:这里我们需要在当前程序中调用另一个现成的系统命令,这样就利用利用Runtime类和Process类,可以创建一个子进程,并且让子进程来执行一个相关的命令(编译:javac,运行:java)
//1.获取Runtime对象,Runtime对象是一个单例的
Runtime runtime=Runtime.getRuntime();
//2.通过Runtime对象中的exec方法来执行一个指令
//相当于在命令行中输入cmd命令并执行
Process process=runtime.exec(cmd);
重定向:使用重定向,是为了把进程输出的内容写到指定的文件中去;
实现重定向,需要先获取到创建出的新的进程对象:process;然后我们只对标准重定向,不一定能看到最终结果,一个命令的输出内容也有可能是通过标准错误来打印的;所以,我们定义两个文件标准输出、标准错误;
//3.针对标准输出进行重定向
if(stdoutFile!=null){
//进程的标准输出中的结果就可以通过这个InputStream获取到
InputStream stdoutFrom=process.getInputStream();
OutputStream stdoutTo=new FileOutputStream(stdoutFile);
int ch=-1;
while((ch=stdoutFrom.read())!= -1){
stdoutTo.write(ch);
}
stdoutFrom.close();
stdoutTo.close();
}
//4.针对标准错误也进行重定向
if(stderrFile!=null){
InputStream stderrFrom =process.getErrorStream();
OutputStream stderrTo=new FileOutputStream(stderrFile);
int ch=-1;
while((ch=stderrFrom.read())!=-1){
stderrTo.write(ch);
}
stderrFrom.close();
stderrTo.close();
}
进程等待:多进程和多线程一样,都是并发式执行的,为了能够确保子进程先执行完,就先让父进程进行等待;
//5.为了确保子进程先执行完,需要加上进程等待
// 父进程会在waitfor阻塞等待,直到子进程执行结束,再继续往下执行
int exitCode= process.waitFor();
return exitCode;
得到的临时文件的目录:
在Task类里面定义方法compileAndRun来描述编译运行的过程;
主要过程为:
这个操作主要在Linux上进行,提前要在Linux上安装好JDK、MySQL、tomcat、Maven
设计的数据库为:
在Linux上访问利用的方法是把代码打包放到云服务器上进行执行;
进行数据库的操作:
进行数据库的连接操作时,采用的是DataSource数据库连接池,这里,为了提高效率,采用的是线程安全版本的单例模式,核心操作为:synchronized加锁、双重if判断、volatile关键字:
private static volatile DataSource dataSource = null;
public static DataSource getDataSource(){
if(dataSource == null){
synchronized (DBUtil.class) {
if(dataSource == null){
dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setURL(URL);
((MysqlDataSource)dataSource).setUser(USERNAME);
((MysqlDataSource)dataSource).setPassword(PASSWORD);
}
}
}
return dataSource;
}
此项目需要数据库的操作还有:获取所有题目信息、获取指定id的题目、新增一个题目到数据库、删除指定信息;
设计过程是和需求有关的,需求如下:
Gson介绍:
com.google.code.gson
gson
2.8.2
// 方式1
Gson gson = new gson();
// 方式2
Gson gson = new GsonBuilder().create();
// 将Java对象转换为JSON字符串(problems是创建的对象)
String jsonString = gson.toJson(problems);
// 将JSON字符串转换为Java对象(body为请求到的前端数据,将他转换为CompileRequest对象)
CompileRequest compileRequest = gson.fromJson(body,CompileRequest.class);
ProblemServlet类作用:
1.利用get获取到所有的题目列表:获取到题目列表只需要知道题目的id,title,level;那么可以通过ProblemDAO类里面的selectAll来获取数据库里面的类信息,最后将它转换为Json字符串,最后将他返回到前端;
2.利用get获取到题目的详细信息:获取到题目的详细信息要根据具体的题目id来确定,方法和上一个类似;
CompileServlet类作用:把编译框中的代码进行编译运行并给出结果
1.创建两个类,用来完成请求解析和相应的构建
2.在doPost()方法中处理过程:
步骤为:先读取请求中的所有数据——>解析json数据,得到CompileRequest对象——>根据CompileRequest对象得到id,按照id从数据库中读取到对应的测试用例代码,再根据CompileRequest对象得到用户输入的代码——>把用户输入的代码和测试用例进行组装,成一个完整的可执行编译的代码——>创建Task对象对刚才的代码进行编译运行——>把运行结果构造成响应数据,并写会给客户端;
需要写一些网页,通过这些网页把后端的逻辑串到一起,组织成一个功能相对完整的程序;
系统主要有两个页面:
借助JQuery这个库,完成从网页访问访问服务器的操作:
借助Vue这个框架,实现把数据渲染到页面上:
实现题目列表页和题目详情页和数据之间的关联,同时实现两个页面之间的切换;
题目的录入是要每次在后台录入,不能在线录入,比较麻烦;
没有用户管理,不能清楚的统计每个人做的题;
https://github.com/yaoguo00/java-oj