截止目前,前面第6节遗留的问题还有一个没处理:
我们测试时用的URL全部是以http://.../faces/.../xxx.xhtml路径来访问的,如果以http//.../xxx.faces来访问就可以看到dojo.js没生效,这是相对路径不匹配造成dojo.js无法加载,这个问题也需要解决。
先看效果
这是用/faces访问的效果
下面这是用.faces访问的效果
可以看到用.faces访问时dojo.js没运行,这是因为两个原因:1、dojo.js没加载。2、<script>require(["dojo/parser", ...这段脚本依赖的js文件没有加载。
第1个原因通过查看两个页面的源代码就能发现,用/faces访问时,产生的代码是
<link type="text/css" rel="stylesheet" href="/dojo4j/faces/javax.faces.resource/dojo/resources/dojo.css" />
而用.faces访问时,产生的代码是
<link type="text/css" rel="stylesheet" href="/dojo4j/test/textBoxTest.faces/javax.faces.resource/dojo/resources/dojo.css" />
明显的路径错误,解决这个问题只需修改Header.java,重写资源文件引用的代码,加入方法
private String getResourcePath(String resourceName) { String uri; FacesContext context = FacesContext.getCurrentInstance(); String facesServletMapping = Util.getFacesMapping(context); // If it is extension mapped if (Util.isPrefixMapped(facesServletMapping)) { uri = facesServletMapping + ResourceHandler.RESOURCE_IDENTIFIER + '/' + resourceName; } else { uri = ResourceHandler.RESOURCE_IDENTIFIER + '/' + resourceName + facesServletMapping; } HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); return request.getContextPath() + uri; }
writer.writeAttribute("href", request.getContextPath() + context.getExternalContext().getRequestServletPath() + "/javax.faces.resource/dojo/resources/dojo.css", null); writer.writeAttribute("href", request.getContextPath() + context.getExternalContext().getRequestServletPath() + "/javax.faces.resource/dijit/themes/" + this.theme + "/" + this.theme + ".css", null); writer.writeAttribute("src", request.getContextPath() + context.getExternalContext().getRequestServletPath() + "/javax.faces.resource/dojo/dojo.js", null);
writer.writeAttribute("href", getResourcePath("dojo/resources/dojo.css"), null); writer.writeAttribute("href", getResourcePath("dijit/themes/" + this.theme + "/" + this.theme + ".css"), null); writer.writeAttribute("src", getResourcePath("dojo/dojo.js"), null);
<link type="text/css" rel="stylesheet" href="/dojo4j/faces/javax.faces.resource/dojo/resources/dojo.css" />
用.faces访问时生成
<link type="text/css" rel="stylesheet" href="/dojo4j/javax.faces.resource/dojo/resources/dojo.css.faces" />
这样就可以正常生成所需的链接代码。
尽管这样,但dojo在运行<script>require(["dojo/parser", ...时,并不能加载所依赖的脚本文件,通过Google Chrome浏览器调试,可以看到
所依赖的parser.js、TextBox.js、Button.js无法下载,这是因为dojo在加载时这几个脚本文件时路径,用的是标准的.js结尾,但是这种情况JSF是不能正常处理的,必须要在路径后面加上.faces后缀,JSF才能正确读取资源文件
解决这个问题只有用Filter。
Filter.java
package org.dojo4j.webapp; import java.io.IOException; import javax.faces.application.ResourceHandler; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.sun.faces.util.Util; public class DojoFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { if (!(req instanceof HttpServletRequest) || !(resp instanceof HttpServletResponse)) { throw new ServletException("DojoFilter just supports HTTP requests"); } HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; String uri = request.getRequestURI(); if (uri.contains(ResourceHandler.RESOURCE_IDENTIFIER)) { String facesServletMapping = getFacesMapping(request); //只处理facesServletMapping为.xxx后缀的情况,不处理facesServletMapping为/xxx为前缀的情况 if (!Util.isPrefixMapped(facesServletMapping)) { //只有从引用资源的页面URL中才能取得Faces Servlet的后缀 String referer = request.getHeader("Referer"); if (referer != null && referer.trim().length() > 0) { if (referer.indexOf('?') > -1) { referer = referer.substring(0, referer.indexOf('?')); } if (referer.indexOf('.') > -1) { String subfix = referer.substring(referer.lastIndexOf('.')); //给没有Faces Servlet的后缀的资源加上后缀,确保通过JSF框架来解析资源文件 if (!facesServletMapping.equals(subfix)) { String url = request.getServletPath() + subfix + (request.getQueryString() == null ? "" : "?" + request.getQueryString()); // System.out.println(url); request.getRequestDispatcher(url).forward(request, response); return; } } } } } chain.doFilter(req, resp); } private String getFacesMapping(HttpServletRequest request) { String servletPath = request.getServletPath(); String pathInfo = request.getPathInfo(); if (servletPath == null) { return null; } if (servletPath.length() == 0) { return "/*"; } if (pathInfo != null) { return servletPath; } else if (servletPath.indexOf('.') < 0) { return servletPath; } else { return servletPath.substring(servletPath.lastIndexOf('.')); } } @Override public void init(FilterConfig arg0) throws ServletException { } @Override public void destroy() { } }
<filter> <filter-name>DojoFilter</filter-name> <filter-class>org.dojo4j.webapp.DojoFilter</filter-class> </filter> <filter-mapping> <filter-name>DojoFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
下载代码
到这里,开发简单的组件所需的技术就介绍得差不多了,我相信通过这些技术应该能够采用一些简单的js库来制作自己的JSF组件库了,如jquery ui、liger ui等。
后面我会写如何处理JSF ajax方式更新js组件,而不是html控件(下拉框联动),如何改造jsf.js实现同步请求(ajax表格取数、分页、排序),如何自定义dojo js控件来适应JSF组件开发的需要(树、表格、树表格),这些知识深入JSF和DOJO内部,比较复杂,但是对打造企业级的框架又必不可少,如果有兴趣请继续关注。