《深入剖析Tomcat》一3.2 应用程序

3.2 应用程序

从本章开始,每章的应用程序都会按照模块进行划分。本章的应用程序包含3个模块:连接器模块、启动模块和核心模块。
启动模块只有一个类(Bootstrap),后者负责启动应用程序。连接器模块中的类可分为以下5个类型:
连接器及其支持类(HttpConnector和HttpProcessor);
表示HTTP请求的类(HttpRequest)及其支持类;
表示HTTP响应的类(HttpResponse)及其支持类;
外观类(HttpRequestFacade和HttpResponseFacade);
常量类。
核心模块包含两个类,servletProcessor类和StaticResourceProcessor类。
图3-1展示了本章中应用程序的UML类图。为了使类图更容易理解,一些与HttpRequest类和HttpResponse类相关的类被省略掉。在讨论Request对象和Response对象时,你会看到它们各自的UML类图。

该图相比于图2-1中的图,第2章中的HttpServer类分成HttpConnector和HttpProcessor两个类,Request类和Response类分别被HttpRequest和HttpResponse代替。此外,本章的应用程序中还使用了一些其他的类。
在第2章中,HttpServer类负责等待HTTP请求,并创建Request和Response对象。在本章的应用程序中,等待HTTP请求的工作由HttpConnector实例完成,创建Request和Response对象的工作由HttpProcessor实例完成。
在本章中,HTTP请求对象使用HttpRequest类表示,HttpRequest类实现javax.servlet.http.HttpServletRequest接口。HttpRequest对象会被转型为HttpServletRequest对象,然后作为参数传递给被调用的servlet实例的service()方法。因此,必须正确地设置每个HttpRequest实例的成员变量供servlet实例使用。需要设置的值包括;URI、查询字符串、参数、Cookie和其他一些请求头信息等。因为连接器并不知道被调用的servlet会使用哪些变量,所以连接器必须解析从HTTP请求中获取的所有信息。但是,解析HTTP请求涉及一些系统开销大的字符串操作以及一些其他操作。若是连接器仅仅解析会被servlet实例用到的值它就会节省很多CPU周期。比如,如果servlet实例不会使用任何请求参数(即它不会调用javax.servlet.http.HttpServletRequest类的getParameter()、getParameterMap()、getParameterNames()或getParameterValues()方法),连接器就不需要从查询字符串和/或HTTP请求体中解析这些参数。在这些参数被servlet实例真正调用前,Tomcat的默认连接器(包括本章应用程序中的连接器)是不会解析它们的,这样就可以使程序执行得更有效率。
Tomcat中的默认连接器和本章应用程序中的连接器使用SocketInputStream类从套接字的InputStream对象中读取字节流。SocketInputStream实例是java.io.InputStream实例的包装类,SocketInputStream实例可以通过调用套接字的getInputStream()方法获得。SocketInputStream类提供了两个重要的方法,分别是readRequestLine()和readHeader()。readRequestLine()方法会返回一个HTTP请求中第1行的内容,即包含了URI、请求方法和HTTP版本信息的内容。由于从套接字的输入流中处理字节流是指读取从第1个字节到最后1个字节(无法从后向前读取)的内容,因此readRequestLine()方法必须在readHeader()方法之前调用。每次调用readerHeader()方法都会返回一个名/值对,所以应重复调用readHeader()方法,直到读取了所有的请求头信息。readRequestLine()方法的返回值是一个HttpRequestLine实例,readHeader()方法的返回值是一个HttpHeader对象。接下来的章节将会对HttpRequestLine类和HttpHeader类进行讨论。
HttpProcessor对象负责创建HttpRequest的实例,并填充它的成员变量。HttpProcessor类使用其parse()方法解析HTTP请求中的请求行和请求头信息,并将其填充到HttpRequest对象的成员变量中。但是,parse()方法并不会解析请求体或查询字符串中的参数。这个任务由各个HttpRequest对象自己完成。这样,只有当servlet实例需要使用某个参数时,才会由HttpRequest对象去解析请求体或查询字符串。
相比与前面章节中的应用程序,本章中的另一个功能点是使用启动类ex03.pyrmont.startup.Bootstrap来启动应用程序。
下面的几节将会介绍应用程序的细节。
启动应用程序
连接器
创建HttpRequest对象
创建HttpResponse对象
静态资源处理器和servlet处理器
运行应用程序

3.2.1 启动应用程序

本章中的应用程序是通过ex03.pyrmont.startup.Bootstrap类来启动的。该类的定义在代码清单3-1中给出。

Bootstrap类的main()方法通过实例化HttpConnector类,并调用其start()方法就可以启动应用程序。HttpConnector类的定义在代码清单3-2中给出。

3.2.2 HttpConnector类

连接器是ex03.pyrmont.connector.http.HttpConnector类的实例,负责创建一个服务器套接字,该套接字会等待传入的HTTP请求。HttpConnector类已在代码清单3-2中给出。
HttpConnector类实现了java.lang.Runnable接口。当你启动应用程序时,会创建一个HttpConnector实例,该实例另起一个线程来运行。
HttpConnector类实现 java.lang.Runnable ,这样它可以专用于自己的线程。当启动应用程序时,创建HttpConnector的一个实例并执行它的run ()方法。
注意 你可以阅读文章“Working with Threads”来学习如何创建Java线程的相关知识。
run()方法包含一个while循环,在循环中会执行如下三个操作:
等待HTTP请求;
为每个请求创建一个HttpPorcessor实例;
调用HttpProcessor对象的process()方法。
注意 HttpConnector类的run()方法其实与第2章中HttpServer1类的await()方法实现了类似的功能。
你可以看到,实际上,HttpConnector类与ex02.pyrmont.HttpServer1类非常相似,区别在于从java.net.ServerSocket类的accept()方法中获取一个套接字,创建一个HttpProcessor实例并传入该套接字,调用其process()方法。
注意 HttpConnector类有一个getScheme()方法,后者会返回请求协议(如HTTP协议)。
HttpProcessor类的process()方法接收来自传入的HTTP请求的套接字。对每个传入的HTTP请求,它要完成4个操作:
创建一个HttpRequest对象;
创建一个HttpResponse对象;
解析HTTP请求的第1行内容和请求头信息,填充HttpRequest对象;
将HttpRequest对象和HttpResponse对象传递给servletProcessor或StaticResourceProcessor的process()方法。与第2章中相同,servletProcessor类会调用请求的servlet实例的service()方法,StaticResourceProcessor类会将请求的静态资源发送给客户端。
HttpProcessor类的process()方法的实现在代码清单3-3中给出。

process()方法首先会获取套接字的输入流和输出流。但请注意,在这种方法中,使用了继承自java.io.InputStream的SocketInputStream类:

然后,它会创建一个HttpRequest实例和一个HttpResponse实例,然后将HttpRequest实例赋值给HttpResponse实例:

在本章的应用程序中,HttpResponse类比第2章中的Response类稍微复杂一点。首先,可以调用HttpResponse类的setHeader()方法向客户端发送响应头信息:

其次,process()方法会调用HttpProcessor类的两个私有方法来解析请求:

然后,它会根据请求的URI模式来判断,将HttpRequest对象和HttpResponse对象发送给ServletProcessor类或StaticResourceProcessor类来处理:

最后,它关闭套接字:

注意,HttpProcessor类使用了org.apache.catalina.util.StringManager类来发送错误消息:

调用HttpProcessor类的私有方法—parseRequest()、parseHeaders()和normalize()—来帮助填充HttpRequest对象。这些方法将在3.3.3节中进行讨论。

3.2.3 创建HttpRequest对象

HttpRequest类实现了javax.servlet.http.HttpservletRequest接口。其外观类是HttpRequestFacade类。图3-2展示了HttpRequest类及其相关类的UML类图。

其中HttpRequest类的很多方法都是空方法(在第4章中会提供完整实现),但是servlet程序员已经可以从中获取引入的HTTP请求的请求头、Cookie和请求参数等信息了。这三类数据分别存储在如下引用变量中:

注意 ParameterMap类将会在本节下面的“5.获取参数”节中介绍。
因此,servlet程序员就可以通过调用javax.servlet.http.HttpservletRequest类的一些方法获取HTTP请求信息,这些方法包括getCookies()、getDateHeader()、getHeader()、getHeaderNames()、getHeaders()、getParameter()、getPrameterMap()、getParameterNames()和getParameterValues()。当使用正确的值填充请求头、Cookie和请求参数后,相关方法的实现就比较简单了,就像在HttpRequest类中看到的那样。
当然,这里最主要的工作是解析HTTP请求并填充HttpRequest对象。对每个请求头和Cookie,HttpRequest类都提供了addHeader()方法和addCookie()方法来填充相关信息,这两个方法会由HttpProcessor类的parseHeaders()方法进行调用。当真正需要用到请求参数时,才会使用HttpRequest类的parseParameters()方法解析请求参数。相关方法将会在本节中介绍。
解析HTTP请求相对来说比较复杂,所以下面将分成5个小节来进行说明:
读取套接字的输入流
解析请求行
解析请求头
解析Cookie
获取参数
1. 读取套接字的输入流
在第1章和第2章中,ex01.pyrmont.HttpRequest类和ex02.pyrmont.HttpRequest类对HTTP请求进行过初步的解析。通过调用java.io.InputStream类的read()方法,从请求行中获取到了方法、URI、HTTP协议版本等信息:

不需要试图进一步解析这两个应用程序的请求。在本章的应用程序中,将会使用ex03.pyrmont.connector.http.SocketInputStream类来进行解析,该类实际上就是org.apache.catalina.connector.http.SocketInputStream类的一个副本。该类提供了一些方法来获取请求行和请求头信息。
需要传入一个InputStream对象和一个指明缓冲区大小的整数来创建一个SocketInputStream实例。在本章的应用程序中,在ex03.pyrmont.connector.http.HttpProcessor类的process()方法中会创建一个SocketInputStream对象,如以下代码段所示:

如前所述,之所以使用SocketInputStream类就是为了调用其readRequestLine()方法和readHeader()方法。
2.解析请求行
HttpProcessor类的process()方法会调用私有方法parseRequest()来解析请求行,即HTTP请求的第1行内容。下面是一个HTTP请求行的示例:
GET /myApp/Modernservlet?userName=tarzan&password=pwd HTTP/1.1
请求行的第2部分是URI加上一个可选的查询字符串。在这个例子中,URI是:

/myApp/Modernservlet

问号后面的部分就是查询字符串,如下所示:

userName=tarzan&password=pwd

查询字符串可以包含0个或多个请求参数。在上面的例子中,包含了两个名/值对,userName/tarzan和password/pwd。在servlet/JSP编程中,参数名jsessionid用于携带一个会话标识符。会话标识符通常是作为Cookie嵌入的,但是当浏览器禁用了Cookie时,程序员也可以将会话标识符嵌入到查询字符串中。
当从HttpProcessor类的process()方法调用parseRequest()方法时,request变量会指向一个HttpRequest实例。parseRequest()方法解析请求行,从而获取一些值,并将其赋给HttpRequest对象。代码清单3-4展示了parseRequest()方法的实现。


parseRequest()方法首先会调用SocketInputStream类的readRequestLine()方法:

input.readRequestLine(requestLine);

其中,变量requestLine是HttpProcessor中的一个HttpRequestLine实例:

private HttpRequestLine requestLine = new HttpRequestLine();

调用其readRequestLine()方法,使用SocketInputStream对象中的信息填充HttpRequestLine实例。
接着,parseRequest()方法从请求行中获取请求方法、URI和请求协议的版本信息:

但是,在URI后面可能会有一个查询字符串。若有,则查询字符串与URI是用一个问号分隔的。因此,parseRequest()方法会首先调用HttpRequest类的setQueryString()方法来获取查询字符串,并填充HttpRequest对象:

但是,大多数的URI都指向一个相对路径中的资源,当然URI也可以是一个绝对路径中的值,例如:

http://www.brainysoftware.com/index.html?name=Tarzan

parseRequest()方法会进行如下检查:

然后,查询字符串可能也会包含一个会话标识符,参数名为jsessionid。因此,parseRequest()方法还要检查是否包含会话标识符。若在查询字符串中包含jsessionid,则parseRequest()方法要获取jsessionid的值,并调用HttpRequest类的setRequestedSessionId()方法填充HttpRequest实例:

若存在参数jsessionid,则表明会话标识符在查询字符串中,而不在Cookie中。因此,需要调用该请求的setRequestSessionURL()方法并传入true值。否则,调用setRequestSessionURL()方法并传入false值,同时调用setRequestedSessionURL()方法并传入null。
此刻,jsessionid已经不包含uri的值。
然后,parseRequest()方法会将URI传入到normalize()方法中,对非正常的URL进行修正。例如,出现“\”的地方会被替换为“/”。若URI本身是正常的,或不正常的地方可以修正,则normalize()方法会返回相同的URI或修正过的URI。若URI无法修正,则会认为它是无效的,normalize()方法返回null。在这种情况下(normalize()方法返回null),parseRequest()方法会在方法的末尾抛出异常。
最后,parseRequest()方法会设置HttpRequest对象的一些属性:

另外,若normalize()方法返回null,该方法会抛出异常:

3.解析请求头
请求头信息由一个HttpHeader类表示。该类将在第4章中详细讨论,但在这里,有以下5件事需要了解:
可以通过其类的无参构造函数来创建一个HttpHeader实例;
创建了HttpHeader实例后,可以将其传给SocketInputStream类的readHeader()方法。若有请求头信息可以读取,readHeader()方法会相应地填充HttpHeader对象。若没有请求头信息可以读取,则HttpHeader实例的nameEnd和valueEnd字段都会是0;
要获取请求头的名字和值,可以使用如下的方法:

parseHeaders()方法中有一个while循环,后者不断地从SocketInputStream中读取请求头信息,直到全部读完。在循环开始时,会先创建一个HttpHeader实例,然后将其传给SocketInputStream类的readHeader()方法:

然后,可以通过检查HttpHeader实例的nameEnd和valueEnd字段来判断是否已经从输入流中读取了所有的请求头信息:

若还有请求头没有读取,可以使用下面的方法获取请求头的名称和值:

当获取了请求头的名称和值之后,就可以调用HttpRequest对象的addHeader()方法,将其添加到HttpRequest对象的HashMap请求头中:

request.addHeader(name, value);

某些请求头包含一些属性设置信息,例如,当调用javax.servlet.ServletRequest类的getContentLength()方法时,会返回请求头“content-length”的值,而请求头“cookies”中是一些Cookie的集合。下面是相关的处理过程:

对Cookie的解析将在下一小节中详细讨论。
4. 解析Cookie
Cookie是由浏览器作为HTTP请求头的一部分发送的。这样的请求头的名称是“cookie”,其对应值是一些名/值对。下面是一个Cookie请求头的例子,其中包含两个Cookie:userName和password。
Cookie: userName=budi; password=pwd;
对Cookie的解析是通过org.apache.catalina.util.RequestUtil类的parseCookieHeader()方法完成的。该方法接受Cookie请求头,返回javax.servlet.http.Cookie类型的一个数组。数组中元素的个数与Cookie请求头中名/值对的数目相同。代码清单3-5给出了parseCookieHeader()方法的实现。

下面是HttpProcessor类的parseHeader()方法的一部分,后者处理Cookie的实现:

  1. 获取参数
    在调用javax.servlet.http.HttpservletRequest的getParameter()、getParameterMap()、getParameter-Names()或getParameterValues()方法之前,都不需要解析查询字符串或HTTP请求体来获得参数。因此,在HttpRequest类中,这4个方法的实现都会先调用parseParameter()方法。
    参数只需要解析一次即可,而且也只会解析一次,因为,在请求体中包含参数,解析参数的工作会使SocketInputStream类读完整个字节流。HttpRequest类使用一个名为parsed的布尔变量来标识是否已经完成对参数的解析。
    参数可以出现在查询字符串或请求体中。若用户使用GET方法请求servlet,则所有的参数都会在查询字符串中;若用户使用POST方法请求servlet,则请求体中也可能会有参数。所有的名/值对都会存储在一个HashMap对象中。servlet程序员可以将其作为一个Map对象获取参数(通过调用HttpservletRequest类的getParameterMap()方法)和参数名/值。但有一点需要注意,servlet程序员不可以修改参数值。因此,这里使用了一个特殊的HashMap类:org.apache.catalina.util.ParameterMap。
    ParameterMap类继承自java.util.HashMap,其中有一个名为locked的布尔变量。只有当变量locked值为false时,才可以对ParameterMap对象中的名/值对进行添加、更新或者删除操作。否则,会抛出IllegalStateException异常。然而,可以在任何时候读取该值。ParameterMap类的定义在代码清单3-6中给出。它重写了对值进行添加、更新和删除的方法。对值的读取可在任何时候执行,但只有当变量locked的值为false时,才能调用添加、更新和删除值的方法。

下面,来看一下parseParameters()方法到底是如何工作的。
由于参数可以存在于查询字符串或HTTP请求体中,因此parseParameters()方法必须对这两者都进行检查。当解析完成时,参数会存储到对象变量parameters中,所以parseParameters()方法首先会检查布尔变量parsed,若该变量值为true,则方法直接返回:

然后,parseParameters()方法会创建一个名为results的ParameterMap类型的变量,将其指向变量parameters。若变量parameters为null,则parseParameters()方法会新创建一个ParameterMap对象:

然后,parseParameters()方法打开parameterMap对象的锁,使其可写:

results.setLocked(false);

接下来,parseParameters()方法检查字符串encoding,若encoding为null,则使用默认编码:

然后,parseParameters()方法会对参数进行解析,解析工作会调用org.apache.Catalina.util.RequestUtil类的parseParameters()方法完成:

接着,parseParameters()方法会检查HTTP请求体是否包含请求参数。若用户使用POST方法提交请求时,请求体会包含参数,则请求头“content-length”的值会大于0,“content-type”的值为“application/x-www-form-urlencoded”。下面的代码用于解析请求体:

最后,parseParameters()方法会锁定ParameterMap对象,将布尔变量parsed设置为true,将变量results赋值给变量parameters:

3.2.4 创建HttpResponse对象

HttpResponse类实现javax.servlet.http.HttpServletResponse接口,与其对应的外观类是HttpResponseFacade。图3-3展示了HttpResponse类及其相关类的UML类图。

在第2章中,HttpResponse类只实现了部分功能。例如,当调用它的其中一个print()方法时,它的getWriter()方法返回的java.io.PrintWriter对象并不会自动将结果发送到客户端。本章的应用程序将解决此问题。在此之前,为了便于理解,先说明一下什么是Writer。
在servlet中,可以使用PrintWriter对象向输出流中写字符。可以使用任意编码格式,但在向浏览器发送字符的时候,实际上都是字节流。因此,对于第2章,在ex02.pyrmont.HttpResponse类中使用了如下的getWriter()方法,也就没什么好奇怪的了:

那么,如何创建PrintWriter对象呢?可以通过传入一个java.io.OutputStream实例来创建PrintWriter对象。所传给PrintWriter类的print()方法或println()方法的任何字符串都会被转换为字节流,使用基本的输出流发送到客户端。
在第3章中,会使用ex03.pyrmont.connector.ResponseStream类的一个实例作为PrintWriter类的输出流对象。注意,ResponseStream类java.io.OutputStream类的直接子类。
也可以使用ex03.pyrmont.connector.ResponseWriter类来向客户端发送信息,该类继承自PrintWriter类。ResponseWriter类重写了所有的print()方法和println()方法,这样对这些方法进行调用时,会自动将信息发送客户端。因此使用一个ResponseWriter实例和一个基本ResponseStream对象。
可以通过传入ResponseStream对象的一个实例来实例化ResponseWriter类。但是,这里使用了一个java.io.OutputStreamWriter对象作为ResponseWriter对象和ResponseStream对象之间的桥梁。
使用OutputStreamWriter类,传入的字符会被转换为使用指定字符集的字节数组。其中所使用的字符集可以通过名称显式指定,也可以使用平台的默认字符集。每次调用写方法时,都会先使用编码转换器对给定字符进行编码转换。在被写入基本输出流之前,返回的字节数组会先存储在缓冲区中。缓冲区的大小是固定的,对于大多数应用来说,其默认值是足够大的。注意,传递给写方法的字符是没有缓冲的。
因此,getWriter()方法的实现如下所示:

3.2.5 静态资源处理器和servlet处理器

本章的ServletProcessor类与第2章的ex02.pyrmont.ServletProcessor类相似,都只有一个方法:process()方法。但是,ex03.pyrmont.connector.ServletProcessor类中的process()方法会接受一个HttpRequest对象和一个HttpResponse对象,而不是Request或Response的实例。下面是本章应用程序中process()方法的签名:

public void process(HttpRequest request, HttpResponse response);

此外,process()方法使用HttpRequestFacade类和HttpResponseFacade类作为request和response对象的外观类。当调用完servlet的service()方法后,它还会调用一次HttpResponse类的finishResponse()方法:

本章应用程序中的StaticResourceProcessor类几乎与ex02.pyrmont.StaticResourceProcessor类完全相同。

3.2.6 运行应用程序

要在Windows平台下运行该应用程序,需要在工作目录下执行如下命令:

java -classpath ./lib/servlet.jar;./ ex03.pyrmont.startup.Bootstrap

要在Linux平台下运行,需要使用冒号来代替库文件之间的分号:

java -classpath ./lib/servlet.jar:./ ex03.pyrmont.startup.Bootstrap

要显示index.html文件,可以使用如下的URL:

http://localhost:808O/index.html

要调用Primitiveservlet类,可以在浏览器中使用如下的URL:

http://localhost:8080/servlet/Primitiveservlet

可以在浏览器中看到如下的输入:

Hello. Roses are red.
Violets are blue.

注意 在第2章运行PrimitiveServlet 不会显示第2行内容。
现在,可以通过如下的URL调用ModernServet类,该servlet在第2章的servlet容器中是无法运行的:

http://localhost:8080/servlet/Modernservlet

注意 ModernServlet类的源代码并不在工作目录的webroot目录下。
可以在测试servlet的URL后面添加查询字符串。例如使用如下的URL:

http://localhost:8080/servlet/ModernServlet?userName=tarzan&password=pwd

图3-4给出了使用上述URL运行ModernServlet的返回结果:

时间: 2024-08-04 14:27:58

《深入剖析Tomcat》一3.2 应用程序的相关文章

在docker中部署tomcat并且部署java应用程序的步骤详解_docker

先给大家简单说下Docker的概念 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全使用沙箱机制,相互之间不会有任何接口. 1.先说如何在docker中部署tomcat 第一步:root用户登录在系统根目录下创建文件夹tomcat7,命令如:mkdir tomcat7,并且切换到该目录下:cd tomcat7: 第二步:创建Dockerfile,命令如:touch Docker

《深入剖析Tomcat》一3.1 StringManager类

3.1 StringManager类 像Tomcat这样的大型应用程序必须小心仔细地处理错误消息.在Tomcat中,错误消息对系统管理员和servlet程序员来说都是很有用的.例如,系统管理员可以很容易地根据Tomcat的错误日志消息定位到发生异常的位置.而对于servlet程序员来说,在抛出的每个javax.servlet.servletException异常中,Tomcat都会发送一条特殊的错误消息,这样,程序员就可以知道servlet程序到底哪里出错了.Tomcat处理错误消息的方法是将错

《深入剖析Tomcat》一2.2 应用程序 1

2.2 应用程序 1 下面从servlet容器的角度审视servlet程序的开发.简单来说,对一个Servlet的每个HTTP请求,一个功能齐全的servlet容器有以下几件事要做: 当第一次调用某个servlet时,要载入该servlet类,并调用其init()方法(仅此一次): 针对每个request请求,创建一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例: 调用该servlet的service()方法,将

《深入剖析Tomcat》一1.3 应用程序

1.3 应用程序 本章的Web服务器应用程序位于ex01.pyrmont包下,包括三个类: HttpServer Request Response 应用程序的入口点(静态main()方法)在HttpServer类中.main()方法创建一个HttpServer实例,然后,调用其await()方法.顾名思义,await()方法会在指定端口上等待HTTP请求,对其进行处理,然后发送响应信息回客户端.在接收到关闭命令前,它会保持等待状态. 该应用程序仅发送位于指定目录的静态资源的请求,如HTML文件和

《深入剖析Tomcat》一2.3 应用程序2

2.3 应用程序2 应用程序1中有一个严重的问题.在ServletProcessor1类的process()方法中,必须将ex02.pyrmont.Request的实例向上转型为javax.servlet.ServletRequest实例,将ex02.pyrmont.Response实例向上转型为javax.servlet.ServletResponse实例,然后将它们作为参数传递给servlet的service()方法: 这是不安全的做法,了解这个servlet容器内部工作原理的servlet

《深入剖析Tomcat》一导读

前 言 欢迎阅读本书.本书剖析了Tomcat 4.1.12版本和Tomcat 5.0.18版本的基本结构,并解释了其servlet容器Catalina的内部工作原理.Catalina是开源.免费的,也是最受欢迎的servlet容器之一.Tomcat本身是一个复杂的系统,包含了许多不同的组件.若你想学习Tomcat的工作方式,应该从了解这些组件开始.本书描述Tomcat的总体结构,并针对每个组件建立一个简单的版本,使你更好地理解组件的工作机制,之后对真实组件进行描述."本书结构"一节会对

《深入剖析Tomcat》一第 3 章 连 接 器

第 3 章 连 接 器 正如前言所述,Catalina中有两个主要的模块,连接器(connector)和容器(container).在本章中,将会建立一个连接器来增强第2章中的应用程序的功能,用一种更好的方法来创建request对和response对象.兼容Servlet 2.3和2.4规范的连接器必须要负责创建javax.servlet.http.HttpServletRequest对象和javax.servlet.http.HttpServletResponse的实例,并将它们作为servl

tomcat第二次访问LambdaProbe程序提示权限不够

问题描述 我无奈了.每次部署probe以后,即刻访问probe,可以进去.重新启动tomcat,再次从manager里连到probe,提示权限不够,让我修改probe.xml的privileged为true,但privileged就是true阿如果把probe卸载了,重新部署一次,就又能进去了.但重启tomcat,就又进不去了,说权限不够.我在tomcat5,tomcat6上都遇到这个问题.按照probe官网所说(如下),给tomcat多配置了四个权限,问题依然存在.Lambda Probe r

深入剖析tomcat之一个简单的web服务器

这个简单的web服务器包含三个类 HttpServer Request Response 在应用程序的入口点,也就是静态main函数中,创建一个HttpServer实例,然后调用其await()方法.顾名思义,await方法会在制定的端口上等待http请求,并对其进行处理,然后发送相应的消息回客户端.在接收到命令之前,它会一直保持等待的状态. HttpServer类 package simpleHttpServer; import java.io.File; import java.io.IOE