How Tomcat Works(Scala语言) 02 一个简单的Servlet容器
来源:互联网 发布:装修工程预算软件 编辑:程序博客网 时间:2024/06/05 02:49
3 一个简单的Servlet容器
- Servlet容器除了能访问静态资源以外,还能访问Servlet。所以比前一章多了两个类StaticResourceProcessor和ServletProcessor
- 此处的Servlet依据Servlet规范实现,所以需要servlet.jar。后续会自己实现此接口
- 访问静态资源的方式和前篇代码完全相同,只是将response.sendStaticResource()抽成单独的StaticResourceProcessor类
- 根据url来区分访问的内容,类似/servlet/*的uri会调用ServletProcessor否则调用StaticResourceProcessor
- ServletProcessor的功能是根据url来加载指定路径下的class,并调用相应的方法
- 具体区别请见类内注释
package simpleimport java.io.InputStreamimport java.io.BufferedReaderimport java.util.Enumerationimport java.util.Localeimport java.util.Mapimport javax.servlet.{ServletInputStream, RequestDispatcher, ServletRequest}import io.Source/** * Created with IntelliJ IDEA. * User: Administrator * Date: 13-1-2 * Time: 下午10:32 * To change this template use File | Settings | File Templates. *///Request类和前一章的类完全相同//区别在于,由于此类继承了ServletRequest,所以必须要实现其所有的抽象方法,这里都是空实现class Request(input: InputStream) extends ServletRequest { var uri: String = _ def parseUri(requestString: String): String = { val pattern = """[^ ]* *([^ ]*) *[\s\S]*""".r val pattern(result) = requestString result } def parse { val lines = Source.fromInputStream(input).getLines() if (lines.hasNext) { uri = parseUri(lines.next()) } } def getAttribute(attribute: String): AnyRef = { return null } def getAttributeNames: Enumeration[_] = { return null } def getRealPath(path: String): String = { return null } def getRequestDispatcher(path: String): RequestDispatcher = { return null } def isSecure: Boolean = { return false } def getCharacterEncoding: String = { return null } def getContentLength: Int = { return 0 } def getContentType: String = { return null } def getInputStream: ServletInputStream = { return null } def getLocale: Locale = { return null } def getLocales: Enumeration[_] = { return null } def getParameter(name: String): String = { return null } def getParameterMap: Map[_, _] = { return null } def getParameterNames: Enumeration[_] = { return null } def getParameterValues(parameter: String): Array[String] = { return null } def getProtocol: String = { return null } def getReader: BufferedReader = { return null } def getRemoteAddr: String = { return null } def getRemoteHost: String = { return null } def getScheme: String = { return null } def getServerName: String = { return null } def getServerPort: Int = { return 0 } def removeAttribute(attribute: String) { } def setAttribute(key: String, value: AnyRef) { } def setCharacterEncoding(encoding: String) { }}
package simpleimport java.io.OutputStreamimport java.io.FileInputStreamimport java.io.FileNotFoundExceptionimport java.io.Fileimport java.io.PrintWriterimport java.util.Localeimport javax.servlet.ServletResponseimport javax.servlet.ServletOutputStream/** * Created with IntelliJ IDEA. * User: Administrator * Date: 13-1-2 * Time: 下午10:34 * To change this template use File | Settings | File Templates. *///Response类和前一章的类完全相同//区别在于,由于此类继承了ServletResponse,所以必须要实现其所有的抽象方法,这里都是空实现object Response { val BUFFER_SIZE: Int = 1024 val bytes: Array[Byte] = new Array[Byte](Response.BUFFER_SIZE) val FileNotFoundMessage = """HTTP/1.1 404 File Not Found |Content-Type: text/html |Content-Length: 23 | |<h1>File Not Found</h1>"""}class Response(output: OutputStream) extends ServletResponse { def sendStaticResource() { var fis: FileInputStream = null try { val file: File = new File(HttpServer.WEB_ROOT, request.uri) fis = new FileInputStream(file) //将文件内容写到响应中 writeToResponse(fis) } catch { case e: FileNotFoundException => { //将文件内容写到响应中 output.write(Response.FileNotFoundMessage.getBytes()) } } finally { if (fis != null) fis.close } } //递归读取文件,写入到响应流 def writeToResponse(fis: FileInputStream) { val ch = fis.read(Response.bytes, 0, Response.BUFFER_SIZE) if (ch != -1) { output.write(Response.bytes, 0, ch) writeToResponse(fis) } } /** implementation of ServletResponse */ def flushBuffer { } def getBufferSize: Int = { return 0 } def getCharacterEncoding: String = { return null } def getLocale: Locale = { return null } def getOutputStream: ServletOutputStream = { return null } def getWriter: PrintWriter = { writer = new PrintWriter(output, true) return writer } def isCommitted: Boolean = { return false } def reset { } def resetBuffer { } def setBufferSize(size: Int) { } def setContentLength(length: Int) { } def setContentType(`type`: String) { } def setLocale(locale: Locale) { } var request: Request = _ var writer: PrintWriter = _}
package simpleimport java.net.ServerSocketimport java.net.InetAddressimport java.io.File/** * Created with IntelliJ IDEA. * User: Administrator * Date: 13-1-2 * Time: 下午10:28 * To change this template use File | Settings | File Templates. */object HttpServer { val WEB_ROOT: String = System.getProperty("user.dir") + File.separator + "webroot" val SHUTDOWN_COMMAND: String = "/SHUTDOWN" def main(args: Array[String]) { val server: HttpServer = new HttpServer server.await }}class HttpServer { def await { val port: Int = 8080 val serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")) while (!shutdown) { try { val socket = serverSocket.accept val input = socket.getInputStream val output = socket.getOutputStream val request: Request = new Request(input) request.parse val response: Response = new Response(output) response.request = request //此处是HttpServer与前一章不同的地方 //根据uri是否以/servlet/开头,来判断是静态资源,还是servlet //如果是静态资源则调用StaticResourceProcessor,否则调用ServletProcessor if (request.uri.startsWith("/servlet/")) { val processor: ServletProcessor = new ServletProcessor processor.process(request, response) } else { val processor: StaticResourceProcessor = new StaticResourceProcessor processor.process(request, response) } socket.close shutdown = request.uri == HttpServer.SHUTDOWN_COMMAND } catch { case e: Exception => e.printStackTrace } } } private var shutdown: Boolean = false}
package simple/** * Created with IntelliJ IDEA. * User: Administrator * Date: 13-1-2 * Time: 下午10:36 * To change this template use File | Settings | File Templates. *///仅一行,调用response的sendStaticResource返回静态资源//此代码原来在HttpServer中,现单独为一个类class StaticResourceProcessor { def process(request: Request, response: Response) { response.sendStaticResource }}
package simpleimport java.net.URLimport java.net.URLClassLoaderimport java.net.URLStreamHandlerimport java.io.Fileimport javax.servlet.Servletimport javax.servlet.ServletRequestimport javax.servlet.ServletResponse/** * Created with IntelliJ IDEA. * User: Administrator * Date: 13-1-2 * Time: 下午10:37 * To change this template use File | Settings | File Templates. *///根据url,从类路径下加载相应的servlet类//并相继调用service方法class ServletProcessor { def process(request: Request, response: Response) { //根据url找到servletName val servletName: String = request.uri.substring(request.uri.lastIndexOf("/") + 1) //根据class路径,创建classLoader val urls: Array[URL] = new Array[URL](1) val streamHandler: URLStreamHandler = null val classPath: File = new File(HttpServer.WEB_ROOT) val repository: String = (new URL("file", null, classPath.getCanonicalPath + File.separator)).toString urls(0) = new URL(null, repository, streamHandler) val loader = new URLClassLoader(urls) //使用classLoader根据servletName加载类 val myClass = loader.loadClass(servletName) //实例化类并调用service方法 val servlet = myClass.newInstance.asInstanceOf[Servlet] servlet.service(request.asInstanceOf[ServletRequest], response.asInstanceOf[ServletResponse]) }}
上面的代码完成了基本的Servlet容器功能。但是在ServletProcessor中有个问题,就是当你把Request和Response传递给servlet时,你需要强制转换为ServletRequest和 ServletResponse。而如果开发人员知道ServletRequest和ServletResponse是Request和Response的话,他就可以强制转回去,并调用parse和sendStaticResource方法。 由于其他类需要调用这两个方法,所以你不能将其设为private。当然你可以设为private[simple],使得其只能在simple包内访问,但是开发人员依然可以将servlet包设为simple来进行访问。 这里可以使用Facade来解决这个问题。
- 添加RequestFacade和ResponseFacade方法
- 修改ServletProcessor类,使用RequestFacade和ResponseFacade来封装request和response
package facadeimport javax.servlet.{RequestDispatcher, ServletInputStream, ServletRequest}import java.util.{Map, Enumeration, Locale}import java.io.BufferedReader/** * Created with IntelliJ IDEA. * User: Administrator * Date: 13-1-3 * Time: 下午8:31 * To change this template use File | Settings | File Templates. *///仅仅是对request对应方法的调用//屏蔽了parse方法class RequestFacade(val request: Request) extends ServletRequest { def getAttribute(attribute: String): AnyRef = { return request.getAttribute(attribute) } def getAttributeNames: Enumeration[_] = { return request.getAttributeNames } def getRealPath(path: String): String = { return request.getRealPath(path) } def getRequestDispatcher(path: String): RequestDispatcher = { return request.getRequestDispatcher(path) } def isSecure: Boolean = { return request.isSecure } def getCharacterEncoding: String = { return request.getCharacterEncoding } def getContentLength: Int = { return request.getContentLength } def getContentType: String = { return request.getContentType } def getInputStream: ServletInputStream = { return request.getInputStream } def getLocale: Locale = { return request.getLocale } def getLocales: Enumeration[_] = { return request.getLocales } def getParameter(name: String): String = { return request.getParameter(name) } def getParameterMap: Map[_, _] = { return request.getParameterMap } def getParameterNames: Enumeration[_] = { return request.getParameterNames } def getParameterValues(parameter: String): Array[String] = { return request.getParameterValues(parameter) } def getProtocol: String = { return request.getProtocol } def getReader: BufferedReader = { return request.getReader } def getRemoteAddr: String = { return request.getRemoteAddr } def getRemoteHost: String = { return request.getRemoteHost } def getScheme: String = { return request.getScheme } def getServerName: String = { return request.getServerName } def getServerPort: Int = { return request.getServerPort } def removeAttribute(attribute: String) { request.removeAttribute(attribute) } def setAttribute(key: String, value: AnyRef) { request.setAttribute(key, value) } def setCharacterEncoding(encoding: String) { request.setCharacterEncoding(encoding) }}
package facadeimport javax.servlet.{ServletOutputStream, ServletResponse}import java.util.Localeimport java.io.PrintWriter/** * Created with IntelliJ IDEA. * User: Administrator * Date: 13-1-3 * Time: 下午8:31 * To change this template use File | Settings | File Templates. *///仅仅是对response相应方法的调用//屏蔽了sendStaticResource方法class ResponseFacade(val response: Response) extends ServletResponse { def flushBuffer { response.flushBuffer } def getBufferSize: Int = { return response.getBufferSize } def getCharacterEncoding: String = { return response.getCharacterEncoding } def getLocale: Locale = { return response.getLocale } def getOutputStream: ServletOutputStream = { return response.getOutputStream } def getWriter: PrintWriter = { return response.getWriter } def isCommitted: Boolean = { return response.isCommitted } def reset { response.reset } def resetBuffer { response.resetBuffer } def setBufferSize(size: Int) { response.setBufferSize(size) } def setContentLength(length: Int) { response.setContentLength(length) } def setContentType(`type`: String) { response.setContentType(`type`) } def setLocale(locale: Locale) { response.setLocale(locale) }}
package facadeimport java.net.URLimport java.net.URLClassLoaderimport java.net.URLStreamHandlerimport java.io.Fileimport javax.servlet.Servletimport javax.servlet.ServletRequestimport javax.servlet.ServletResponse/** * Created with IntelliJ IDEA. * User: Administrator * Date: 13-1-2 * Time: 下午10:37 * To change this template use File | Settings | File Templates. *///根据url,从类路径下加载相应的servlet类//并相继调用service方法class ServletProcessor { def process(request: Request, response: Response) { //根据url找到servletName val servletName: String = request.uri.substring(request.uri.lastIndexOf("/") + 1) //根据class路径,创建classLoader val urls: Array[URL] = new Array[URL](1) val streamHandler: URLStreamHandler = null val classPath: File = new File(HttpServer.WEB_ROOT) val repository: String = (new URL("file", null, classPath.getCanonicalPath + File.separator)).toString urls(0) = new URL(null, repository, streamHandler) val loader = new URLClassLoader(urls) //使用classLoader根据servletName加载类 val myClass = loader.loadClass(servletName) //实例化类并调用service方法 val servlet = myClass.newInstance.asInstanceOf[Servlet] //是用facade封装request和response,屏蔽parse和sendStaticResource方法 servlet.service(new RequestFacade(request).asInstanceOf[ServletRequest], new ResponseFacade(response).asInstanceOf[ServletResponse]) }}
Blog URL:http://www.ivanpig.com/blog/?p=491
- How Tomcat Works(Scala语言) 02 一个简单的Servlet容器
- how tomcat works 读书笔记(二)----------一个简单的servlet容器
- [How Tomcat Works]第2章 一个简单的Servlet容器
- 《How To Tomcat Works》-第2章:一个简单的Servlet容器
- How Tomcat Works(Scala语言) 01 一个简单的Web服务器
- How tomcat works——2 一简单的Servlet容器
- how tomcat works 五 servlet容器 上
- how tomcat works 5 servlet容器 下
- 《How To Tomcat Works》-第一章:一个简单的Web服务器
- how tomcat works 读书笔记(一)----------一个简单的web服务器
- 《how tomcat works》第一章 构建一个简单的web服务器
- How Tomcat works之(一个简单的web服务器)
- tomcat(2)一个简单的servlet容器
- [How Tomcat Works]第1章 一个简单的Web服务器
- How tomcat works——1 一个简单的Web Server
- 《How To Tomcat Works》-第五章 容器
- How tomcat works——5 容器
- tomcat学习笔记(二) 模拟一个简单的servlet容器
- android wifi调试总结 theros AR6K命令小结 android wifi debug
- jfreechart生成饼状图
- Winform 拖放
- wakelock 详解
- hbase之宽表与窄表对split的影响
- How Tomcat Works(Scala语言) 02 一个简单的Servlet容器
- android wakelock申请与释放
- iOS持久化(数据库)
- atheros wifi芯片ics高通平台 wifi睡眠策略分析
- 小波的例子
- android lcd调试 高通平台lcd调试深入分析总结(mipi和rgb接口)
- OpenNI 和kinect学习资料汇总
- 【hibernate】赵雅智_hibernate 4 (hibernate对象的三种状态)
- 内存对齐问题