|
[WARNING] POM for 'org.hibernate:jtidy:pom:r8-20060801:runtime' is invalid. It will be ignored for artifact resolution. Reason: Parse error reading PO
M. Reason: TEXT must be immediately followed by END_TAG and not START_TAG (position: START_TAG seen ...<licenses>\n\t\t\t<license>... @12:13) for pro
ject org.hibernate:jtidy at F:\Document\Datafile\repository\org\hibernate\jtidy\r8-20060801\jtidy-r8-20060801.pom
把原來的maven的local repository里面org\hibernate\jtidy\r8-20060801\jtidy-r8-20060801.pom目錄下的pom.xml文件中
<licenses>
<licenses>
<license>
<name>Java HTML Tidy License</name>
<url>http://svn.sourceforge.net/viewvc/*checkout*/jtidy/trunk/jtidy/LICENSE.txt?revision=95</url>
<distribution>repo</distribution>
</license>
</licenses>
</licenses>
改為:
<licenses>
<license>
<name>Java HTML Tidy License</name>
<url>http://svn.sourceforge.net/viewvc/*checkout*/jtidy/trunk/jtidy/LICENSE.txt?revision=95</url>
<distribution>repo</distribution>
</license>
</licenses>
也就是刪除多余的<licenese>起止標簽.
應該是central的repository里面的這個文件有問題。
在FreeMarker中可以使用Strust2的標簽set調用Java類的方法來獲取返回值
<@s.set name="val" value="@com.test.utils.Property@getInstance().get('Value')"/>
然后就可以使用以下的語句在FreeMarker中取得val的值
<#if (Request.appType)?default("") == "OK">
</#if>
使用方法:
var now = new Date();
now.format("m/dd/yy");
// Returns, e.g., 6/09/07
// Can also be used as a standalone function
dateFormat(now, "dddd, mmmm dS, yyyy, h:MM:ss TT");
// Saturday, June 9th, 2007, 5:46:21 PM
// You can use one of several named masks
now.format("isoDateTime");
// 2007-06-09T17:46:21
// ...Or add your own
dateFormat.masks.hammerTime = 'HH:MM! "Can\'t touch this!"';
now.format("hammerTime");
// 17:46! Can't touch this!
// When using the standalone dateFormat function,
// you can also provide the date as a string
dateFormat("Jun 9 2007", "fullDate");
// Saturday, June 9, 2007
// Note that if you don't include the mask argument,
// dateFormat.masks.default is used
now.format();
// Sat Jun 09 2007 17:46:21
// And if you don't include the date argument,
// the current date and time is used
dateFormat();
// Sat Jun 09 2007 17:46:22
// You can also skip the date argument (as long as your mask doesn't
// contain any numbers), in which case the current date/time is used
dateFormat("longTime");
// 5:46:22 PM EST
// And finally, you can convert local time to UTC time. Either pass in
// true as an additional argument (no argument skipping allowed in this case):
dateFormat(now, "longTime", true);
now.format("longTime", true);
// Both lines return, e.g., 10:46:21 PM UTC
// ...Or add the prefix "UTC:" to your mask.
now.format("UTC:h:MM:ss TT Z");
// 10:46:21 PM UTC
/*
* Date Format 1.2.2
* (c) 2007-2008 Steven Levithan <stevenlevithan.com>
* MIT license
* Includes enhancements by Scott Trenda <scott.trenda.net> and Kris Kowal <cixar.com/~kris.kowal/>
*
* Accepts a date, a mask, or a date and a mask.
* Returns a formatted version of the given date.
* The date defaults to the current date/time.
* The mask defaults to dateFormat.masks.default.
*/
var dateFormat = function () {
var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
timezoneClip = /[^-+\dA-Z]/g,
pad = function (val, len) {
val = String(val);
len = len || 2;
while (val.length < len) val = "0" + val;
return val;
};
// Regexes and supporting functions are cached through closure
return function (date, mask, utc) {
var dF = dateFormat;
// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
if (arguments.length == 1 && (typeof date == "string" || date instanceof String) && !/\d/.test(date)) {
mask = date;
date = undefined;
}
// Passing date through Date applies Date.parse, if necessary
date = date ? new Date(date) : new Date();
if (isNaN(date)) throw new SyntaxError("invalid date");
mask = String(dF.masks[mask] || mask || dF.masks["default"]);
// Allow setting the utc argument via the mask
if (mask.slice(0, 4) == "UTC:") {
mask = mask.slice(4);
utc = true;
}
var _ = utc ? "getUTC" : "get",
d = date[_ + "Date"](),
D = date[_ + "Day"](),
m = date[_ + "Month"](),
y = date[_ + "FullYear"](),
H = date[_ + "Hours"](),
M = date[_ + "Minutes"](),
s = date[_ + "Seconds"](),
L = date[_ + "Milliseconds"](),
o = utc ? 0 : date.getTimezoneOffset(),
flags = {
d: d,
dd: pad(d),
ddd: dF.i18n.dayNames[D],
dddd: dF.i18n.dayNames[D + 7],
m: m + 1,
mm: pad(m + 1),
mmm: dF.i18n.monthNames[m],
mmmm: dF.i18n.monthNames[m + 12],
yy: String(y).slice(2),
yyyy: y,
h: H % 12 || 12,
hh: pad(H % 12 || 12),
H: H,
HH: pad(H),
M: M,
MM: pad(M),
s: s,
ss: pad(s),
l: pad(L, 3),
L: pad(L > 99 ? Math.round(L / 10) : L),
t: H < 12 ? "a" : "p",
tt: H < 12 ? "am" : "pm",
T: H < 12 ? "A" : "P",
TT: H < 12 ? "AM" : "PM",
Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
};
return mask.replace(token, function ($0) {
return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
});
};
}();
// Some common format strings
dateFormat.masks = {
"default": "ddd mmm dd yyyy HH:MM:ss",
shortDate: "m/d/yy",
mediumDate: "mmm d, yyyy",
longDate: "mmmm d, yyyy",
fullDate: "dddd, mmmm d, yyyy",
shortTime: "h:MM TT",
mediumTime: "h:MM:ss TT",
longTime: "h:MM:ss TT Z",
isoDate: "yyyy-mm-dd",
isoTime: "HH:MM:ss",
isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};
// Internationalization strings
dateFormat.i18n = {
dayNames: [
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
],
monthNames: [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
]
};
// For convenience
Date.prototype.format = function (mask, utc) {
return dateFormat(this, mask, utc);
};
補充說明:
Mask |
Description |
d |
Day of the month as digits; no leading zero for single-digit days. |
dd |
Day of the month as digits; leading zero for single-digit days. |
ddd |
Day of the week as a three-letter abbreviation. |
dddd |
Day of the week as its full name. |
m |
Month as digits; no leading zero for single-digit months. |
mm |
Month as digits; leading zero for single-digit months. |
mmm |
Month as a three-letter abbreviation. |
mmmm |
Month as its full name. |
yy |
Year as last two digits; leading zero for years less than 10. |
yyyy |
Year represented by four digits. |
h |
Hours; no leading zero for single-digit hours (12-hour clock). |
hh |
Hours; leading zero for single-digit hours (12-hour clock). |
H |
Hours; no leading zero for single-digit hours (24-hour clock). |
HH |
Hours; leading zero for single-digit hours (24-hour clock). |
M |
Minutes; no leading zero for single-digit minutes.
Uppercase M unlike CF timeFormat 's m to avoid conflict with months. |
MM |
Minutes; leading zero for single-digit minutes.
Uppercase MM unlike CF timeFormat 's mm to avoid conflict with months. |
s |
Seconds; no leading zero for single-digit seconds. |
ss |
Seconds; leading zero for single-digit seconds. |
l or L |
Milliseconds. l gives 3 digits. L gives 2 digits. |
t |
Lowercase, single-character time marker string: a or p.
No equivalent in CF. |
tt |
Lowercase, two-character time marker string: am or pm.
No equivalent in CF. |
T |
Uppercase, single-character time marker string: A or P.
Uppercase T unlike CF's t to allow for user-specified casing. |
TT |
Uppercase, two-character time marker string: AM or PM.
Uppercase TT unlike CF's tt to allow for user-specified casing. |
Z |
US timezone abbreviation, e.g. EST or MDT. With non-US timezones or in the Opera browser, the GMT/UTC offset is returned, e.g. GMT-0500
No equivalent in CF. |
o |
GMT/UTC timezone offset, e.g. -0500 or +0230.
No equivalent in CF. |
S |
The date's ordinal suffix (st, nd, rd, or th). Works well with d .
No equivalent in CF. |
'…' or "…" |
Literal character sequence. Surrounding quotes are removed.
No equivalent in CF. |
UTC: |
Must
be the first four characters of the mask. Converts the date from local
time to UTC/GMT/Zulu time before applying the mask. The "UTC:" prefix
is removed.
No equivalent in CF. |
And here are the named masks provided by default (you can easily change these or add your own):
Name |
Mask |
Example |
default |
ddd mmm dd yyyy HH:MM:ss |
Sat Jun 09 2007 17:46:21 |
shortDate |
m/d/yy |
6/9/07 |
mediumDate |
mmm d, yyyy |
Jun 9, 2007 |
longDate |
mmmm d, yyyy |
June 9, 2007 |
fullDate |
dddd, mmmm d, yyyy |
Saturday, June 9, 2007 |
shortTime |
h:MM TT |
5:46 PM |
mediumTime |
h:MM:ss TT |
5:46:21 PM |
longTime |
h:MM:ss TT Z |
5:46:21 PM EST |
isoDate |
yyyy-mm-dd |
2007-06-09 |
isoTime |
HH:MM:ss |
17:46:21 |
isoDateTime |
yyyy-mm-dd'T'HH:MM:ss |
2007-06-09T17:46:21 |
isoUtcDateTime |
UTC:yyyy-mm-dd'T'HH:MM:ss'Z' |
2007-06-09T22:46:21Z |
A couple issues:
- In the unlikely event that there is ambiguity in the meaning of your mask (e.g.,
m followed by mm ,
with no separating characters), put a pair of empty quotes between your
metasequences. The quotes will be removed automatically.
- If you need to include literal quotes in your mask, the following rules apply:
- Unpaired quotes do not need special handling.
- To
include literal quotes inside masks which contain any other quote marks
of the same type, you need to enclose them with the alternative quote
type (i.e., double quotes for single quotes, and vice versa). E.g.,
date.format('h "o\'clock, y\'all!"')
returns "6 o'clock, y'all". This can get a little hairy, perhaps, but I
doubt people will really run into it that often. The previous example
can also be written as date.format("h") + "o'clock, y'all!" .
通過上兩篇文章的研究,
詳見:
我的struts2項目性能調優三步曲:http://www.aygfsteel.com/usherlight/archive/2008/07/01/211869.html
我的struts2項目性能調優三步曲(續):http://www.aygfsteel.com/usherlight/archive/2008/07/12/214462.html
得出的結論是:影響Struts2性能的原因在于Ognl的Value Stack的性能不佳。那么如果解決呢:
* 我首先嘗試使用JSF。
一開始選擇JSF的原因主要是:
1、Stuts2自己提供了JSF的Plugin
2、JSF是Sun作為標準提出,而且已經通過的。從Google的趨勢搜索上也可以看出,搜索JSF的人在增多。
3、JSF作為一種以組件為基礎的Web Framework有其獨到之處,其內建的和其他許多開源的組件使用起來相當方便、強大。當然,對于不同的應用來說也有不利之處(后面會提到),但是如果能夠堅持長期使用,逐漸積累組件庫的話,JSF是一個很好的選擇。
4、JSF的文檔(或者說是書籍)還是比較多的。
經過測試使用后,發現其性能與Struts2相比確實提升不少。但是后來遇到了一個問題,所以最后還是放棄了JSF。這個問題是關于JSF的DataTable的,JSF提供的DataTable其實使用起來很方便,可定制化程度也不錯,只是剛好缺少了我所希望的功能(也可能是我不知道如何實現)。我的應用中的DataTable是一個動態的結果集,也就是說輸出的列是不能預先確定的,而DataTable卻要求先聲明好所有的DataColumn,我不知道如何解決這個問題。所以最后放棄了JSF。
* 我的第二個選擇是FreeMarker
選擇FreeMarker的原因是:
1、FreeMarker是Struts2缺少的模板引擎,Struts2的標簽大部分是使用FreeMarker的,使用FreeMarker的話,連Plugin都省去了。
2、FreeMarker相對比較輕量級、因為他本身只是一個模板引擎,與JSF這樣一個大而全的WebFramework相對,輕巧多了。
3、FreeMarker的學習起來非常容易,只要把他網站上的Document過一遍,基本上就OK了。
4、FreeMarker雖然體積小,功能還是相當強的,I18N,Converter之類的東西基本都全了,至少我所需要的功能全有。
5、FreeMarker相當靈活,他不象JSF把底層的東西封裝了以后,暴露出一些屬性可以設置,如果你需要的屬性不能設置,你就沒有辦法了。在FreeMarker你直接操作最底層的東西,擁有很大的靈活性。當然,犧牲了一些方便性,比如,要用FreeMarker生成一個下拉框,就需要較多的工作量了。
測試之后,使用FreeMarker的性能很不錯,在大數據量操作的情況下,至少一個數量級的性能提升。
主要原因是freeMarker的值直接從action中取得的,所以避開了ognl的stack value.
* 我的最終結論,如果要在Struts2中,展示或者操作大量數據,強烈推薦使用FreeMarker。
原文鏈接:http://www.theserverside.com/news/thread.tss?thread_id=50360
Jt2.7已經發布了,Jt是一個針對Java應用快速實現的面向模式的框架。Jt已經在數個重要的大型系統被采用。Jt實現了很多個被熟知的模式,包括:Dao,GoF的設計模式以及一些J2EE的模式。
Jt2.7的部分部件的功能得到了增強,而且增加了一個Jt自動化向導。Jt向導一個在Jt框架上建起來的應用,能夠自動生成應用的框架結構。Jt向導能夠在設計模式(包括Jt消息、DAO,MVC,GoF)的基礎上自動生成應用模塊。目前Jt向導實現了與MVC Struts和DAO Hibernate的集成。DAO的映射文件,Struts的配置文件,視圖(JSPs),Java類都能夠使用Jt向導來自動生成。
具體的功能包括:
* 實現了J2EE設計模式,包括J2EE business delegate,J2EE Session Facade,J2EE Service Locator和J2EE Value Object。
* 通過實現Web Service適配器和代理集成Web Service。Jt messaging API極大地簡化了web service的開發和部署。
* 與business process modeling(BPM)的集成。Jt框架提供了一個jBPM的適配器。jBPM是一個BPM技術的開源實現。Jt應用現在可以使用流程圖來模塊化,這是一個非常好的模塊化業務流程的方法。
* 與MVC設計模式和Ajax的集成。統一化的Jt模塊和適配器提供了Jt框架API和上述兩種技術間的透明接口。業務邏輯可以由Jt框架模塊或BPM業務流程來實現。
* 與Hibernate的集成。Jt適配器提供了與Hibernate的透明接口。
* 與JDBC的集成
* 通過實現命令模式,支持Log,排隊機制和操作的回退。
* 與JavaMail的集成
* 與EJB的集成。EJB客戶端可以透明地存取遠程框架對象。比EJB開發要簡單的多。另外還實現了J2EE Service Locator模式。
* 方便的定制應用。主要通過配置文件完成:對象的屬性可以從資源文件中加載。
* 與JSP的集成
* 通過XML適配器、助手、和內建的bean/XML映射與XML API的集成
Jt的在線文檔: http://jt.dev.java.net/servlets/ProjectDocumentList
其他的一些信息可以在這里找到:http://jt.dev.java.net
前一陣子使用了DHtmlx的Tree,視覺效果不錯,功能也不弱。具體參見: http://dhtmlx.com
現在把Struts2結合DHtmlxTree的經驗心得整理一下,發表出來:
一、Struts.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="demo" extends="struts-default">
<action name="menu" method="execute" class="demo.TreeMenuAction">
<result>/WEB-INF/menu.jsp</result>
</action>
</package>
</struts>
二、tree.jsp
<%@ taglib prefix="s" uri="/struts-tags"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path;
%>
<html>
<head>
<title>Main Page</title>
<!-- 注意此處的路徑需要根據各自具體情況修改 -->
<link rel="STYLESHEET" type="text/css" href="styles/dhtmlxtree.css">
<script src="scripts/dhtmlxcommon.js"></script>
<script src="scripts/dhtmlxtree.js"></script>
</head>
<body onload="loadTree(); " style="padding: 0; margin: 0; overflow: hidden; height: 100%;">
<script>
String.prototype._dhx_trim = function(){
return this.replace(/ /g," ").replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g,"");
}
/* init tree */
var tree;
function loadTree(){
tree=new dhtmlXTreeObject("doctree_box","100%","100%",0);
tree.setImagePath("images/"); <!-- 注意此處的路徑需要根據各自具體情況修改 -->
tree.setOnClickHandler(
function(id){ openPathDocs(id); }
);
tree.loadXML("<%=basePath%>/menu.do");
}
/* open path funtion */

function openPathDocs(id){
if (tree.getUserData(id, "thisurl") != null ){
window.frames.sampleframe.location.href = "<%=path%>/" + tree.getUserData(id, "thisurl") + ".do";
return;
}
}
function autoselectNode(){
tree.selectItem(node,true);
tree.openItem(node);
}
</script>
<table width="100%" height="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td valign="top" width="276">
<div id="doctree_box" style="width: 274px; height: 100%;"></div>
</td>
<td width="10" background="images/grid.gif">
</td>
<td align="right">
<iframe id="sampleframe" name="sampleframe" width="100%" height="99%" frameborder="0" src="blank.html" style="border: 0px solid #cecece;"></iframe>
</td>
</tr>
</table>
</body>
</html>

上面的JavaScript基本上是從dhtmlx的例子中修改而來,理解起來并不復雜,只有
String.prototype._dhx_trim = function(){
return this.replace(/ /g," ").replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g,"");
}
這一段代碼含義不明。
三、Action
package demo;
 public class TreeMenuAction {
private String menuString;
 public String execute() {
StringBuffer buf = new StringBuffer();
buf.append("<tree id=\"0\">");
buf.append(" <item text=\"Java\">");
buf.append(" <item text=\"Thinking in java\">");
buf.append(" <userdata name=\"thisurl\">java_tij.do</userdata>");
buf.append(" </item>");
buf.append(" <item text=\"Head first design pattern\">");
buf.append(" <userdata name=\"thisurl\">java_hfdp.do</userdata>");
buf.append(" </item>");
buf.append(" </item>");
buf.append(" <item text=\"Fiction\">");
buf.append(" <item text=\"Harry Porter\">");
buf.append(" <userdata name=\"thisurl\">fiction_hp.do</userdata>");
buf.append(" </item>");
buf.append(" <item text=\"Oliver Twist\">");
buf.append(" <userdata name=\"thisurl\">fiction_ot.do</userdata>");
buf.append(" </item>");
buf.append(" </item>");
buf.append("</tree>");
menuString = buf.toString();
return "success";
}
 public String getMenuString() {
return menuString;
}
 public void setMenuString(String menuString) {
this.menuString = menuString;
}
}

四、menu.jsp
<%@ page contentType="text/xml;charset=UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
< s:property value="menuXmlString" escape="false"/>
過程是這樣的:首先在瀏覽器地址欄中輸入:http://......./tree.jsp
展示tree.jsp,在load函數中調用menu.do
menu.do對應TreeMenuAction,返回menu.jsp,而menu.jsp只包含menuString的值,注意在menu.jsp中的escape="false"
最近,繼續研究了Struts2性能的調優方法,總結了一下,得出新三步曲:
4. 使用FreeMarker的最新版本2.3.13,因為在版本2.3.11中,FreeMarker針對性能進行了改進,以下是FreeMarker2.3.11的release notes:
2.3.11
Date of release: 2007-12-04
This release contains several performance and usability improvements.
5. ognl2.7
所稱ognl2.7相對于2.6在性能上有了“顯著”的提升,于是下載了2.7以及2.7所需要的javassist-3.8.0.GA.jar
其實,經過上面2個步驟,我并沒有發現應用的性能有顯著的改善,可能我的頁面中從ValueStack中的存取操作并不是特別多,也不是特別的復雜,所以,Ognl對我的影響并不明顯。
6. 最后使用了JProfiler對Tomcat進行了監控,最后發現問題在自定義模板上,我將頁面的自定義模板全部刪除,果然頁面的響應速度有了較大的提升。
前一段時間有反映說是一個使用了struts2的生產系統的頁面顯示速度太慢。登錄后發現確實如此,于是進行了一番性能調優的研究和測試。
一,根據struts2官方的性能調優說明進行了一些參數的修改。
http://struts.apache.org/2.x/docs/performance-tuning.html
http://cwiki.apache.org/WW/performance-tuning.html
Turn off logging and devMode.(關閉logging和Devmode)
這個當然沒問題,但是全部關閉logging不現實,我只是關閉了struts2相關package的logging
Do not use interceptors you do not need.
把struts.xml中不需要的interceptor統統刪除
Use the correct HTTP headers (Cache-Control & Expires).
不確定應該如何修改
Copy the static content from the Struts 2 jar when using the Ajax theme (Dojo) or the Calendar tag.
關于這點,后面會提到
Create a freemarker.properties file in your WEB-INF/classes directory.
照做
Create the freemarker.properties file and add the following setting (or whatever value you deem fitting):
template_update_delay=60000
照做
Enable Freemarker template caching
As of Struts 2.0.10, setting the property struts.freemarker.templatesCache to true will enable the Struts internal caching of Freemarker templates. This property is set to false by default.
照做
進行上述修改后,發現頁面打開的速度并沒有明顯的提高.
二,此時我已經基本鎖定網頁打開速度慢的原因與ajax(或者說是dojo)有關。因為dojo的js庫大概有450K左右,先嘗試使用gzip壓縮javascript,減小傳輸量,看能否加快頁面的加載速度
在Tomcat的server.xml的connector中添加如下配置,激活gzip功能
compression="on"
compressionMinSize="2048"
noCompressionUserAgents="gozilla, traviata"
compressableMimeType="text/html,text/xml,text/javascript,application/x-javascript,application/javascript"
進行上述修改后,發現頁面打開的速度還是沒有明顯的提高.
三,經過上述兩個實驗,覺得應該是struts2所封閉的dojo的性能問題了。于是引入JQuery.
JQuery的js文件最小是55K, gzip后應該更小,頁面的響應速度明顯改善(一個數量級以上的提高),主要原因在于與服務器交互的處理上極大地提升了效率。而且頁面處理代碼更加簡潔明了。
最后,我刪除了所有的<s:head theme="ajax"/>和 <s:head/>(如果頁面中加入<s:head />,那么在Struts2生成的html中后包含dojo.js),使用JQuery來完成所有的Ajax和javascript功能。
在利用網頁展示查詢結果,經常會遇到要求導出成Excel的需求。采用這種方法可以定制輸出的格式和內容(還不支持合并單元格和公式),生成真正的Excel格式(不是csv)的Excel。
一、struts.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.i18n.encoding" value="UTF-8"/>
<package name="demo" extends="struts-default">
<action name="excel" method="execute" class="demo.ExcelAction">
<result name="excel" type="stream">
<param name="contentType">application/vnd.ms-excel</param> <!-- 注意這里的ContentType -->
<param name="inputName">excelStream</param> <!-- 這里需要和Action里的變量名一致 -->
<param name="contentDisposition">filename="standard.xls"</param>
<param name="bufferSize">1024</param>
</result>
</action>
</package>
</struts>
二、Struts2的 Action
package demo;
public class ExcelAction {
private InputStream excelStream; // 需要生成getter和setter
public String execute() throws Exception {
StringBuffer excelBuf = new StringBuffer();
excelBuf.append("BookName").append("\t").append("Year").append("\t").append("author").append("\n");
excelBuf.append("Thinking in Java").append("\t").append("2001").append("\t").append("Eckel").append("\n");
excelBuf.append("Spring in action").append("\t").append("2005").append("\t").append("Rod").append("\n");
String excelString = excelBuf.toString();
logger.debug("result excel String: " + excelString);
excelStream = new ByteArrayInputStream(excelString.getBytes(), 0, excelString.length());
return "excel";
}
// getter and setter
...
}
三、Jsp頁面
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<s:head />
</head>
<body>
<s:form action="" method="post">
<s:submit key="button.submit"/>
</s:form>
</body>
</html>
iBatis自己帶了一個simple的數據庫連接池,基本的功能都有。但是在處理部分數據庫(比如mysql)的連接空閑時間太長(mysql是8小時)自動超時的時候,就比不上象c3p0這樣的連接池軟件了(c3p0能自動處理數據庫連接被關閉的情況)。
我目前采用的方法是iBatis本身提供的一種算得上是取巧的辦法,基本思想就是每隔一段時間往數據庫發一條查詢語句,這樣使得數據庫空閑時間不會太長,而使得其自動關閉。
方法是在SqlMapConfig.xml的dataSource進行如下配置:
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="${jdbc.driverClassName}"/>
<property name="JDBC.ConnectionURL" value="${jdbc.url}"/>
<property name="JDBC.Username" value="${jdbc.username}"/>
<property name="JDBC.Password" value="${jdbc.password}"/>
<property name="Pool.PingEnabled" value="true"/>
<property name="Pool.PingQuery" value="select 1"/>
<property name="Pool.PingConnectionsOlderThan" value="0"/>
<property name="Pool.PingConnectionsNotUsedFor" value="3600000"/>
</dataSource>
開始的3行是關于數據庫連接信息的,不需要說明了。
Pool.PingEnabled:是用于設置開啟是否允許檢測連接狀態
Pool.PingQuery:是用于檢測連接的查詢語名,當然是越簡單越好
Pool.PingConnectionOlderThan:對持續連接時間超過設定值(毫秒)的連接進行檢測,我將其設置為0(不進行此項檢測),否則,iBatis在超過這個時間后,執行每個sql以前檢測連接,對于性能可能會有一定的影響。
Pool.PingConnectionsNotUsedFor:對空閑超過設定值(毫秒)的連接進行檢測,我設置為1小時(mysql缺省的關閉時間是8小時)
當然,還有一個辦法是使用c3p0這樣的連接池
但是需要自己寫一部分代碼,實現以下接口:
public interface DataSourceFactory {
public void initialize(Map map);
public DataSource getDataSource();
}
|