自己模拟的一个简单的tomcat

  • servlet容器的职责

总的来说,一个全功能的servlet容器会为servlet的每个HTTP请求做下面的一些工作:

1,当第一次调用servlet的时候,加载该servlet类并调用servlet的init方法,只有一次,

2,对于每次请求,都要new出一个request请求和response相应实例,

3,调用servlet的service方法,同时传递ServletRequest和ServletResponse对象,

4,当servlet类被关闭的时候,调用servlet的destroy方法写在servlet类。

现在我自己模拟实现了一个最简单的tomcat,除了上面的第1点和第4点,基本的功能还是实现了,代码如下:

package linkin;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class HttpServer1
{
	/**
	 * WEB_ROOT is the directory where our HTML and other files reside. For this
	 * package, WEB_ROOT is the "webroot" directory under the working directory.
	 * The working directory is the location in the file system from where the
	 * java command was invoked.
	 */
	// shutdown command
	private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
	// the shutdown command received
	private boolean shutdown = false;

	public static void main(String[] args)
	{
		HttpServer1 server = new HttpServer1();
		server.await();
	}

	public void await()
	{
		ServerSocket serverSocket = null;
		int port = 8080;
		try
		{
			serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
		}
		catch (IOException e)
		{
			e.printStackTrace();
			System.exit(1);
		}
		// Loop waiting for a request
		while (!shutdown)
		{
			Socket socket = null;
			InputStream input = null;
			OutputStream output = null;
			try
			{
				socket = serverSocket.accept();
				input = socket.getInputStream();
				output = socket.getOutputStream();
				// create Request object and parse
				Request request = new Request(input);
				request.parse();
				// create Response object
				Response response = new Response(output);
				response.setRequest(request);
				// check if this is a request for a servlet or
				// a static resource
				// a request for a servlet begins with "/servlet/"
				if (request.getUri().startsWith("/servlet/"))
				{
					ServletProcessor1 processor = new ServletProcessor1();
					processor.process(request, response);
				}
				else
				{
					StaticResourceProcessor processor = new StaticResourceProcessor();
					processor.process(request, response);
				}
				// Close the socket
				socket.close();
				// check if the previous URI is a shutdown command
				shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
			}
			catch (Exception e)
			{
				e.printStackTrace();
				System.exit(1);
			}
		}
	}
}
package linkin;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
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;

public class Request implements ServletRequest
{
	private InputStream input;
	private String uri;

	public Request(InputStream input)
	{
		this.input = input;
	}

	public String getUri()
	{
		return uri;
	}

	private String parseUri(String requestString)
	{
		int index1, index2;
		index1 = requestString.indexOf(' ');
		if (index1 != -1)
		{
			index2 = requestString.indexOf(' ', index1 + 1);
			if (index2 > index1)
				return requestString.substring(index1 + 1, index2);
		}
		return null;
	}

	public void parse()
	{
		// Read a set of characters from the socket
		StringBuffer request = new StringBuffer(2048);
		int i;
		byte[] buffer = new byte[2048];
		try
		{
			i = input.read(buffer);
		}
		catch (IOException e)
		{
			e.printStackTrace();
			i = -1;
		}
		for (int j = 0; j < i; j++)
		{
			request.append((char) buffer[j]);
		}
		System.out.print(request.toString());
		uri = parseUri(request.toString());
	}

	/* implementation of ServletRequest */
	public Object getAttribute(String attribute)
	{
		return null;
	}

	public Enumeration getAttributeNames()
	{
		return null;
	}

	public String getRealPath(String path)
	{
		return null;
	}

	public RequestDispatcher getRequestDispatcher(String path)
	{
		return null;
	}

	public boolean isSecure()
	{
		return false;
	}

	public String getCharacterEncoding()
	{
		return null;
	}

	public int getContentLength()
	{
		return 0;
	}

	public String getContentType()
	{
		return null;
	}

	public ServletInputStream getInputStream() throws IOException
	{
		return null;
	}

	public Locale getLocale()
	{
		return null;
	}

	public Enumeration getLocales()
	{
		return null;
	}

	public String getParameter(String name)
	{
		return null;
	}

	public Map getParameterMap()
	{
		return null;
	}

	public Enumeration getParameterNames()
	{
		return null;
	}

	public String[] getParameterValues(String parameter)
	{
		return null;
	}

	public String getProtocol()
	{
		return null;
	}

	public BufferedReader getReader() throws IOException
	{
		return null;
	}

	public String getRemoteAddr()
	{
		return null;
	}

	public String getRemoteHost()
	{
		return null;
	}

	public String getScheme()
	{
		return null;
	}

	public String getServerName()
	{
		return null;
	}

	public int getServerPort()
	{
		return 0;
	}

	public void removeAttribute(String attribute)
	{
	}

	public void setAttribute(String key, Object value)
	{
	}

	public void setCharacterEncoding(String encoding) throws UnsupportedEncodingException
	{
	}

	@Override
	public String getLocalAddr()
	{
		return null;
	}

	@Override
	public String getLocalName()
	{
		return null;
	}

	@Override
	public int getLocalPort()
	{
		return 0;
	}

	@Override
	public int getRemotePort()
	{
		return 0;
	}
}
package linkin;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Locale;

import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;

public class Response implements ServletResponse
{
	private static final int BUFFER_SIZE = 1024;
	public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
	Request request;
	OutputStream output;
	PrintWriter writer;

	public Response(OutputStream output)
	{
		this.output = output;
	}

	public void setRequest(Request request)
	{
		this.request = request;
	}

	/* This method is used to serve static pages */
	public void sendStaticResource() throws IOException
	{
		byte[] bytes = new byte[BUFFER_SIZE];
		FileInputStream fis = null;
		try
		{
			/* request.getUri has been replaced by request.getRequestURI */
			File file = new File(WEB_ROOT, request.getUri());
			fis = new FileInputStream(file);
			/*
			 * HTTP Response = Status-Line(( general-header | response-header |
			 * entity-header ) CRLF) CRLF [ message-body ] Status-Line =
			 * HTTP-Version SP Status-Code SP Reason-Phrase CRLF
			 */
			int ch = fis.read(bytes, 0, BUFFER_SIZE);
			while (ch != -1)
			{
				output.write(bytes, 0, ch);
				ch = fis.read(bytes, 0, BUFFER_SIZE);
			}
		}
		catch (FileNotFoundException e)
		{
			String errorMessage = "HTTP/1.1 404 File Not Found\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 23\r\n" + "\r\n" + "<h1>File Not Found</h1>";
			output.write(errorMessage.getBytes());
		}
		finally
		{
			if (fis != null)
				fis.close();
		}
	}

	/** implementation of ServletResponse */
	public void flushBuffer() throws IOException
	{
	}

	public int getBufferSize()
	{
		return 0;
	}

	public String getCharacterEncoding()
	{
		return null;
	}

	public Locale getLocale()
	{
		return null;
	}

	public ServletOutputStream getOutputStream() throws IOException
	{
		return null;
	}

	public PrintWriter getWriter() throws IOException
	{
		// autoflush is true, println() will flush,
		// but print() will not.
		writer = new PrintWriter(output, true);
		return writer;
	}

	public boolean isCommitted()
	{
		return false;
	}

	public void reset()
	{
	}

	public void resetBuffer()
	{
	}

	public void setBufferSize(int size)
	{
	}

	public void setContentLength(int length)
	{
	}

	public void setContentType(String type)
	{
	}

	@Override
	public void setLocale(Locale locale)
	{
	}

	@Override
	public String getContentType()
	{
		return null;
	}

	@Override
	public void setCharacterEncoding(String arg0)
	{

	}

}
package linkin;

import java.io.IOException;

public class StaticResourceProcessor
{
	public void process(Request request, Response response)
	{
		try
		{
			response.sendStaticResource();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}
}
package linkin;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class ServletProcessor1
{
	public void process(Request request, Response response)
	{
		String uri = request.getUri();
		String servletName = uri.substring(uri.lastIndexOf("/") + 1);
		URLClassLoader loader = null;
		try
		{
			// create a URLClassLoader
			URL[] urls = new URL[1];
			URLStreamHandler streamHandler = null;
			File classPath = new File(Response.WEB_ROOT);
			// the forming of repository is taken from the
			// createClassLoader method in
			// org.apache.catalina.startup.ClassLoaderFactory
			String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString();
			// the code for forming the URL is taken from
			// the addRepository method in
			// org.apache.catalina.loader.StandardClassLoader.
			urls[0] = new URL(null, repository, streamHandler);
			loader = new URLClassLoader(urls);
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		Class myClass = null;
		try
		{
			System.out.println(servletName);
			myClass = loader.loadClass(servletName);
		}
		catch (ClassNotFoundException e)
		{
			System.out.println(e.toString());
		}
		Servlet servlet = null;
		try
		{
			//跑一下反射,初始化一个实例,然后调用这个servlet的service方法。
			servlet = (Servlet) myClass.newInstance();
			servlet.service((ServletRequest) request, (ServletResponse) response);
		}
		catch (Exception e)
		{
			System.out.println(e.toString());
		}
		catch (Throwable e)
		{
			System.out.println(e.toString());
		}
	}
}
package linkin;

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;

public class PrimitiveServlet implements Servlet
{
	@Override
	public void init(ServletConfig config) throws ServletException
	{
		System.out.println("init");
	}

	@Override
	public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException
	{
		System.out.println("from service");
		PrintWriter out = response.getWriter();
		out.println("<html><head><title>linkin.html</title>"+
				"<meta http-equiv=\"keywords\" content=\"keyword1,keyword2,keyword3\">"+
				"<meta http-equiv='description' content='this is my page'>"+
				"<meta http-equiv='content-type' content='text/html; charset=UTF-8'>"+
				"<!--<link rel='stylesheet' type='text/css' href='./styles.css'>-->"+
				"</head><h1>NightWish,气质在作祟。。。</h1><br></body></html>");
		out.println("值得注意的是,要是去web应用去找一个文件那么直接指定物理路径找就好了,要是用类加载加载一个class文件,是要在classpath里面去找的");
		out.println("现在一般不需要配置java的classpath了,默认情况就是当前的工作路径,也就是这个项目的bin下面的路径,在类加载找这个文件的时候一定要写上包名");
	}

	public void destroy()
	{
		System.out.println("destroy");
	}

	public String getServletInfo()
	{
		return null;
	}

	public ServletConfig getServletConfig()
	{
		return null;
	}

}

补充说明:

首先先看上面的servlet的处理器里面调用servlet的service方法这2行代码:

try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) request,(ServletResponse) response);
}

这里有一个问题:我们写的这个处理器并不是说只能接受servlet的请求,所以这里我们传入的参数就是request和response类型的,但是我们现在在调用servlet的时候,又要传入servletRequest和servletResponse类型的参数,这里向下强转是很危险的,万一人家这个请求和相应不是servlet类型的呢,对吧,所以我们就是要达到一种效果就是说:我贴出的这request和response这2块代码呢只能在我们自己的类里面使用,在我自己的包下面使用,在其他的地方不能被访问到,这样子我就随便向下转保证肯定不会出错了,而且也可以保证这2个类里面的方法也是安全的了,比如说外部的类肯定不能调用request的parseURI方法了,怎么办呢?一共有2种实现方式:

1,让上面的request类和response类拥有默认的访问权限,也就是说只能在自己的包中被访问到,那么这样子就安全了

2,使用门面设计模式,具体的讲解请查看我设计模式里面的那篇文章。大致的意思就是在上面的request和response外面再自己封装一次门面,将这个request和response私有化,里面所有的方法呢其实还是用这个request和response类来调用,这样子的话包外面就不能直接访问到request和response这2个类了。代码如下:

package linkin;

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;

public class RequestFacade implements ServletRequest
{
	private ServletRequest request = null;

	public RequestFacade(Request request)
	{
		this.request = request;
	}

	/* implementation of the ServletRequest */
	public Object getAttribute(String attribute)
	{
		return request.getAttribute(attribute);
	}

	public Enumeration getAttributeNames()
	{
		return request.getAttributeNames();
	}

	......................
}

时间: 2024-08-30 08:01:37

自己模拟的一个简单的tomcat的相关文章

自己模拟的一个简单的web服务器

首先我为大家推荐一本书:How Tomcat Works.这本书讲的很详细的,虽然实际开发中我们并不会自己去写一个tomcat,但是对于了解Tomcat是如何工作的还是很有必要的. Servlet容器是如何工作的 servlet容器是一个复杂的系统.不过,一个servlet容器要为一个servlet的请求提供服务,基本上有三件事要做: 1,创建一个request对象并填充那些有可能被所引用的servlet使用的信息,如参数.头部. cookies.查询字符串. URI 等等.一个 request

自己动手模拟开发一个简单的Web服务器

开篇:每当我们将开发好的ASP.NET网站部署到IIS服务器中,在浏览器正常浏览页面时,可曾想过Web服务器是怎么工作的,其原理是什么?“纸上得来终觉浅,绝知此事要躬行”,于是我们自己模拟一个简单的Web服务器来体会一下. 一.请求-处理-响应模型 1.1 基本过程介绍 每一个HTTP请求都会经历三个步凑:请求-处理-响应:每当我们在浏览器中输入一个URL时都会被封装为一个HTTP请求报文发送到Web服务器,而Web服务器则接收并解析HTTP请求报文,然后针对请求进行处理(返回指定的HTML页面

java servlet----模拟一个简单的tomcat服务器

package com.mytomcat; import java.net.*; import java.io.*; public class Mytomcat { public static void main(String[] args)throws Exception { ServerSocket s = new ServerSocket(9988); System.out.println("waiting...."); Socket s1 = s.accept(); Outpu

java模拟而一个电话本操作

哈哈,大家平时都在使用电话本,下面使用java来模拟而一个简单的电话本吧... 首先给出联系人的抽象类 package net.itaem.po; /** * * 电话人的信息 * */ public class User { private String name; private String phoneNumber; private String companyName; private String email; private String address; private Strin

IntelliJ IDEA 15 部署Tomcat及创建一个简单的Web工程

一.部署Tomcat 二.创建一个简单的Web工程 2.1创建一个新工程 创建一个新工程 设置JDK及选择Web Application (创建的是Web工程) 点击Next,选择工作空间,起个工程名 2.2项目部署 在工具栏点击 Project: 无需任何设置,选择默认编译目录(或自定义编译目录) Modules: 将Tomcat加入 Libraries:无需任何设置.这里描述了此项目的依赖. Facets: 无需任何设置.这里描述了此项目所适配的服务框架 Artifacts: 无需任何配置.

java-第十三章-类的无参方法(一)-模拟一个简单的购房商贷月供计算器

package 本章总结; public class A02class { double Money = 0; public double showA(double money, int choice) { switch (choice) { case 1: Money = (money + money * 0.0603) / 36; break; case 2: Money = (money + money * 0.0612) / 60; break; case 3: Money = (mon

一个简单的Webservice的demo,简单模拟服务

前段时间一直在学习WCF,匆匆忙忙的把<WCF全面解析>和<WCF服务编程>看了一遍,好多东西都不是很懂,又听了一下WCF分布式开发的网络教程,算是马马虎虎的明白点了.回顾了一下Webservice,将二者进行比较学习.考虑到以后的发展,当时决定学习WCF,希望自己在不久的将来能将WCF学的稍微精通点吧.这几天又将Webservice看了一遍,回想当时学习Webservice处处碰到坑,由于没人指点,连最基本地点发布都折腾好长时间,只能一点一点的填坑跳坑.这几天闲了,想写一个简单的

how tomcat works 读书笔记(二)----------一个简单的servlet容器

app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/article/details/39378157) 回顾我们上一章,我们开发了一个最最简单的web服务器,它可以使用户访问服务器内的静态资源.当然这是远远不够的,在这一节里,我们就试着让服务器在能相应静态资源的基础上继续支持servlet. servlet接口 javax.servlet.Servlet接口

一个简单的dos脚本, svn 获取代码 - Tomcat 备份 - Maven 编译 - 停止/启动Tomcat - Tomcat站点 发布

获取最新代码 svn update --username %SVN_USER% --password %SVN_PASSWORD% >> "../%LOG_FILE%" 备份Tomcat 站点 md "%APP_ROOT%\backup\%MVN_PROFILE%-%CUR_DATE%-%myran%" >> "%LOG_FILE%" xcopy "%APP_ROOT%\%MVN_PROFILE%" &