2011年9月30日 星期五

apache tiles 2 學習筆記

Apache tiles是一個版型模組化的工具.

通常網頁至少會將版型切割為4個區塊, 例如下圖:


tiles 的定義檔如下:
<tiles-definitions>
  <definition name="indexTemplate" template="/WEB-INF/views/tiles/indexTemplate.jsp">
    <put-attribute name="title" value="Welcome" />
    <put-attribute name="header" value="/WEB-INF/views/tiles/header.jsp" />
    <put-attribute name="category" value="/WEB-INF/views/tiles/category.jsp" />
    <put-attribute name="browse" value="/WEB-INF/views/tiles/browse.jsp" />
    <put-attribute name="footer" value="/WEB-INF/views/tiles/footer.jsp" />
  </definition>
</tiles-definitions>


而頁面變更的時候, 通常都只是更換整頁中的某一個區塊, 其它區塊不需要變化.

例如登入時, 只需要將右邊的區塊變更為登入頁.


登入頁 tiles 的設定如下, 只要更換 title 及 browse 二個變數的定義即可.
<!-- Login Page -->
<definition name="login" extends="indexTemplate">
    <put-attribute name="title" value="Login Page" />
    <put-attribute name="browse" value="/WEB-INF/views/tiles/login.jsp" />
</definition>


但考慮更複雜一點的情況呢?

例如電子商務中常見的, 點擊商品目錄.

此時商品目錄會展開, 且右邊會列出該目錄下的所有商品.

示意圖如下:


這時候就得在同一個 controller 裡處理商品及目錄的邏輯.
然後分別放入不同的 tiles 變數中.

等一下下...似乎有點奇怪, 目錄跟商品雖然有關係, 但畢竟各自有不同的處理邏輯.
放在同一個 controller 內處理似乎怪怪的...

如果再回頭看一下登入頁的例子, 目錄的頁面雖然不需變動, 但此時目錄頁應呈現第一層目錄結構.

第一層目錄結構的邏輯, 跟登入有什麼關係呢 ?


是否有辦法將各區塊的內容獨立處理, 讓彼此不相干的邏輯不要混在一起呢?


還好, tiles 提供了 View Preparer 來處理這種情況.


2011年9月28日 星期三

Spring MVC 的怪事一椿

今天遇到怪事一椿, 其實已經困擾我一陣子了, 只是今天才發現原因所在.

Spring MVC 的 @RequestMapping 註解可用來標示 Url, 例如:


@RequestMapping(value={"index"}, method=RequestMethod.POST)
public ModelAndView index(HttpSession session) {
    ...
}


如果同時有二個相同的註解, spring 會報錯, 訊息如下:

2318 [main] ERROR org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping#0': Initialization of bean failed; nested exception is java.lang.IllegalStateException: Cannot map handler 'xxxxController' to URL path [/index]: There is already handler of type [class yyyyController$$EnhancerByCGLIB$$349da4f8] mapped.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:900)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:455)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:294)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:215)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4206)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4705)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1057)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
at org.apache.catalina.core.StandardService.start(StandardService.java:525)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:754)
at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)


但有一個例外, 就是 / , 一般看到網站的 / 會參考 tomcat 的 web.xml中 welcome-file 的設定.

若是下面的寫法, spring 不會報錯, welcome-file也不會生效, 但會連到那個 URL 就不知道了...


@RequestMapping(value={"/", "index"})
public ModelAndView index(HttpSession session) {
...
}


@RequestMapping(value={"/", "login"})
public ModelAndView login(HttpSession session) {
...
}

2011年9月20日 星期二

動態載入圖示 Animated Logo

今天才知道, 原來常見的動態載入小圖示, 如圖:

Throbber





有個名字, 叫做 Throbber

還有網站可以幫忙製作呢.


搭配 jQuery 的 BlockUI Plugin
可以很快做出不錯的效果.

範例如下:

$.blockUI({ message: '<h3><img src="/images/ajax-loader.gif" /> Please wait...</h3>' });


實際操作的時候, 發現圖常常出不來, 應是圖片載入過慢, 需將圖檔做預載(pre-load)處理, 作法如下:

$(document).ready(function() {
throbber = new Image() ;
throbber.src = "/images/ajax-loader.gif" ;
}) ;




2011年9月5日 星期一

tomcat jsessionid url problem

Spring MVC 可以 cache static content, 例如圖檔, js, css...

參考文件

但我之前遇到一個問題, 就是原本應顯示為 /appbased/static-1.0.0/images/xxx 的 URL

第一次執行時, URL 卻會變成 /appbased/static-1.0.0?jsessionid=481AFDE46F818357ED2096824F250AFF/images/xxx

需重新 reload 一次才回復正常.

當時我的作法是, 在首次登入頁面的 jsp 前面加一行
<% request.getSession(true).invalidate(); %>
即可.


但最近又遇到另一個問題, 就是首頁有可能是登入狀態, 也有可能是未登入狀態, 總不能老是把 session 抓出來幹掉裝作沒事, 故查了一下解法.

2011年8月11日 星期四

Country & Province/State 國家代表表及省/州代碼表

找了好久. 這種東西很多地方需要啊.

OpenCart 有附 SQL, 不過國家只有 239 個, 與目前 iso 3166定義的 249 個國家有點差距.

iso 也沒提供 3166-2 的資料, 要買才有.

還好這邊有, 資料看起來挺新, 還可以下載, 也有 city & currency 的資料, 真是佛心來著.


我已把資料轉成 SQL 檔並修正一些亂碼, 有需要請自行取用

iso3166_1.sql (Country)


iso3166_2.sql (Subdivision: Province/State)


2011年8月8日 星期一

Spring internationalization (i18n) 學習筆記

Spring 提供了下列幾種 MessageSource 給 i18n 用.
StaticMessageSource
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource

使用及設定上3者都差不多, ReloadableResourceBundleMessageSource 的設定檔不需要放在 classpath 內, 且可以指定 cache, 故我選擇它實作.


實作環境Spring 3.1.0M1

2011年7月22日 星期五

Spring Transcation 學習筆記

參考來源: Transaction Management


* 使用預設的Proxy mode時, @Transactional 只能用在 public method 上. 除非改用 AspectJ mode.

* Spring 不建議 @Transactional 用於 interface 上. (除非你使用 interface-based proxy )


* 預設只有在發生 Unchecked Exception 時才會自動 rollback, 可以指定Exception來Rollback.
ex @Transactional(rollbackFor=Exception.class)


* TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 可以手動rollback


* eclipse 似乎無法 auto import Annotation 中的變數, 當使用 Propagation 時會出現 Propagation cannot be resolved to a variable 的錯誤, 手動 import org.springframework.transaction.annotation.Propagation; 即可.

2011年6月24日 星期五

Final Fantasy XIII 感想

(圖片來自http://www.square-enix.co.jp/fabula/ff13/)


其實已經破關一段時間了, 看到 FF XIII-2 快出了, 來寫一下心得.


玩到遊戲中期(尤其是 Snow 救 Hope 那段)時, 感覺這也許是繼 FF VIII、FF X之後, 讓我再次喜歡上 FF 的一款遊戲.


戰鬥系統很棒, 雖然玩到後來, 喜歡的陣形跟攻擊模式大致會固定. 但遇到某些特定敵人仍不得不改變戰略. 讓佔 rpg 遊戲最多時間的戰鬥玩起來一點也不枯燥乏味. GJ.


萬萬沒想到, 後來整個爛尾啊...前面鋪陳繭與下界的恩怨情仇都是假的, 反正衝進去幹掉最後頭目就對了...更...


我猜也許是因為要順應 online game 的風潮, 所以主線弄比較短, 但任務很多...

這是本末倒置了...


對 FF XIII - 2 不抱什麼期待~~~

到頭來,我們記住的,不是敵人的攻擊,而是朋友的沉默

最近很多人轉的影片.

片中那位德佬真是令人感動.

除了引用聖經請服務生 Don't Judge 外.

也用馬丁.路德.金的名言"到頭來,我們記住的,不是敵人的攻擊,而是朋友的沉默"來鼓勵那家人.

查了原文是 In the end, we will remember not the words of our enemies but the silence of our friends

太帥了~~~

2011年6月20日 星期一

Spring + Proxool 注意

今天把 tomcat 6 的 jdk 從 1.5 換成 1.6 後, 怪事發生了.
一直出現 java.sql.SQLException: org.logicalcobwebs.proxool.ProxoolException: Attempt to refer to a unregistered pool by its alias 'proxool_pool'

這問題之前遇過, 是因為 web.xml 中 servlet load-on-startup 順序沒設定好造成的.
<servlet>
<servlet-name>proxoolServletConfigurator</servlet-name>
<servlet-class>org.logicalcobwebs.proxool.configuration.ServletConfigurator</servlet-class>
<init-param>
    <param-name>xmlFile</param-name>
    <param-value>WEB-INF/config/proxool.xml</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>

怎麼只換了個 jdk 又沒改 xml 卻又發生了?

2011年6月15日 星期三

如何在新增資料後取得自動遞增的主鍵值? How to retrieve the auto-generated database key

自動遞增(ex: mysql 的 auto increment)的主鍵(Primary key)值, 如何在新增(insert)一筆資料後, 取得剛新增的資料鍵值呢?

Spring 的 NamedParameterJdbcTemplate 的 int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder) , 可於新增後取得鍵值.

這裡有範例.


2011年6月10日 星期五

Spring 3 + 檔案下載 (file download / export file)

網路上看到的文件都是在講怎麼處理 pdf or excel 的檔案,
但如果是要給使用者下載 jpg or txt 檔該怎麼做呢?

今天試了一下, 其實還滿簡單的. 繼承 AbstractView 即可.
下面是示範的程式碼:

2011年6月9日 星期四

Spring + Json

為了使用Json, 今天在spring xml 加入下面的設定,

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="mediaTypes">
        <map>
            <entry key="json" value="application/json" />
            <entry key="xml" value="text/xml" />
            <entry key="htm" value="text/html" />
            <entry key="html" value="text/html" />
        </map>
    </property>
    <property name="defaultContentType" value="text/html" />
    <property name="viewResolvers">
        <list>
            <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
            <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                <property name="requestContextAttribute"><value>rc</value></property>
                <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
                <property name="prefix" value="/WEB-INF/jsp/"/>
                <property name="suffix" value=".jsp"/>
            </bean>
        </list>
    </property>
    <property name="defaultViews">
        <list>
            <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
        </list>
    </property>
    <property name="ignoreAcceptHeader" value="true" />
</bean>


卻出現 exception 如下
Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.view.ContentNegotiatingViewResolver#0' defined in ServletContext resource [/WEB-INF/config/webmvc-config.xml]: Cannot create inner bean 'org.springframework.web.servlet.view.json.MappingJacksonJsonView#17b0998' of type [org.springframework.web.servlet.view.json.MappingJacksonJsonView] while setting bean property 'defaultViews' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.view.json.MappingJacksonJsonView#17b0998' defined in ServletContext resource [/WEB-INF/config/webmvc-config.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.web.servlet.view.json.MappingJacksonJsonView]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/codehaus/jackson/map/ObjectMapper
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.view.json.MappingJacksonJsonView#17b0998' defined in ServletContext resource [/WEB-INF/config/webmvc-config.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.web.servlet.view.json.MappingJacksonJsonView]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/codehaus/jackson/map/ObjectMapper


是因為少了Jackson Library的關係, 可按此下載


Json 簡單的範例如下:
Java Code
@RequestMapping(value={"JsonTester"})
@ResponseBody
public ObjectNode jsonTester() {
    ObjectNode rootNode = new ObjectMapper().createObjectNode();
    rootNode.put("result", true);


    return rootNode ;
}

html code(via jQuery)
$(document).ready(function() {
    $.post("JsonTester.json",function(data) {
        if (data.result) {
           alert("成功");
        } else {
            alert("失敗");
       }
    });
});


@ResponseBody會依據收到的的 Accept Header 自動將回傳物件轉為客戶端可接受的型態.
此例會將 Java 物件轉為 Json 物件.

Spring 內建了數種轉換物件, 詳情可參考 Spring 說明文件


以下為回傳HashMap的範例:

Java Code:
@RequestMapping(value={"GetMap"})
@ResponseBody
public ObjectNode getMap(String inputValue) {
ObjectNode rootNode = new ObjectMapper().createObjectNode();


HashMap<String, String> result = myService.getMap(inputValue);
rootNode.put("result", true);
rootNode.putPOJO("myMap", result);


return rootNode ;
}



html code (via jQuery):
$.getJSON("GetMap.json", {inputValue: variable }, function(data) {
        if (data.result) {
            $.each(data.myMap, function(key, value){
                alert(key + "-" + value) ;
            });
        } else {
            alert("Get Map failed");
        }
})



ContentNegotiatingViewResolver 會依據
1. URL 的結尾(extension)

2. Accept header

3. 以上都沒有辦法判斷的話, 使用 defaultContentType

來判斷 request 的要求是那一種型態.



2011年6月7日 星期二

mysql 中文亂碼問題

網路上大多文章是說, 在 my.ini 中加入下面幾行.
[mysqld]
default-character-set=utf8

[client]
default-character-set=utf8


但我加了後卻出現:
mysqld: unknown variable 'default-character-set=utf8'

原來是 [mysqld] 中的default-character-set已廢棄不用了, 改成下面這行即可.

character-set-server=utf8

2011年5月25日 星期三

Spring 3 SimpleJdbcDaoSupport

網路上似乎沒看到用Annotation 又 extends SimpleJdbcDaoSupport的, 花了我一個下午的時間測試.

Spring 3 中如果要用DaoSupport的類別, 二種作法如下:
(奇怪的是, Spring Framework Reference Documentation完全沒提DaoSupport)

Annotation的作法如下:

2011年5月23日 星期一

DD-WRT adduer manually

最近為了 ssh tunnel, 將AP改為 DD-WRT.
想說可以多開幾個帳號分享用.

沒想到 DD-WRT 把 adduser 功能拿掉了.

以下為 workaround 作法:

2011年5月21日 星期六

2011年5月20日 星期五

SSH Tunnel via DD-WRT

今天終於把 ssh tunnel 設定完成了.
(TP-LINK 741ND真是便宜又大碗的選擇)

圖式如下:





詳細步驟請參考 wiki 說明

2011年5月19日 星期四

How to get the current URL in JSP page (View)

如果把 jsp 藏在 /WEB-INF/jsp/ 下, 不論是 request.getServletPath() 或 request.getRequestURL() 回傳的 URL 並不正確.

查了一下有二種作法, 我採用下面這種方式(比較單純)

<% out.print(request.getAttribute("javax.servlet.forward.request_uri")) ;%>


作法二請參考此連結 .

Spring + GWT 學習筆記

第一次嘗試 Spring MVC程式, 在動手前已先看完 Spring in Action 2nd 以及 Spring in Action 3rd (Early access edition).

但啟動過程中卻不順利, 在此紀錄一下遇到的問題及解決方法.

Environment:
Windows 7 32bit
JDK 1.5.0.22
Tomcat 6.0.32
Spring 3.1.0.M1 (w/ webflow 2.3.0)
GWT 2.3.0


2011年5月14日 星期六

深入淺出設計模式 Head first design patterns



本書贏得 2005 Jolt Product Excellence Award 非浪得虛名.

真的一本非常優秀的Design Patterns書籍, 淺白易懂的範例, 配合Head First系列貫用的大頭人物插圖, 流暢的翻譯.

整本書讀完, 令人通體舒暢. (我自首, 看 GoF's Design Patterns (物件導向設計模式) 時睡著了好幾次...)

GoF作者之一的Erich Gamma(Eclipse作者, Design Patterns也是從他的博士論文修改而來)對本書的評語是┌內容相當有趣,但是涵蓋層面很廣,而且切中要點,這本書讓我感到印象深刻┘

2011年5月11日 星期三

Selenium 2 / WebDriver deal with Modal Dialogs

又遇到難題了, Selenium 2 / WebDriver 要怎麼處理 Modal Dialog 哩?

目前看起來還沒有解決, 以下連結有 workaround 解法, 但我試不出來(也許是 for selenium 1), 僅供參考.


Working with Modal Dialogs and Selenium - 2

Issue 284

2011年5月6日 星期五

GTAC 2010: The Future of Front-End Testing

GTAC 2010: The Future of Front-End Testing

摘要如下:

松本行弘的程式世界



前幾天在金石堂書局裡翻了"松本行弘的程式世界 ─ 成為一流程式設計師的14種思考術", 松本行弘為Ruby 的創造者.


這書還滿有趣的, 從一個語言創造者的角度來看程式設計師會遇到的問題及處理方式. 與一般教你怎麼寫程式的書不同, 我滿認同"成為一流程式設計師的14種思考術"這個副標的.


書裡也提到了幾個 Design Patterns, 令我印象深刻的是 Prototype, 他說Prototype其實就是複製物件, 但在 GoF Design Patterns一書中, 以迷宮來解釋此Pattern, 讓人不是很容易理解, 因為C++這個靜態語言並不適合呈現此種概念, 接著就以 Ruby 做了示範...


翻譯得滿有日本味的. 例如"程式弱點與攻擊手法"一章, 他說曾遇過駭客, 進系統後不做什麼, 卻下了 rm -rf * 的指令, 駭客大概是個愉快犯吧~~~令人會心一笑 :)

松本行弘的程式世界 ─ 成為一流程式設計師的14種思考術
(此為讀冊生活的連結, 若您因此連結而購買此書, 我可獲得2%分紅)

Outlook PST Viewer


Free Oulook PST Viewer 試用心得

缺點:慢, UI難用, 不能 Copy

優點:不用錢、原本要密碼的 pst 檔, 居然不用密碼也可以開........@@

Migrate to Selenium 2 / WebDriver



因為某些網站沒好好地處理 https 混雜 http 連結的問題, 當瀏覽器遇到這種情況, 會跳出警告視窗. 導致原本應該"自動化"的程式, 還需要人點掉警告視窗才能繼續...

剛好最近比較有時間, 就把原本 selenium 的程式改為 selenium 2.
(se2處理方式如下:

WebDriver.switchTo().alert().accept() ;
)
不知道是不是因為 se2 還在 bata 的關係, 改寫過程並不是很順利.
以下是改寫時遇到的問題紀錄(bata3)


2011年2月28日 星期一

iPhone JB 後推薦軟體

最近因為升級 iOS 4.2.1, 原先的軟體都重裝了一份, 還是做個紀錄, 未來遇到這種情況能比較快速反應...

iOS升級前記得先將 hosts 檔內的 74.208.10.249 gs.apple.com 註解掉(前面加個#字號)


我裝的軟體如下:
Appsync (軟體源:cydia.hackulo.usrepo.hackyouriphone.org, 請安裝對應 iOS 的版本)
  可以從 iTunes 安裝下載的 ipa 檔案

SBSettings (會自動安裝Activator)
  

Kuaidial (軟體源: kuaidial.googlecode.com/svn/deb)
  可阻檔不想接聽的電話

OpenSSH
  
iFile
  檔案管理軟體,要錢,但試用版就很夠用。

afc2add (7.1.x 改裝 Apple File Conduit"2")
  可以安裝/刪除 iOS中的檔案

DataDeposit
  儲存遊戲記錄存到 dropbox 的工具。也可以下載回復。

iProctect
  將應用程式另加密碼保護

SwipeSelection
  打字時方便移動游標的好工具。

需額外增加的應用源如下:
http://apt.178.com
http://cydia.51ipa.com