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;
}