Implement a simple Tomcat

Posted by Nat on Tue, 10 Dec 2019 21:15:05 +0100

1. Tomcat function

Our web application will run in Tomcat. Obviously, the request must arrive at Tomcat first. Tomcat will actually process the request as follows:

  • Provide Socket service: the start of Tomcat must be Socket service, which supports http protocol.
  • Request distribution: a Tomcat can provide services for multiple web applications, so the url needs to be distributed to different web applications.
  • We need to encapsulate the request and response into request and response: when we write back-end code, we use request and response directly, because Tomcat has done well.

Let's realize these three steps by ourselves.

2. Implementation code

Project structure:

  src
    └─mytomcat
            BookServlet.java
            CarServlet.java
            MyRequest.java
            MyResponse.java
            MyServlet.java
            MyTomcat.java
            ServletMapping.java
            ServletMappingConfig.java

2.1 encapsulating http requests and responses

package mytomcat;

import java.io.IOException;
import java.io.InputStream;

/**
 * Encapsulate http requests
 */
public class MyRequest {
    
    private String url;
    private String method;
    
    public MyRequest(InputStream inputStream) throws IOException {
        
        String httpRequest = "";
        byte[] httpRequestBytes = new byte[1024];
        int length = 0;
        if((length = inputStream.read(httpRequestBytes)) > 0) {
            httpRequest = new String(httpRequestBytes, 0, length);
        }
        
        String httpHead = httpRequest.split("\n")[0];
        url = httpHead.split("\\s")[1];
        method = httpHead.split("\\s")[0];
        
        System.out.println(this.toString());
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }
    
    @Override
        public String toString() {
            return "MyRequest -- url:" + url + ",method:" + method;
        }

}
package mytomcat;

import java.io.IOException;
import java.io.OutputStream;

/**
 * Encapsulate http response
 */
public class MyResponse {
    
    private OutputStream outputStream;
    
    public MyResponse (OutputStream outputStream) {
        this.outputStream = outputStream;
    }
    
    public void write(String content) throws IOException {
        StringBuffer httpResponse = new StringBuffer();
        httpResponse.append("HTTP/1.1 200 OK\n")
                    .append("Content-Type: text/html\n")
                    .append("\r\n")
                    .append(content);
        
        outputStream.write(httpResponse.toString().getBytes());
        outputStream.close();
    }

}

2.2 implementing different servlets

package mytomcat;
/**
 * Servlet abstract class
 */
public abstract class MyServlet {
    
    public abstract void doGet(MyRequest myRequest, MyResponse myResponse);
    
    public abstract void doPost(MyRequest myRequest, MyResponse myResponse);
    
    public void service(MyRequest myRequest, MyResponse myResponse) {
        if(myRequest.getMethod().equalsIgnoreCase("POST")) {
            doPost(myRequest, myResponse);
        }else if(myRequest.getMethod().equalsIgnoreCase("GET")) {
            doGet(myRequest, myResponse);
        }
    }
}
package mytomcat;

import java.io.IOException;

/**
 * Process http request for operation 'book'
 */
public class BookServlet extends MyServlet {

    @Override
    public void doGet(MyRequest myRequest, MyResponse myResponse) {
        try {
            myResponse.write("[get] book...");
        }catch(IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(MyRequest myRequest, MyResponse myResponse) {
        try {
            myResponse.write("[post] book...");
        }catch(IOException e) {
            e.printStackTrace();
        }
    }

}
package mytomcat;

import java.io.IOException;

/**
 * Process http request for operation 'vehicle'
 */
public class CarServlet extends MyServlet {

    @Override
    public void doGet(MyRequest myRequest, MyResponse myResponse) {
        try {
            myResponse.write("[get] car...");
        }catch(IOException e) {
            e.printStackTrace();
        }
        
    }

    @Override
    public void doPost(MyRequest myRequest, MyResponse myResponse) {
        try {
            myResponse.write("[post] car...");
        }catch(IOException e) {
            e.printStackTrace();
        }
    }

}

2.3 define Servlet mapping POJO class

package mytomcat;

public class ServletMapping {
    
    private String servletName;
    private String url;
    private String className;
    
    public ServletMapping(String servletName, String url, String className) {
        super();
        this.servletName = servletName;
        this.url = url;
        this.className = className;
    }

    public String getServletName() {
        return servletName;
    }

    public void setServletName(String servletName) {
        this.servletName = servletName;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }
    
}

2.4 configure Servlet mapping relationship

package mytomcat;

import java.util.ArrayList;
import java.util.List;

/**
 * Configure the correspondence between the request url and the servlet to be processed
 */
public class ServletMappingConfig {
    
    public static List<ServletMapping> servletMappingList = new ArrayList<>();;
    
    static {
        servletMappingList.add(new ServletMapping("Book", "/book", "mytomcat.BookServlet"));
        servletMappingList.add(new ServletMapping("Car", "/car", "mytomcat.CarServlet"));
    }

}

2.5 main class

package mytomcat;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class MyTomcat {
    private int port;
    //The correspondence between saving request url and processing request servlet
    private Map<String, String> urlServletMap = new HashMap<String, String>();
    
    public MyTomcat(int port) {
        this.port = port;
    }
    
    public void start() {
        initServletMapping();
        
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("MyTomcat is start...\n Listening port:" + port);
            
            while(true) {
                System.out.println("Waiting for a request...");
                Socket socket = serverSocket.accept();
                InputStream inputStream = socket.getInputStream();
                OutputStream outputStream = socket.getOutputStream();
                
                MyRequest myRequest = new MyRequest(inputStream);
                MyResponse myResponse = new MyResponse(outputStream);
                
                //Request distribution
                disPatch(myRequest, myResponse);
                socket.close();
            }
        }catch(IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket != null) {
                try {
                    serverSocket.close();
                }catch(IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    //The corresponding relationship between the initialization url and the servlet to be processed
    private void initServletMapping() {
        for(ServletMapping servletMapping: ServletMappingConfig.servletMappingList) {
            urlServletMap.put(servletMapping.getUrl(), servletMapping.getClassName());
        }
    }
    
    //Distribute processing requests
    private void disPatch(MyRequest myRequest, MyResponse myResponse) {
        String className = urlServletMap.get(myRequest.getUrl());
        
        //reflex
        try {
            Class<MyServlet> myServletClass = (Class<MyServlet>) Class.forName(className);
            MyServlet myServlet = myServletClass.newInstance();
            
            myServlet.service(myRequest, myResponse);
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        MyTomcat myTomcat = new MyTomcat(8080);
        myTomcat.start();
    }
}

3. test

Run the MyTomcat main class, and then enter http://localhost:8080/car in the browser. You can see that [get] car... Is returned, and it is finished.

Source address:

https://github.com/WangJun-SCU/mytomcat

Topics: Java socket Tomcat github