Monday, October 10, 2005

Develop Rails with vim

ช่วงหลังๆนี้เริ่มทดลองเขียน ruby(rails) เยอะขึ้น
ก็เริ่มมองหา editor ที่ใช้เขียน ว่าจะใช้อะไรเขียนดี
เริ่มตั้งแต่ RDT ที่ใช้ eclipse เป็น platform
,BBEdit, JEdit
สุดท้ายสูงสุดคืนสู่สามัญ
กลับมาใช้ vi เป็น editor หลัก
มูลเหตุที่ใช้ vi ก็เพราะว่า พึ่งจะฉลาดรู้ว่า
vi ที่เป็น vim version นั้น มันมี feature แพรวพราวไม่เบาเลย
(เมื่อก่อนใช้ vi ดัั้งเดิม ใน hp เป็นหลัก
เลยไม่นึกว่าเวลาเปลี่ยน แล้ว vi จะเปลี่ยนด้วย)

ลองมาดูว่าผมทำอะไรลงไปบ้าง เพื่อให้ใช้ vim กับ ruby
ได้อย่างสบายใจ

เริ่มดูการไป copy VimExtension จาก rubyforge ก่อน
ใน package นี้มี vim extension อยู่ 4 ส่วนคือ
  • Systax Highlighting อันนี้ไม่ต้องอธิบาย
  • Smart Indenting อันนี้ก็ไม่ต้องอธิบายเช่นเดียวกัน
  • Compiler อันนี้ช่วย map คำสั่ง make กับ error format
    ทำให้เรา run และแก้ไข error ที่เกิด ขณะอยู่ใน vim ได้


เพื่อให้ใช้งาน extension ตามข้างบน ก็ต้องมีการ
set .vimrc ดังนี้

set nocompatible " We're running Vim, not Vi!
syntax on " Enable syntax highlighting
filetype on " Enable filetype detection
filetype indent on " Enable filetype-specific indenting
filetype plugin on " Enable filetype-specific plugins


ด้วยเหตุผลอะไรก็ไม่ทราบ
(คิดว่าคงเนื่องจากไม่รู้จัก vim ดีพอมากกว่า)
ทำให้ function compiler บนเครื่องผมไม่ work
ผมก็เลยต้อง copy command ที่อยู่ใน compiler/ruby.vim
ไปไว้ใน ftplugin/ruby.vim แทน

และเพื่อให้สะดวกในการ Run
โดยให้มัน save ก่อน run
ผมก็เลยเพิ่ม map key เข้าไปดังนี้

function! RunRuby()
w
make %
endfunction

map <C-R> :call RunRuby()<CR>


ได้ข้างบนมาก็ช่วยได้ระดับหนึ่งแล้ว
ก็ต้องเพิ่มความสามารถด้วยการทำ folding
โดยกำหนด ให้ใช้ toggle การแสดง folder guild line
ส่วน tab key ก็ใช้ให้เป็นตัว toggle show/hidden fold

set fdm=syntax
set foldtext=getline(v:foldstart)
set foldcolumn=3
set fillchars=fold:\ "follow by white space
map <TAB> za

function! ToggleFoldColumn()
if &foldcolumn == 3
set foldcolumn=0
else
set foldcolumn=3
endif
endfunction
map <F3> :call ToggleFoldColumn()<CR>


จากนั้นก็ map ให้เป็น key ที่ใช้ toggle การแสดง linenumber

map :call ToggleNumber()
function! ToggleNumber()
if &number
set nonumber
else
set number
endif
endfunction


สุดท้ายก็ลง script winmanager เพื่อช่วยให้ access file
กับ switch buffer ได้ง่ายขึ้น

Related link from Roti

Sunday, October 09, 2005

บทความน่าสนใจ [2005-10-9]

  • Urban performance legends, revisited

    Pop quiz: Which language boasts faster raw allocation performance, the Java language, or C/C++? The answer may surprise you -- allocation in modern JVMs is far faster than the best performing malloc implementations.

Related link from Roti

Groovestry

Groovy เป็น script language ตัวหนึ่งที่เขียนด้วย java
เป็น script ที่น่าใช้ทีเดียว เพราะมี feature หลายอย่างที่ลอกมาจาก
ruby, python, smalltalk

วันนี้อ่านเจอใน Tapestry Mailing list ว่ามี project ที่ชื่อ Groovestry
ก็เลย load มาทดลองดู

เป็าหมายของโปรเจค ก็คือ นำ groovy เข้ามาช่วยเขียน code
ในส่วนของ Controller (MVC Pattern)
เพราะเดิมเวลาเราเขียน Web Application
เรามักจะมีขั้นตอนประมาณนี้
  1. start tomcat
  2. ใช้ browser เรียกทดสอบ programe
  3. โปรแกรมทำงานไม่ถูกต้อง
  4. แก้ไขโปรแกรม
  5. reload application
  6. วนกลับไปทำขั้นตอนที่ 2

ขั้นตอนที่น่าเบื่อ ก็คือตอนที่รอมัน reload application
ถ้า project ใหญ่ๆ มันจะช้าจนน่ารำคาญ
แน่นอน ถ้าทำ test case ไว้ดี ก็จะลดรอบ
ของการ iterate แบบที่ว่าไปได้เยอะ
แต่บางที เราก็อยากเขียนโปรแกรม
ในแบบของศิลปินบ้าง เขียนไป ดูผลลัพท์ไป ออกแบบไป
เหมือนช่างปั้น ที่ค่อยๆใส่ดินเข้าไป ตัดออก โปะเข้าไปใหม่ เกลี่ย ปาด..
(สำหรับคนที่ใช้ framework อื่น
tapestry ใช้วิธี dynamic enhance class
ในตอนที่ load controller ขึ้นมาทำงาน
ทำให้เราไม่สามารถใช้ feature reload class
ของ tomcat ได้)

เมื่อนำ Groovestry เข้ามาใช้
รอบ cycle การพัฒนาของเราก็จะเร็วขึ้น
เพราะสามารถตัดขั้นตอน การ reload application ออกไปได้

การใช้ Groovestry ก็ง่ายดาย
เพียงแค่ add jar เข้าไปใน WEB-INF/lib
แล้วก็กำหนดให้ page class เป็น ScriptPage
<page-specification class="org.apache.tapestry.contrib.script.ScriptPage">

เมื่อกำหนดแบบนี้แล้ว เวลา controller ถูกเรียกใช้งาน
มันก็จะมองหา groovy file ที่มีชื่อเดียวกับ page แต่มีนามสกุลเป็น
.groovy ให้เอง

ตัว controller ที่เขียนด้วย groovy จะมีหน้าตาประมาณนี้
package application.script

import org.apache.tapestry.contrib.script.ScriptObject

class Home extends ScriptObject {

void pageBeginRender(event) {
.....
}

public void submit(cycle) {
page2 = cycle.getPage("Page2");
page2.title = "< " + formTitle + " >";
page2.customerId = customerId;
cycle.activate(page2);
}
}

โดย script ของเราจะ extend จากScriptObject
(จริงๆไม่ extend ก็ได้ แต่จะขาด feature ของการ access
component, page property ไป)

เวลาใช้งานใน mode develope ก็ให้ set flag
-Dorg.apache.tapestry.disable-caching=true ด้วย
เพื่อไม่ให้ tapestry ทำ template-caching

หลังจากลองใช้ไปสักพักหนึ่ง
ก็พบว่า Groovestry มันขาด feature หนึ่งของ
tapestry ไป
feature ที่ว่า ก็คือการ binding ระหว่าง component กับ controller
ในลักษณะ ognl Expression
ยกตัวอย่าง
สมมติเรามี dynamic title อยู่อันหนึ่ง
<title>
<span jwcid="@Insert" value="ognl:title">xxx</span>
</title>

ถ้าเรา implement Controller ด้วย Java
เราก็ต้องมี method getTitle
ที่จะถูก bind เข้ากับ component Insert ใน html template
public class MyPage extends BasePage {
...
public String getTitle() {
return "my page title";
}
....
}

แต่พอมาใช้ Groovestry ก็พบว่าไม่สามารถ
เรียกข้ามไปใช้ method getTitle ที่เขียนไว้ใน groovy controller ได้
หลังจากแกะ code ดูสักพัก
ก็พบว่า จริงๆแล้ว มันพอที่จะทำได้เหมือนกัน
แต่ต้องแก้ code จำนวนหนึ่ง

สุดท้าย feature ที่เพิ่มเข้าไป ก็ทำให้สามารถเรียกใช้ ในลักษณะนี้ได้
<title>
<span jwcid="@Insert"
value="ognl:doAction('getTitle')">xxx</span>
</title>

ดูไม่ค่อยเนียนตานัก แต่ก็ทำงานได้
(java มันขาด feature แบบ ruby ที่สามารถ
implement missing method handler ได้
ไม่งั้น เราก็สามารถทำ dynamic method ได้เนียนกว่านี้)

Related link from Roti

JBI Interface Name & Service Name

ในการ implement JBI component
จะมีศัพท์พื้นฐานที่ต้องทำความเข้าใจอยู่จำนวนหนึ่ง
ไม่เช่นนั้นอาจจะต้องปวดหัวกับการ ส่ง message ไม่ได้
หรือ message วิ่งหายต๋อมไม่รู้ไปไหน

ศัพท์ตัวแรกก็คือคำว่า interface
เป็นศัพท์ที่ define ไว้ใน spec ของ WSDL 2.0
มีความหมายเดียวกับ คำว่า porttype ใน spec WSDL 1.1
10 ปากว่า ไม่เท่าตาเห็น
ลองดู WSDL 1.1 ของ google ดู
<portType name="GoogleSearchPort">

<operation name="doGoogleSearch">
<input message="typens:doGoogleSearch"/>
<output message="typens:doGoogleSearchResponse"/>
</operation>

</portType>

ถ้าเปรียบเทียบกับ OOP term
interface ใน WSDL 2.0 ก็มีความหมายเหมือนกัน interface ใน OOP

ศัพท์ตัวถัดไปก็คือ service
ความหมายก็คือ implementation ของ interface นั่นเอง

ศัพท์ตัวถัดไปก็คือ endpoint
endpoint คือจุดที่เกิดการเชื่อมต่อจริงๆ
คล้ายๆความหมายของ port ในเรื่อง socket (network)
ลองดูความหมายที่ WSDL 2.0 define ไว้ (JBI endpoint มีความหมายเดียวกับ WSDL endpoint)
Endpoints, in WSDL 2.0, refer to a particular address, accessible by a particular protocol, used to access a
particular service




ความสัมพันธ์ระหว่าง service กับ endpoint เป็น one-to-many ก็คือ
service หนึ่งๆมี endpoint ได้หลาย endpoint

เวลาเราส่ง Message ใน JBI
เราสามารถระบุ target ที่จะส่งได้ 2 วิธีคือ
  • ระบุแค่ service name
    ถ้าระบุแค่นี้ ตัว JBI container จะตัดสินให้ว่า ควรจะส่งให้ endpoint ไหนดี
  • ระบุทั้ง service name และ endpoint name (มีศัพท์เรียกว่า service endpoint)


ที่นี้ลองดูที่ jbi.xml จริงๆบ้าง
<services binding-component="false"
xmlns:logger="http://pok/dblogger/">
<provides interface-name="logger:log-interface"
service-name="logger:log"/>
</services>

source ข้างบนก็คือ การ define service ของ provider
จะเห็นว่ามีการระบุ interface-name กับ service-name

ถ้าเราไปดู source code ของ Java Class ที่ implement service นี้
public class DbLoggerComponent implements 
ServiceInterfaceImplementation,
ServiceEndPointImplementation {

public QName getInterfaceName() {
return new QName("http://pok/dblogger/",
"log-interface");
}

public void onMessage(MessageExchange msg) {
...
}
}

จะเห็นว่ามี method ที่ชื่อ getInterfaceName

เวลาเราเขียน component ใน servicemix
เราสามารถเลือกวิธีเขียนได้ 2 วิธีคือ
  • implment interface ของ JBI-api ด้วยตนเอง
  • ใช้ client-api ของ ServiceMix
    อันนี้จะสะดวกสบายขึ้นหน่อย จำนวนบรรทัดของ source code
    ที่เขียน จะลดลงหน่อย


ในที่นี้เราจะพูดถึงการเขียน component แบบที่ใช้ client-api เท่านั้น
เริ่มโดย เวลาเราเขียน component เราก็จะเขียน class ที่ implement
interface ServiceInterfaceImplementation
ซึ่งใน interface นี้จะบังคับให้เรา implement method ที่ชื่อ
getInterfaceName
ส่วน service name และ endpoint name
คงจะใช้การ lookup จาก jbi.xml

กรณีที่เรามี endpoint มากกว่า 1 endpoint ใน 1 service
การ implement แค่ ServiceInterfaceImplementation
จะไม่เพียงพอ ต้องมีการ implement ServiceEndPointImplementation
interface นี้จะบังคับเราให้เรา implement method getServiceName
กับ getEndpointName

Related link from Roti

Wednesday, October 05, 2005

ServiceMix Component

คราวก่อนทดลอง deploy component ด้วยการ wire เข้าไปใน container ตรงๆ
คราวนี้ได้ทดลอง deploy แบบ Hot deploy ดูบ้าง (ประเภทที่เอา zip file ไปวาง
ไว้ใต้ directory ที่กำหนด)

มี tutorial ที่พูดถึงเรื่องนี้อยู่ที่นี่
Develop JBI Component with Spring Client Toolkit

ทดลองทำตามแล้วไม่ได้ผลลัพท์ตามที่เขาว่าไว้
หลังจากนั่งมั่วอยู่ครึ่งวัน ก็ได้ข้อสรุปดังนี้

  • set Environment Variable SERVICEMIX_HOME
  • เวลาเรียกใช้งานให้เรียกใช้งาน program ที่ root path (/)
  • แก้ไข shell script $SERVICEMIX_HOME/bin/servicemix
    เปลี่ยนชื่อ path ต่างๆที่อยู่ข้างในให้เป็นชื่อเต็ม
  • เรียกใช้งานโดยระบุ configuration file เข้าไปด้วย
    $SERVICEMIX_HOME/bin/servicemix $SERVICEMIX_HOME/conf/servicemix.xml


การพัฒนา JBI Component นั้น
servicemix หลีกเลี่ยงขั้นตอนยุ่งยากทั้งหลาย
โดยการห่อ component ของเราไว้ใต้ SpringComponent
(สงสัยเหมือนกันว่าวิธีนี้จะนำไป deploy ใน JBI Container ตัวอื่นได้หรือเปล่า
แต่ขี้เกียจลอง)

Component ที่เรา deploy สามารถประพฤติตนได้ 2 แบบ
คือเป็น provider หรือเป็น consumer
provider คือ component ที่มีบริการให้เรียกใช้
ส่วน consumer คือ component ที่เรียกใช้บริการ
component หนึ่งๆ สามารถเป็นได้ทั้ง consumer และ provider

ตามตัวอย่าง tutorial ข้างบน จะเป็นการทำ component
TimerComponent กับ LoggerComponent
TimerComponent จะส่ง message ออกมา
ส่วน loggerComponent ถ้าได้รับ message ก็จะแสดงผลออกมาที่ console



เวลาเรา config component ฝั่ง provider เราจะประกาศ xml ดังนี้
<services binding-component="false"
xmlns:logger="http://pok/logger">
<provides interface-name="logger:log"
service-name="logger:myLogger"/>
</services>

จะเห็นว่า LoggerComponent ประกาศตัวเองว่า เป็น service ที่ชื่อ QName("http://pok/logger", "myLogger")
และมี interface ที่ชื่อ QName("http://pok/logger", "log")
Note: สงสัยเหมือนกันว่า 1 service มีได้มากกว่า 1 interface หรือเปล่า ?

ส่วน ฝั่ง consumer จะ ประกาศ xml ดังนี้
<services binding-component="false"
xmlns:logger="http://pok/logger">
<consumes interface-name="logger:log"
service-name="logger:myLogger"/>
</services>

สังเกตุว่า TimerComponent จะบอกว่าตัวเองต้องการใช้ service อะไร
และ interface ชื่ออะไร

จะเห็นว่าทั้งคนส่งและคนรับ จะรู้จักกันผ่านทางชื่อ interface และ ชื่อ service เท่านั้น
การแยก dependency ออกจากกันแบบนี้ เป็น key หลัก key หนึ่งของ SOA
(service oriented architecture)
การระบุชื่อแบบนี้ JBI เรียก End Point

ลักษณะการส่งของ Tutorial นี้ เป็นแบบ Explicitly นั่นคือ
ผู้ส่งกำหนดเองว่าต้องการส่งให้ใคร
ใน Spec JBI นั้น วิธีการระบุ End Point สามารถทำได้อีก 2 วิธีคือ
  • Implicitly ผู้ส่งจะระบุประเภทของ service อย่างเดียว ตัว NMR เลือกหาให้เอง
    (เบื้องหลังคือ NMR จะส่งคำถามไปยัง component ทีอยู่ในข่ายว่าเป็น candidate
    ว่าจะ accept ไหม)
  • dynamically อันนี้อ่านแล้วไม่่ค่อยเข้าใจ
    เดาๆว่า provider จะสร้าง vocabulary ชุดหนึ่งที่อธิบายถึง service ของตัวเอง
    ซึ่ง vocabulary พวกนี้ consumer สามารถ query ไปดูได้
    และ consumer ก็เลือกเอาว่าจะส่งให้ provider


ถ้าเราดู source code ของ TimerComponent ในส่วนของการส่ง
InOnly inOnly = serviceContext.createInOnly(new QName(
"http://pok/logger", "log"));
NormalizedMessage message = inOnly.createMessage();
message.setContent(new StreamSource(new StringReader(
"<hello>world</hello>")));
serviceContext.done(inOnly);

จะเห็นว่ามีการใช้ Object ที่ชื่อ InOnly
ลักษณะการส่ง Message ใน JBI จะแยกเป็น 4 แบบ คือ
  • One-Way หลังจากส่งไปแล้ว ผู้ส่งจะไม่รู้เลยว่า คนรับสามารถ process message ได้หรือไม่
    อันนี้ใช้ Class InOnly ในการส่ง)
  • Reliable One-Way ผู้รับสามารถแจ้ง fault กลับไปยังผู้ส่งได้ ในกรณีที่ไม่สามารถ process message ได้
    อันนี้ใช้ Class RobustInOnly ในการส่ง
  • Request-Response อันนี้ผู้ส่งคาดหวังที่จะได้ผลลัพท์ จากผู้ให้บริการ
    ใช้ Class InOut ในการส่ง
  • Request Optional-Response อันนี้พิสดารหน่อย
    อธิบายยาก ต้องลองเปิดดู diagram ใน JBI Specification
    แล้วจะร้องอ๋อ (แบบงงๆ ว่าทำไมมันต้องทำแบบนี้ด้วยวะ)

Related link from Roti

Corpse Bride

หนังเรื่องใหม่ของ Tim Burton.
เป็นหนังแบบ stop-motion (พวกที่ถ่ายทีละ frame)
หนังเรื่องนี้ใช้กล้อง canon EOS-1D MarkII ถ่าย
แล้วก็ตัดต่อด้วย program Final Cut Pro บน G5
ลองอ่านบทความ ว่าด้วยเบื้องหลังดูครับ
Link

ลองดู Trailer ได้ที่นี่ครับ
เนียนมาก ดูเหมือนสร้างด้วย computer graphic เลย
Link

Related link from Roti

Tuesday, October 04, 2005

Maven-Eclipse-Plugin

ช่วงนี้ทดลองพัฒนา JBI (Java Business Integration) Component อยู่
ก็เลยมีเหตุให้ต้องได้ใช้ Maven-Eclipse-Plugin อีกครั้ง
ก็เลยขอ Review หรือทบทวนตัว plugin นี้อีกครั้ง

ปกติเวลาเราเริ่มต้น project ที่จะใช้ Maven เป็น build tool
เราจะเริ่มด้วยการสร้าง file ที่ชื่อ project.xml กับ project.properties
ตัว project.xml จะเป็น descriptor ที่อธิบาย
  • ชื่อของโปรเจค
  • version ของ project
  • dependencies ของโปรเจค
  • directory ที่ใช้เก็บ source, test


<project>
<pomVersion>2</pomVersion>
<name>Echo</name>
<id>Echo</id>
<currentVersion>1.0</currentVersion>
<package>org.servicemix.demo</package>
<shortDescription>A new JBI component</shortDescription>
<description>A new JBI component</description>
<dependencies>
<dependency>
<id>servicemix</id>
<version>1.0</version>
<type>jar</type>
<url>http://www.servicemix.org</url>
</dependency>
.....
<dependency>
<groupId>servicemix</groupId>
<artifactId>servicemix-client</artifactId>
<version>1.0</version>
<properties>
<jbi.bundle>true</jbi.bundle>
</properties>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<unitTestSourceDirectory>src/test/java</unitTestSourceDirectory>
</build>
</project>


ส่วน project.properties จะทำหน้าที่เป็นตัว customize ข้อมูล
ของ plugin ต่างๆ ให้ประพฤติหรือปฎิบัติตนตามที่เราต้องการ
เช่น ต้องการให้ javac compile โดยมี debug information ด้วย

เมื่อเราเขียน project.xml เสร็จ ก็ให้สั่ง
maven eclipse


Maven-Eclipse-Plugin ก็จะทำการ generate
file .classpath กับ file .project ให้เรา
ซึ่ง 2 file นี้เป็น file หลักที่ eclipse ใช้เก็บข้อมูล project

ที่นี้ลองมาดูว่าเขาเขียน Maven-Eclipse-plugin นี้กันอย่างไร
เริ่มที่ว่า เวลาเราสั่ง maven eclipse นั้น
มันจะเริ่มทำงานที่ไหน อย่างไร

plugin ของ Maven ปกติจะอยู่ใน Directory MAVEN_HOME/plugins
โดยอยู่ในรูป jar file
ซึ่ง maven จะแอบแตก file เหล่านี้เก็บไว้ใน directory $USER_HOME/.maven/cache
โดยแต่ละ plugin จะกลายเป็น 1 sub directory
ในแต่ละ plugin จะมี file หลักๆอยู่ 4 ตัวคือ
  • project.xml
    เป็น file ที่อธิบายถึง plugin นั้น
    โครงสร้างหน้าตาเหมือนกับ project.xml ของ project เรา
  • project.properties
    ตัวนี้ไม่ค่อยได้ใช้อะไรอ
  • plugin.jelly
    file นี้แหล่ะทีเป็นหัวใจของ plugin
    เพราะเป็น file ที่เก็บว่า plugin นี้ทำอะไรได้บ้าง
    รวมทั้งแต่ละ task ที่ทำ มี step หรือขั้นตอนการทำอย่างไร
  • plugin.properties
    file นี้เก็บข้อมูล properties ของ plugin
    ที่สามารถ override จาก project.properties ของเราได้


file plugin.jelly เขียนด้วย Jelly
ซึ่งเป็น template engine แบบหนึ่ง (เหมือนๆพวก jsp, velocity,...)
ถ้าเราลองเปิดดู file นี้จะเห็นว่ามีการ declare goal ที่ืชื่อ eclipse ไว้
<!--==================================================================-->
<!-- Generate Eclipse .project and .classpath files -->
<!--==================================================================-->
<goal name="eclipse"
description="Generate Eclipse project files"
prereqs="eclipse:generate-project, eclipse:generate-classpath">
<ant:echo>Now refresh your project in Eclipse (right click on the project and
select "Refresh")</ant:echo>
</goal>

ลักษณะของ goal จะเขียนเหมือนกับ ant target
ก็คือ ระบุว่ามี depend ถึง goal ไหนหรือไม่
task ภายในที่ต้องทำมีอะไรบ้าง
โดยส่วนใหญ่แล้ว maven plugin จะส่งต่อไปให้ ant task
เป็นตัวทำงานจริง
(จริงๆมันเป็นลูกผสม ระหว่าง ant กับ jelly
ปัญหาของ ant คือ มันไม่ได้เป็น language ทำให้เขียน build file
ไม่ได้เต็มที่. maven ก็เลยใส่ syntax ของ jelly เข้าไปผสม)

ถ้าลองตามไปดู goal eclipse:generate-project
ที่ถูกอ้างถึง จะเห็น source code ดังนี้
<goal name="eclipse:generate-project"
description="Generate Eclipse .project file">

<ant:echo>Creating ${basedir}/.project ...</ant:echo>
<j:file name="${basedir}/.project" prettyPrint="true" xmlns="dummy">
<j:import file="${plugin.resources}/templates/project.jelly" inherit="true"/
>
</j:file>

</goal>


อธิบายได้ง่ายๆคือ มันจะสร้าง file ที่ชื่อ .project
โดยเนื้อหาของ file ได้มาจากการ run template ที่ชื่อ template/project.jelly

ถ้าลองตามไปดู file template/project.jelly
ก็จะเห็น file หน้าตาประมาณนี้
<projectDescription>
<name>${pom.artifactId}</name>
<comment>${pom.description}</comment>
<projects>
<j:forEach var="lib" items="${pom.artifacts}">
<j:set var="eclipseDependency"
value="${lib.dependency.getProperty('eclipse.dependency')}"/>
<j:if test="${eclipseDependency == 'true'}">
<project>${lib.dependency.artifactId}</project>
</j:if>
</j:forEach>
</projects>

....

</projectDescription>


ผลลัพท์ของการ run template ข้างบน
จะได้ file หน้าตาอย่างนี้ออกมา
<projectDescription>
<name>Echo</name>
<comment>A new JBI component</comment>
<projects>
</projects>
...
</projectDescription>


จะเห็นได้ว่าวิธีการใช้ template จะไม่ค่อยยากนัก
เสียแต่ว่าดูแล้วลายตาไปหน่อย

Related link from Roti

Sunday, October 02, 2005

เริ่มทดลอง Tapestry 4

ช่วงนี้ Tapestry ออก version beta-8 แล้ว
ปลายปีนี้คงได้ฤกษ์ release แล้ว
ก็เลยได้ฤกษ์ load มาทดลองเหมือนกัน

เล่าย้อนหลังสำหรับคนที่ไม่รู้จัก Tapestry หน่อยหนึ่ง
ตัว Tapestry เป็น Framework ที่ใช้พัฒนา Web Application
มี model แบบ Component model
ทำให้ reuse component ได้ดี

ต้นเหตุที่ผมเปลี่ยนมาใช้เจ้า Tapestry ก็คือ
เจ้า struts มันไม่ค่อย productive ในด้าน ui เท่าไร
การ reuse source code ทำได้ยาก
จริงๆแล้วสามารถ reuse ในลักษณะ taglib ก็ได้แหล่ะ
แต่ปัญหาคือ มันมองไม่เห็นว่าหน้าตาเป็นอย่างไร
จนกว่าจะ run จริง
ส่วน tapestry ออกแบบมาให้เรา map component
เข้ากับ html page
ดังนั้นเราสามารถออกแบบ html page โดยใช้ wyswyg tool ได้
จากนั้นก็ค่อยๆ map component เข้ากับ html element ต่างๆ

ปัญหาของ tapestry อยู่ตรง
learning curve ค่อนข้างสูง
ตัว document ค่อนข้างน้อย
ปัจจุบันมีหนังสือแค่ 2 เล่มเองที่อธิบาย tapestry
(จริงๆมี 3 เล่ม แต่เล่มที่ 3 เป็นภาษาเยอรมัน ก็เลยไม่นับ)
แต่ถ้าผ่าน learning curve มาได้เมื่อไร
ก็เหมือนได้ติดปีกแล้ว, productivity ที่ได้ดีมาก

ปัจจุบัน ผมมีเกณฑ์ในการ implement project ดังนี้
กรณีที่เป็น Application ใหญ่ๆ ใช้ Tapestry + Spring + Hibernate
ถ้า App มีลักษณะที่ต้อง integrate legacy หรือระบบที่คนอื่นทำไว้ด้วย
จะเพิ่ม ServiceMix เข้าไปด้วย
แต่ถ้าเป็น App เล็กๆ ก็จะเปลี่ยนไปใช้ RubyOnRails แทน
นอกจากนี้ยังมอง solution แบบลูกผสมไว้ด้วย
นั่นคือ ในกรณีที่เป็น CRUD ธรรมดา
(พวก maintain base table)
ก็จะใช้ RubyOnRails เข้ามา
ส่วนที่ complex ค่อยใช้ Tapestry เข้ามาจัดการ
(ใช้ CAS เป็น single sign-on ระหว่าง tapestry กับ Rails)

กลับมาเข้าเรื่อง Tapestry ต่อ
ใครที่ยังไม่เคยใช้เลย ขอแนะนำว่าควรเริ่มต้นที่ version 3 ก่อน
เพราะ stable แล้ว
ส่วน version4 บอกได้คำเดียวว่า แค่ install ก็ยุ่งแล้ว

Tapestry version 4 มีการ restructure
โครงสร้างภายใน โดยเปลี่ยนไปใช้ hivemind เป็นแกน
ทำให้เราสามารถ ถอดเปลี่ยน module ภายในของ tapestry ได้ง่าย
(ในอีกทาง การเพิ่ม hivemind ก็ทำให้
ต้องเพิ่ม learning curve ที่ต้องรู้จัก hivemind เพิ่มอีกตัว)
Feature ที่สำคัญตัวหนึ่งของ hivemind
ก็คือ Configuration Points
ถ้าใครเคย implement Plugin บน Eclipse Platform
พอเห็น Concept Configuration Points
ก็คงจะได้กลิ่น Concept Extension point ของ Eclipse ขึ้นมาตะหงิดๆ

Related link from Roti

Friday, September 30, 2005

mnoGoSearch Alias

วันก่อนพูดถึง mnoGoSearch ไป
จุดประสงค์ที่ทดลองใช้ mnoGoSearch ก็เพื่อที่จะนำไปทำ
search engine สำหรับ
Code Snippet Application ที่เขียนขึ้นมา

ซึ่ง Feature ที่ดี mnoGoSearch อย่างหนึ่ง
ก็คือสามารถ access เข้า database
เพื่อทำ index จาก Database table ได้โดยตรง
Feature นี้ mnoGoSearch ตั้งชื่อ protocol ว่า htdb

ปัญหาที่เจอก็คือ เมื่อเราใช้ front-end tool
ของ mnogoSearch ค้นหาข้อมูล
ผลลัพท์ที่ได้ จะแสดงในรูป
htdb:/xx
โดย xx คือ primary key ของ table ที่ทำ index



ซึ่งในกรณีที่เราใช้ program ด้วยกันเป็นคน search
ก็คงไม่มีปัญหาอะไร เพราะสามารถตัดต่อ string
เพื่อนำไปสร้าง url ที่เหมาะสมได้

สำหรับกรณีที่เราต้องการให้ front-end tool ของ mnogosearch
สามารถแสดงผลเป็น url ที่สามารถ click ได้จริงๆ
ก็สามารถทำได้โดย
เข้าไปแก้ไข template file ที่ชื่อ search.htm
(เก็บอยู่ใน /usr/local/mnogosearch/etc)
เพิ่มคำสั่ง
Alias htdb:/ http://localhost/snn/snippets/show/

เท่านี้ mnoGoSearch ก็จะทำการ rewrite ผลลัพท์ให้เราใหม่

Related link from Roti

Config fastcgi ให้กับ rails

ช่วงนี้จะใช้ Snippets Application ที่เขียนขึ้นเองแล้ว
ก็เลยจะ deploy ให้เหมือนกันสถานการณ์การทำงานจริง



ปกติใน rails เราสามารถ run application ได้ 3 แบบหลักๆ คือ
  • ผ่านทาง WebBrick
    เจ้า WebBrick เป็น web server ที่เขียนด้วย Ruby
    เป็นสภาพแวดล้อมที่เหมาะสมอย่างยิ่งกับการ debug
  • ผ่านทาง Apache CGI
    อันนี้จะช้าหน่อย แต่เหมาะแก่การ test deploy ครั้งแรก
    เพราะจะได้ check พวก configuration, permission
    ให้ถูกต้องก่อน
  • ผ่านทาง Apache + mod_fastcgi


ที่เราจะทดลองก็คือ ใช้ FastCGI

เริ่มแรก ก็ต้อง install mod_fastcgi ก่อน
ขั้นนี้ไม่ยุ่งยากอะไร ก็แค่ download source code มา แล้วก็ build

จากนั้นก็ install fcgi developer kit
(ruby fcgi ต้องการใช้)

สุดท้ายก็ install ruby-fcgi

ส่วนการ deploy rails application
ถ้าอย่างง่าย ก็เพียงแต่ทำ link จาก htdocs
ไปยัง directory project/public

จากนั้นก็ config Directory ที่เราทำ link ไว้
โดย enable ให้สามารถ run cgi ได้
<Directory "/Library/WebServer/Documents/snn">
Options +ExecCGI FollowSymLinks
AllowOverride all
Allow from all
Order allow,deny
</Directory>


จากนั้นก็เข้าไปแก้ไข file .htaccess ที่อยู่ใน directory project/public
(file นี้ rails generate ให้เราอัตโนมัติ)
แก้ไข ส่วน rewrite rule จากของเดิมที่ใช้ dispatch.cgi
ไปเป็น dispatch.fcgi แทน

ปรับส่วน rewrite rule ให้เหมาะสมกับ structure ใหม่ของเราด้วย
(ขั้นนี้แหล่ะที่ค่อนข้างมั่ว เพราะเจ้า apache rewrite มันช่าง
เข้าใจยากจริงๆ)

เข้าไปแก้ไข projectdir/public/dispatch.fcgi
ระบุ directory ของ log file ที่ต้องการ
RailsFCGIHandler.process! '/tmp/snippet_fcgi_crash.log'


เมื่อเราเรียกใช้ครั้งแรกสุด apache จะ start process dispatch.fcgi ให้อัตโนมัติ
แต่บางทีเราต้องการให้ start process ขึ้นมารอเลย
(เพื่อไม่ให้เกิดอาการ lag เมื่อ user เริ่มเข้ามาใช้)
ในกรณีนี้ ก็ให้เพิ่ม directive นี้เข้าไปใน httpd.conf
<IfModule mod_fastcgi.c>
FastCgiIpcDir /tmp/fcgi_ipc
FastCgiServer /Library/WebServer/Documents/snn/dispatch.fcgi \
-initial-env RAILS_ENV=production -processes 15 -idle-timeout 60
</IfModule>

Related link from Roti

ทดลอง mnoGoSearch

วันนี้อ่านเจอใน Planet Eclipse ว่า
site eclipse.org จะปรับปรุง search engine ใหม่
โดยจะปรับปรุงกฎการให้น้ำหนัก
ที่น่าสนใจก็คือ เขาบอกด้วยว่า เขาใช้ mnoGoSearch เป็น search engine
ก็เลยตามไปดูสักหน่อย

ตัว mnoGoSearch เป็น search engine ที่ใช้ database table
ในการทำ index (lucene ใช้ file system)
ตัวมันประกอบด้วย 2 ส่วนคือ
  • Indexer ที่ใช้ scan web document เพื่อทำ index
  • CGI front-end ที่ใช้ในการ search


ที่น่าสนใจ ก็คือ mnoGoSearch support ภาษาไทย ด้วย
โดยมี dictionary thai มาให้ด้วย โดยมีจำนวนศัพท์มาให้ 32895 คำ

ในขั้นตอนการทำ index เราสามารถ config
ให้มัน scan web site ได้หลายลักษณะ เช่น

  • page scan เฉพาะ page ที่ระบุ
  • path scan เฉพาะ document ที่อยู่ใต้ path ที่ระบุ
  • site scan documents ที่อยู่ใน host นั้น
  • world scan any document



ตัวอย่างเช่น

# To index whole server "localhost":
Server http://localhost/
#
# You can also specify some path to index subdirectory only:
Server http://localhost/subdir/
#
# To specify the only one page:
Server page http://localhost/path/main.html


ผมไม่ค่อยสนใจ feature ที่ใช้ scan static web site เท่าไร
ที่สนใจก็คือ feature ที่สามารถใช้ scan database table ได้โดยตรงมากกว่า
ตัว mnoGoSearch มี protocol ที่เรียกว่า HTDB สำหรับทำแบบนี้

ลักษณะการทำงาน เป็นดังนี้
  • ระบุ connection ที่จะใช้ต่อ database (dbname, user, password)
  • select primary key ของ table ที่เราต้องการ scan ขึ้นมาทั้งหมด
  • กำหนด select ที่ทำให้ได้เนื้อหาของข้อมูลที่เราต้องการทำ index


ลองดูตัวอย่าง

HTDBAddr mysql://user:passwd@localhost/dbname/?dbmode=single

เริ่มด้วยการระบุ database


HTDBList "select id from snippets"

ในที่นี้เราต้องการทำ index บน table snippets


HTDBDoc "select concat('HTTP/1.0 200 OK\\r\\n', 'Content-type: text/plain;charset=utf-8\\r\\n', 'Content-Language: th\\r\\n','\\r\\n', description) from snippets where id = $1 "

จากนั้นก็ระบุ sql ที่ทำให้ได้เนื้อหาที่ต้องการทำ index
โดย mnoGoSearch จะ pass primary key เข้ามาให้
ในชื่อตัวแปร $1

จะเห็นได้ว่าเราต้องแปลงเนื้อหาของเรา
ให้อยู่ในรูปแบบของ http response เสียก่อน
key ที่สำคัญตัวที่หนึ่งก็คือ การระบุ language
ไม่งั้นมันจะพยายามเดา language ให้เอง
ส่วนตัวที่ 2 ก็คือการระบุ encoding ที่ใช้


ส่วนรูปข้างล่างนี้เป็น screen dump หน้าจอ search ของ mnoGoSearch



จะเห็นได้ว่า ผลลัพท์ที่เราได้จะมี url ในรูปของ htdb:/nn
ตัวเลข nn ก็คือ primary key ของ row ที่ search เจอนั่นเอง

ในส่วนของการ scan static web site นั้น
เท่าที่ลองใช้ดู ทดลอง scan 2 ที่นี้ดู

Server page http://www.pantip.com/cafe/index.html
Server page http://bact.blogspot.com/2005/09/web-20-office.html




ของ pantip ทำ index ออกมาได้ ทดลองค้นหาดู
พบว่าคำบางคำ ก็ค้นเจอ บางคำก็ไม่เจอ
เช่น ถ้าใช้คำว่า"ห้องสมุด" จะเจอ ส่วนคำว่า "ไกล", "ไกลบ้าน"
นั้นหาไม่เจอ แต่ถ้าเปลี่ยนเป็น "บ้าน" เฉยๆ ก็จะเจอ



ส่วนของ bact จะเห็นว่าอ่านภาษาไทยมาถูกต้อง
แต่ search ด้วยภาษาไทยไม่เจอเลย
ไปดูใน datadict ของ mnoGoSearch มันระบุว่า
site นี้ Content-Language เป็น ภาษาอังกฤษ (en)
ก็เป็นไปได้ว่ามันเดาภาษาผิด ก็เลยไม่ได้เอา dict ไทยไปจับ

Related link from Roti

Wednesday, September 28, 2005

The Wall of DEATH

เอามาจาก blog The Fishbowl
เขาพูดถึงวิธีการที่จะช่วยให้ developer focus งานที่ต้องทำ

ชอบตรง
The rules of the wall are:
1. Developers take an issue off the wall, and work on it until it is done
2. Developers must not work on anything that is not on the wall
3. Corollary to rule 2: unanticipated additional work must be filed in JIRA, then written on a card and placed on the wall
4. Developers must never take more than one issue off the wall at a time
5. Once an issue is done, the developer records the actual time taken in the other corner of the issue, dumps it in the ‘done’ pile, and updates the whiteboard.
6. Repeat until no issues remain on the wall


มันจับต้องได้ดี
ไว้โปรเจคหน้าจะเตรียมผนังไว้บ้าง

Related link from Roti

Monday, September 26, 2005

ทดลอง send message จาก ruby ไป java ผ่าน Stomp Protocol

วันนี้ทดลองใช้ ActiveMQ เป็นตัวกลางในการ communicate
ระหว่าง Java กับ Ruby

ในส่วนของ Java ผมใช้ jencks ซึ่งเป็น lightweight container
ที่สามารถ deploy ใน Spring ได้ ตัว jencks ทำให้เรา run
Message Bean นอก j2ee server ได้

ตัว Message Bean ทดลองเขียนดังนี้
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class EchoBean implements MessageListener {

public void onMessage(Message msg) {
try {
System.out.println("i got message");
TextMessage txt = (TextMessage) msg;
System.out.println(txt.getText());
} catch (JMSException je) {
je.printStackTrace();
}
}
}


ส่วนของ Spring Application Config file นี้ยุ่งขึ้นมาหน่อย
เขียนดังนี้

เริ่มด้วยการ add jencks ก่อน
<bean id="jencks" class="org.jencks.JCAContainer">
<property name="bootstrapContext">
<bean class="org.jencks.factory.BootstrapContextFactoryBean">
<property name="threadPoolSize" value="25"/>
</bean>
</property>

<property name="resourceAdapter">
<bean id="activeMQResourceAdapter" class="org.activemq.ra.ActiveMQResour
ceAdapter">
<property name="serverUrl" value="tcp://localhost:61616"/>
</bean>
</property>
</bean>


จากนั้นก็ใส่ EchoBean ที่เราเขียนเข้าไป
<bean id="monitor" factory-method="addConnector" factory-bean="jencks" singleton
="true">
<property name="activationSpec">
<bean class="org.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="Monitor.Queue"/>
<property name="destinationType" value="javax.jms.Queue"/>
</bean>
</property>

<property name="ref" value="echoBean"/>
</bean>

<bean id="echoBean" class="mx.test.mq.EchoBean" singleton="true"/>


เท่านี้ก็เรียบร้อย เวลา run ก็เพียงแต่ load Application Context
ClassPathXmlApplicationContext appContext = 
new ClassPathXmlApplicationContext(
new String[] {"beans.xml"});


ก่อนที่จะเขียนส่วน Ruby Client
เราต้อง config ActiveMQ ให้เปิด port ที่รับ Stomp protocol เสียก่อน
ซึ่งทำได้โดย เข้าไปแก้ activemq.xml
เพิ่มบรรทัดนี้เข้าไป
<connector>
<serverTransport uri="stomp://localhost:61626"/>
</connector>


ทีนี้มาว่าด้วยส่วน ruby บ้าง
ตัว api ที่ใช้ interface กับ Stomp สามารถ download ได้ที่นี่ Link
วิธีการใช้งานทำได้ดังนี้

เริ่มต้นด้วยการ open connection กับ ActiveMQ ก่อน
conn = Stomp::Connection.open('user','pass','localh',port)


ในกรณีที่ต้องการส่ง Message ก็เพียงแต่
conn.send('/queue/test', 'hello');

Note: เราต้องระบุชนิดของ destination เสมอ
โดยการระบุ prefix ที่จะต้องขึ้นต้นด้วย /queue/ หรือ /topic/


กรณีที่ต้องการส่งหลายๆ message และให้มี transaction control ด้วย
ก็เพียงแต่ใส่ begin, commit ครอบลงไป
conn.begin
conn.send('/queue/test', 'hello');
conn.send('/topic/test', 'hello');
conn.commit


ส่วนกรณีที่ต้องการรับ Message
ก็ให้ subscribe เข้ากับ Topic หรือ Queue ที่ต้องการ
conn.subscribe('/queue/test') { |msg| 
puts msg.body
}

Related link from Roti

Friday, September 23, 2005

cherry tree

ใน Processing Blogs
blprnt เขา post source code ที่่ paint รูปต้นไม้

  • cherrytree (***** เอาไปห้าดาว)
  • birchtree (ตอนที่กิ่งน้อยๆ สวยกว่าตอนกิ่งเยอะๆ)

Related link from Roti

Thursday, September 22, 2005

My First JBI Component

วันนี้ทดลอง implement JBI Component
(Java Technology and Business Integration Services)
เพื่อที่จะทดลอง deploy เข้าไปใน ServiceMix

เหมือนเคย ถ้าทำตาม spec ของ Sun แล้ว
คงต้องทำอะไรต่างๆนานายุบยับไปหมด
(ตามประสา sun ที่ spec มักจะแบ่งเป็นขั้นเป็นตอน
ขั้นนี้ให้ role นี้ทำนะ ขั้นนู้นให้ role นู้นเขาเป็นคนทำ)
พวก developer มือเดี่ยวอย่างเรา (ทำมันทุก role)
ก็เลยขัดอกขัดใจ

โชคดีที่ ServiceMix เปิดช่องให้เรา
ใส่ component ของเรา ผ่านเข้าไปโดยใช้
Spring config file ทำให้ชีวิตง่ายขึ้น

เนื่องจากเป็น component แรกในชีวิต ก็เลยจัดการลอก
จากตัวอย่างที่มีนั่นแหล่ะดัดแปลงนิดหน่อย

เป้าหมายที่ทดสอบ


Quartz timer component ===msg===> Log Component



Quartz Timer Component ก็ใช้ build in ของ ServiceMix
ไม่ต้องลงแรง
ส่วน Log Component ก็ลอกเขามาเลย


public class LogComponent extends ComponentSupport {

private Log log = LogFactory.getLog(TraceComponent.class);


public Log getLog() {
return log;
}

public void setLog(Log log) {
this.log = log;
}

public void onMessageExchange(MessageExchange exchange) throws MessagingExce
ption {
// lets dump the incoming message
NormalizedMessage message = exchange.getMessage("in");
if (message == null) {
log.warn("Received null message from exchange: " + exchange);
}
else {
log.info("Exchange: " + exchange + " received IN message: " + messag
e);
}
}
}


ทดลอง run ปรากฎว่า เงียบหาย
ไม่เห็น message อะไรที่ console
ก็เลยสงสัยว่าเกิดอะไรขึ้น
นั่งพยายามค้นหาอยู่นาน
ในที่สุดก็พบว่า
  • ห้ามลืม implements MessageExchangeListener เด็ดขาด ถ้าลืมมันจะไม่สามารถรับ message ได้
    public class LogComponent extends ComponentSupport 
    implements MessageExchangeListener {...}

    อันนี้ผิดเอง ลอกเขามาแล้วยังลอกผิดอีก
  • ที่เราสั่ง log แสดงผลนั้น มันจะไม่แสดงผลอะไรออกมาให้เราเห็น
    ส่วนสาเหตุนั้นยังนึกไม่ออก ตอนแรกนึกว่าเป็นเพราะชื่อ package เราไม่ตรงกับเขา
    ตัว Logging Filter ก็เลยไม่แสดงผลของเรา
  • คำสั่ง System.out.println จะไม่แสดงผลออกที่ console
    ไม่รู้มัน redirect ไปไว้ไหน
  • สุดท้ายก็เลยต้องใช้วิธีเขียน log ลง File System เอง
    ก็เลยพบว่ามันทำงานได้เป็นปกติ


หลังจากอ่านเอกสารของ ServiceMix ไปสักพัก
ก็พบว่า เราสามารถ implement Component
ในลักษณะ POJO ได้เลย
public class PojoReceiver implements MessageExchangeListener {

public void onMessageExchange(MessageExchange exchange) throws MessagingExce
ption {
NormalizedMessage message = exchange.getMessage("in");
....

}

}


เป้าหมายที่จะลองถัดไปก็คือ
check ดูว่า ActiveMQ ที่ bundle มานั้น support Stomp Protocol แล้วหรือยัง
ถ้ายังก็จะได้ upgrade เสีย
หลังจากนั้นก็ทดลอง integrate ruby application
เข้ากับ ServiceMix ผ่านทาง Stomp protocol

Related link from Roti

convert wma to mp3

มี file wma แต่อยาก convert เป็น mp3
ก็เลย search ใน internet ดู
เจอ script นี้

#!/bin/bash
#
# Dump wma to mp3

for i in *.wma
do
if [ -f $i ]; then
rm -f “$i.wav”
mkfifo “$i.wav”
mplayer -vo null -vc dummy -af resample=44100 -ao pcm -waveheader “$i” -aofile “$i.wav” &
dest=`echo “$i”|sed -e ’s/wma$/mp3/’`
lame -h -b 192 “$i.wav” “$dest”
rm -f “$i.wav”
fi
done

ลอง run แล้ว ตอนแรกนึกว่า Error
เพราะเห็น message บอกว่า malloc ... ยาวเหยียด
แต่จริงๆแล้วไม่ใช่
คงเป็นเพราะเป็นการแปลงแบบไม่รู้ความยาวที่แน่นอน
(ใช้ fifo)

Related link from Roti

Wednesday, September 21, 2005

JBoss Seam

เมื่อวานอ่านเจอเรื่อง JBoss Seam
ซึ่งเป็น Application Framework สำหรับ Java EE5
จุดเด่นของ Seam ก็คือการ integrate context ระหว่าง
Presentation Context (JSF) กับ Business Logic Context (EJB)

Gavin King (founder ของ Hibernate) บอกว่า
One of the (several) goals of Seam is to bring Ruby On Rails style productivity to the Java EE platform


วันนี้ก็เลยนั่งทำ Diagram ที่ present ให้เห็นภาพว่า
การ integrate ระหว่าง jsf context กับ ejb context
ว่าเชื่อมต่อกันอย่างไร



  1. เริ่มจาก user submit ข้อมูลจาก jsp
    seam จะพยายาม binding data เข้าไปที่ Entity Bean
    เมื่อพบว่ามีการระบุ user.name
    seam ก็จะมองหา Entity Bean ที่มี annotaion ระบุ @Name("user")
    เมื่อรู้ว่าเป็น entity ตัวไหนแล้ว
    seam ก็จะ initialize Entity Bean นั้นขึ้นมา
    พร้อมกับทำการ set ค่าให้
  2. ใน submit component ที่ jsp มีการระบุ action ไว้ว่า
    ต้องการให้ trig action ที่ชื่อ register.register
    seam ก็จะทำการหา Session Bean ที่มีการ annotation ระบุเป็น @Name("register")
    แล้ว call method ที่ชื่อ register ให้
  3. ในจังหวะก่อนที่จะเริ่ม call นั้น
    seam จะตรวจว่า Session Bean นั้นต้องการให้ inject
    อะไรบ้าง ซึ่งจะพบว่า RegisterAction ต้องการให้ inject
    user (ที่เกิดจากข้อ 1) เข้ามาด้วย
    นอกจากนี้ยังสามารถ inject FacesContext (Context ของ Presentaion Layer)
    เข้ามาด้วย ซึ่งทำให้เราสามารถส่ง message กลับไปยัง Presentation Layer ได้ง่าย
    เช่นการ push Error Message กลับไป
  4. หลังจากทำงานสำเร็จ method register
    ก็จะ return name ของ Page ที่ต้องการแสดงผลกลับมา
    ซึ่งจะถูกนำไป search ใน face-config.xml file
    เพื่อหาว่า jsp ตัวไหนเป็นผู้รับผิดชอบในการ render


ดูแล้วเยี่ยมมากเลย
เมื่อบวกกับ ประเด็นที JBoss มี Embed EJB3 Container ออกมา
(สามารถนำไป embed ใน Tomcat ได้)
ทำให้ Seam ดูจะเป็นคู่แข่งที่น่ากลัวของ Tapestry 4



ส่วนใครที่ยังไม่เคยเห็น Spec ของ EJB3 นั้น
แนวทางของ EJB3 จะออกมาดังนี้

ใช้ Annotation เข้ามาช่วย
ทำให้ลด xml config file ไปได้จำนวนหนึ่ง
พร้อมทั้งเปลี่ยน model ในการเขียน Session Bean
ไปเป็น POJO (เหมือนที่ spring ทำอยู่)
ทำให้ลด Overhead ในการเขียน EJB แบบเดิม
ซึ่งต้องมี interface วุ่นวายไปหมด

ในส่วนของ Entity Bean ใน Spec EJB3 นั้น
สำหรับคนที่ใช้ Hibernate เป็นอยู่แล้ว
ก็แทบจะไม่ต้องเรียนรู้อะไรเพิ่มเติมเลย

อ่านเพ่ิมเติม
SEAM - Contextual Components

Related link from Roti

Monday, September 19, 2005

เขียน Code Snippet Web Application

ตอนนี้ศึกษาหลาย topic, language พร้อมๆกัน
ทำให้หน่วยความจำในสมองไม่พอใช้ เกิดอาการหลงลืม
คำสั่งต่างๆ ก็เลยคิดจะทำ code snippet ดีๆ ไว้ใช้เอง
วันนี้ก็เลยนั่งเขียนโปรแกรมนี้ทั้งวัน

feature ที่ต้องการก็คือ
  • syntax highlight code ได้
  • ระบุ Tag สำหรับแต่ละ snippet ได้
  • search by Tags ได้


opensource project ที่ load มาใช้ประกอบด้วย
  • Rails -> framework ที่ใช้เขียน
  • Taggable -> เป็น extension ของ ActiveRecord ใช้จัดการเรื่อง Tag
  • Syntax -> ใช้ทำ syntax highlighting
    (build-in support แค่ ruby, yaml, xml แต่สามารถเขียน plugin เองได้)


เวลาที่ใช้เขียนรวมๆ แล้วก็ประมาณ 6 ชั่วโมง
(ยังไม่ได้ทำเรื่องสวยงาม อันนี้ใช้เวลาเยอะกว่าเขียน code แน่ๆ)

ซึ่งไม่เลวเลยสำหรับ framework ที่ยังไม่เคยเขียน
จริงๆจังมาก่อน (เขียนไปนั่งเปิด manual อ่านไป)
ถือได้ว่า rails มี productivity ที่ใช้ได้เลย

ตัวที่ใช้เวลามากๆหน่อย ก็จะเป็นในส่วน
  • syntax highlight ที่ต้องเขียน plugin ให้ render java code ได้
  • taggable -> อันนี้พบ bug ในส่วน finder sql
    คนเขียนเข้าใช้ sql ที่ไม่เป็นมาตรฐาน ทำให้ run บน postgres ไม่ได้ ก็เลยต้องเขียน patch
    ที่ run บน postgres ได้


การเขียนด้วย opensource library แบบนี้ได้ความรู้ดี
เพราะถ้าเป็น opensource ใหม่ๆหรือที่ไม่ได้รับความนิยม
ก็จะไม่ค่อยมีเอกสารมากนัก ต้องเข้าไปใล่ source code ของเขาดูเอง
และเนื่องจากเป็น project ที่ขนาดไม่ใหญ่
ทำให้เราไล่ source code ได้ไม่ยากนัก

เป้าหมายถัดไป ก็คือไป load เอา source code ของ
http://www.bigbold.com/snippets/
มานั่งไล่ source code ดู
ตัวนี้เขียนด้วย rails เหมือนกัน

Related link from Roti

Implement Domain Validatation ด้วย "AspectJ Introduction"

เวลา Design Application เรามักต้องตัดสินใจว่า
จะ implement Validation Logic ไว้ที่ Layer ไหนดี

RubyOnRails implement Validation Logic ไว้ที่ Model
(domain layer)
struts (รวมทั้ง MVC อื่นๆของ java) provide Validation Logic
ไว้ที่ Presentation Layer

ข้อดีของการ provide Validation Logic ไว้ที่ domain
ก็คือ กรณีที่มี Presentation Layer หลายๆแบบ
validation logic ก็จะ share อยู่ที่เดียว ไม่กระจัดกระจาย

วันนี้เราจะลองเอา AspectJ ซึ่งเป็น AOP
มาลองทำ Validation แบบ Mixin บ้าง
สมมติเรามี Class Person
public class Person {

private String name;
private int age;

public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

Note: Person Class ที่เราเขียน จะเป็น POJO เพียวๆเลย
ไม่มี business logic อะไรอยู่ในนั้นเลย


ออกแบบ Validataion Framework ให้มี Class ดังนี้



เป้าหมายที่เราต้องการ ก็คือ เราจะเขียน logic ในส่วนของการ
validate สำหรับ Person แยกไว้เป็นอีก file หนึ่ง (เรียก Aspect)
แล้วใช้ AspectJ ทำการ mix Person class กับ Aspect ที่เขียนไว้เข้าด้วยกัน

สร้าง Aspect ที่ชื่อ PersonValidate.aj
public aspect PersonValidate {
declare parents: Person implements Validatable;

public Errors Person.validate() {
Errors errs = new Errors();
if (this.getName() == null ||
this.getName().length() == 0) {
errs.addError("name", "name must not be empty");
}
if (this.getAge() <= 0) {
errs.addError("age", "age must not be negative or zero");
}
return errs;
}

}

ใน PersonValidate.aj จะมีการ declare parents: Person implements Validatable;
การสั่งแบบนี้ ก็คือเราจะกำหนดให้ Person Class implement Interface ที่ชื่อ Validatable
จากนั้นก็กำหนด method validate ให้กับ Person class
(method validate เป็น method ที่ declare ไว้ใน Validatable interface)
สังเกตุดูใน code จะเห็นว่าเราสามารถใช้ this ในการอ้างถึง person instance
ที่ถูก modify ได้

ทดลอง Run
    public static void main(String[] args) {
Person p = new Person();
p.setAge(-1);

Validatable vt = (Validatable) p;
Errors err = vt.validate();

for (int i = 0; i < err.size(); i++) {
System.out.println(err.getError(i));
}
}


ได้ผลลัพท์ดังนี้

name:name must not be empty
age:age must not be negative or zero


จะเห็นได้ว่า แนวคิดนี้ ดูน่าสนใจทีเดียว (ในแง่ Architecture)
แต่ความคิดนี้ยังไม่ได้ทดลองทำจริงนะ ก็เลยยังไม่รู้ว่าจะเกิดโทษ
หรือเกิดประโยชน์อย่างไรบ้าง

Related link from Roti

Sunday, September 18, 2005

Net อืดสุดๆ

ช่วง 3-4 วันมานี้ net ช้ามาก
ได้ความเร็วแค่ 2 k เอง

วันนี้ความเร็วเริ่มเพิ่มขึ้นแล้ว
ก็เลยฉลอง เข้าไปวาดรูปเล่นซะ 1 รูป
http://artpad.art.com/?imz0kkg8vsc

Related link from Roti