Archive

Archive for the ‘Framework’ Category

Google AppEngine for Java Announced

April 9, 2009 (Thursday) 1 comment

Today is a great day while Google announced the Java version of AppEngine.

The first thing to keep in mind is the security sandbox on the AppEngine server. And obviously it would be the challenge for many Java frameworks to run on AppEngine server out-of-the-box.

After having a brief run, here are something I found:

  • SiteMesh cannot work since it called javax.naming.InitialContext somewhere.
  • Stripes cannot startup by default because the DefaultMultipartWrapperFactory delegate to somewhere called System.getProperty(“java.io.tmpdir”). By providing a empty implementation of MultipartWrapperFactory, this could be solved easily. (Without considering the multipart function really works, since the AppEngine sandbox locked the file writing).
  • Freemarker works fine except there is an error on using JSP tags.
  • Spring framework seems fine as far as I go.

Looks like most of the issues can be solved. But the local environment does not work as the sandbox on AppEngine server. This makes the debug harder and the cycle longer.

Advertisements

Google App Engine – 新時代來臨的震撼

April 28, 2008 (Monday) 4 comments

Google App Engine (GAE) 月初在公開發表,announce 的那幾天每天都佔據了各大技術網站的版面。一個全世界都在看,全世界都在玩的產品,一定有它獨到之處,當然一定要來研究研究。Download 了 SDK,試著開發了一個簡單的程式,其實我對 python 並不熟,加上對 Django 一無所知,但卻不會覺得開發很困難,反而覺得比開發 Java 還要快還要順。雖然使用 GAE 的經驗很淺,不過 GAE 給人一種新時代來臨的感受。

在 Business model 方面,其實有太多太多的討論跟介紹了,一定很容易找到。會覺得是一個新時來的來臨,不是因為這是一個可以跟 Amazon S3 / EC2 對抗的 service,而是在開發技術上面 GAE 也做了一些突破。

跟 Java web development 比較起來,GAE 在開發上讓人覺得最驚艷的地方就是修改 code 的 feedback 速度。一般的 Java web development 在修改 code 之後,需要 compile -> deploy -> restart web container 才能看到修改,但使用 GAE,基本上你的修改馬上可以 apply,沒有 compile、沒有 restart server。這個說法也許有點落伍,很多人馬上就會想到 Ruby on Rails (RoR);也有人會想到這不是本來 Python + Django 就有的能力?就算是 Java 界,Groovy / GrailsJRuby 等也可以做到同樣的事情,何必大驚小怪呢?

GAE 當然不是只是拿 Python 跟 Django 出來騙騙小孩的玩意,真正強大的就在 Big Table 的部份。不要問我 Big Table 是什麼,我答不出個所以來,但站在 GAE developer 的角度而言,沒有 DB 這個東西。使用 GAE 在開發時,你的 code 中的 domain model 就是唯一的 model,沒有 DB,也沒有 DB schema、版本等問題。修改 domain model 的 code 馬上反應在下一次執行裡。GAE 的 domain model code 建立在 Django data model 之上,用起來很方便,類似 RoR 的 Active Record,但是少了 DB 的麻煩。

即使使用 RoR,在 domain model 上還是有得頭痛,因為 DB 的 schema 如何跟 code 裡的 domain model 保持同步就是一個大問題,一般 DB Migration 可以應付這個問題,在 domain model 改變的同時,準備一段 migration 的 code / description 就可以讓開發架構幫你把 DB 改到同一個版本。在 Java 也有 liquibase 可以做到這件事。但免不了要下去改東西,更嚴重的是,這個修改很可能沒有辦法即時反應 (至少在 Java 應該還是免不了要 compile -> db migration -> restart),所以又掉回了比較慢的 development lifecycle。

那麼用 Big Table 有什麼 trade off 嗎?有的,就是大家熟悉的 SQL / relational model 等等武功全廢。雖然 GAE 提供了一個叫 GQL 的東西,語法類似 SQL,但沒有 join 等等在 SQL 裡威力最強大的功能,思考也不宜使用 relational model 去想你的 data,因為這樣會完全想不通要怎麼辦。會不會有什麼事變成做不到了?我想有可能,但絕對不會太嚴重,因為絕大部份講究 OO 的 code 本來就很少直接運用 relational model 在操作,所以才有 OR mapping 的出現。如今 GAE 一不做二不休使用了 Big Table,除了宣示它在 scalability 上舉世無雙以外,也帶領 GAE 的 developer 進入一個新的時代,一個不去用 relation model 思考的年代。

我不是花了很多的時間在 GAE 上,寫進階的系統會不會出問題我沒有辦法知道,但以基本 web 常見的功能而言,很容易開發。Big Table 完全不會影響到我的思考,反而在 development lifecycle 上給了很大的進步,幾乎所有修改都能馬上看到 feedback。Agile development 中,fast feedback 佔了很重要的一個地位,愈快有 feedback 代表了開發的速度愈快。拿 Java 作比較,在 code 的修改上 Python + Django 的 feedback speed 就已經快了一大段,再加上 Big Table 又勝過了所有使用 DB 的 solution (包括 RoR、Django、Grails and JRuby 等等)。這個開發速度上的震撼讓我一直在想,用 Java 開發 web 是不是已經過時了呢?在 GAE 的範圍以外又如何去趕上這個突破,跨入這個新的時代呢?我想世界上有其他更多的人已經在想這個問題,新的工具、機制我想很快就會出現,open source big table 的技術也慢慢的浮上來了,新時代也許會靜靜的到來。至於這個新的突破跟誰會有關?是不是只有如 Google 般大的公司才適合用 Big Table?這個就留待日後驗證好了。

LiquiBase – A Database Refactoring Tool

December 16, 2007 (Sunday) Leave a comment

Change is the only thing unchanged. 軟體當然也脫離不了這個道理,但改變通常是會對軟體造成一些衝擊和影響的,有名的軟體技術很多都是教導如何面對改變而出現的。Refactoring 的技術鼓勵大家勇於面對改變,只有發現有不夠好的地方,馬上藉由 refactoring 的手法讓 code 變得更好。因此我們可以經由 refactoring 讓程式一步一步的改善,但 enterprise software 一般都會有 database,如果程式能夠一直改進,但 database 的 schema 沒有辦法修改的話,在改善的程度上會有很大的限制。因此 Database refactoring 也是有被研究討論的技術,Refactoring Database 正是討論這部份的一本好書。

既然 refactoring code 的時候需要使用工具的幫忙 (Eclipse 等 IDE 都有 support source code refactoring),那麼 Database refactoring 也需要有工具才會容易使用。LiquiBase 就是做 database refactoring 的工具,由 developer 提供的 change log file,裡面寫上想要做的 change set (如:Step 2 的樣子)。在執行 liquibase 的時候,它會去看目前的 database 是在哪一個 change set version,執行還沒有 apply 的 change set。當中 change set 版本的管理也是由 liquibase 來做。來舉一個實際的例子:

如果我想要建立一個 Table PERSON, 裡面的 column 有 ID, NAME。那我把它寫成 change set 1。這時執行 liquibase,由於我的 DB 裡本來沒有任何 table  所以 liquibase 會幫我執行 change set 1,也就是建立 PERSON table,同時會在 DB 裡記錄目前是在 change set 1。

接下來我可能覺得 PERSON 需要記錄更多的東西,所以想要增加 AGE 這個 column ,我就把它寫成 change set 2。這時執行 liquibase,它會發現剛剛的 DB 是在 change set 1 這個 version,所以就執行 change set 2。如果我把 DB 砍掉再執行 liquibase,這時它就會一次執行 change set 1, 2。

有了這個工具之後,我們對於 database 的修改都可以一一被記錄下來,加上 VCS (Version Control System) 的機制,就可以回去任何一個 DB 的版本。另外,liquibase 支援 MySQL, PostgreSQL , Oracle, MS-SQL, Sybase, DB2, Derby, HSQL, H2 等等,即使使用不一樣的 DB 也可以用同樣的語法寫 change set,這也是 liquibase 的一大功能。有了 liquibase 再搭配上針對 database schema 所做的測試,就可以隨時做 database refactoring,而且就跟 refactoring source code 一樣的簡單又有保障。

Unitils — 一次滿足所有 unit test 的需求

December 2, 2007 (Sunday) Leave a comment

談到 unit test,大家馬上想到的應該會是 JUnit;又或許是比較新一點的 TestNG。有些比較有經驗的人可能就會提出,那 servlet 如何測試?Spring bean 又如何測試?DAO 又如何測試,怎麼去建立 DB 的資料?如何去 mock 一個物件?

這些問題早就出現了,也早就有解法了,只是每一個問題可能都會對應到一個  library 或小型的 framework。 一直都沒有一個一次把問題解決的方案,直到 Unitils 的出現。Unitils 發源於 Ordina J-Technologies 的一個 unit test discussion group,會中他們討論出一份 Unit testing 的 Guidelines,而 Unitils 正是這份 guideline 的實作。

在 unit testing framework 的部份,Unitils 是 JUnit3, JUnit4, TestNG 三種都支援,所以不管你最熟悉的是哪一個都可以順利的使用 Unitils。在功能方面,Unitils 主要 support 以下幾項功能:

這些功能的文件都寫得很簡單易懂,其實觀念也很簡單,歸納一下只有幾個:

  • 使用 Java 的 equals 去 compare 通常不會是我們真的要的狀況,所以提供了比較寬鬆的比較
  • Unit Testing Database 必需要能夠自動的維護 (建立與更新版本)
  • DB 的資料必需要能夠在測試中自動加入
  • Test fixture 像是 Spring bean, mock object 等等盡量使用 annotation 標註,由 framework 自動產生

另外,Unitils 本身是由一堆 module 所組成的,如果它所提供的 module 還是無法滿足你的需求的話,要擴充其實很簡單。有興趣的人看一下 Unitils 的 source code 以及 unitils-default.properties 這個設定檔應該就知道怎麼做了。其實很難得一個小小的 unit testing framework 居然在模組化上做得這麼精美。

以前在這裡討論過 full stack application framework 的興起,同樣的 Unitils 這個可以滿足所有 unit test 需求的 framework 也很有可能慢慢會變成主流。只要一種技術成熟到一個程度之後,將會有整合的環境出現。在 JUnit、TestNG 都沒有太多的突破之後,Unitils 補足了這個空缺的需求。 如果你還在煩惱要怎麼開始做 unit test,那就用看看 Unitils 吧!

當 Spring 2.5 遇上 Hibernate Annotations – 自動搜尋 classpath 內的 entity

November 24, 2007 (Saturday) Leave a comment

最近推出的 Spring 2.5 提倡使用 annotation 的方式取代 xml 的設定,這個部份幾乎完全取代了 Spring Annotations 的功能,看來這個 project 應該已經沒有什麼生存空間。但 spring annotations 的 hibernate module 所提供的 AutomaticAnnotationSessionFactoryBean 一直是我很喜歡的功能。以往使用 Hibernate Annotations 時我們只能以 fully qualified class name 或 fully qualified 的 package name 來指定 entity class。透過 AutomaticAnnotationSessionFactoryBean 我們可以自動找到所有標示為 @javax.persistence.Entity 的 classes 自動加入到 hibernate 的 mapping classes。Spring 2.5 既然本身就可以自動的搜尋到 @Component 以及相關的 bean class,那我們應該也可以做到 @Entity 的搜尋囉。

我按照 spring annotations 的做法,提供一個自製的 session factory bean:


import org.hibernate.HibernateException;
import org.hibernate.cfg.AnnotationConfiguration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean;
import org.springframework.util.ClassUtils;

import javax.persistence.Entity;
import java.io.IOException;

/**
 * Created on: 2007/11/24
 *
 * @author Alan She
 */
public class ClasspathScanningAnnotationSessionFactoryBean extends AnnotationSessionFactoryBean {

    private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
    private final TypeFilter entityFilter = new AnnotationTypeFilter(Entity.class);
    private String resourcePattern = DEFAULT_RESOURCE_PATTERN;
    private String[] basePackages;

    public void setBasePackages(String... basePackages) {
        this.basePackages = basePackages;
    }

    protected void postProcessAnnotationConfiguration(AnnotationConfiguration config) throws HibernateException {
        for (String basePackage : basePackages) {
            try {
                String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                        ClassUtils.convertClassNameToResourcePath(basePackage) + "/" + this.resourcePattern;
                Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
                for (int i = 0; i < resources.length; i++) {
                    Resource resource = resources[i];
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    if (isEntity(metadataReader)) {
                        String classFileFullPath = resource.getURL().getPath();
                        String basePackageResourcePath = ClassUtils.convertClassNameToResourcePath(basePackage);
                        int startIndex = classFileFullPath.indexOf(basePackageResourcePath);
                        final String classFilePath = classFileFullPath.substring(startIndex,
                                classFileFullPath.length() - ClassUtils.CLASS_FILE_SUFFIX.length());
                        Class entityClass = null;
                        try {
                            entityClass = ClassUtils.forName(ClassUtils.convertResourcePathToClassName(classFilePath));
                        } catch (ClassNotFoundException e) {
                            throw new HibernateException("Entity class not found during classpath scanning", e);
                        }
                        config.addAnnotatedClass(entityClass);
                    }
                }
            }
            catch (IOException ex) {
                throw new HibernateException("I/O failure during classpath scanning", ex);
            }
        }
    }

    private boolean isEntity(MetadataReader metadataReader) throws IOException {
        if (entityFilter.match(metadataReader, this.metadataReaderFactory)) {
            return true;
        }
        return false;
    }
}

這個 class 的內容是以 Spring 2.5 的 ClassPathBeanDefinitionScanner 為骨幹而來的。原理很簡單,以 PathMatchingResourcePatternResolver 去找到所以 basePackage 下的 classes,一一比對是否有 annotate 了 @javax.persistence.Entity,如果有就加入到 session factory 的 annotationClass。


<bean id="sessionFactory" class="package.ClasspathScanningAnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="hibernateProperties">
    <props>
        <prop key="hibernate.dialect">${hibernate.dialect}</prop>
    </props>
    </property>
    <property name="basePackages">
        <list>
            <value>package.model</value>
        </list>
    </property>
</bean>

使用上也非常簡單,跟一般 spring 宣告 session factory 一樣,只需指定 basePackage 作為搜尋的範圍即可。

自動搜尋當然有優點也有缺點,但我個人討厭一一去設定 entity。有人可能會覺得萬一如果我有在 classpath 裡的 entity 又不想加入那不就只能全都用手動設定?其實如果想要有些彈性可以加入 include / exclude pattern 等東西,讓設定更活。畢竟設定檔時代已經慢慢過去,在 convention over configuration 的大趨勢之下,想辦法制定規則以及將規則以自動化落實才是長久之計。

優美的 UI component – YUI and yui-ext

February 10, 2007 (Saturday) 9 comments

不少人都知道 Yahoo! 的 YUI (Yahoo! UI Libraray),那是一個簡單又好用的 UI component。因為使用的技術是 HTML + JavaScript + CSS,所以並沒有限制在什麼平台或語言下使用。YUI 最強的地方是它的 packing 跟 ease of use,不會因為你要用一個小元件就要裝一大包東西,文件跟範例也算是很完整了。在 Ajax 開發時如果找機套上 YUI 去使用應該會事半功倍。比起 DojoQooxdoox-desktop 等,YUI 的功能其實不強,但卻很實用。

yui-ext Documentation Center 是一個叫 yui-ext 的 library,很明顯它是 YUI 的一個擴充。也許因為 YUI official 提供的功能其實不夠強大,就有人來做擴充啦。繼承了 YUI 包裝上的便利性、文件完整和容易使用的特性,yui-ext 提供了不少很實用的功能。有些功能其實是把 YUI 過於基層的功能再做一些包裝。功能大概有以下幾項:

  • Dialogs: 跳出一個 <div> 對話框,彈出跟收入的 animation 動作看了就讓人快樂。
  • Grid: 幾乎是無敵的 component,Table、Form 通通可以由它來做,還有 js coloumn sorting、inline editing,有這個我應該就懶得用其他的東西了。
  • Layouts: 強大的 layout manager,雖然很難說明,但可以看一下 Feed Viewer 的 example。
  • Resizable: 沒錯!圖片、文字框都可以即時改變大小!
  • Tab Panel: 這已經是 Ajax 時代必備的元件了,不用多解釋了。

總觀 yui-ext 的功能要比還是比不過 Dojo 等超強的 js 元件,但問題是實用性。為了一個 dojo fisheye 去用 dojo 可能有點痛苦,而 YUI 及 yui-ext 在絕大多數的情況你只要選你用的元件就好了。在 code 的撰寫方面,YUI 系列的 javascript 更是優雅到一個極點,寫起來完全跟在寫一般的 local application 的 UI 有接近。是一個非常推薦使用的元件,不管是只用一小部份還是全系統套用,都非常有價值。

在 web-based application 的環境,大家都想把很多很多的功能放在網站上,版面就變成亂七八糟,也佔得滿滿的,使用者永遠找不到它要的東西。如果有 Tab Panel 跟 Layouts 兩個元件,就像 Feed Viewer 的範例一樣,把版面做最好的利用。有時候,user 也會無聊的跟你要求什麼要大一點,什麼又要小一點,與其為了這種雞毛蒜皮的事情浪費人力,不如弄個可以 resize 的給 user 自己去調!當 web 不再是只有 page 的時候,可以套用的外觀設計也會跟著開闊。

technorati tags:, , ,

Blogged with Flock

The Role of the Enterprise Service Bus

October 27, 2006 (Friday) 2 comments

InfoQ: Presentation: The Role of the Enterprise Service Bus

這是一個很讚的 ESB (Enterprise Service Bus) Presentation,演講者 Mark Richards 對 ESB 有非常深入的研究。演講回答了一個很多人會問的問題,為什麼要用 ESB?

根據 Mark 所說的,ESB 能夠把 business service 跟 implementation service 分開來,business service 是指該 domain 的一些服務,例如下單等等專業的語言;而 implementation service 只是一些 CRUD 一類的程式操作,來實作出像下單樣的功能。把這兩個部份分開完全合符了 SOA 的架構跟概念,也把 business process 跟 technical 切開。ESB 其中兩個重要的能力,process choreography 負責 business process 的管理;而 service orchestration 處理 implementation service 的串接。這是一個很重要的觀念。

演講最後提到了 open source 的 ESB,包括了 MuleServiceMix。比較之下,Mule 比較像是一個 lightweight 的框架,讓你以 POJO 實作任何型式的 service。而 ServiceMix 則是已經把該有的都整合好,能以各種各樣的方式去實作出 ESB 的 end point。

ESB 雖然仍很少在實際上看到運用的機會,但它建基於 XML、Web Services、BPEL 以及 SOA 等等的概念與技術之上,成為一個實現學術界論文描繪的 Web Services 烏托邦不可或缺的架構。也是至今真正能把 BPEL 等技術串接起來比較讓人信服的方式。ESB 會不會就是以後資訊系統的樣子,則還要慢慢去觀察。

technorati tags:, , ,

Blogged with Flock