近期,发现Struts2的页面初次加载时很慢,想了个点子,在tomcat启动完后预先访问这些页面。主要出现两个问题:
1:什么时候检测到tomcat启动完成?
2:页面访问都是需要预先登录的,而且还有验证码,如何绕过去?
对于第一个问题,直观的想法是在最后一个Listener中做,但是如果直接在Listener中做会导致死锁,因为Listener不返回Tomcat是不会接受请求的。一个办法是创建一个线程,在线程中请求页面,这样这个线程会自动阻塞到Tomcat启动完毕。
第二个问题,比较麻烦。思路是先模拟登录,然后再请求页面。需要特别注意JSESSIONID的处理,确保每次请求都在一个Session中。请求部分的代码如下:
protected static String postUrl(String strUrl, String strParams, String []strSession, String strMethod) throws IOException {
URL url = new URL(strUrl);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
if(strSession!=null && strSession.length>0 && !Utility.isEmpty(strSession[0])) {
connection.setRequestProperty( "Cookie", strSession[0]);
}
connection.setRequestMethod(strMethod);
connection.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows 2000)");
if(!Utility.isEmpty(strParams)) {
connection.setDoOutput(true);
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "GB2312");
out.write(strParams);
out.flush();
out.close();
}
String sCurrentLine = "";
String sTotalString = "";
InputStream l_urlStream = connection.getInputStream();
BufferedReader l_reader = new BufferedReader(new InputStreamReader(l_urlStream));
while ((sCurrentLine = l_reader.readLine()) != null) {
sTotalString += sCurrentLine + "\r\n";
}
if(strSession!=null && strSession.length>0) {
String session_value = connection.getHeaderField("Set-Cookie" );
if(!Utility.isEmpty(session_value) && Utility.isEmpty(strSession[0])) {
String[] sessionId = session_value.split(";");
strSession[0] = sessionId[0];
}
}
return sTotalString;
}
思路比较清晰,一开始不设置SessionId,如果Tomcat创建了SessionId,则以后都用这个SessionId。获取了SessionId后,Tomcat返回其他SessionId时一定忽略,这点特别要注意。登录的代码如下:
protected static void login(String []strSession) {
try {
String strUrl = ApplicationParameter.URLPREFIX + "/codeimage?86";
postUrl(strUrl, "", strSession, "GET");
} catch (Exception e) {
e.printStackTrace();
}
try {
String strUrl = ApplicationParameter.URLPREFIX + "/j_spring_security_check?logurl=/index.action&Verify=false&logintype=wz";
JdbcTemplate jdbcTemplate = (JdbcTemplate)SpringTools.getBean("jdbcTemplate");
List<Map<String, String>> lstTmp = jdbcTemplate.queryForList("select t.pwd as pwd from t_user t where t.name = ?", new Object[]{FETCHEARLY_USERNAME});
String strPassWd = lstTmp!=null&&lstTmp.size()>0 ? lstTmp.get(0).get("PWD") : "";
String strSessionId = strSession[0].substring(strSession[0].indexOf("=")+1);
strSessionId = strSessionId.indexOf(";")==-1 ? strSessionId : strSessionId.substring(0, strSessionId.indexOf(";"));
String strParams = "j_username=" + FETCHEARLY_USERNAME
+ "&j_password=" + (strPassWd.length()>=4 ? strPassWd.substring(0, strPassWd.length()-4) : "")
+ "&imageCodeName=" + SessionListener.getSession(strSessionId).getAttribute("KAPTCHA_SESSION_KEY");
postUrl(strUrl, strParams, strSession, "POST");
} catch (Exception e) {
e.printStackTrace();
}
}
思路也很明确,先请求验证码页面,目的是让Tomcat为我们生成一个验证码,然后我们就可以在Session中获取验证码了。账户和密码是直接从数据库中获取的。登录框架是Spring Security。主要的代码如下:
protected static void logoff(String []strSession) {
String strUrl = ApplicationParameter.URLPREFIX + "/j_spring_security_logout";
String strParams = "";
try {
postUrl(strUrl, strParams, strSession);
} catch (IOException e) {
}
}
原理也很简单,直接访问Spring Security的注销页面即可。
登录后就可以进行有状态的页面访问了,只要确保SessionId一致即可。