为了在JSF开发中联合使用AJAX和Mozilla XUL技术,组件创建者必须提供这些技术所需要的任何资源文件(例如图象,式样表或脚本等)。为一个JSF组件库提供资源文件的标准方式是直接从Web应用程序根文件系统中提供服务。这些资源通常用一个档案文件(如一个ZIP文件)打包,并且独立于JSF组件库发行。
本篇将介绍一种新的开源工程Weblets(http://weblets.dev.java.net)。这个工程的目标是,为JSF组件开发者提供一种工具以便开发者可以直接从JAR中提取资源文件,而不是从Web应用程序根文件系统中提供相应的服务。不象使用定义在web.xml文件中的静态配置的URL映射的传统Web应用程序,它们需要一种基于现有的组件库JAR文件进行动态配置的URL映射。实质上,Weblets为开发者提供了一种非常容易的打包Web应用程序的方法—Web应用程序实现代码可以与其资源文件驻留在同一个Java JAR文件中。
一、资源装载
让我们假定,我们有一个JSF组件,它需要有一个JavaScript文件myScript.js服务于客户端。这个JavaScript文件为组件所用以实现与用户某种程度的丰富交互。传统情况下,这个JavaScript文件是由Web应用程序服务的—经由一个硬编码到JSF组件的实际的生成器代码中的相对路径确定。这样以来,需要Web开发者发布以独立文档文件形式交付和打包的其它相关资源。
值得注意的是,JSF HTML基本RenderKit并没有任何图象,式样,或脚本;因此,在Faces资源打包问题方面没有标准的解决方案。
下面的示例生成器代码展示了一种从Web应用程序根文件系统下提供一个JavaScript文件(/myresources/myScript.js)服务的可安装方法。
ViewHandler handler = context.getApplication().getViewHandler();String resourceURL = handler.getResourceURL(context,"/myresources/myScript.js");out.startElement("script", null);out.writeAttribute("type", "text/javascript", null);out.writeAttribute("src", resourceURL, null);out.endElement("script");
尽管这种可安装的方法方便了JSF组件作者,但是它也确实增加了Web开发者的安装负担—他们必须记住:每当把该组件库升级到一个新的版本时都要提取可安装的文档。因此,我们非常需要一种新的方法来把我们程序的其它资源打包到相同的包含Renderer类的JAR文件中,以便简化Web开发者使用我们的组件库进行发布的问题。
二、使用Weblets
这个开源Weblets工程的主要思想是,用一种通用的可扩展的方式解决资源打包问题,以便它可以被所有JSF组件创作者所利用,而同时把最小的安装任务交给Web开发者来完成。
其实,一个Weblet充当一个“调停人”的作用—它解释来自于客户端的请求,并且使用简短的Web URL从一个JAR文件中提供资源服务。不同于Servlet或Filter方式,一个Weblet可以在一个JAR内部进行注册和配置,这样以来组件库生成器、它们的资源文件还有Weblet配置文件(weblets-config.xml)都可以被打包到相同的JAR文件中。对所有的组件库来说,Weblet容器只能在Web应用程序配置文件(web.xml)中注册一次。当把组件库升级到新版本时,不需要独立地发布其它可安装程序。
值得注意的是,所有由Weblets所服务的资源都是内部资源,只能为生成器所使用。任何由应用程序所提供的资源(例如图像),都以组件属性值方式提供并且从根上下文中作为外部资源加载。
三、Weblet架构分析
尽管Weblets被设计由任何Web客户端所使用,但是,通过使用一种定制的ViewHandler和WebletsViewHandler,Weblets实现已经与JSF集成到一起。在主JSF页面生成期间,WebletsViewHandler负责把Weblet特定的资源URL转换成实际的由浏览器使用以请求Weblet托管的资源的URL。
在收到为主页面生成的标注后,浏览器使用一个独立的请求下载其它各个资源。这其中的每个请求(下载一种Weblet托管的资源)被WebletsPhaseListener所拦截,这样以来,WebletContainer必须把Weblet托管的资源文件进行“流化”而从组件库JAR中导出。
WebletContainer的设计目的是,尽可能利用浏览器缓存。这样以来,通过最小化Weblet托管的资源文件的请求数量,从而可以大大提高整体数据生成性能。
为了确保灵活性和进行优化,并且避免与现有Web应用程序资源发生冲突,Web开发者可以配置Weblets以覆盖由组件作者所提供的任何缺省的设置。
四、把Weblets应用于组件库
Weblets是使用weblets-config.xml文件进行配置的,它必须存储在组件库JAR的/META-INF目录下。配置一个Weblet类似于配置一个Servlet或一个过滤器(Filter)。在weblets-config.xml文件中的每一个Weblet入口都有一个Weblet名字、实现类和初始化参数。Weblet映射把一个特别的URL模式与一个特定的Weblet名(例如,org.myapp.html)相关联。Weblet名和默认的URL模式定义该Weblet托管的资源的公共API。特别注意,在你的组件库的各个发行版本之间不应修改这些名称和URL模式以便实现向后兼容性。
Weblets配置文件weblets-config.xml
<?xml version="1.0" encoding="UTF-8" ?><weblets-config xmlns="http://weblets.dev.java.net/config" ><weblet><weblet-name>org.myapp.html</weblet-name><weblet-class>net.java.dev.weblets.packaged.PackagedWeblet</weblet-class><init-param><param-name>package</param-name><param-value>org.myapp.faces.renderer.html.resources</param-value></init-param></weblet><weblet-mapping><weblet-name>org.myapp.html</weblet-name><url-pattern>/myresources/*</url-pattern></weblet-mapping></weblets-config>
使用1.0版本控制的Weblets配置文件以便提高软件生效效率
<?xml version="1.0" encoding="UTF-8" ?>
<weblets-config xmlns="http://weblets.dev.java.net/config" >
<weblet>
<weblet-name>org.myapp.html</weblet-name>
<weblet-class>net.java.dev.weblets.packaged.PackagedWeblet</weblet-class>
<weblet-version>1.0</weblet-version>
<init-param>
<param-name>package</param-name>
<param-value>org.myapp.faces.renderer.html.resources</param-value>
</init-param>
</weblet>
<weblet-mapping>
<weblet-name>org.myapp.html</weblet-name>
<url-pattern>/myapp/*</url-pattern>
</weblet-mapping>
</weblets-config>
我们的组件库把资源打包到org.myapp.faces.renderer.html.resources中,并且通过使用默认的URL映射(/myresources/*)使其可用于浏览器中。
PackagedWeblet是一个内置Weblet实现,可以使用ClassLoader从一个特定的Java包中读它并且把相应的结果“流回”到浏览器端。包初始化参数告诉PackagedWeblet,当实现Weblet托管的资源请求时,应该把哪一个Java包作为根使用。
五、Weblet版本控制
Weblets还为组件库的版本控制提供内置的支持。这用于允许浏览器在可能的情况下缓存打包的资源(例如myScript.js),从而阻止不必要的与Web服务器之间的来回通讯。
在每次浏览器生成页面时,它都确保所有的为该页面所用的资源可以使用。在该页面的初始生成期间,通过从Web服务器下载一个新的副本,浏览器用每一个资源URL的内容填充它的缓存。当它这样做时,浏览器从响应信息头部记下Last-Modified和Expires时间戳。如果当前时间比Expires时间戳还晚,那么被缓冲的内容已经到期。
在下一次生成这一页面时,浏览器检查是否本地缓冲资源已经过期。如果它没有过期,则重用本地缓冲的副本。否则,将向Web服务器发出一种新的请求,包括在If-Modified-Since请求头中的Last-Modified信息。通过指示浏览器缓存尚待更新,或者通过使用响应头中的更新的Last-Modified和Expires时间戳把新的资源内容“流回”到浏览器,Web服务器作出响应。
Weblets使用版本控制机制来利用浏览器缓存行为,这样,打包的资源就可以被下载并且被尽可能有效地缓冲。当缓存被“倒空”或当组件库在Web服务器端已经升级时,浏览器仅仅需要检查新的更新即可。
通过指定一个Weblet版本,你可以指示被打包的资源不会发生改变,直到版本号发生改变为止。因此,版本号作为在运行时刻由WebletsViewHandler(例如,/myresources$1.0/myScript.js)决定的资源URL的一部分被包括在内。当WebletContainer服务这一请求时,它从URL中提取版本号并且确定资源应该被缓冲并且从未到期。一旦一个新版本的组件库被发布到Web应用程序,在运行时刻由WebletsViewHandler创建的资源URL(例如,/myresources$2.0/myScript.js)就会改变,这样浏览器中的myScript.js的缓冲副本版本1.0不再有效,因为URL已经发生变化。
在开发期间,被打包资源的内容可能经常发生变化,所以,使浏览器保持回检以便使Web服务器检测最新的资源URL目录是非常重要的。默认情况下,每当生成主Web页面(且Weblet版本被从weblets-config.xml中忽略时)时,即进行这种检查。
作为选择,Weblet配置允许组件创作者把-SNAPSHOT添加到版本号上。例如,1.0-SNAPSHOT(见下面的代码)说明这个文件正处于开发中。
<?xml version="1.0" encoding="UTF-8" ?>
<weblets-config xmlns="http://weblets.dev.java.net/config" >
<weblet>
<weblet-name>org.myapp.html</weblet-name>
<weblet-class>net.java.dev.weblets.packaged.PackagedWeblet </weblet-class>
<weblet-version>1.0-SNAPSHOT</weblet-version>
...
</weblet>
...
</weblets-config>
安全性
当从一个JAR中服务打包资源时,特别注意一定不要使Java类文件或另外的敏感信息为URL所存取。在桌面Java应用程序中,资源文件经常存储在一个子包“resources”中,它位于使用资源文件的Java实现类的下面。同样的策略也适于在JSF组件库中打包的资源,并且还具有安全方面的优点—可以确保仅有资源文件可为URL所存取。所有另外的JAR文件内容,包括Java实现类,都不是URL可存取的,因为“resources”包和任何“resources”的子包中都不存在Java类。
Weblets协议
在讨论了如何配置Weblets后,现在我们来看一下如何在我们的生成器中引用由Weblet所定义的资源。这个由Weblet合同所定义的语法用于返回一个到JSF页面的适当的URL,如下所示:
<prefix><weblet name><resource>
在此,prefix指示这是一种Weblet托管的资源,而且它的后面即跟着Weblet名字和要求的资源。
以前,在我们的Renderer类中,我们把URL /myresources/myScript.js作为一个参数传递到ViewHandler的getResourceURL()方法。在下面的示例代码中,我们通过使用Weblet协议也可以实现这一功能。
ViewHandler handler = context.getApplication().getViewHandler();
String resourceURL = handler.getResourceURL(context, "weblet://org.myapp.html/myScript.js");
out.startElement("script", null);
out.writeAttribute("type", "text/javascript", null);
out.writeAttribute("src", resourceURL, null);
out.endElement("script");
这种类似Weblet协议的语法方便使用而且也很容易理解。这种语法以weblet://开始,后面跟着Weblet名,例如org.myapp.html,最后跟着路径信息或资源文件,例如/myScript.js。注意,URL映射和版本号都没有包括到Weblet资源语法中。Weblet URL映射和版本号由WebletsViewHandler所使用来创建一种该Weblet将会服务的资源URL。
当组件创作者没有使用Weblets时,他不会使用weblet://资源路径语法并且将发行一个独立的可安装的zip。当组件创作者使用Weblets时,他开始在生成器中使用weblet://资源路径语法,并且把这些资源包括到JAR中。实际上,在同一个组件库中的相同版本的资源中混合使用这些方法并无多大益处。
六、在JSF应用程序中使用Weblets
为了帮助Web开发者简化安装,组件创作者应该为它们的组件库选择一个缺省的URL映射。组件创作者不需要把任何Weblet特定的配置添加到web.xml文件,因为WebletsPhaseListener能够被自动激活以服务于针对Weblet托管资源的输入请求。
七、小结
作为一个新的开源工程,Weblets为Web客户端和JSF组件开发社区提供一种“事实上”通用的和可配置的资源加载工具。这种工具的主要优点在于,它能够简化JSF组件及其资源的打包,并用最小开支来安装和建立针对特定Web应用程序工程的JSF组件库。
总之,本文探讨了一种打包JSF组件资源的新方式。现在,通过包括一个合适的weblets-config.xml文件并使用weblet://协议风格的语法来引用Weblet托管的资源,你应该能够在你自己的组件库中利用Weblets。
本篇将介绍一种新的开源工程Weblets(http://weblets.dev.java.net)。这个工程的目标是,为JSF组件开发者提供一种工具以便开发者可以直接从JAR中提取资源文件,而不是从Web应用程序根文件系统中提供相应的服务。不象使用定义在web.xml文件中的静态配置的URL映射的传统Web应用程序,它们需要一种基于现有的组件库JAR文件进行动态配置的URL映射。实质上,Weblets为开发者提供了一种非常容易的打包Web应用程序的方法—Web应用程序实现代码可以与其资源文件驻留在同一个Java JAR文件中。
一、资源装载
让我们假定,我们有一个JSF组件,它需要有一个JavaScript文件myScript.js服务于客户端。这个JavaScript文件为组件所用以实现与用户某种程度的丰富交互。传统情况下,这个JavaScript文件是由Web应用程序服务的—经由一个硬编码到JSF组件的实际的生成器代码中的相对路径确定。这样以来,需要Web开发者发布以独立文档文件形式交付和打包的其它相关资源。
值得注意的是,JSF HTML基本RenderKit并没有任何图象,式样,或脚本;因此,在Faces资源打包问题方面没有标准的解决方案。
下面的示例生成器代码展示了一种从Web应用程序根文件系统下提供一个JavaScript文件(/myresources/myScript.js)服务的可安装方法。
ViewHandler handler = context.getApplication().getViewHandler();String resourceURL = handler.getResourceURL(context,"/myresources/myScript.js");out.startElement("script", null);out.writeAttribute("type", "text/javascript", null);out.writeAttribute("src", resourceURL, null);out.endElement("script");
尽管这种可安装的方法方便了JSF组件作者,但是它也确实增加了Web开发者的安装负担—他们必须记住:每当把该组件库升级到一个新的版本时都要提取可安装的文档。因此,我们非常需要一种新的方法来把我们程序的其它资源打包到相同的包含Renderer类的JAR文件中,以便简化Web开发者使用我们的组件库进行发布的问题。
二、使用Weblets
这个开源Weblets工程的主要思想是,用一种通用的可扩展的方式解决资源打包问题,以便它可以被所有JSF组件创作者所利用,而同时把最小的安装任务交给Web开发者来完成。
其实,一个Weblet充当一个“调停人”的作用—它解释来自于客户端的请求,并且使用简短的Web URL从一个JAR文件中提供资源服务。不同于Servlet或Filter方式,一个Weblet可以在一个JAR内部进行注册和配置,这样以来组件库生成器、它们的资源文件还有Weblet配置文件(weblets-config.xml)都可以被打包到相同的JAR文件中。对所有的组件库来说,Weblet容器只能在Web应用程序配置文件(web.xml)中注册一次。当把组件库升级到新版本时,不需要独立地发布其它可安装程序。
值得注意的是,所有由Weblets所服务的资源都是内部资源,只能为生成器所使用。任何由应用程序所提供的资源(例如图像),都以组件属性值方式提供并且从根上下文中作为外部资源加载。
三、Weblet架构分析
尽管Weblets被设计由任何Web客户端所使用,但是,通过使用一种定制的ViewHandler和WebletsViewHandler,Weblets实现已经与JSF集成到一起。在主JSF页面生成期间,WebletsViewHandler负责把Weblet特定的资源URL转换成实际的由浏览器使用以请求Weblet托管的资源的URL。
在收到为主页面生成的标注后,浏览器使用一个独立的请求下载其它各个资源。这其中的每个请求(下载一种Weblet托管的资源)被WebletsPhaseListener所拦截,这样以来,WebletContainer必须把Weblet托管的资源文件进行“流化”而从组件库JAR中导出。
WebletContainer的设计目的是,尽可能利用浏览器缓存。这样以来,通过最小化Weblet托管的资源文件的请求数量,从而可以大大提高整体数据生成性能。
为了确保灵活性和进行优化,并且避免与现有Web应用程序资源发生冲突,Web开发者可以配置Weblets以覆盖由组件作者所提供的任何缺省的设置。
四、把Weblets应用于组件库
Weblets是使用weblets-config.xml文件进行配置的,它必须存储在组件库JAR的/META-INF目录下。配置一个Weblet类似于配置一个Servlet或一个过滤器(Filter)。在weblets-config.xml文件中的每一个Weblet入口都有一个Weblet名字、实现类和初始化参数。Weblet映射把一个特别的URL模式与一个特定的Weblet名(例如,org.myapp.html)相关联。Weblet名和默认的URL模式定义该Weblet托管的资源的公共API。特别注意,在你的组件库的各个发行版本之间不应修改这些名称和URL模式以便实现向后兼容性。
Weblets配置文件weblets-config.xml
<?xml version="1.0" encoding="UTF-8" ?><weblets-config xmlns="http://weblets.dev.java.net/config" ><weblet><weblet-name>org.myapp.html</weblet-name><weblet-class>net.java.dev.weblets.packaged.PackagedWeblet</weblet-class><init-param><param-name>package</param-name><param-value>org.myapp.faces.renderer.html.resources</param-value></init-param></weblet><weblet-mapping><weblet-name>org.myapp.html</weblet-name><url-pattern>/myresources/*</url-pattern></weblet-mapping></weblets-config>
使用1.0版本控制的Weblets配置文件以便提高软件生效效率
<?xml version="1.0" encoding="UTF-8" ?>
<weblets-config xmlns="http://weblets.dev.java.net/config" >
<weblet>
<weblet-name>org.myapp.html</weblet-name>
<weblet-class>net.java.dev.weblets.packaged.PackagedWeblet</weblet-class>
<weblet-version>1.0</weblet-version>
<init-param>
<param-name>package</param-name>
<param-value>org.myapp.faces.renderer.html.resources</param-value>
</init-param>
</weblet>
<weblet-mapping>
<weblet-name>org.myapp.html</weblet-name>
<url-pattern>/myapp/*</url-pattern>
</weblet-mapping>
</weblets-config>
我们的组件库把资源打包到org.myapp.faces.renderer.html.resources中,并且通过使用默认的URL映射(/myresources/*)使其可用于浏览器中。
PackagedWeblet是一个内置Weblet实现,可以使用ClassLoader从一个特定的Java包中读它并且把相应的结果“流回”到浏览器端。包初始化参数告诉PackagedWeblet,当实现Weblet托管的资源请求时,应该把哪一个Java包作为根使用。
五、Weblet版本控制
Weblets还为组件库的版本控制提供内置的支持。这用于允许浏览器在可能的情况下缓存打包的资源(例如myScript.js),从而阻止不必要的与Web服务器之间的来回通讯。
在每次浏览器生成页面时,它都确保所有的为该页面所用的资源可以使用。在该页面的初始生成期间,通过从Web服务器下载一个新的副本,浏览器用每一个资源URL的内容填充它的缓存。当它这样做时,浏览器从响应信息头部记下Last-Modified和Expires时间戳。如果当前时间比Expires时间戳还晚,那么被缓冲的内容已经到期。
在下一次生成这一页面时,浏览器检查是否本地缓冲资源已经过期。如果它没有过期,则重用本地缓冲的副本。否则,将向Web服务器发出一种新的请求,包括在If-Modified-Since请求头中的Last-Modified信息。通过指示浏览器缓存尚待更新,或者通过使用响应头中的更新的Last-Modified和Expires时间戳把新的资源内容“流回”到浏览器,Web服务器作出响应。
Weblets使用版本控制机制来利用浏览器缓存行为,这样,打包的资源就可以被下载并且被尽可能有效地缓冲。当缓存被“倒空”或当组件库在Web服务器端已经升级时,浏览器仅仅需要检查新的更新即可。
通过指定一个Weblet版本,你可以指示被打包的资源不会发生改变,直到版本号发生改变为止。因此,版本号作为在运行时刻由WebletsViewHandler(例如,/myresources$1.0/myScript.js)决定的资源URL的一部分被包括在内。当WebletContainer服务这一请求时,它从URL中提取版本号并且确定资源应该被缓冲并且从未到期。一旦一个新版本的组件库被发布到Web应用程序,在运行时刻由WebletsViewHandler创建的资源URL(例如,/myresources$2.0/myScript.js)就会改变,这样浏览器中的myScript.js的缓冲副本版本1.0不再有效,因为URL已经发生变化。
在开发期间,被打包资源的内容可能经常发生变化,所以,使浏览器保持回检以便使Web服务器检测最新的资源URL目录是非常重要的。默认情况下,每当生成主Web页面(且Weblet版本被从weblets-config.xml中忽略时)时,即进行这种检查。
作为选择,Weblet配置允许组件创作者把-SNAPSHOT添加到版本号上。例如,1.0-SNAPSHOT(见下面的代码)说明这个文件正处于开发中。
<?xml version="1.0" encoding="UTF-8" ?>
<weblets-config xmlns="http://weblets.dev.java.net/config" >
<weblet>
<weblet-name>org.myapp.html</weblet-name>
<weblet-class>net.java.dev.weblets.packaged.PackagedWeblet </weblet-class>
<weblet-version>1.0-SNAPSHOT</weblet-version>
...
</weblet>
...
</weblets-config>
安全性
当从一个JAR中服务打包资源时,特别注意一定不要使Java类文件或另外的敏感信息为URL所存取。在桌面Java应用程序中,资源文件经常存储在一个子包“resources”中,它位于使用资源文件的Java实现类的下面。同样的策略也适于在JSF组件库中打包的资源,并且还具有安全方面的优点—可以确保仅有资源文件可为URL所存取。所有另外的JAR文件内容,包括Java实现类,都不是URL可存取的,因为“resources”包和任何“resources”的子包中都不存在Java类。
Weblets协议
在讨论了如何配置Weblets后,现在我们来看一下如何在我们的生成器中引用由Weblet所定义的资源。这个由Weblet合同所定义的语法用于返回一个到JSF页面的适当的URL,如下所示:
<prefix><weblet name><resource>
在此,prefix指示这是一种Weblet托管的资源,而且它的后面即跟着Weblet名字和要求的资源。
以前,在我们的Renderer类中,我们把URL /myresources/myScript.js作为一个参数传递到ViewHandler的getResourceURL()方法。在下面的示例代码中,我们通过使用Weblet协议也可以实现这一功能。
ViewHandler handler = context.getApplication().getViewHandler();
String resourceURL = handler.getResourceURL(context, "weblet://org.myapp.html/myScript.js");
out.startElement("script", null);
out.writeAttribute("type", "text/javascript", null);
out.writeAttribute("src", resourceURL, null);
out.endElement("script");
这种类似Weblet协议的语法方便使用而且也很容易理解。这种语法以weblet://开始,后面跟着Weblet名,例如org.myapp.html,最后跟着路径信息或资源文件,例如/myScript.js。注意,URL映射和版本号都没有包括到Weblet资源语法中。Weblet URL映射和版本号由WebletsViewHandler所使用来创建一种该Weblet将会服务的资源URL。
当组件创作者没有使用Weblets时,他不会使用weblet://资源路径语法并且将发行一个独立的可安装的zip。当组件创作者使用Weblets时,他开始在生成器中使用weblet://资源路径语法,并且把这些资源包括到JAR中。实际上,在同一个组件库中的相同版本的资源中混合使用这些方法并无多大益处。
六、在JSF应用程序中使用Weblets
为了帮助Web开发者简化安装,组件创作者应该为它们的组件库选择一个缺省的URL映射。组件创作者不需要把任何Weblet特定的配置添加到web.xml文件,因为WebletsPhaseListener能够被自动激活以服务于针对Weblet托管资源的输入请求。
七、小结
作为一个新的开源工程,Weblets为Web客户端和JSF组件开发社区提供一种“事实上”通用的和可配置的资源加载工具。这种工具的主要优点在于,它能够简化JSF组件及其资源的打包,并用最小开支来安装和建立针对特定Web应用程序工程的JSF组件库。
总之,本文探讨了一种打包JSF组件资源的新方式。现在,通过包括一个合适的weblets-config.xml文件并使用weblet://协议风格的语法来引用Weblet托管的资源,你应该能够在你自己的组件库中利用Weblets。