Web容器必须能支持HTTP会话,当处理不支持cookie使用的客户机发送来的HTTP请求时,web容器通常使用URL重写机制。
7.2 创建会话
一个会话被认为是新的,只有当它是唯一一个预期会话并还没没有被建立。因为,HTTP是基于request-response的协议,HTTP会话被认为是新的,直到有客户机加入它.当会话跟踪信息返回服务器时,意味着一个会话已经被建立了,此时,客户机加入了会话.直到客户机加入会话为止,它都不能假定下一个来自该客户机的请求隶属于某个会话的一部分.只要下列条件成立,会话被认为是新的:
.客户机还不知道该会话 .客户机选择不加入会话
这些情况定义了servlet容器没有机制将一个请求和上一个请求关联起来.
servlet开发人员必须将它的应用设计为可以处理客户机没有,不能,不会加入会话的情形.
7.3 会话范围
HttpSession对象在应用级别(servlet context)范畴.
下列机制,例如cookie,对于不同context可以是相同的,但该对象引用,包扩属性,容器必须按context区别开来.
用一个例子来说明:如果一个servlet使用RequestDispatcher调用属于另一个web应用的servlet,任何对于执行调用的servlet创建的,可 见的会话信息,必须不同于被调用的servlet.
7.4 在会话中绑定属性
servlet可以通过名称绑定对象属性到HttpSession的实现中.任何绑定对象对于同一servlet context下在同一会话中处理请求的其他servlet都是可用的.
会话中的一些对象被放入或是移出时,可能需要获得通知,这可以通过实现HttpSessionBindingListener接口的对象获得.该接口定义了下列方法,当对象被绑定或解除绑定时,发出信号.
.valueBound .valueUnbound
valueBound方法必须在该对象通过执行HttpSession的setAttribute方法而可用之前被调用valueUnbound方法必须在该对象通过执行HttpSession的setAttribute方法而不再可用之前被调用.
7.5 会话超时
在HTTP协议中,客户机不再活跃时没有明确的终止信号.这意味着只有超时才是标识客户机不再活跃的唯一机制.
缺省的会话超时由servlet容器定义,在HttpSession接口上调用getMaxInactiveIntervalfangaf得到该值.开发人员可以使用HttpSession接口上的setMaxInactiveInterval方法改变该值.时间单位是秒.若设置为-1,表示该会话永不过期.
7.6最后访问时间
HttpSession接口上的getLastAccessedTime方法允许servlet判定该会话在本次请求前被访问的最后时间.当容器第一次处理隶属于该会话的请求时,认为该会话被访问.
7.7 重要的会话语义 7.7.1 线程
多个servlet执行请求线程也许同时使用一个会话对象,开发人员有责任同步对会话资源的访问.
7.7.2 分布式环境
在分布式应用中,所有隶属于某个会话的请求在某一时间必须同一个虚拟机处理,容器必须能适当地处理使用setAttribute或putValue方法放进HttpSession实例的所有对象.下列约束是强制的: .接受的对象应实现Serializable接口
.容器可以选择支持在HttpSession中存储其他指定的对象,例如:EJB和事务
.移动会话将由容器具体设施处理
若放进会话的对象未实现Serializable接口,或对该类对象的支持还不可用,servlet容器可以抛出IllegalArgumentException异常,只有在容器不能支持移动会话时抛出该异常.
这些约束表明开发人员被保证没有发生在非分布式容器之外的并发问题.
通过拥有移动会话对象的能力,从分布式系统中的任一个活跃节点移动到系统中的另一个节点,容器提供者能保证服务的可伸缩性和品质,负载均衡和故障转移等特性.
如果分布式容器坚持或移动会话来提供服务特性的品质,就不强求在序列化HttpSession和它们的属性时使用本地虚拟机的序列化机制.容器不会保证调用会话属性上的readObject和writeObject方法.但是保证它们的属性可序列化关闭将被保存.
在移动会话期间,容器必须通知实现了HttpSessionActivationListener接口的会话属性.通知钝化监听器必须优先于序列化监听器,活化则要迟于会话反序列化.
应用开发人员编写分布式应用,应该明白因为允许容器运行在多个java虚拟机上,他就不能依赖静态变量来存储应用程序状态.应使用EJB或数据库.
7.7.3 客户机语义
实际上cookie和SSL认证都是由web浏览器控制的,而不是和浏览器的某个特定窗口相关联,也许从所有窗口发送到servlet容器的请求是属于同一个会话.为了尽量方便,开发人员总是假定客户机的所有窗口参加的是同一个会话.
八 转发请求
使用RequestDispatcher接口,向另一个servlet转发请求,或者在response中包含另一个servlet的输出.
8.1 获得RequestDispatcher
从ServletContext上可以获得RequestDispatcher的实现对象: .getRequestDispatcher .getNamedDispatcher
getRequestDispatcher方法的参数为一个字符串变量,描述在ServletContext范围内的路径.该路径必须相对于ServletContext的根.并且以\开始.该方法使用此路径寻找到servlet后,用一个RequestDispatcher对象包装返回.如果在该路径上没有找到对应的servelt,一个包装了该路径内容的RequestDispatcher被返回.
getNamedDispatcher方法参数为一个字符串变量,它表示ServletContext内已知servlet的名称,如找到该servelt,用RequestDispatcher对象包装后返回,如未找到,返回null。
使用相对于当前请求的相对路径也可以获得RequestDispatcher对象,以下方法在ServletRequest接口提供: .getRequestDispatcher
该方法的行为类似于ServletContext中同名函数,servlet容器使用request对象中的信息转换给定的相对路径为完整的路径。例如:
在context“/”和一个到“/garden/tools.html”的请求,此时,调用request上的getRequestDispatcher(\方法效果等同于ServletContext.getRequestDispatcher(\。
8.1.1 转发请求路径里的查询字符串
ServletContext和ServletRequest里使用路径参数获得RequestDispatcher的方法,允许添加查询字符串到该路径上。例如,开发人员用以下代码:
String path = “/raisons.jsp?orderno=5”;
RequestDispatcher rd = context.getRequestDispatcher(path); rd.include(request, response);
查询字符串里指定的参数,顺序要优先于同名的其他被传进的参数。该参数与RequestDispatcher相关联,只在include或forward调用中有效。
8.2 使用request Dispatcher
使用request Dispatcher,调用该接口上的include或forward方法。参数可以是通过service方法传入的request,response对象,也可以是它们被包装后的对象。
容器提供者应确保将请求分发到目的servlet必须发生在原始请求所在的虚拟机上。
8.3 include方法
该方法必须可以在任意时刻调用,目标servlet的include方法可以五约束地访问request对象,但在访问response对象上有一些限制。它只能向response对象的ServletOutputStream或Writer写入信息,在response的缓冲写完后提交response,或直接调用flushBuffer方法提交response,但不能设置头信息,或者调用任何可能改变头信息的方法。任何此类操作将被忽略。
8.3.1 Included参数
除了使用getNamedDispatcher获得的servlet外,a servlet being used from within an include has access to the path by which it was invoked. 以下request属性被设置:
javax.servlet.include.request_uri javax.servlet.include.context_path javax.servlet.include.servlet_path javax.servlet.include.path_info
javax.servlet.include.query_string
被包含的servlet可以调用getAttribute访问这些属性。
以getNamedDispatcher方式获得的被包含servlet不能得到这些属性。
8.4 Forward方法
在负责调用的servlet还没有向客户机提交输出的时候,才可以调用RequestDispatcher上的Forward方法。在目标servlet的service方法调用前,如果response缓冲中存在还没提交的数据,这些缓冲内容必须被清空,如果response已经提交,将抛出IllegalStateException异常。request对象暴露给目的servlet的路径元素必须反射用于获得RequestDispatcher路径。唯一的例外是,如果RequestDispatcher是通过 getNamedDispatcher方法获得的。
在forward方法返回前,response内容必须被发送和提交,并被容器关闭。
8.4.1query string
在forward或include请求的时候,请求转发机制负责组合查询字符串。
8.5 错误处理
如果request dispatcher的目标servlet扔出运行时错误或一个被检查类
型的意外ServletException或IOException,它应当被传播到调用它的servlet。所有其他异常则应被包装为ServletException,被传播以前,异常引起的最初原因应被设置到原始异常。
第九章 web 应用
web应用是web server中某个特定的路径。例如,一个catalog应用可能在http://www.mycorp.com/catalog.所有以此路径开始的请求都被发送到catalog应用处理。
servlet容器能建立自动产生web应用的规则,例如,~user/ 可以映射到基于/home/user/public_html/的一个web应用。
默认情形下,在任意时间内,一个web应用的实例必须运行在一个虚拟机上。但如果是分布式应用,这是可以改变的,分布式应用需要遵循一套更严格的规则,这些规则不在本规范内。
9.2 和ServletContext的关系
servlet 容器在web应用和Servlet context之间必须强制一对一的对应关系。ServletContext对象相servelt提供了整个应用视图。
9.3 web应用的元素
web应用可以由以下元素构成: .servlet .JSP页面
.工具类(Utility class)
.静态文档(html,image,sound 等) .客户端java applet,beans,和类 .组合所有以上元素的描述元信息
9.4发布层次
本规范定义了一个层次结构,用于发布和打包,可以文件系统形式存在,也可以以压缩包文件形式。建议servlet容器支持此结构为运行时表示。
9.5 目录结构
web应用以结构化层次目录形式存在。该层次结构的根为文档的根。例如,context路径为/catalog的web应用,应用根路径下的index.html文件可以用/catalog/index.html请求来访问。映射的规则在第11章有叙述。因为应用的context路径决定了内容的URL名字空间,web容器必须拒绝可能引起URL名字空间潜在冲突的context定义。这有可能发生,例如试图以相同的context路径发布另一个web应用,或者其中一个context路径是另一个context的一部份。匹配是大小写敏感的,所以,冲突检测也是一样的。
应用层次中有个特殊的目录叫“WEB-INF”,此目录包含了不在应用根文档的所有东西。WEB-INF节点不是应用文档树的公开部分,不能被客户机直接访问。但该目录的内容对于servlet代码是可见的,可使用ServletContext