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