一、动态资源的获取
因为TimeServlet无法直接调用,所以获得相应的路径。
package processor;
import connector.ConnectorUtils;
import connector.Request;
import connector.Response;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class ServletProcessor {
//准备好URLClassLoader
URLClassLoader getServletLoader() throws MalformedURLException {
File webRoot = new File(ConnectorUtils.WEB_ROOT);
URL webRootUrl = webRoot.toURI().toURL();
return new URLClassLoader(new URL[]{webRootUrl});
}
Servlet getServlet(URLClassLoader loader, Request request) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
/*
/servlet/TimeServlet
*/
String uri = request.getRequestUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
Class servletClass = loader.loadClass(servletName);
Servlet servlet = (Servlet) servletClass.getDeclaredConstructor().newInstance();
return servlet;
}
public void process(Request request, Response response) throws IOException {
try {
URLClassLoader loader = getServletLoader();
Servlet servlet = getServlet(loader, request);
servlet.service(request, response);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
}
二、测试ServletProcessor
package processor;
import connector.Request;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import test.util.TestUtils;
import javax.servlet.Servlet;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
/**
* @ClassName:
* @Description:
* @author: hszjj
* @date: 2019/11/26 15:32
*/
public class ServletProcessorTest {
private static final String SERVLET_REQUEST="GET /servlet/TimeServlet HTTP/1.1";
@Test
public void givenServletRequest_thenLoadServlet() throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Request request= TestUtils.createRequest(SERVLET_REQUEST);
ServletProcessor processor=new ServletProcessor();
URLClassLoader loader=processor.getServletLoader();
Servlet servlet=processor.getServlet(loader,request);
Assertions.assertEquals("webroot.TimeServlet",servlet.getClass().getName());
}
}
三、使用facade模式
示例中这个代码明显出现问题,既涉及到服务器的开发也涉及到Servlet的开发,现实中这两个开发应属于不同群体。所以我们要保证服务器的开发不会被Servlet的人员过分利用,例如如下情况:
这种情况很危险,Servlet的人员可以肆意调用函数。
为了防止这种问题发生,我们需要应用Facade模式。
首先需要写如下两个函数
ResponseFacade
把ServletResponse放置在ResponseFacade中就可以有效防止这种非法转型。
package connector;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;
public class ResponseFacade implements ServletResponse {
private ServletResponse response = null;
public ResponseFacade(Response response) {
this.response = response;
}
@Override
public String getCharacterEncoding() {
return response.getCharacterEncoding();
}
@Override
public String getContentType() {
return response.getContentType();
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return response.getOutputStream();
}
@Override
public PrintWriter getWriter() throws IOException {
return response.getWriter();
}
@Override
public void setCharacterEncoding(String s) {
response.setCharacterEncoding(s);
}
@Override
public void setContentLength(int i) {
response.setContentLength(i);
}
@Override
public void setContentLengthLong(long l) {
response.setContentLengthLong(l);
}
@Override
public void setContentType(String s) {
response.setContentType(s);
}
@Override
public void setBufferSize(int i) {
response.setBufferSize(i);
}
@Override
public int getBufferSize() {
return response.getBufferSize();
}
@Override
public void flushBuffer() throws IOException {
response.flushBuffer();
}
@Override
public void resetBuffer() {
response.resetBuffer();
}
@Override
public boolean isCommitted() {
return response.isCommitted();
}
@Override
public void reset() {
response.reset();
}
@Override
public void setLocale(Locale locale) {
response.setLocale(locale);
}
@Override
public Locale getLocale() {
return response.getLocale();
}
}
RequestFacade
同理
package connector;
import javax.servlet.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
public class RequestFacade implements ServletRequest {
private ServletRequest request = null;
public RequestFacade(Request request) {
this.request = request;
}
@Override
public Object getAttribute(String s) {
return request.getAttribute(s);
}
@Override
public Enumeration<String> getAttributeNames() {
return request.getAttributeNames();
}
@Override
public String getCharacterEncoding() {
return request.getCharacterEncoding();
}
@Override
public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
request.setCharacterEncoding(s);
}
@Override
public int getContentLength() {
return request.getContentLength();
}
@Override
public long getContentLengthLong() {
return request.getContentLengthLong();
}
@Override
public String getContentType() {
return request.getContentType();
}
@Override
public ServletInputStream getInputStream() throws IOException {
return request.getInputStream();
}
@Override
public String getParameter(String s) {
return request.getParameter(s);
}
@Override
public Enumeration<String> getParameterNames() {
return request.getParameterNames();
}
@Override
public String[] getParameterValues(String s) {
return request.getParameterValues(s);
}
@Override
public Map<String, String[]> getParameterMap() {
return request.getParameterMap();
}
@Override
public String getProtocol() {
return request.getProtocol();
}
@Override
public String getScheme() {
return request.getScheme();
}
@Override
public String getServerName() {
return request.getServerName();
}
@Override
public int getServerPort() {
return request.getServerPort();
}
@Override
public BufferedReader getReader() throws IOException {
return request.getReader();
}
@Override
public String getRemoteAddr() {
return request.getRemoteAddr();
}
@Override
public String getRemoteHost() {
return request.getRemoteHost();
}
@Override
public void setAttribute(String s, Object o) {
request.setAttribute(s,o);
}
@Override
public void removeAttribute(String s) {
request.removeAttribute(s);
}
@Override
public Locale getLocale() {
return request.getLocale();
}
@Override
public Enumeration<Locale> getLocales() {
return request.getLocales();
}
@Override
public boolean isSecure() {
return request.isSecure();
}
@Override
public RequestDispatcher getRequestDispatcher(String s) {
return request.getRequestDispatcher(s);
}
@Override
public String getRealPath(String s) {
return request.getRealPath(s);
}
@Override
public int getRemotePort() {
return request.getRemotePort();
}
@Override
public String getLocalName() {
return request.getLocalName();
}
@Override
public String getLocalAddr() {
return request.getLocalAddr();
}
@Override
public int getLocalPort() {
return request.getLocalPort();
}
@Override
public ServletContext getServletContext() {
return request.getServletContext();
}
@Override
public AsyncContext startAsync() throws IllegalStateException {
return request.startAsync();
}
@Override
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
return request.startAsync();
}
@Override
public boolean isAsyncStarted() {
return request.isAsyncStarted();
}
@Override
public boolean isAsyncSupported() {
return request.isAsyncSupported();
}
@Override
public AsyncContext getAsyncContext() {
return request.getAsyncContext();
}
@Override
public DispatcherType getDispatcherType() {
return request.getDispatcherType();
}
}
应用
修改Processor代码
public void process(Request request, Response response) throws IOException {
URLClassLoader loader = getServletLoader();
try {
Servlet servlet = getServlet(loader, request);
RequestFacade requestFacade=new RequestFacade(request);
ResponseFacade responseFacade=new ResponseFacade(response);
servlet.service(requestFacade, responseFacade);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
四、动态资源支持的实现
首先修改Connector的代码:
public void run() {
try {
server=new ServerSocket(port);
System.out.println("服务器已启动,监听端口【"+port+"】");
while (true){
Socket socket=server.accept();
InputStream input=socket.getInputStream();
OutputStream output=socket.getOutputStream();
Request request=new Request(input);
request.parse();
Response response=new Response(output);
response.setRequest(request);
if (request.getRequestUri().startsWith("/servlet/")){
ServletProcessor processor=new ServletProcessor();
processor.process(request,response);
}else {
StaticProcessor processor = new StaticProcessor();
processor.process(request, response);
}
shutDown(socket);
}
} catch (IOException e) {
e.printStackTrace();
}
}
更改TestClient类
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* @ClassName:
* @Description:
* @author: hszjj
* @date: 2019/11/26 14:33
*/
public class TestClient {
public static void main(String[] args) {
try {
Socket socket=new Socket("localhost",9999);
OutputStream outputStream=socket.getOutputStream();
outputStream.write("GET /servlet/TimeServlet HTTP/1.1".getBytes());
socket.shutdownOutput();
InputStream inputStream=socket.getInputStream();
byte[] buffer=new byte[2048];
int length=inputStream.read(buffer);
StringBuilder response=new StringBuilder();
for (int j=0;j<length;j++){
char a=(char)buffer[j];
response.append(a);
}
System.out.println(response.toString());
socket.shutdownInput();
socket.close();
} catch (IOException i) {
i.printStackTrace();
}
}
}
结果显示如下:
五、使用NIO模型重写
package connector;
import processor.ServletProcessor;
import processor.StaticProcessor;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;
public class NioConnector implements Runnable {
private static final int DEFAULT_PORT = 9999;
private ServerSocketChannel server;
private Selector selector;
private int port;
private void shutDown(Closeable... closeables) {
try {
for (Closeable shut : closeables) {
if (shut != null) {
shut.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public NioConnector(int port) {
this.port = port;
}
public NioConnector() {
this(DEFAULT_PORT);
}
public void start() {
try {
server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(port));
selector=Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("启动服务器,监听端口["+port+"]");
while (true){
selector.select();
Set<SelectionKey> selectionKeys=selector.selectedKeys();
for (SelectionKey key:selectionKeys){
handles(key);
}
selectionKeys.clear();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
shutDown(selector,server);
}
}
private void handles(SelectionKey key) throws IOException {
//ACCEPT
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
}else if (key.isReadable()){
SocketChannel client= (SocketChannel) key.channel();
key.cancel();
client.configureBlocking(true);
Socket clientSocket=client.socket();
InputStream inputStream=clientSocket.getInputStream();
OutputStream outputStream=clientSocket.getOutputStream();
Request request=new Request(inputStream);
request.parse();
Response response=new Response(outputStream);
response.setRequest(request);
if (request.getRequestUri().startsWith("/servlet/")){
ServletProcessor processor=new ServletProcessor();
processor.process(request,response);
}else {
StaticProcessor processor=new StaticProcessor();
processor.process(request,response);
}
}
}
@Override
public void run() {
new Thread(this).start();
}
}