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