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
原创粉丝点击