How Tomcat Works(Scala语言) 02 一个简单的Servlet容器

3 一个简单的Servlet容器

  • Servlet容器除了能访问静态资源以外,还能访问Servlet。所以比前一章多了两个类StaticResourceProcessor和ServletProcessor
  • 此处的Servlet依据Servlet规范实现,所以需要servlet.jar。后续会自己实现此接口
  • 访问静态资源的方式和前篇代码完全相同,只是将response.sendStaticResource()抽成单独的StaticResourceProcessor类
  • 根据url来区分访问的内容,类似/servlet/*的uri会调用ServletProcessor否则调用StaticResourceProcessor
  • ServletProcessor的功能是根据url来加载指定路径下的class,并调用相应的方法
  • 具体区别请见类内注释
package simple

import java.io.InputStream
import java.io.BufferedReader
import java.util.Enumeration
import java.util.Locale
import java.util.Map
import 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 simple

import java.io.OutputStream
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.File
import java.io.PrintWriter
import java.util.Locale
import javax.servlet.ServletResponse
import 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 simple

import java.net.ServerSocket
import java.net.InetAddress
import 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 simple

import java.net.URL
import java.net.URLClassLoader
import java.net.URLStreamHandler
import java.io.File
import javax.servlet.Servlet
import javax.servlet.ServletRequest
import 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 facade

import 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 facade

import javax.servlet.{ServletOutputStream, ServletResponse}
import java.util.Locale
import 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 facade

import java.net.URL
import java.net.URLClassLoader
import java.net.URLStreamHandler
import java.io.File
import javax.servlet.Servlet
import javax.servlet.ServletRequest
import 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值