项目:在线OJ--MinMIn's Online OJ

MinMin's Online OJ

1.项目目标

仿照Leetcode实现一个简单的刷题的项目,用户可以在浏览器访问到题目列表页面,并点击题目进入题目详情页面,并在这里进行代码的编写、编译和运行,并返回结果到浏览器页面

2.项目实现的功能

1.可以显示所有的题目列表

2.点击题目可以看到题目的详情和代码编辑框以及一个可以提交编译运行的按钮

3.可以对提交的代码编译运行,并自动执行设定的测试用例,返回测试结果

3.项目所使用的技术栈

  • 开发环境:

    Win10 :计算机的操作系统

    IDEA:Java代码编写工具

    Maven:用来管理依赖包,并将项目打包成war包

    Linux :进行环境的部署

  • 使用的技术:

    Servlet:实现服务器相关的API

    Gson: Google提供的用来在Java对象和JSON数据之间进行映射的Java类库

    前端页面技术:用来展示页面并与后段数据接口交互

    Java多进程:使用多进程来控制JDK

    JDBC:Java提供的一个可以访问数据库的接口
    项目:在线OJ--MinMIn's Online OJ

4.项目的开发流程

4.1 编译模块

项目:在线OJ--MinMIn's Online OJ

4.1.1 实现的功能:

给定一个java文件,能够通过代码来控制jdk进行编译和运行

4.1.2 实现的细节

1.CommandandUtil.java

先实现一个可以执行某个指令的类,借助这个类,把整个java程序的编译和运行都组合到一起

  • .JDK中有一个Runtime类,借助这个类的对象,可以创建一个子进程,并且让子进程执行一个相关命令
 //1.获取Runtime对象.Runtime 是一个单例模式
  Runtime runtime = Runtime.getRuntime();
  • .使用Runtime对象的exec方法可以

​ 1)创建一个子进程

​ 2)进行进程程序替换

 //创建一个进程,在单独的进程中执行指定的命令
Process process = runtime.exec(cmd);
  • .使用重定向:将我们想要实际执行到的java进程process的内容重定向写入输出到指定的文件,因为父进程是我们在IDEA中创建的,process进程是在父进程里边被创建的,所以运行输出后,控制台的输出是父进程的,不是我们要用的

  • .开始重定向:

    (1)获取到子进程的对象process

(2)使用输入流读取进程执行过程中的消息;

(3)输出流把这些消息写到一个专门的文件stdoutFile或stderrFile中(标准输出重定向或标准错误重定向

(4)最后注意流的关闭

  //5.针对标准错误也进行重定向
        if (stderrFile != null) {
            InputStream stderrFrom = process.getErrorStream();
            OutputStream stderrTo=new FileOutputStream(stderrFile);
            int ch2=-1;
            while ((ch2 = stderrFrom.read()) != -1) {
                stderrTo.write(ch2);
            }
            stderrFrom.close();
            stderrTo.close();
        }
  • .应该确保子进程先执行完,所以要加上进程等待waitFor()
// exitCode 进程的退出码 0--执行成功,非0--失败
//process所依赖的父进程会被阻塞,直到process执行完毕,或被迫中止。被阻塞的进程才继续执行
int exitCode = process.waitFor();

项目:在线OJ--MinMIn's Online OJ

2.Question.java类:

需要编译执行的内容代码,有两个属性:code和stdin

     //要编译和执行的代码内容
    private String code;
    //要执行时标准输入要输入的内容
    private String stdin;
3.Answer.java类:

编译得到的结果,即编译过程中产生的数据:

    //通过error表示当前的错误类型,0--成功 1--编译出错 2--运行出错
    private int error;
    //表示具体的出错原因,编译错误、运行错误
    private String reason;
    //执行时标准输出对应的内容
    private String stdout;
    //执行时标准错误对应的内容
    private String stderr;
4.Task.java类:

描述一次完整的编译运行的过程,并定义一些属性作为临时文件,讲这些临时文件全部存储到tmp目录中,方便调试

(1)创建临时文件目录tmp,存放一些临时文件,把中间结果记录下来,方便调试

包括:Solution.java类\stdin.txt\stdout.txt\stderr.txt\compile_error.txt编译错误对应的文件(出错的具体原因)

(2)写一个编译运行的方法compileAndRun(要编译的代码),将结果返回

1.编译指令:javac -encoding utf-8 ./tmp/Solution.java -d ./tmp/ 
  注意:-d:表示将编译后的字节码文件存放到./tmp/目录中
  编译正确:则COMPILE_ERROR文件为空,表示编译顺利,非空表示编译出错,出错就不再继续了
2.运行指令:java -classpath ./tmp/Solution
  注意:classpath选项来指定加载路径
  运行正确:STDERR为空,使用FileUtil中的readFile方法查看文件内容,参数是文件路径
String cmd=String.format("javac -encoding utf8 %s -d %s",CODE,WORK_DIR);
CommandUtil.run(cmd, null, COMPILE_ERROR);
 String compileError = FileUtil.readFile(COMPILE_ERROR);
        if (!"".equals(compileError)) {
            System.out.println("编译出错");
            answer.setError(1);
            answer.setReason(compileError);
            return answer;
        }

4.2 设计工具类

4.3.1.FileUtil--读写文件的类

public static String readFile(String filePath)//读取文件
public static void writeFile(String filePath, String content)//写入文件

4.3.2.DBUtil--数据库操作类

1.数据源获取

2.获取连接

3.关闭连接

4.3 题目管理模块

4.2.1 实现的功能

可以存储获取所有的题目相关信息,进行题目的添加、删除的操作

4.2.2 实现的细节

1.设计数据库

设计主键id为自增

项目:在线OJ--MinMIn's Online OJ

2.获取数据源

在数据库操作类DButil.java中

使用线程安全版本的单例模式进行设计

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;
    }
3.建立数据库连接
public static Connection getConnection(){    
    try {        //内置了数据库连接池        
        return getDataSource().getConnection();    
    } catch (SQLException e) {       
        e.printStackTrace();    
    }    
    return null;
}
4.数据库连接关闭
 public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
        try {
            if(resultSet!=null){
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
5.题目数据的操作

项目:在线OJ--MinMIn's Online OJ

4.4 前后端交互

1.进入首页,可以获取所有题目的API

2.点击题目,可以看到题目详情的API

3.点击提交,可以执行编译运行的API

4.4.1GSON:

Gson是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个Json字符转成一个Java对象,或者将一个Java转化为Json字符串。

1.引入gson依赖

</dependencies>
    <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.2</version>
    </dependency>
</dependencies>

2.创建gson对象

private Gson gson=new GsonBuilder().create();

3.常用方法

public String toJson(Objcet obj)//toJson方法,可以将对象转化为json字符串。
public T fromJson(String jsonStr,T.class)//fromJson方法,可以将json字符串转化为Java对象 

3.特点:

​ a、快速、高效

  b、代码量少、简洁

  c、面向对象

  d、数据传递和解析方便

4.4.1.ProblemServlet

1.使用doGet方法获取题目列表,并返回给浏览器

private Gson gson=new GsonBuilder().create();
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String id = req.getParameter("id");
        if (id == null || "".equals(id)) {
            //1.没有id这个参数,就执行查找全部题目      }
            selectAll(resp);
        } else {
            //2.存在id参数,执行查找指定题目信息逻辑
            selectOne(Integer.parseInt(id), resp);
        }
    }
private void selectOne(int problemId, HttpServletResponse resp) throws IOException {
        resp.setContentType("application/json;charset=utf-8");
        ProblemDAO problemDAO=new ProblemDAO();
        Problem problem = problemDAO.selectOne(problemId);
        //测试代码不应该告诉前端用户,此时手动把这个内容清理掉
        problem.setTestCode("");
        String jsonString = gson.toJson(problem);
        resp.getWriter().write(jsonString);
    }
 private void selectAll(HttpServletResponse resp) throws IOException {
        //ContentType--描述body中的数据的类型是啥样的
        //常见取值:html:text/html
        //图片:image/png  image/jpg
        //json:application/json
        //css:text/css
        //javascript:application/javascript
        //此处还能同时设置字符集
        resp.setContentType("application/json;charset=utf-8");
        ProblemDAO problemDAO = new ProblemDAO();
        List<Problem> problems = problemDAO.selectAll();
        //把结果组织成json结构
        //【注意】需要把problem中的有序字段注释掉
        String jsonString = gson.toJson(problems);
        resp.getWriter().write(jsonString);
    }

4.4.2-CompileServlet.java

获取用户编译框中的代码,并进行编译运行

(1)static class CompileRequest 这个类用于辅助解析body中的数据请求,提交代码

private int id;
private String code;

(2)static class CompileResponse{//这个类用于辅助构造最终响应的body数据,解析

private int ok;
private String reason;
private String stdout;
doPost(req,resp)
1.读取请求的body的所有数据
2.按照API约定的格式来解析JSON数据,得到CompileRequest对象
3.通过对象的Id获取到用户编译的代码
4.通过对象的Id获取到该题目的测试用例的代码
5.进行组装
6.将组装的新代码重新创建一个question的对象
7.将该对象传入Task中编译运行得到结果answer
8.把运行结果构造成响应数据,并写回给客户端

4.5前端页面设计

4.5.1 实现功能

1.展示项目首页和题目列表

2.展示项目详情页

4.5.2 实现的细节

1.引入JQuery函数库
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>

1.jQuery是一个JavaScript函数库。是一个轻量级的"写的少,做的多"的JavaScript库。

2.jQuery库包含以下功能:

  • HTML 元素选取和操作
  • CSS 操作
  • AJAX:与服务器交换数据的技术,它在不重载全部页面的情况下,实现了对部分网页的更新。
2.引入Vue

构建用户界面的渐进式框架, 通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

猜你喜欢

转载自blog.51cto.com/14234228/2506265
OJ