[Yugong series] January 2022 Java Teaching Course 74-HTTP server (reflective version)

Posted by smsharif on Tue, 25 Jan 2022 04:55:09 +0100

Article catalogue

I http server

1. Preparation

  • Modify four places

Httpresponse - > constant web_ APP_ The value of path is consistent with the current module

In the httpserver - > main method, the port is changed to 80

Httpresponse - > add a write method and a construction method with parameters

Httpresponse - > add a contentType member variable to generate the corresponding set/get method

  • Sample code
// 1. Httpresponse - > constant web_ APP_ The value of path is consistent with the current module
public class HttpResponse {
  ...
  public static final String WEB_APP_PATH = "http-dynamic-server\\webapp";
  ...
}

// 2. In the httpserver - > main method, the port is changed to 80
public class HttpServer {
  public static void main(String[] args) throws IOException {
    ...
    //2. Bind this channel to a port
  	serverSocketChannel.bind(new InetSocketAddress(80));
    ...
  }
}  

// 3. Httpresponse - > add a write method and a construction method with parameters
public class HttpResponse {
  ...
  // The selectionKey has been provided, so the previous method that receives this parameter can be removed and can be used directly
  // HttpRequest is also optimized in this way, defining member variables, assigning values in the construction method, and other methods can be used directly
  private SelectionKey selectionKey;
  
  public HttpResponse(SelectionKey selectionKey) {
        this.selectionKey = selectionKey;
    }
  
  //The method of responding data to the browser ---- the method by which the browser responds to data when requesting dynamic resources
  //Content: the content of the response
  public void write(String content){
  }
  ...
}

public class HttpServer {
  public static void main(String[] args) throws IOException {
    ...
    //Response data / / parameters to be passed in the modified construction method
    HttpResponse httpResponse = new HttpResponse(selectionKey);
    ...
  }
}  

// 4. Httpresponse - > add a contentType member variable to generate the corresponding set/get method
public class HttpResponse {
  ...
  private String contentType;//MIME type
  
  public String getContentType() {
        return contentType;
    }
  public void setContentTpye(String contentType) {
        this.contentType = contentType;
        //Add to map collection
        hm.put("Content-Type",contentType);
    }
  ...
}

2. Browser requests dynamic resources

  • Two small problems

How does the server determine whether the browser requests static resources or dynamic resources?

We can specify that if the uri in the browser address bar starts with "/ servlet", it means that dynamic resources are requested

There are many classes and methods in a project. After the request comes, which method will be executed?

Write a UserServlet class and write the service method in the class

We can specify that if dynamic resources are requested, this class object will be created and the service method will be called to indicate that the server has processed the current request

  • Implementation steps

Parsing http requests

Processing browser requests

Define a UserServlet class in which the service method is defined to handle the dynamic resources requested by the browser

After parsing the http request, determine whether the uri starts with / servlet

response

  • Sample code
public class UserServlet{
  public void service(){
        //Simulate business processing -- you can judge and verify the mobile phone number
        System.out.println("UserServlet The user's request was processed...");
    }
}
public class HttpServer {
  public static void main(String[] args) throws IOException {
    	...
    	//Response data
    	HttpResponse httpResponse = new HttpResponse(selectionKey);
        httpResponse.setHttpRequest(httpRequest);

        if(httpRequest.getRequestURI().startsWith("/servlet")){
          	//Dynamic resources requested this time
          	//handle 
          	UserServlet userServlet = new UserServlet();
          	userServlet.service();
          	//response
        	httpResponse.setContentTpye("text/html;charset=UTF-8");
        	httpResponse.write("ok,UserServlet This request has been processed....");  
        }else{
          //Static resources requested this time
          httpResponse.sendStaticResource();
        }
    	...
  }
} 

public class HttpResponse {
  	...
	//The method of responding data to the browser ---- the method by which the browser responds to data when requesting dynamic resources
    //Content: the content of the response
    public void write(String content){
        //Prepare response line data
        this.version = "HTTP/1.1";
        this.status = "200";
        this.desc = "ok";

        //Splice the response lines together
        String responseLine = this.version + " " + this.status + " " + this.desc + "\r\n";

        //Prepare response header
        StringBuilder sb = new StringBuilder();
        Set<Map.Entry<String, String>> entries = hm.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            //entry represents each key value pair object in turn
            //Key --- the name of the response header
            //Value --- the value of the response header
            sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
        }

        //Processing response blank lines
        String emptyLine = "\r\n";

        //Splice response line, response header, response blank line
        String result = responseLine + sb.toString() + emptyLine;

        try {
            //Give the browser a response line, a response header, and a blank response line
            ByteBuffer byteBuffer1 = ByteBuffer.wrap(result.getBytes());
            SocketChannel channel = (SocketChannel) selectionKey.channel();
            channel.write(byteBuffer1);

            //Give browser response body
            ByteBuffer byteBuffer2 = ByteBuffer.wrap(content.getBytes());
            channel.write(byteBuffer2);

            //Release resources
            channel.close();

        } catch (IOException e) {
            System.out.println("Response data failed....");
            e.printStackTrace();
        }

    }    
 	 ...
}

3.main method and Servlet optimization

main method optimization

  • demand Extract the code requesting dynamic resources into a separate class and method to simplify the code in main
  • code implementation
public class DynamicResourceProcess {
  
    //Executes the service method of the specified dynamic resource
    //Parameter one
    //Because the corresponding processing may be made according to the uri requested by the user in the later stage
    //Parameter two
    //To respond data to users, you need to use httpResponse
    public void process(HttpRequest httpRequest,HttpResponse httpResponse) {
        // Create a UserServlet object and call the service method for processing
        UserServlet userServlet = new UserServlet();
        userServlet.service();

        //Respond to browser
        httpResponse.setContentTpye("text/html;charset=UTF-8");
        httpResponse.write("ok,UserServlet This request has been processed....");
    }
}

public class HttpServer {
  public static void main(String[] args) throws IOException {
    	...
    	//Response data
    	HttpResponse httpResponse = new HttpResponse(selectionKey);
        httpResponse.setHttpRequest(httpRequest);

        if(httpRequest.getRequestURI().startsWith("/servlet")){
          	//Dynamic resources requested this time
       		DynamicResourceProcess drp = new DynamicResourceProcess();
            drp.process(httpRequest,httpResponse);
        }else{
          //Static resources requested this time
          httpResponse.sendStaticResource();
        }
    	...
  }
} 

Servlet optimization

  • demand Write the code that responds to the browser to the Servlet
  • code implementation
public class UserServlet implements HttpServlet{

    //Method of processing browser request
    //Parameter one
    //Because the corresponding processing may be made according to the uri requested by the user in the later stage
    //Parameter two
    //To respond data to users, you need to use httpResponse
    public void service(HttpRequest httpRequest, HttpResponse httpResponse){
        //Simulate business processing -- you can judge and verify the mobile phone number
        System.out.println("UserServlet The user's request was processed...");
        //Respond to browser
        httpResponse.setContentTpye("text/html;charset=UTF-8");
        httpResponse.write("ok,UserServlet This request has been processed....");
    }
}

public class DynamicResourceProcess {
  
    //Executes the service method of the specified dynamic resource
    //Parameter one
    //Because the corresponding processing may be made according to the uri requested by the user in the later stage
    //Parameter two
    //To respond data to users, you need to use httpResponse
    public void process(HttpRequest httpRequest,HttpResponse httpResponse) {
        // Create a UserServlet object and call the service method for processing
        UserServlet userServlet = new UserServlet();
        userServlet.service(httpRequest,httpResponse);
    }
}

4. Multiple dynamic resources

  • Multiple dynamic resources For each business operation, we will define a corresponding Servlet to complete it. Many servlets will be generated on the server
  • Implementation steps
    • Define an interface HttpServlet, and define the service method in the interface.
    • For each service, a servlet class is defined corresponding to it, which implements the HttpServlet interface
    • Get the uri of the request, make a judgment, and call the service method in different servlet classes
  • code implementation
// 1. Define an interface HttpServlet, and define the service method in the interface
public interface HttpServlet {

    //Define the method of business processing
    public abstract void service(HttpRequest httpRequest, HttpResponse httpResponse);
}

// 2. For each service, define a servlet class corresponding to it, which implements the HttpServlet interface
public class UserServlet implements HttpServlet{
    //Method of processing browser request
    //Parameter one
    //Because the corresponding processing may be made according to the uri requested by the user in the later stage
    //Parameter two
    //To respond data to users, you need to use httpResponse
    public void service(HttpRequest httpRequest, HttpResponse httpResponse){
        //Simulate business processing -- you can judge and verify the mobile phone number
        System.out.println("UserServlet The user's request was processed...");
        //Respond to browser
        httpResponse.setContentTpye("text/html;charset=UTF-8");
        httpResponse.write("ok,UserServlet This request has been processed....");
    }
}

public class LoginServlet implements HttpServlet{
    @Override
    public void service(HttpRequest httpRequest, HttpResponse httpResponse) {
       //handle
        System.out.println("LoginServlet Login request processed");
       //response
        httpResponse.setContentTpye("text/html;charset=UTF-8");
        httpResponse.write("Login succeeded");
    }
}

public class RegisterServlet implements HttpServlet{
    @Override
    public void service(HttpRequest httpRequest, HttpResponse httpResponse) {
       //handle
        System.out.println("RegisterServlet Registration request processed");
       //response
        httpResponse.setContentTpye("text/html;charset=UTF-8");
        httpResponse.write("login was successful");
    }
}

public class SearchServlet implements HttpServlet{
    @Override
    public void service(HttpRequest httpRequest, HttpResponse httpResponse) {
       //handle
        System.out.println("SearchServlet Search item request processed");
       //response
        httpResponse.setContentTpye("text/html;charset=UTF-8");
        httpResponse.write("Responded to some product information");
    }
}

// 3. Obtain the uri of the request, make judgment, and call the service method in different servlet classes
public class DynamicResourceProcess {
  
    public void process(HttpRequest httpRequest,HttpResponse httpResponse){
          //Gets the uri of the request
          String requestURI = httpRequest.getRequestURI();
 
          //Judge according to the requested uri
          if("/servlet/loginservlet".equals(requestURI)){
              //Login request
              LoginServlet loginServlet = new LoginServlet();
              loginServlet.service(httpRequest,httpResponse);
          }else if("/servlet/registerservlet".equals(requestURI)){
              //Registration request
              RegisterServlet registerServlet = new RegisterServlet();
              registerServlet.service(httpRequest,httpResponse);
          }else if("/servlet/searchservlet".equals(requestURI)){
              //Search for product requests
              SearchServlet searchServlet = new SearchServlet();
              searchServlet.service(httpRequest,httpResponse);
          }else{
              //Indicates the default processing method
              //Create a UserServlet object and call the service method for processing
              UserServlet userServlet = new UserServlet();
              userServlet.service(httpRequest,httpResponse);
          }
      }
}

5. Optimize through reflection and configuration file

  • Optimization steps
  1. Write the Servlet information to the properties configuration file

Format: servlet info = / servlet / userservlet, full class name/ servlet/loginServlet, full class name

  1. Define an interface ServletConcurrentHashMap. The interface defines ConcurrentHashMap. This collection stores all servlet information
  2. Define an interface, ParseServletConfig, in which a method (parse) is defined
  3. Define the implementation class of ParseServletConfig, parse the configuration file, and save the Servlet information in the configuration file into the map collection
  4. In the first line of the main method, start a thread to execute the code that parses the configuration file
  5. Modify the process method in the DynamicResourceProcess
  • code implementation
// 1. Write the Servlet information into the properties configuration file
// In webapp \ config \ servlet info In the properties file, write the following
servlet-info=/servlet/loginservlet,com.itheima.myservlet.LoginServlet;/servlet/registerservlet,com.itheima.myservlet.RegisterServlet;/servlet/searchservlet,com.itheima.myservlet.SearchServlet;/servlet/lostpasswordservlet,com.itheima.myservlet.LostPasswordServlet

// 2. Define an interface ServletConcurrentHashMap. The interface defines ConcurrentHashMap, which stores all servlet information
public interface ServletConcurrentHashMap {
    //A map collection that stores the request path and the corresponding servlet
    //Key: requested uri
    //Value: the corresponding Servlet object
    public static final ConcurrentHashMap<String,  HttpServlet> map = new ConcurrentHashMap<>();
}

// 3. Define an interface ParseServletConfig, in which a method (parse) is defined
public interface ParseServletConfig {
    //Method of parsing data
    public abstract void parse();
}

// 4. Define the implementation class of ParseServletConfig, parse the configuration file, and save the Servlet information in the configuration file into the map set
public class PropertiesParseServletConfig implements ParseServletConfig {
    @Override
    public void parse() {

        try {
            //1. Read the data in the configuration file
            Properties properties = new Properties();
            FileReader fr = new FileReader("http-dynamic-server/webapp/config/servlet-info.properties");
            properties.load(fr);
            fr.close();

            //2. Get the attribute value of servlet info in the collection
            String properValue = (String) properties.get("servlet-info");
            // uri, full class name; uri, full class name

            //3. Analysis
            String[] split = properValue.split(";");
            for (String servletInfo : split) {
                String[] servletInfoArr = servletInfo.split(",");
                String uri = servletInfoArr[0];
                String servletName = servletInfoArr[1];

                //We need to create his object through servletname (full class name)
                Class clazz = Class.forName(servletName);
                HttpServlet httpServlet = (HttpServlet) clazz.newInstance();
                //4. Add uri and httpServlet to the map set
                ServletConcurrentHashMap.map.put(uri,httpServlet);
            }
        } catch (Exception e) {
            System.out.println("Parsing data exception.....");
            e.printStackTrace();
        }
    }
}

public class LoaderResourceRunnable implements  Runnable {
    @Override
    public void run() {
        //Execute parse method
        ParseServletConfig parseServletConfig = new PropertiesParseServletConfig();
        parseServletConfig.parse();
        
    }
}

// 5. In the first line of the main method, start a thread to execute the code for parsing the configuration file
public class HttpServer {
    public static void main(String[] args) throws IOException {
        //Start a thread to parse the configuration file
        new Thread(new LoaderResourceRunnable()).start();
        ...
    }
}

// 6. Modify the process method in dynamic resourceprocess
public class DynamicResourceProcess {
  
    public void process(HttpRequest httpRequest,HttpResponse httpResponse){
          	//Gets the uri of the request
          	String requestURI = httpRequest.getRequestURI();
          	//Directly find the corresponding servlet object in the map collection according to the requested uri
        	HttpServlet httpServlet = ServletConcurrentHashMap.map.get(requestURI);
            //Call the service method to process the request and respond
            httpServlet.service(httpRequest,httpResponse);
    }
}    

7. The servlet forgot to implement HttpServlet interface processing

  • Occurrence

When writing a Servlet, I forgot to implement the HttpServlet interface

  • Result

After the reflection creates an object, a type conversion exception will be reported when it is strongly converted to HttpServlet

  • Solution

After the reflection object is created, judge before strong conversion to HttpServlet

If the HttpServlet interface is implemented, perform forced conversion

Otherwise, an exception is thrown

  • code implementation
public class PropertiesParseServletConfig implements ParseServletConfig {
    @Override
    public void parse() {

        try {
            //1. Read the data in the configuration file
            Properties properties = new Properties();
            FileReader fr = new FileReader("http-dynamic-server/webapp/config/servlet-info.properties");
            properties.load(fr);
            fr.close();

            //2. Get the attribute value of servlet info in the collection
            String properValue = (String) properties.get("servlet-info");
            // uri, full class name; uri, full class name

            //3. Analysis
            String[] split = properValue.split(";");
            for (String servletInfo : split) {
                String[] servletInfoArr = servletInfo.split(",");
                String uri = servletInfoArr[0];
                String servletName = servletInfoArr[1];

                //We need to create his object through servletname (full class name)
                Class clazz = Class.forName(servletName);

                //Get all the interface information implemented by this class and get an array
                Class[] interfaces = clazz.getInterfaces();

                //Define a variable of boolean type
                boolean flag =  false;
                //Traversal array
                for (Class clazzInfo : interfaces) {
                    //Judge whether the bytecode object of the currently traversed interface is the same as the bytecode file object of HttpServlet
                    if(clazzInfo == HttpServlet.class){

                        //If it is the same, you need to change the flag value End cycle
                        flag = true;
                        break;
                    }
                }

                if(flag){
                    //true indicates that the current class has implemented the HttpServlet interface
                    HttpServlet httpServlet = (HttpServlet) clazz.newInstance();
                    //4. Add uri and httpServlet to the map set
                    ServletConcurrentHashMap.map.put(uri,httpServlet);
                }else{
                    //false means that the current class has not implemented the HttpServlet interface
                    throw new NotImplementsHttpServletException(clazz.getName() + "Not Implements HttpServlet");
                }
            }
        } catch (NotImplementsHttpServletException e) {
            e.printStackTrace();
        }catch (Exception e) {
            System.out.println("Parsing data exception.....");
            e.printStackTrace();
        }
    }
}

8. Response 404

  • Occurrence

The client browser requested a dynamic resource that does not exist in the server

  • Result

The code in the server is abnormal and the program stops

  • Solution

If the requested dynamic resource does not exist, and the server finds the corresponding Servlet according to the requested uri, it will be null. If you continue to call the method, an exception will appear

Add a non null judgment. If it is not null, continue to process the request and call the method

If null, response 404

  • code implementation
public class DynamicResourceProcess {
    //Executes the service method of the specified dynamic resource
    //Parameter one
    //Because the corresponding processing may be made according to the uri requested by the user in the later stage
    //Parameter two
    //To respond data to users, you need to use httpResponse
    public void process(HttpRequest httpRequest,HttpResponse httpResponse){
        //Gets the uri of the request
        String requestURI = httpRequest.getRequestURI();
        //Directly find the corresponding servlet object in the map collection according to the requested uri
        HttpServlet httpServlet = ServletConcurrentHashMap.map.get(requestURI);
        if(httpServlet != null){
            //Call the service method to process the request and respond
            httpServlet.service(httpRequest,httpResponse);
        }else{
            //The dynamic resource requested by the browser does not exist
            //Response 404
            response404(httpResponse);
        }
    }
    //The browser requests that the dynamic resource does not exist and responds to 404
    private void response404(HttpResponse httpResponse) {
        try {
            //Prepare response line
            String responseLine = "HTTP/1.1 404 NOT FOUND\r\n";
            //Prepare response header
            String responseHeader = "Content-Type: text/html;charset=UTF-8\r\n";
            //Prepare response blank line
            String emptyLine = "\r\n";
            //Spliced together
            String result = responseLine + responseHeader + emptyLine;

            //Send the response line, response header and empty response line to the browser
            SelectionKey selectionKey = httpResponse.getSelectionKey();
            SocketChannel channel = (SocketChannel) selectionKey.channel();

            ByteBuffer byteBuffer1 = ByteBuffer.wrap(result.getBytes());
            channel.write(byteBuffer1);

            //Content of response body to browser
            ByteBuffer byteBuffer2 = ByteBuffer.wrap("404 NOT FOUND....".getBytes());
            channel.write(byteBuffer2);

            //Release resources
            channel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}