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 卻又發生了?