Java基礎-blank server的實現思路

單例模式實現SAX方式XML讀取

SAX方式讀取,效率更高,懶漢式單例模式定義ServerHandler,使得服務中只有一個Handler對象。

server.xmlutil

server.xmlutil.ServerHandler.java

package server.xmlutil;

import server.entity.Servlet;
import server.entity.ServletMapping;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.HashMap;
import java.util.Map;

/**
 * 單例實現Server.xml的讀取
 * @author Blank
 */
public class ServerHandler extends DefaultHandler {
    private Map<String, String> servlets;
    private Servlet servlet;
    private Map<String, String> servletMappings;
    private ServletMapping servletMapping;
    private String port;
    private String errorPage;
    private String welcomePage;
    private String tag;

    private static ServerHandler serverHandler;

    private ServerHandler() {}

    public static ServerHandler getHandler() {
        if (serverHandler == null)
            serverHandler = new ServerHandler();
        return serverHandler;
    }

    @Override
    public void startDocument() throws SAXException {
        servlets = new HashMap<>();
        servletMappings = new HashMap<>();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if (qName != null) {
            tag = qName;
            switch (tag) {
                case "servlet":
                    servlet = new Servlet();
                    break;
                case "servlet-mapping":
                    servletMapping = new ServletMapping();
                    break;
                default:
                    break;
            }
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if (tag != null) {
            String content = new String(ch,start,length).trim();
            if (content.length() > 0) {
                switch (tag) {
                    case "name":
                        if (servlet != null)
                            servlet.setName(content);
                        if (servletMapping != null)
                            servletMapping.setName(content);
                        break;
                    case "class":
                        servlet.setClazz(content);
                        break;
                    case "url":
                        servletMapping.setUrl(content);
                        break;
                    case "server-port":
                        port = content;
                        break;
                    case "error-page":
                        errorPage = content;
                        break;
                    case "welcome-page":
                        welcomePage = content;
                        break;
                }
            }
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if (qName != null) {
            switch (qName) {
                case "servlet":
                    servlets.put(servlet.getName(),servlet.getClazz());
                    servlet = null;
                    break;
                case "servlet-mapping":
                    servletMappings.put(servletMapping.getUrl(),servletMapping.getName());
                    servletMapping = null;
                    break;
                default:
                    break;
            }
        }
        tag = null;
    }

    public Map<String, String> getServlets() {
        return servlets;
    }

    public Map<String, String> getServletMappings() {
        return servletMappings;
    }

    public String getPort() {
        return port;
    }

    public String getErrorPage() {
        return errorPage;
    }

    public String getWelcomePage() {
        return welcomePage;
    }
}

server.xmlutil.ServerXMLUtil.java

package server.xmlutil;

import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;

/**
 * 工具類實現xml讀取 SAX方式
 * @author Blank
 */
public class ServerXMLUtil {
    private ServerXMLUtil() {}

    public static void read(String xmlFile) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        SAXParser saxParser = saxParserFactory.newSAXParser();
        ServerHandler handler = ServerHandler.getHandler();
        InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream(xmlFile);
        if (resource != null) {
            saxParser.parse(resource, handler);
        }
    }
}

多線程實現路由轉發器

多線程實現路由轉發,防止阻塞式接收被堵塞。

server.service.Dispatcher.java

package server.service;

import server.xmlutil.ServerHandler;
import server.inter.BasicServlet;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Map;

/**
 * 請求分發器
 * @author Blank
 */
public class Dispatcher extends Thread {
    // 常量
    public static final String CRLF = "\r\n";
    public static final String SP = " ";
    // Socket
    private Socket accept;
    // 請求頭的第一行信息
    private String requestHeader;
    // post請求的參數字符串
    private String postParamStr;

    public Dispatcher(Socket accept) {
        this.accept = accept;
    }

    @Override
    public void run() {
        try {
            init();
            dispatch();
        } catch (IOException | ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }

    /**
     * 轉發
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws ClassNotFoundException
     * @throws IOException
     */
    private void dispatch() throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
        Request request = new Request(accept,requestHeader,postParamStr);
        Response response = new Response();
        String route = request.getRoute();
        if (route.equals("") || route.equals("/")) {
            String welcomePage = ServerHandler.getHandler().getWelcomePage();
            if (welcomePage == null || welcomePage.equals(""))
                welcomePage = "server/defaultpage/index.html";
            request.forward(welcomePage);
        }else {
            String errorPage = ServerHandler.getHandler().getErrorPage();
            if (errorPage == null || errorPage.equals(""))
                errorPage = "server/defaultpage/error.html";
            Map<String, String> servletMappings = ServerHandler.getHandler().getServletMappings();
            if (servletMappings.containsKey(route)) {
                String name = servletMappings.get(route);
                Map<String, String> servlets = ServerHandler.getHandler().getServlets();
                if (servlets.containsKey(name)) {
                    // Servlet定義了
                    String className = servlets.get(name);
                    BasicServlet servlet = (BasicServlet) Class.forName(className).newInstance();
                    servlet.service(request, response);
                } else {
                    // Servlet未定義
                    request.forward(errorPage);
                }
            } else {
                // 路由不存在 404
                request.forward(errorPage);
            }
        }
    }

    /**
     * 初始化
     * 獲取請求頭和post參數串和host信息
     * @throws IOException
     */
    private void init() throws IOException {
        InputStream is = accept.getInputStream();
        byte[] bytes = new byte[1024*1024*1024];
        int length = is.read(bytes);
        String requestStr = new String(bytes,0,length);
        int first_crlf = requestStr.indexOf(CRLF);
        requestHeader = requestStr.substring(0,first_crlf);
        int last_crlf = requestStr.lastIndexOf(CRLF) + CRLF.length();
        postParamStr = requestStr.substring(last_crlf);
    }
}

封裝Request、Response

server.service.Request.java

package server.service;

import server.xmlutil.ServerHandler;

import java.io.*;
import java.net.Socket;
import java.net.URLDecoder;
import java.util.*;

/**
 * Request
 * @author Blank
 */
public class Request {
    // 常量
    private static final String CRLF = "\r\n";
    private static final String SP = " ";

    private static Socket accept;

    private static String method;
    private static String url;
    private static String route;
    private static String protocol;

    private Map<String, Set<String>> params;

    // 默認code 200
    private int code = 200;
    private static Map<Integer,String> codeInfo;

    static {
        codeInfo = new HashMap<>();
        // 成功
        codeInfo.put(200,"OK");
        // 資源未找到
        codeInfo.put(404,"Not Found");
        // 服務器錯誤
        codeInfo.put(500,"Internal Server Error");
        // 重定向
        codeInfo.put(300,"Multiple Choices");

    }

    public Request(Socket accept,String requestHeader,String postParamStr) {
        this.accept = accept;
        params = new HashMap<>();
        String[] info = requestHeader.split(SP);
        method = info[0];
        url = info[1];
        protocol = info[2];
        // 獲取 route 和 param
        if (method.equals("GET")) {
            if (url.contains("?")) {
                // 有參數
                int i = url.indexOf("?");
                route = url.substring(0,i);
                dealParam(url.substring(i+1));
            }else {
                // 無參數
                route = url;
            }
        }else if (method.equals("POST")) {
            route = url;
            dealParam(postParamStr);
        }
    }

    /**
     * 處理單參數
     * @param p
     */
    private void dealSingleParam(String p) {
        // 單參數
        if (p.contains("=")) {
            // 可分割
            String[] temp = p.split("=");
            if (temp.length > 0) {
                if (!params.containsKey(temp[0])) {
                    params.put(temp[0],new HashSet<>());
                }
                if (temp.length < 2) {
                    // 處理特殊如 name=
                    params.get(temp[0]).add("");
                }else {
                    params.get(temp[0]).add(temp[1]);
                }
            }
        }else {
            // 不可分割 給值賦""
            if (!params.containsKey(p)) {
                params.put(p,new HashSet<>());
            }
            params.get(p).add("");
        }
    }

    /**
     * 處理多參數
     * @param paramStr
     */
    private void dealParam(String paramStr) {
        // 有參數
        if (!paramStr.equals("")) {
            if (paramStr.contains("&")) {
                // 多參數
                String[] p = paramStr.split("&");
                for (String s : p) {
                    // 進行單參數處理
                    dealSingleParam(s);
                }
            }else {
                // 單參數
                dealSingleParam(paramStr);
            }
        }
    }

    /**
     * 轉發到頁面
     * @param path
     */
    public void forward(String path) throws IOException {
        // 獲取path的resource
        InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
        String content = "";
        if (resource == null) {
            code = 404;
            path = ServerHandler.getHandler().getErrorPage();
            resource = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
        }

        StringBuilder sbbody = new StringBuilder();
        // 讀取文件
        byte[] bytes = new byte[1024*1024];
        int length;
        while ((length = resource.read(bytes)) != -1) {
            sbbody.append(new String(bytes,0,length));
        }
        content = sbbody.toString();

        // 封裝請求頭
        pushInfo(content, code);
    }

    /**
     * 獲取參數單值
     * @param key
     * @return
     */
    public String getParameter(String key) throws UnsupportedEncodingException {
        String value = null;
        if (params.containsKey(key)) {
            Set<String> strings = params.get(key);
            Object[] values = strings.toArray();
            if (values.length == 1)
                value = values[0].toString();
        }
        return URLDecoder.decode(value,"UTF-8");
    }

    /**
     * 獲取參數多值
     * @param key
     * @return
     */
    public String[] getParameterValues(String key) throws UnsupportedEncodingException {
        String[] retValues = null;
        if (params.containsKey(key)) {
            Set<String> strings = params.get(key);
            Object[] values = strings.toArray();
            if (values.length > 1) {
                retValues = new String[values.length];
                for (int i = 0; i < values.length; i++) {
                    retValues[i] = URLDecoder.decode(values[i].toString(),"UTF-8");
                }
            }
        }
        return retValues;
    }

    public static void pushInfo(String content, int code) throws IOException {
        StringBuilder sb = new StringBuilder();
        sb.append(protocol).append(SP)
                .append(code).append(SP)
                .append(codeInfo.get(code))
                .append(CRLF);
        sb.append("Server:")
                .append("blank/0.0.1")
                .append(CRLF);
        sb.append("Date:")
                .append(new Date())
                .append(CRLF);
        sb.append("Content-Type:")
                .append("text/html")
                .append(CRLF);
        sb.append("Content-Length:")
                .append(content.getBytes().length)
                .append(CRLF);
        sb.append(CRLF).append(content)
                .append(CRLF);
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
        bw.write(sb.toString());
        bw.flush();
        bw.close();
    }

    public String getRoute() {
        return route;
    }

    public String getMethod() {
        return method;
    }

    public String getProtocol() {
        return protocol;
    }

    public Map<Integer, String> getCodeInfo() {
        return codeInfo;
    }
}

server.service.Response.java

package server.service;

import java.io.IOException;

/**
 * 模仿Tomcat的HttpResponse的部分功能
 * @author Blank
 */
public class Response {

    /**
     * 重定向到頁面
     * @param path
     */
    public void redirect(String path) throws IOException {
        StringBuilder sb = new StringBuilder();
        sb.append("<script>")
                .append("location.href = '")
                .append(path)
                .append("';")
                .append("</script>");
        // 封裝信息
        Request.pushInfo(sb.toString(),200);
    }
}

提供一個基礎的Servlet接口

server.inter.BasicServlet.java

package server.inter;

import server.service.Request;
import server.service.Response;

import java.io.IOException;

/**
 * 基礎Servlet接口
 * @author Blank
 */
public interface BasicServlet {
    /**
     * 服務方法,處理請求
     * @param request
     * @param response
     */
    void service(Request request, Response response) throws IOException;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章