2009年6月25日 星期四

[Struts2]整合Open Flash Chart 2

基本上對於使用strust2的人來說
這是一個非常好上手的例子

Open Flash Chart 2官網download檔案後
java-stuts-2這個資料夾裡就是一個整合在struts2的範例

三支java檔
Bar.java是action
Element.java/Title.java是chart element

struts.xml
<package name="json-chart" namespace="/json-chart" extends="json-default">
<action name="bar" class="action.Bar">
<result type="json"/>
</action>
</package>
由於ofc2返回的數據都是json格式
所以必須加入json plugin
這裡也只有extends="json-default"result type="json"比較不一樣

index.jsp
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<script type="text/javascript" src="<s:url value="/js/swfobject.js"/>"></script>
<script type="text/javascript">
swfobject.embedSWF("<s:url value="/open-flash-chart.swf"/>"
, "my_chart", "1024", "300", "9.0.0"
, "expressInstall.swf"
, {"data-file":"<s:url value="/json-chart/bar.action"/>"});
</script>
</head>
<body>
<div id="my_chart"></div>
</body>
</html>
data-file是透過bar.action在server端產生的

這裡別忘了加入swfobject.js
並且把open-flash-chart.swf放到根目錄

就可以很順利的看到圖了!

[Struts2]<s:url>的includeParams屬性

<s:url>的includeParams屬性共有三種值可設定
none, get, all
default為none(版本Struts 2.1.3之後)

none是不帶任何參數
get是帶著HTTP-GET所帶著的參數
all則是帶著HTTP-GET/HTTP-POST所帶著的參數

要預設includeParams為none的話
可以在struts.properties文件中設置
struts.url.includeParams=none

[Struts2]多個默認值的下拉選單

官網的Example

<s:select label="Pets"
name="petIds"
list="petDao.pets"
listKey="id"
listValue="name"
multiple="true"
size="3"
required="true"
value="%{petDao.pets.{id}}"
/>

%{petDao.pets.{id}}的值有出現在listKey的id裡
就會被auto-selected

[jQuery]元素名為變數時的選取

一般來講jQuery的id selector的形式為
$("#elementID");//取得id為elementID的元素

不過當id為變數時 就得改成
$("#"+variableElementID)

2009年6月22日 星期一

[Struts2]interceptor

Strust2 default interceptor
<default-interceptor-ref name="defaultStack"/>
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="profiling"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>

如果有多個自定義的interceptor
使用interceptor stack的形式來處理比較方便
<interceptors>
<interceptor-stack name="myStack">
<interceptor-ref name="i18n" />
<interceptor-ref name="params" />
<interceptor-ref name="static-params" />
<interceptor-ref name="LoginInterceptor" /> <!--自定義的-->
</interceptor-stack>
<interceptor name ="LoginInterceptor" class ="myClass" />
</interceptors>

直接引用interceptor stack就不用一個一個加了
<action name="myAction" class="myClass">
<interceptor-ref name="myStack"/>
...
</action>

[Quartz]JobListener, TriggerListener

透過JobListener, TriggerListener
可以監控job和trigger的運作情況

JobListener code
public class JobMonitor implements JobListener {
static Logger logger = Logger.getLogger(JobMonitor.class);

public String getName() {
return getClass().getSimpleName();
}

public void jobToBeExecuted(JobExecutionContext context) {
String sJobName = context.getJobDetail().getName();
LogUtil.logDebug(logger, "The job "+sJobName+" will be execute!");
}

public void jobExecutionVetoed(JobExecutionContext context) {
String sJobName = context.getJobDetail().getName();
LogUtil.logDebug(logger, "The job "+sJobName+" was Vetoed by trigger!");
}

public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
String sJobName = context.getJobDetail().getName();
LogUtil.logDebug(logger, "The job "+sJobName+" was executed!");
}
}

TriggerListener code
public class TriggerMonitor implements TriggerListener {
static Logger logger = Logger.getLogger(TriggerMonitor.class);

public String getName() {
return getClass().getSimpleName();
}

public void triggerFired(Trigger trigger, JobExecutionContext context) {
String sTriggerName = trigger.getName();
LogUtil.logDebug(logger, sTriggerName + " was fired");
}

public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
String sTriggerName = trigger.getName();
LogUtil.logDebug(logger, sTriggerName + " was not vetoed");
return false;
}

public void triggerMisfired(Trigger trigger) {
String sTriggerName = trigger.getName();
LogUtil.logDebug(logger, sTriggerName + " misfired");
}

public void triggerComplete(Trigger trigger, JobExecutionContext context, int triggerInstructionCode) {
String sTriggerName = trigger.getName();
LogUtil.logDebug(logger, sTriggerName + " is complete");
}
}

然後在scheduler加入這兩個listener
JobListener jobMonitor = new JobMonitor();
scheduler.addGlobalJobListener(jobMonitor);
TriggerListener triggerMonitor = new TriggerMonitor();
scheduler.addGlobalTriggerListener(triggerMonitor);
實際跑了一下
監看的流程如下
TestQjobTrigger was fired
->TestQjobTrigger was not vetoed
->The job TestQjob will be execute!
->The job TestQjob was executed!
->TestQjobTrigger is complete

[Quartz]interrupt job

如果想中斷執行中的job
在Quartz官網的FAQ有提到解決的辦法
Q:How do I stop a Job that is currently executing?
A:See the org.quartz.InterruptableJob interface
, and the
Scheduler.interrupt(String, String) method.

透過Scheduler.interrupt(String jobName, String groupName)
則Scheduler會調用job的interrupt method

這裡有個前提
job必須implements InterruptableJob
否則會出現下面兩行的錯誤訊息
org.quartz.UnableToInterruptJobException: Job 'TestQjob' of group 'TestJob,
can not be interrupted, since it does not implement org.quartz.InterruptableJob

2009年6月21日 星期日

[prototype]AJAX物件

Ajax
屬性:
activeRequestCount
函式:
getTransport()

Ajax.Responders
這是個Gobal物件,故無論在程式中使用了多少個Ajax.Request或是Ajax.Updater物件,都可以用同一份回應函式來處理結果。
函式:
register():註冊新的回應函式
unregister():取消已註冊的回應函式。

Ajax.Base
屬性:
method
parameters
asynchronous
requestHeader
postBody
onxxxxxxx:onUninitialization, onLoading, onLoaded, onInteractive, onComplete
onSuccess,onFailure
onException
函式:
setOptions()
responseIsSuccess()
responseIsFailure()

Ajax.Request
屬性:
transport
url
函式:
建構子(url, options)

Ajax.Update
屬性:
evalScripts:設為True時,Ajax.Updater物件會自動執行任保回傳內容中script區塊
函式:
建構子(container,url,options)

Ajax.PeriodicalUpdater
函式:
建構子(container,url,options)
start()
stop()















參考書目:Ajax網頁程式設計 Google成功背後的技術

[AJAX]XMLHttpRequest

依據不同的瀏覽器,取得 XMLHttpRequest 物件
function createAJAX() {
if (window.ActiveXObject) { //IE
try {
return new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
return new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
return null;
}
}
} else if (window.XMLHttpRequest) { //FF,safari
return new XMLHttpRequest();
} else {
return null;
}
}

XMLHttpRequest的函式
void open(string method, string url, boolean asynch, string username, string password)
開啟對伺服端的連結;method為請求方式(GET、POST);url是目標URL;asynch為非同步設定,預設是true。
void send(content)
對伺服端傳送請求,content以放XML、輸入串流、字串、JSON格式的內容,放進去會放在POST本體中發送。
void setRequestHeader(string header, string value)
為HTTP請求設定一個給定的 header 設定值。
void abort()
用來中斷請求。
string getAllResponseHeaders()
傳回一個字串,其中包含HTTP請求的所有回應標頭。
string getResponseHeader(string header)
傳回一個字串,其中包含指定的回應標頭值。

XMLHttpRequest 的屬性
onreadystatechange
readyState每次改變時,都會呼叫onreadystatechange所參考的函式。

readyState

會有0到4的數值,分別表示不同的請求狀態:
0 = 未初始化的連線(uninitialized),還沒呼叫open()
1 = 載入中(loading),呼叫open(),還沒呼叫send()
2 = 已載入(loaded),呼叫send(),請求header/status準備好
3 = 互動中(interactive),正在與伺服器互動中
4 = 請求完成(completed),完成請求

responseText

伺服器傳來的請求回應文字,會設定給這個屬性。

responseXML

伺服器傳來的請求回應如果是XML,會成為DOM設定給這個屬性。

status
伺服器回應的狀態碼,例如200是OK,404為Not Found

statusText
伺服器回應的狀態文字。

參考書目:《Ajax網頁程式設計》旗標出版社

2009年6月19日 星期五

[SQL]序列

建立序列
SQL>CREATE SEQUENCE SEQ_NAME
MINVALUE 1 最小值
MAXVALUE 99999999 最大值
INCREMENT BY 1 遞增或遞減值
NOCYCLE 是否循環
CACHE 200;

查詢序列下一個值
SELECT SEQ_NAME.nextval FROM DUAL

查詢目前序列號
SELECT SEQ_NAME.currval FROM DUAL

查詢所有序列
SELECT* FROM USER_SEQUENCES

刪除序列
DROP SEQUENCE SEQ_NAME

2009年6月18日 星期四

[CSS]IE/FireFox 對width屬性的解讀

.link {
padding:5px
width: 100px;
}

IE的解讀是寬度為100px,width的100px包含padding的5px
FF的解讀是寬度為105px,width+padding

下面是三種兼容的寫法
1.
.link {
padding:5px
width: 95px !important;
width: 100px;
}
在FF要用的width後面加上!important(FF認識這個字-"提升優先權")
而且要放在另一個width前面
因為有重複的屬性IE只抓最後一個

2.
link {
padding:5px
width/**/: 95px;
width: 100px;
}
FF要用的加上/**/
IE看到/**/只會把那行註釋掉

3.
.link {
padding:5px
width: 100px; > width: 95px;
}
IE認識 '>'
所以會用比較大的那個

[quartz]cron expression

0 0/5 * * * ?

從左到右以空白格隔後有六個值
分別代表著
1.Seconds (0-59)
2.Minutes (0-59)
3.Hours (0-23)
4.Day-of-Month ( 0-31,但是要注意的是每月的天數有所不同)
5.Month (0-11或是用 JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC)
6.Day-of-Week (1-7或是 SUN, MON, TUE, WED, THU, FRI and SAT)
7.Year (optional field)

而特殊符號代表的意思如下
/ -> Minutes 設成 '0/15' ,表示從0分鐘開始,每15分鐘執行一次,如果設成 '3/20',則表示從3分鐘開始,每20分鐘執行一次(也就是說 3,23,43分的時候)
* ->所有的,放在月份,表示每個月都要,1,2,3同理
? ->可以用在 day-of-month 和 day-of-week 的設定,表示沒有去特別限定
# ->The '#' is used to specify "the nth" XXX. For example, day-of-week 如果設為 "6#3" 或是 "FRI#3" ,則表示這個月的第三個星期五
L -> 表示最後一個,如果 day-of-month 設成L ,一月就表示 31號,二月指的就是28或29號;若day-of-week 設成L,則表示星期六或是一個禮拜的最後一天.另外,若設成 "6L" 或是 "FRIL",就表示這個月的最後一個星期五
W -> 表示最接近的WeekDay(星期一到星期五),For example,如果 day-of-month 設成15W,則表示這個月中最接近15號那天的WeekDay

2009年6月17日 星期三

ROME

ROME是個可以產生,分析RSS的開源的java tool
我除了下載ROME還下載了ROME Fetcher
這樣多了一些函式可以用比較方便

抓取RSS
FeedFetcher feedFetcher = new HttpURLFeedFetcher();
SyndFeed feed = feedFetcher.retrieveFeed(new URL(feedUrl));//feedUrl是RSS的來源

分析RSS
第一種寫法
for (java.util.Iterator iter = feed.getEntries().iterator(); iter.hasNext(); ) {
SyndEntry entry = (SyndEntry) iter.next();
System.out.println(entry.getLink() + entry.getTitle() + entry.getPublishedDate());
}

第二種寫法
List entryList = feed.getEntries();
for(int i=0;i<entryList.size();i++)
{
entry = (SyndEntry) entryList.get(i);
System.out.println(entry.getLink() + entry.getTitle() + entry.getPublishedDate());
}
差異在使用不同的物件類型而已

ROME的使用也很直覺
getLink()就是取得RSS裡<link>內的值
getTitle()也是同樣的道理

大部分網站發佈的格式都差不多
有不一樣的地方的話大概是在<pubDate>的時間格式
ROME已經將大部分的時間格式都加進去了
如果發生因為時間格式不符合而產生錯誤的話
可以增加一個rome.properties在classpath底下
內容加入datetime.extra.masks=EEE dd MMM yyyy HH:mm:ss z
這裡的日期格式就是不符合內建的時間格式
有多個話就用分隔線(|)隔開

整合JCaptcha

JCaptcha是個用來產生圖形驗證碼的開源組件




Captcha class
主要是字型,出現的字元,驗證碼的字數等初始化設定
public class Captcha extends ListImageCaptchaEngine
{
protected void buildInitialFactories() {

WordGenerator wgen = new RandomWordGenerator("abcdefghijklmnopqrstuvwxyz123456789");
RandomRangeColorGenerator cgen = new RandomRangeColorGenerator(
new int[] { 0, 100 }, new int[] { 0, 100 },
new int[] { 0, 100 });

TextPaster textPaster = new RandomTextPaster(new Integer(4),
new Integer(4), cgen, true);

BackgroundGenerator backgroundGenerator = new FunkyBackgroundGenerator(
new Integer(150), new Integer(50));

Font[] fontsList = new Font[] { new Font("Arial", 0, 10),
new Font("Tahoma", 0, 10), new Font("Verdana", 0, 10), };

FontGenerator fontGenerator = new RandomFontGenerator(new Integer(30),
new Integer(30), fontsList);

WordToImage wordToImage = new ComposedWordToImage(fontGenerator,
backgroundGenerator, textPaster);
this.addFactory(new GimpyFactory(wgen, wordToImage));
}
}
servlet
public class DynaImageServlet extends HttpServlet {
public static class CaptchaServiceSingleton {
private static DefaultManageableImageCaptchaService instance =
new DefaultManageableImageCaptchaService(
new FastHashMapCaptchaStore(),
new Captcha(),
180,
100000,
75000);

public static ImageCaptchaService getInstance() {
return instance;
}
}

public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);}

protected void doGet(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws ServletException, IOException {

byte[] captchaChallengeAsJpeg = null;
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
try {
String captchaId = httpServletRequest.getSession().getId();
BufferedImage challenge = CaptchaServiceSingleton.getInstance().getImageChallengeForID(captchaId,httpServletRequest.getLocale());
JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream);
jpegEncoder.encode(challenge);
} catch (IllegalArgumentException e) {
httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} catch (CaptchaServiceException e) {
httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
httpServletResponse.setHeader("Cache-Control", "no-store");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.setContentType("image/jpeg");
ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
responseOutputStream.write(captchaChallengeAsJpeg);
responseOutputStream.flush();
responseOutputStream.close();
}
}
然後在web.xnl裡加入這個servlet的設定


至於比對使用者所填入的驗證碼是否正確的部份
boolean isResponseCorrect = false;
String captchaId = ServletActionContext.getRequest().getSession().getId();
String sImageNumber = dp.getAttribute("imageNumber", "");
isResponseCorrect = CaptchaServiceSingleton.getInstance().validateResponseForID(captchaId, sImageNumber);

captchaId是session的id
主要是用來比對使用者送出表單時跟產生驗證碼兩者之間的session是否一致
sImageNumber是使用者輸入的值

然後就可以藉由isResponseCorrect的boolean值得知
使用者輸入的跟產生驗證碼是否一樣了!

2009年6月16日 星期二

[dom4j]寫xml

建立document和root節點
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement("root");
doc.setRootElement(root);

在root加入一名為name的子節點,其值為william
Element Type = root.addElement("name").addText("william");

產生檔案
public void createFile(Document doc) throws Exception{
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");//加個編碼可避免掉亂碼的問題
String filename = "F://test123.xml";
FileOutputStream fos = new FileOutputStream(filename);
XMLWriter writer = new XMLWriter(fos, format);
writer.write(doc);
writer.close();
}

2009年6月15日 星期一

[dom4j]讀xml

由url取得xml
public Document readxml(URL url) throws MalformedURLException,DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(url);
return document;
}


由file取得xml
public Document readxml(URL url) throws MalformedURLException,DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(url);
return document;
}

2009年6月14日 星期日

[Struts2]<s:if>對於型態的判斷


<s:if test="itsString=='Yes'">
這樣寫沒問題
<s:if test="itsString=='Y'">
這樣寫會有java.lang.NumberFormatException: For input string: "Y" 的訊息

先撇開itsString是塞Yes還是塞Y給它(這不是重點...)
而是Struts2似乎是認為Y是Char而不是String

解決的方法是加個toString()來避開這個錯誤
<s:if test="itsString=='Y'.toString()">

2009年6月12日 星期五

[Struts2]檔案上傳

利用tag s:file和library commons-io,commons-fileupload
便可以輕鬆的運用加以完成此功能!

JSP code:
<s:file id="file" name="file">
form要加入屬性 enctype="multipart/form-data"

JAVA code:
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
如果遇到jxl.read.biff.BiffException: Unable to recognize OLE stream的Exception
可能是因為在form裡除了file外
還包含了其它像是text,textarea之類的tag element