这个Portlet采用Web2.0技术实现,其原因在于,如果读取目标HTML页面是同步载入的话,当页面含有大量的此类Portlet的时候,Portal页面必须要等到所有的Web剪切Portlet都载入后才能够显示出来,当目标页面所在的服务器在Internet的时候,Portal页面的载入过程将会变得非常缓慢。采用的Web2.0的异步载入的方式,将能够很大程度的提高页面载入速度。
下面描述的是这个Portlet的实现思路:
1. 首先显示一个展示有“载入内容”的HTML页面,Portal页面打开时首先显示该页面;
2. 通过XMLHttpRequest对象请求服务器端的Servlet将目标HTML页面读出来;
3. 解析HTML页面,将要截取的HTML区段解析出来;
4. 通过正则表达式将HTML区段中的相关资源的相对引用转换为绝对引用;
5. 根据需要,将HTML区段中的CSS定义替换为新的CSS定义。
6. 对Portlet进行局部刷新,显示截取内容。
<%@page language="java" contentType="text/html; charset=GB18030" pageEncoding="GB18030" session="false"%><%@taglib uri="http://java.sun.com/portlet" prefix="portlet"%><%@ page import="javax.portlet.PortletPreferences,com.ibm.webclipping.Constants" %><portlet:defineObjects /><div id="content" align="center" style="width:100%;height:100%"><script name="script" language="JavaScript">{ if (window.XMLHttpRequest){ xmlObj = new XMLHttpRequest(); }else if (window.ActiveXObject){ xmlObj = new ActiveXObject("Microsoft.XMLHTTP"); } xmlObj.onreadystatechange = handleResponse;<% PortletPreferences preference = renderRequest.getPreferences(); %>var params = <%= "\"" + Constants.KEY_URL + '=' + preference.getValue(Constants.KEY_URL, "") + '&' + Constants.KEY_CHARTSET + '=' + preference.getValue(Constants.KEY_CHARTSET, "") + '&' + Constants.KEY_BEGINTAG + '=' + preference.getValue(Constants.KEY_BEGINTAG, "") + '&' + Constants.KEY_ENDTAG + '=' + preference.getValue(Constants.KEY_ENDTAG, "") + '&' + Constants.KEY_REGULATOR + '=' + preference.getValue(Constants.KEY_REGULATOR, "") + '&' + Constants.KEY_REPLACEMENT + '=' + preference.getValue(Constants.KEY_REPLACEMENT, "") + "\""%>; xmlObj.open("POST",'<%= renderResponse.encodeURL(renderRequest.getContextPath()+"/ReadContentServlet")%>',true); xmlObj.setRequestHeader("content-type", "application/x-www-form-urlencoded"); xmlObj.setRequestHeader("content-length", params.length); xmlObj.send(params); } function handleResponse() { if (xmlObj.readyState == 4){//xmlObj loaded if (xmlObj.status == 200){ var datas = xmlObj.responseText; var obj = document.getElementById("content"); obj.innerHTML = datas; } } }</script><img src='<%= renderResponse.encodeURL(renderRequest.getContextPath()+"/images/loading.gif") %>'/></div>
Constants.KEY_URL:目标页面的URL
Constants.KEY_CHARTSET:目标页面的编码
Constants.KEY_BEGINTAG:目标页面要截取内容的开始html内容
Constants.KEY_ENDTAG:目标页面要截取内容的结束html内容
Constants.KEY_REGULATOR:匹配正则表达式
Constants.KEY_REPLACEMENT:匹配内容的替换表达式
通过HTTP请求将目标HTML内容读出来,为了达到这个目标,我们采用java.net.URL对象来实现,通过这个对象,我们可以把指定地址的HTML内容读出到一个字符串中,为下一步的处理做准备。
//读取strURL中的HTML内容URL url = new URL(strURL); StringBuffer buffer = new StringBuffer();String strLine = "";//从URL中读取内容,InputStreamReader reader = new InputStreamReader(url.openStream(),charsetName);BufferedReader br = new BufferedReader(reader);while ((strLine = br.readLine()) != null) { buffer.append(strLine.toLowerCase()); buffer.append("\r\n");}br.close();
这段代码中有两个需要注意的地方,第一是变量charsetName,这个变量必须与目标HTML页面的编码一致,否则会有乱码出现,尤其是中文环境下。第二个重点是buffer.append("\r\n"),当我们从目标HTML页面读取内容到一个字符串中的时候,我们必须要保证目标HTML的内容原原本本的读入到字符串中。由于我们在后面的步骤中要对其进行切割和替换,所以这一点尤其重要。
第三步就是要将读出的HTML内容进行截取,获得要剪切的那部分页面内容。我们通过字符串的indexOf方法来实现。
int beginIndex = buffer.indexOf(beginTag,0);if(-1 == beginIndex){result = "未找到起始字符串";}//System.out.println("beginIndex is " + beginIndex);int endIndex = buffer.indexOf(endTag,0);if(-1 == endIndex){ result = "未找到结束字符串";} buffer.substring(beginIndex, endIndex);
下面,我们就要通过正则表达式对截取的HTML内容进行替换。在进行内容截取的时候,页面上的资源(例如,图片,超链接,js等)都是采用的相对地址,这里需要将这些相对地址替换为绝对地址,当展示在Portal上不会出现空连接的错误。同时还可以根据需要对class进行替换,使其与整个门户的风格一致。下面的这段代码通过循环一次性的对HTML中的内容进行替换。
//获取beginTag和endTag之间的内容,并对其进行匹配Pattern pattern = Pattern.compile(reg);Matcher matcher = pattern.matcher(buffer.substring(beginIndex, endIndex));StringBuffer replacementResult = new StringBuffer();//分解reg和replacement的区段// regToken是通过”|”进行分割的正则表达式集合StringTokenizer regToken = new StringTokenizer(reg,"|");// replacementToken是通过”|”进行分割的替换内容集合,与正则表达式集合一一对应StringTokenizer replacementToken = new StringTokenizer(replacement,"|"); //对匹配出的内容依次进行替换while(matcher.find()){ String matched = matcher.group(); //循环判断应该替换哪个内容 while(regToken.hasMoreElements()){ Pattern subPattern = Pattern.compile(regToken.nextToken()); Matcher subMatcher = subPattern.matcher(matched); if(subMatcher.matches()){ matcher.appendReplacement(replacementResult, replacementToken.nextToken()); break; } else //如果匹配不成功,将replacementToken的指针向下移动一级 replacementToken.nextToken(); } //初始化两个Token,重新开始比对 regToken = new StringTokenizer(reg,"|"); replacementToken = new StringTokenizer(replacement,"|"); } matcher.appendTail(replacementResult); result = replacementResult.toString();}
上述方式有一定的局限性,但是在大多数场合能够应用,可以通过定制实现更强大的能力。
相关资源:基于Portal和Portlet技术的Web整合应用研究与实现