设为首页 收藏本站
查看: 371|回复: 0

[经验分享] How tomcat work连载二:简易的Servlet容器

[复制链接]

尚未签到

发表于 2017-2-7 08:56:15 | 显示全部楼层 |阅读模式
  以下代码是仿照《How tomcat work》第二章:如何创建一个简易的Servlet容器,当然会是在上一篇文章http://bestchenwu.iteye.com/blog/1513984的基础上创建的。
  代码示例如下:
  先创建一个简易的Servlet,如下所示:
  package ex02.pyrmont;

import static util.DateUtil.getCurrentDateInTranditional;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.log4j.Logger;
/**
* 类PrimeServlet.java的实现描述:
*/
public class PrimeServlet implements Servlet {
private static final Logger log = Logger.getLogger("actionLog");
@Override
public void init(ServletConfig config) throws ServletException {
if (log.isInfoEnabled()) {
log.info("PrimeServlet init on:" + getCurrentDateInTranditional());
}
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
if (log.isInfoEnabled()) {
log.info("PrimeServlet service on:" + getCurrentDateInTranditional());
}
PrintWriter pw = res.getWriter();
pw.println("Hello world");
pw.flush();
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
if (log.isInfoEnabled()) {
log.info("PrimeServlet destroy on:" + getCurrentDateInTranditional());
}
}
}

  这里我引入了一个自己编写的日期操作类的静态方法,代码如下所示:
  package util;

import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 类DateUtil.java的实现描述:
*/
public class DateUtil {
private static final Format format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 返回当前时间
*
* @return {@link java.util.Date}
*/
public static Date getCurrentDate() {
return new Date();
}
/**
* 返回日期的表示时间,类似与yyyy-MM-dd hh:MM:ss
*
* @return 格式化后的日期
*/
public static String getCurrentDateInTranditional() {
return format.format(getCurrentDate());
}
}

  然后我们还是按照HttpServer、Request、Response的方式来创建:
  先创建HttpServer:
  import static ex02.pyrmont.HttpConstants.SHUT_DOWN;

import static util.DateUtil.getCurrentDateInTranditional;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import org.apache.log4j.Logger;
/**
* 类HttpServer.java的实现描述:
*/
public class HttpServer {
private static final Logger log = Logger.getLogger("actionLog");
private ServerSocket        httpServer;
/**
* 初始化监听Server
*
* @param port 监听端口
* @param backLog 监听队列的最大长度
* @param serverName 监听机器的域名
*/
public HttpServer(int port, int backLog, String serverName){
try {
httpServer = new ServerSocket(port, backLog, InetAddress.getByName(serverName));
if (log.isInfoEnabled()) {
log.info("HttpServer init on:" + getCurrentDateInTranditional());
}
} catch (UnknownHostException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
public static void main(String[] args) {
HttpServer httpServer = new HttpServer(HttpConstants.port, 1, "127.0.0.1");
httpServer.listen();
}
/**
* 启动HttpServer的监听程序
*/
public void listen() {
if (httpServer == null) {
throw new IllegalStateException("httpServer is null");
}
boolean isShutdown = false;
Socket client;
InputStream is;
OutputStream os;
while (!isShutdown) {
try {
client = httpServer.accept();
is = client.getInputStream();
os = client.getOutputStream();
Request request = new Request(is);
request.parse();
Response response = new Response(request);
response.setOutput(os);
/** 检查uri是否是Servelt **/
if (request.checkIsServlet()) {
/** 发送动态消息 **/
Processor processor = new DynamicProcessor(request, response);
processor.sendMessage();
} else {
/** 发送静态消息 **/
Processor processor = new StaticProcessor(request, response);
processor.sendMessage();
}
client.close();
/** 判断是否是关闭命令 **/
isShutdown = request.getUri().equalsIgnoreCase(SHUT_DOWN);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
/** 监听结束,调用关闭HttpServer程序 **/
close();
}
/**
* 关闭HttpServer
*/
public void close() {
if (httpServer != null && !httpServer.isClosed()) {
try {
httpServer.close();
if (log.isInfoEnabled()) {
log.info("httpServer destroy on:" + getCurrentDateInTranditional());
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
}
   看到与之前的HttpServer的区别没?这里的HttpServer尝试去判断uri是否是去请求一个servlet,我们假定所有的Servlet请求都是以/servlet/Servelt类名来结尾的。
  接下来是Request、Response,这次Request改为实现ServletRequest、ServletResponse,当然很多方法我们都是仅提供默认实现或者空实现。
  Request代码如下所示:
  package ex02.pyrmont;

import static ex02.pyrmont.HttpConstants.BUFFER_SIZE;
import static ex02.pyrmont.HttpConstants.SERVLET_PREFIX;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
/**
* 类Request.java的实现描述:
*/
public class Request implements HttpServletRequest {
private static final Logger log = Logger.getLogger("actionLog");
private InputStream         is;
private String              uri;
public Request(InputStream is){
this.is = is;
}
/**
* 将读取到的HttpRequest提取出uri
*/
public void parse() {
StringBuffer sb = new StringBuffer(BUFFER_SIZE);
byte[] buffers = new byte[BUFFER_SIZE];
int ch;
try {
ch = is.read(buffers);
if (ch != -1) {
for (int i = 0; i < ch; i++) {
sb.append((char) buffers);
}
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
if (log.isDebugEnabled()) {
log.debug("Request:" + sb.toString());
}
this.uri = parseUri(sb.toString());
}
/**
* 提取出request中第一行的uri 类似于GET/POST /page/index.html HTTP1.1/1.0
*
* @param request
*/
private String parseUri(String request) {
int index1, index2;
index1 = request.indexOf(' ');
if (index1 > -1) {
index2 = request.indexOf(' ', index1 + 1);
if (index2 > index1) {
return request.substring(index1 + 1, index2);
}
}
return "";
}
/**
* @return the uri
*/
public String getUri() {
return uri;
}
/**
* 检查请求的uri是否是Servlet<br/>
* 即uri是否是
*
* @return boolean true
*/
public boolean checkIsServlet() {
return this.getUri().startsWith(SERVLET_PREFIX);
}
   //...  一些
}
   Response代码如下所示:
  package ex02.pyrmont;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* 类Response.java的实现描述:
*/
public class Response implements ServletResponse {
private Request      request;
private OutputStream os;
public Response(ServletRequest request){
this.request = (Request) request;
}
public void setOutput(OutputStream os) {
this.os = os;
}
public OutputStream getOutput() {
return this.os;
}
/**
* 这个方法覆盖是必须的,用来向页面输出
*/  
@Override
public PrintWriter getWriter() throws IOException {
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
return pw;
}
}
  常量接口类如下所示:
  package ex02.pyrmont;

import java.io.File;
/**
* 类HttpConstants.java的实现描述:
*/
public interface HttpConstants {
/** HttpServer监听的端口 **/
public static final int    port           = 8773;
/** 读取或者写入的缓冲区大小 **/
public static final int    BUFFER_SIZE    = 2048;
/** servlet请求时候的前缀 **/
public static final String SERVLET_PREFIX = "/servlet/";
/** 静态文件的公共目录 **/
public static final String WEB_ROOT       = System.getProperty("user.dir") + File.separator + "webRoot";
/** HttpServer关闭命令 **/
public static final String SHUT_DOWN      = "/shutdown";
/**  静态文件找不到的时候的错误提示页面 **/
public static final String ERROR_HTML     = "<html><body><div><h1>can't find your request</h1></div></body></html>";
}
   接下来是分别针对于静态文件和Servlet的处理类,抽象类实现如下所示:
  package ex02.pyrmont;

import static ex02.pyrmont.HttpConstants.SHUT_DOWN;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.log4j.Logger;
/**
* 类Processor.java的实现描述:
*/
public abstract class Processor {
protected static final Logger log = Logger.getLogger("actionLog");
protected Request             request;
protected Response            response;
public Processor(ServletRequest request, ServletResponse response){
this.request = (Request) request;
this.response = (Response) response;
}
/**
* 检查uri是否合格
*
* @return boolean
*/
protected boolean checkUriIsValid() {
String uri = request.getUri();
if (uri.equalsIgnoreCase(SHUT_DOWN)) {
return false;
}
return true;
}
/**
* 发送消息
*/
public abstract void sendMessage();
@Override
public String toString() {
return String.format("Processor=%s[request=%s,response=%s]", this.getClass().getSimpleName(), this.request,
this.response);
}
}
   静态文件的处理类如下所示:
  package ex02.pyrmont;

import static ex02.pyrmont.HttpConstants.BUFFER_SIZE;
import static ex02.pyrmont.HttpConstants.ERROR_HTML;
import static ex02.pyrmont.HttpConstants.WEB_ROOT;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* 类StaticProcessor.java的实现描述:
*/
public class StaticProcessor extends Processor {
public StaticProcessor(ServletRequest request, ServletResponse response){
super(request, response);
}
@Override
public void sendMessage() {
if (!super.checkUriIsValid()) {
return;
}
try {
OutputStream os = response.getOutput();
File file = new File(WEB_ROOT, request.getUri());
if (!file.exists()) {
log.error("can't find uri:" + request.getUri());
os.write(ERROR_HTML.getBytes());
} else {
int ch;
InputStream is = new FileInputStream(file);
byte[] buffers = new byte[BUFFER_SIZE];
while ((ch = is.read(buffers, 0, BUFFER_SIZE)) != -1) {
/** 输出页面到前台展示页面 **/
os.write(buffers, 0, ch);
}
closeIO(is);
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
/**
* 关闭I/O资源
*
* @param is
*/
private void closeIO(InputStream is) {
if (is != null) {
try {
is.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
}
   动态文件的处理类如下所示:
  至此,一个简易的 Servlet容器创建完毕,先启动HttpServer,然后在浏览器里输入:
  http://127.0.0.1:8773/servlet/ex02.pyrmont.PrimeServlet
  http://127.0.0.1:8773/pages/index.html
  看下是不是可以看到我们的响应输出
  当然这里所述的文件寻址目录都位于工程下的webRoot一级目录,所以你需要确保所有的静态文件、servlet在运行期间都会存在于该目录下,比较简单的方法就是把静态文件也放到src目录下,为它们创建一个单独的目录,比如pages
  这个容器还有个小问题,就是我们的servlet去服务的时候,传入的参数还是Request、Response类型,这里我们可以创建一个适配所有ServletRequest 、ServletResponse的接口,让我们的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;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
/**
* 类RequestFacade.java的实现描述:
*/
public class RequestFacade implements ServletRequest {
private ServletRequest request;
public RequestFacade(Request request){
this.request = request;
}
}
  response也是这样
  这样的话,servlet处理器只需要像下面这样修改:

            Class<Servlet> servletClass = (Class<Servlet>) loader.loadClass(servletClassName);
Servlet servlet = (Servlet) servletClass.newInstance();
/** 初始化Servlet **/
servlet.init(null);
/** 执行它的服务方法 **/
RequestFacade requestFacade = new RequestFacade(request);
ResponseFacade responseFacade = new ResponseFacade(response);
servlet.service(requestFacade, responseFacade);
/** 销毁Servlet **/
servlet.destroy();
  再次启动HttpServer,看下修改后是否能通过。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-338560-1-1.html 上篇帖子: aix6安装apche2及配置负载+tomcat群集 下篇帖子: tomcat 5 下 el和taglib的问题处理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表