Saturday, April 01, 2006

SCA (แถม SD0)

มาอีกแล้ว Term ใหม่ที่ชื่อ SCA (ไม่ใหม่เท่าไร, white paper ที่ผมอ่าน,มัน เดือน november 2005)
SCA (Service Component Architecture)
โต้โผก็คือ IBM, SAP, IONA, BEA, Oracle, ... (ยกเว้น SUN)
ผลักดันเพื่อเป็นตัวเสริมทัพ SOA (Service Oriented Architecture)

ท้าวความ SOA นิดหนึ่งก่อน
SOA จะมี nature อย่างหนึ่งก็คือ
มันเป็น coarse-grained service
nature ของ coarse-grained service ก็คือ
มี method ให้ใช้ไม่มาก, parameter ที่่รับส่งมักจะเป็นแบบ document oriented style

ตัว SCA ก็คือ Framework ที่เราสามารถใช้
fine grained component
ประกอบขึ้นเป็น SOA Service (coarse-grained service)
(ผู้ไม่คุ้นกับ java คงจะเริ่มบ่นว่า ยุ่งจังโว้ย)

ผมเป็นพวกต้องเห็น code ก่อนจึงจะเข้าใจ concept
ดีที่ตอนนี้มี Incubator project ที่ชื่อ Tuscany
submit code เข้าไปใน apache แล้ว
ก็เลยมีตัวอย่างให้ดู

ลองดูตัวอย่าง HelloWorld service ก่อน

เริ่มด้วยการ define interface ก่อนว่า helloworld มี method อะไรให้เรียกใช้
public interface HelloWorldService {

public String getGreetings(String name);

}

จากนั้นก็ตามด้วย implementation
@Service(HelloWorldService.class)
public class HelloWorldServiceComponentImpl implements HelloWorldService {

/*
* @see org.apache.tuscany.samples.helloworld.HelloWorldServiceComponent#getGreetings()
*/

public String getGreetings(String name) {
return "Hello " + name;
}

}

เริ่มสังเกตุเห็น key concept ของ SCA แล้ว
  • Service Implementation มีลักษณะเป็น POJO (plain old java object)
  • ใช้ Annotation เข้ามากำหนด Nature


เมื่อมี interface กับ implementation แล้ว
ก็มาถึง module definition
module ก็คือ group ของ component ที่ deploy ร่วมกัน
(ในกรณี helloword เรามี component เดียว)
<module xmlns="http://www.osoa.org/xmlns/sca/0.9" xmlns:v="http://www.osoa.org/xmlns/sca/values/0.9"
name="sampleHelloworld">

<component name="HelloWorldServiceComponent">
<implementation.java class="org.apache.tuscany.samples.helloworld.HelloWorldServiceComponentImpl"/>
</component>

</module>

ตรงนี้ยังมองไม่เห็นอะไรมากนัก เพราะมันยังมีแค่ component เดียวอยู่
ไว้กลับมาดูอีกทีตอนมีหลาย component

มาดูตอนใช้งานบ้าง
ส่วนของ client ที่เรียกใช้ จะมีหน้าตาดังนี้
public class HelloWorldClient {

public static final void main(String[] args) throws Exception {

// Obtain Tuscany runtime
TuscanyRuntime tuscany = new TuscanyRuntime("hello", null);

// Start the runtime
tuscany.start();

// Obtain SCA module context.
ModuleContext moduleContext = CurrentModuleContext.getContext();

// Locate the HelloWorld service component and invoke it
HelloWorldService helloworldService = (HelloWorldService)
moduleContext.locateService("HelloWorldServiceComponent");

String value = helloworldService.getGreetings("World");

System.out.println(value);

// Stop the runtime
tuscany.stop();
}
}

จะเห็นว่ามี concept ของ Runtime
ตัว SCA เป็นแค่ spec ดังนั้นต้องมี implementation ถึงจะทำงานได้จริง
Tuscany ก็คือ implementation ที่ว่า
นอกจากนี้ จะเห็นได้ว่าเราต้อง lookup service จาก module

ลองดูตัวอย่าง Module definition ที่มีหลาย component บ้าง
<module xmlns="http://www.osoa.org/xmlns/sca/0.9" xmlns:v="http://www.osoa.org/xmlns/sca/values/0.9"
name="sampleHelloworld">

<component name="HelloWorldServiceComponent">
<implementation.java class="org.apache.tuscany.samples.helloworldmc.HelloWorldServiceComponentImpl"/>
<references>
<v:greetingPrefix>GreetingPrefixComponent</v:greetingPrefix>
<v:greetingSuffix>GreetingSuffixComponent</v:greetingSuffix>
</references>
<properties>
<v:greetingMiddle>SCA</v:greetingMiddle>
</properties>
</component>

<component name="GreetingPrefixComponent">
<implementation.java class="org.apache.tuscany.samples.helloworldmc.GreetingPortionProviderImpl"/>
<properties>
<v:greetingPortion>Hello</v:greetingPortion>
</properties>
</component>

<component name="GreetingSuffixComponent">
<implementation.java class="org.apache.tuscany.samples.helloworldmc.GreetingPortionProviderImpl"/>
<properties>
<v:greetingPortion>World</v:greetingPortion>
</properties>
</component>
</module>

เห็นแล้วอย่าพึ่งถอดใจ ปรับสายตาสักนิด จะเห็นว่า
มันใช้หลักการเดียวกับ Spring นั่นคือ Dependency Injection
การร้อย component หรือการ config component ใน SCA
จะใช้หลักการเดียวกับ spring

หลายคนจะเริ่มสงสัยแล้วว่า มันเป็น webservice อย่างไร
กรณีที่เราต้องการให้เป็น web service ก็ต้องมีการ กำหนด entry point
ใน Module ด้วย
<module xmlns="http://www.osoa.org/xmlns/sca/0.9" xmlns:v="http://www.osoa.org/xmlns/sca/values/0.9"
name="sampleHelloworld">

<entryPoint name="HelloWorldService">
<interface.wsdl interface="http://helloworldaxis.samples.tuscany.apache.org#HelloWorldServiceImpl"/>
<binding.ws port="http://helloworldaxis.samples.tuscany.apache.org#helloworld"/>
<reference>HelloWorldServiceComponent/HelloWorldService</reference>
</entryPoint>

<component name="HelloWorldServiceComponent">
<implementation.java class="org.apache.tuscany.samples.helloworldws.HelloWorldServiceComponentImpl"/>
</component>

<import.wsdl
location="wsdl/helloworld.wsdl"
namespace="http://helloworldaxis.samples.tuscany.apache.org"/>

</module>

จะเห็นประเด็นหนึ่งที่ว่า definition ของ entrypoint ได้มาจาก
file wsdl โดยตรง
และมี keyword ที่เรียกว่า "binding"
spec ของ SCA เปิดโอกาสให้เรา binding ในรูป protocol อื่นได้เช่น
    <entryPoint name="HelloWorldService">
<interface.java interface="org.apache.tuscany.samples.helloworldjsonrpc.HelloWorldService"/>
<jsonrpc:binding.jsonrpc/>
<reference>HelloWorldServiceComponent/HelloWorldService</reference>
</entryPoint>

อันนี้เป็นการ binding service เป็น JSON rpc service

ที่ขาดไม่ได้คือ key สำคัญของ SCA ไม่ได้อยู่ที่ model ของ Component อย่างเดียว
แต่อยู่ที่ SDO (Service Data Object) ด้วย
SDO เข้ามาเป็น key ได้อย่างไร
จำประโยคที่ว่า "Coarse Grain Service พึงมี parameter style เป็น Document Style"
SDO ก็คือ Data Model ที่ช่วยให้เราจัดการกับ Document Style นี้ได้ง่ายขึ้น
เพราะ เราสามารถ pass document ในรูป SDO ไปให้ Party อื่นๆ
เมื่อ Party นั้นๆ ทำการแก้ไขรายการใน document เสร็จ (เช่น fill-in, append, delete)
ก็จะส่ง document นั้นคืนมา
ซึ่งเราสามารถใช้ feature ของ SDO เข้าไปดูเฉพาะ
dataset ที่เกิดการเปลี่ยนแปลงได้
เช่นหาเฉพาะ list item ที่ถูก เพิ่ม หรือ ลบ ออกจาก document

ลองดูตัวอย่าง test case ของ SDO
เริ่มด้วยการสร้าง DataObject
(เห็นมันเป็น object อย่างนี้ แต่เวลาที่มันถูก serialize เพื่อรับ/ส่ง
ระหว่าง program แล้ว มันจะถูก serialize ในรูป xml file)

DataGraph dataGraph = SDOUtil.createDataGraph();
DataObject quote = dataGraph.createRootObject("http://www.example.com/simple", "Quote");

กรณีที่ต้องการบันทึกการเปลี่ยนแปลง
ก็จะใช้ method beginLogging กับ endLogging
เข้ามาช่วย
ChangeSummary changeSummary = dataGraph.getChangeSummary();
changeSummary.beginLogging();

quote.setString("companyName", "FlyByNightTechnology");
quote.setBigDecimal("price", new BigDecimal("1000.0"));
quote.setBigDecimal("open1", new BigDecimal("1000.0"));
...

DataObject child = quote.createDataObject("quotes");
child.setBigDecimal("price", new BigDecimal("2000.0"));

changeSummary.endLogging();


ถ้าอยากเห็นว่า IBM นำ SDO ไปประยุกต์ได้อย่างมีประสิทธิภาพอย่างไร
ต้องดู FacesClient Components Developer's Guide

ที่สำคัญคือ IBM เริ่มออก sdo api ที่อยู่บนภาษาอื่นออกมาแล้ว
มี SDO for php ที่สามารถใช้บน php ได้

Related link from Roti

Online Tutoring

Online tutoring pays off at home, abroad
online กันตัวต่อตัว
บ้านเราไม่รู้มีหรือยัง

Bikram Roy founded Chicago-based Studyloft.com in November. Already, its office in Bangalore provides tutoring to 5,400 US students. ''We're trying to leverage the accessibility of the Internet to bring students tutoring when they need it and where they need it," Roy said.


Both Growing Stars and Studyloft say that the majority of their tutors hold master's degrees or doctorates in their subjects;

Related link from Roti

Dabble DB

demo ของ Dabble DB
ได้รางวัล Best in Show Award
เป็น app ที่น่าสนใจทีเดียว
ทั้งในด้าน interface และ การนำไปใช้งาน

Related link from Roti

Tapestry in Ruby

สงสัยมานานว่า ทำไมไม่มีใคร implement
web framework แบบ component oriented ใน ruby บ้าง
วันนี้โชคดี หาเจอแล้ว
CGIKit
(ตั้งชื่อไม่สื่อเลย)
แต่โชคร้ายหน่อย ตรงที่คนเขียนเขาพึ่งจะเลิก Maintainence เอง
เหตุผลก็คือ เขาจะเปลี่ยนไปใช้ python

Related link from Roti

Friday, March 31, 2006

Hibernate Load, subclass mapping

กรณีที่เราออกแบบ class ของเราให้เป็น Inheritance mapping



ถ้าเราใช้คำสั่ง load กับ base class
Person person = session.load(Person.class, id);
Employee emp = (Employee) person; // ClassCastException here

เวลา run จะเกิด ClassCastException
โดย object ที่ return จาก method load จะอยู่ในรูป
proxy object ทำให้ cast ไม่ได้

ไปค้นหาใน Issue Tracking ของ Hibernate
พบ issue ที่ HHH-80
อธิบายว่า มันเป็นพฤติกรรมที่พึ่งมีใน Hibernate 3
กรณีที่ lazy="true" (default setting), คำสั่ง load จะได้ผลลัพท์เป็น proxy object

การแก้ไข เลือกวิิธีการใดการหนึ่งจากต่อไปนี้
  • เลือก config lazy="false"
  • ใช้คำสั่ง session.get แทน session.load

Related link from Roti

Thursday, March 30, 2006

Tapestry 4

หลังจากติดอกติดใจกับ Rails มาพักใหญ่
วันนี้ก็ได้ฤกษ์กลับไป Evaluate Tapestry4.0 อีกครั้ง
(ปัจจุบันที่บริษัทฯใช้ version 3 อยู่)

แรกสุด ก็ต้องดูที่ startup time ก่อน
เมื่อใช้กับ jetty, Tapestry จะใช้เวลา start ประมาณ 5 วินาที
อืมม์..ใช้ได้ จำได้ว่า ตอนที่เป็น beta แรกๆ มันนานกว่านี้เยอะ
ในเวลา 5 วินาทีนี้ เป็นของ Hivemind (ฐานรากของ Tapestry) ไปซะ 3 วินาที

เวลาชุดถัดไปที่ต้องวัด ก็คือเวลาในการ compile page,
tapestry ก็ใช้ technique เดียวกับ JSP
นั่นคือ เมื่อเรียกใช้ ก็จะมีการ compile
(สิ่งที่ tapestry ทำ ก็คือ การ generate class file ขึ้นมา
โดยใช้ javassist)
ผลก็คือ ใช้เวลา compile ไปเสีย 9 วินาที
อันนี้สินาน แต่ก็พอยอมรับได้ (เพราะมันทำเฉพาะครั้งแรกครั้งเดียว)

ความแตกต่างต่อไปที่เห็น ก็คือ
เดิม tapestry ต้องมี xml file ที่ใช้เป็น glue ระหว่าง
html file กับ class file
โดย xml file นี้จะระบุว่า html นี้จับคุ่กับ class file ไหน
มาใน Tapestry 4.0 นี้ มีการใช้ convention แบบ rails เข้ามาช่วย
กล่าวคือ สมมติว่า html ของเราชื่อ Home.html,
Tapestry จะมองหา class file ที่ชื่อ Home.class ใน default package
ถ้าเจอ ก็จะใช้ตัวนั้นเป็น BasePage
ซึ่งเราสามารถ config ระบุ package prefix ที่จะใช้ในการค้นหา class file ได้
<application>  
<meta key="org.apache.tapestry.page-class-packages" value="myproj.pages"/>
</application>

ส่วนกรณีที่ html page เราอยู่ใน subfolder
เวลาค้นหา BasePage, ชื่อของ subfoder ก็จะถูกแปลงเป็น packaege name ด้วย
ยกตัวอย่าง "admin/UserEdit,
การค้นหา class ก็จะค้นหา class ที่ชื่อ UserEdit
ใน package "package_prefix.admin"

ประเด็นที่ยังค้างคาใจ ใน tapestry4 ที่ต้องลองต่อ ก็คือ
การนำเอา annotation มาใช้เป็นกลไกในการ inject (dependency injection)
อยากรู้ว่า มัน work ไหม

Related link from Roti

Wednesday, March 29, 2006

หน้าตาประหลาด

ใน Signal vs Noise
เขาพูดถึง เรื่อง รูปถ่ายแมลง
มีอยู่รูปหนึ่งที่ผมติดใจมาก คือรูปนี้
Pseudocreobotra wahlbergii female

It also has a distinct “#9” spiral in yellow on top of its wing.

ถ้ามาเจอในบ้านเรา ต้องมีคนเอาเลข 9 ไปไปแทงหวยแน่เลย

ใครอยากทำความรู้จักให้มากขึ้นอ่านที่นี่
Link
(คนเขียน เขาเขียนได้ดีมากเลย)

Related link from Roti

Hibernate กับ ทักษิน

ช่วงนี้ บ้าน(ประเทศ)เรากำลังเรียนรู้หลายอย่าง
ในโลก opensource ของ java ก็เริ่มมีการปะทะชุดใหม่เกิดขึ้น

Gavin King (คนเขียน hibernate) ออกมาปะทะคารม
กรณีข้อกล่าวหา "Hibernate hates Spring"

ประเด็นของ Hibernate กับ Spring นี่
ผมไม่รู้เรื่องเลย แต่อ่านแล้ว มันมีประเด็นเรื่อง
  • ความขัดแย้งในเรื่อง JSR-220 spec
    ระหว่าง spring กับ hibernate
  • Gavin ไม่ชอบ HibernateTemplate(ใน spring) อย่างมาก (น่าสนใจว่าทำไม)

    We have already explained in other places that we dislike the noisy, inelegant HibernateTemplate stuff that Spring tries to promote. Everyone using use this stuff today should seriously reconsider.
    Until Spring redesigns their Hibernate integration stuff, we simply cannot recommend it to our users.

  • มีการ remove reference page ที่เกี่ยวกับ Spring ออกจาก Web site ของ hibernate


ส่วนประเด็นว่า แล้วมันไปเกี่ยวอะไรกับทักษิน
ไม่เกี่ยวหรอกครับ
แค่มีกลิ่นบางอย่างคล้ายๆกัน
(โปรดใช้วิจารณญาณ คนแต่ละคนรับรู้และตีความกลิ่นที่ได้รับไม่เหมือนกัน)
(กลิ่น เป็น คำอุปมา)
คือผมอ่านสิ่งที่ Gavin เขียน แล้วรู้สึกเหมือนฟังทักษินพูดอย่างไงไม่รู้
Scott ที่เป็นเจ้าของ blog ก็ยังตอบเลยว่า
I’m also surprised you think I attribute “evil” to Hibernate. The only real evil I associate with Hibernate is arrogance

(ลองเปลี่ยนคำว่า Hibernate เป็น ทักษิน ดูสิ)

Related link from Roti

Tuesday, March 28, 2006

Javascript Favelet

Bookmarklet ของดี
Javascript Object Tree Favelet
(พึ่งเคยได้ยินคำว่า Favelet นี่แหล่ะ
favelet == bookmarklet)

ช่วยให้เราใล่ javascript ได้ง่ายขึ้น
เพราะมันช่วยกรอง source code ที่เราไม่ต้องการดูออกไปได้

Related link from Roti

Implement Scheme with Haskell

พึ่งรู้เกร็ดว่า scheme48 (implementation หนึ่งของ scheme)
ได้เลข 48 มาจาก การที่มันถูกเขียนขึ้นภายใน 48 ชั่วโมง
ในปี 1996 โดยใช้ common lisp เขียน
(scheme syntax มัน minimalist มากเลย)

มาปีนี้ 2006
implementation ที่เป็น java ก็มีแล้ว (JScheme)
ถึงคราวของ haskell บ้าง
ใครที่ถึกพอ ขอแนะนำ
write yourself a scheme in 48 hours
(ใครที่อยากเห็น monads ตัวเป็นๆ ไม่ควรพลาด)

เมื่อคืนผมลองไปบ้างแล้ว
เจอแบบฝึกหัดชุดที่ 3.3 เข้าไป ก็ตัดสินใจสั่ง shutdown แล้วปิดไฟนอน

Related link from Roti

Monday, March 27, 2006

Copy/Paste from command line (Mac OS X)

pbcopy, pbpaste
เผื่อจะช่วยลดการใช้ mouse ได้อีกหน่อย

Related link from Roti

Saturday, March 25, 2006

Alex Axon Photography, Winter Session - Minimalism

Related link from Roti

Cutaway illustration

ใครอยากรู้ว่า Cutaway Illustration ที่เขียนด้วย
Illustrator + Photoshop นั้นทำอย่างไรบ้าง
ลองเข้าไปดูที่นี่

'Empress of the Seas' Cruise Ship Cutaway Illustration

ขนาด Width: 45" Resolution: 350 dpi Format: CMYK Layered Photoshop File
File Size: 1.3gb
Illustration Time: 960 Hours

เวลา click ดูรูปขยายนี่ รู้สึกหนาวเลย
ขยันจริงๆ

Note: ให้ดีต้อง click ดู HowTo ด้วย

Related link from Roti

สร้าง ruby class แบบ dynamic

ถ้าเราดูในเอกสารของ Ruby จะมีคำอธิบายถึง Class ว่า

When a new class is created (typically using class Name ... end), an object of type Class is created and assigned to a global constant (Name in this case).


ถ้าลองนำมาเขียนในแบบที่ไม่ได้ใช้ syntax ปกติดู
Object.const_set("Person", Class.new)
pok = Person.new

method const_set คือการ set ค่า global constant
ในทีนี้ก็คือการ set global constant ที่ชื่อ "Person"

ที่นี้ถ้าต้องการ define method ก็สามารถใช้พวก class_eval ได้เลย
Person.class_eval do
attr_accessor :name, :gender
def to_s
"#{name}, #{gender}"
end
end

pok.name = "polawat"
pok.gender = "male"
puts pok # => polawat, male

Related link from Roti

Friday, March 24, 2006

SwingX

SwingX ผลงานของ Romain Guy
จับ swing มาแต่งหน้า



ดูเปรียบเทีียบกับตอน layout ใน netbeans
(ใช้ matisse ด้วย)



บริเวณ header ที่มีแสงสะท้อน (gloss) กับลายแถบ (strips)
เขาเขียน code แบบนี้
private org.jdesktop.swingx.JXPanel header;

private void setupHeaderPainters() {
GlossPainter gloss = new GlossPainter(new Color(1.0f, 1.0f, 1.0f, 0.2f),
GlossPainter.GlossPosition.TOP);

PinstripePainter stripes = new PinstripePainter();
stripes.setPaint(new Color(1.0f, 1.0f, 1.0f, 0.17f));
stripes.setSpacing(5.0);

MattePainter matte = new MattePainter(new Color(51, 51, 51));

header.setBackgroundPainter(new CompoundPainter(matte, stripes, gloss));
}

MattePainter คือ การลงสี
สังเกตว่ามีการเรียงลำดัับใน CompoundPainter ด้วย

ส่วน wood surface เขาลงสีด้วย BasicGradientPainter
เพื่อให้มีเงาของ menu ข้างบน
จากนั้นก็ใส่ Noise ด้วย NoiseFilter
ตบให้ noise กลายเป็นเส้นด้วย MotionBlurFilter
(พวก xxxFilter นี่เขาใช้ library ของ JH Labs (Java Image Processing)
BasicGradientPainter gradient = new BasicGradientPainter(
new GradientPaint(new Point2D.Double(0.0, 0.0), new Color(0xd67801),
new Point2D.Double(0.0, 1.0), new Color(0xb35b01)));
NoiseFilter noise = new NoiseFilter();
noise.setDistribution(NoiseFilter.GAUSSIAN);
noise.setMonochrome(true);
MotionBlurFilter blur = new MotionBlurFilter();
blur.setDistance(1.0f);
BrushedMetalFilter metal = new BrushedMetalFilter();
metal.setMonochrome(false);
gradient.setEffect(new CompoundEffect(new ImageEffect(noise),
new ImageEffect(blur)));



ปัญหาของการใช้ effect นี้ ก็คือความเร็ว
เครื่องผม (g5) ใช้เวลา render ไป 3 วินาที

เห็นผู้เขียนเขาบอกว่าใน Mustang จะดีกว่านี้
ไม่มีปัญหาเรื่องกระตุก

ใครสนใจ code ลองไป checkout ออกมาเล่นดูได้

Related link from Roti

สร้าง Maven2 Archetype Plugin

วันนี้ลองสร้าง archetype ดู
เป้าหมายก็เพื่อเอาไว้ทำ template สำหรับ setup project ใหม่ๆ
เนื่องจากช่วงนี้มีน้องใหม่เข้ามาทำงานหลายคน
ต้องมี tool เข้ามาช่วยหน่อย
(ไม่งั้น เวลา set project ใหม่ๆ เพื่อไว้ train
ก็ต้องมานั่ง copy, paste กันวุ่นวาย)

เริ่มด้วย
structure ของ Archetype Plugin ต้องมีหน้าตาดังนี้



เริ่มแรกสุดต้องมี file pom.xml
ซึ่ง file ที่เก็บ information ของ plugin เรา
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>mx.plugin.arch.struts</groupId>
<artifactId>strutsApp</artifactId>
<version>0.1</version>
<packaging>maven-plugin</packaging>
</project>


Directory archetype-resources
คือ directory ที่เก็บ template file ของเรา
มีโครงสร้างและ files ตามที่เราอยากให้ archetype generate



file ที่สำคัญที่สุดก็คือ src/main/resources/META-INF/archetype.xml
เนื่องจากเป็น file ที่ใช้บอกว่า archetype ต้อง copy file อะไรบ้าง
<archetype>
<id>strutsApp</id>
<sources>
<source>src/main/java/Main.java</source>
</sources>
<testSources>
<source>src/test/java/Main.java</source>
</testSources>
<resources>
<resource>src/webapp/WEB-INF/web.xml</resource>
<resource>src/webapp/WEB-INF/struts-bean.tld</resource>
<resource>src/webapp/WEB-INF/struts-config.xml</resource>
<resource>src/webapp/WEB-INF/struts-html.tld</resource>
<resource>src/webapp/WEB-INF/struts-logic.tld</resource>
<resource>src/webapp/WEB-INF/struts-nested.tld</resource>
<resource>src/webapp/WEB-INF/tiles-defs.xml</resource>
<resource>src/webapp/WEB-INF/validation.xml</resource>
<resource>src/webapp/WEB-INF/validation-rules.xml</resource>
<resource>src/webapp/WEB-INF/classes/MessageResources.properties</resource>
</resources>
</archetype>


เวลาที่ archetype สร้าง file ให้เรานั้น
archetype ไม่ได้แค่ copy file เฉยๆ
แต่มันจะ transform file ให้เราด้วย
โดยมันจะใช้ velocity เข้ามาช่วย
(file ที่เราเตรียมไว้ จะกลายเป็น input template ของ velocity)

ยกตัวอย่าง file web.xml
ถ้าเรากำหนดเนื้อหาแบบนี้

<web-app>
<display-name>${artifactId}</display-name>

ตอนที่ velocity transform template แปลง
ก็จะมองหา System Property ที่ชื่อ artifactId
ถ้าหาได้ ก็จะนำมาแทนค่าให้

ยังมี case พิเศษอีกคือ
กรณีที่เป็น java code
(archetype รู้ได้จากการใช้ tag sources และ testSources
ใน archetype.xml)
ตอนที่ archetype แปลงนั้น
มันจะทำการแปะ package (สร้้าง directory) ให้เราด้วย
โดยชื่อของ package จะตั้งตาม System Property ที่ชื่อ groupId
ดังนั้นกรณีที่เราไม่อยากให้มันยุ่งกับ java file เรา
ก็กำหนดให้มันเป็น resource แทนที่จะเป็น source

หลังจากเตรียม file ทั้งหมดเสร็จ
ก็ให้ใช้คำสั่ง
mvn install

เพื่อสั่งให้ maven pack plugin เรา และ deploy เข้าไปไว้ใน local repository

เมื่อต้องการใช้ ก็แค่สั่ง (ไม่ใช่ "แค่" เพราะคำสั่งโคตรยาวเลย)
mvn archetype:create -DarchetypeArtifactId=strutsApp -DarchetypeGroupId=mx.plugin.arch.struts -DartifactId=pr1 -DgroupId=mx.pok.test -DarchetypeVersion=0.1   


อ่านเพิ่มเติม
http://maven.apache.org/guides/mini/guide-creating-archetypes.html

Note: เอกสารที่อ้างถึง อธิบายตกไปบางจุด
โชคดีที่มีคน submit project ตัวอย่างเข้ามาใน svn ของ apache
เมื่อ 2 วันก่อน ผมก็เลยรอดตายมาได้
ไม่งั้นคงไล่ไม่เจอ

Related link from Roti

Thursday, March 23, 2006

Torta

Torta เป็นโปรแกรมที่เขียนด้วย clisp
ช่วยในการ present harddisk ของเรา ว่ามีอะไรอยู่บ้าง
โดยมันจะ scan directory ที่เรากำหนด
จากนั้นก็สร้าง flash file ที่แสดง output ให้เรา
 (torta:torta "/Users/pphetra" :output "/tmp/pok.swf")


hdpok

ของเครื่องผม เบอร์หนึ่งคือ Movie ของเจ้าลูกชาย (ไม่ใช่หนังโป๊นะ)
ส่วน image ภาพศิลป์นั้น ไม่กินที่เยอะอย่างที่คิด

ถ้าดูพื้นที่ทำงาน, Java project ของผมกินที่ไป 400 MByte

ที่กินที่เยอะโดยไม่รู้ตัว ก็คือ cocoon
พี่แกเล่นกินที่ส่วน build ไป 336 MByte
(อันนี้คนละพื้นที่กับ java project)

rails ไม่ต้องพูดถึง code มันน้อยๆ
ไม่ติดฝุ่นเขาเลย

Related link from Roti

emacs+ruby, scheme

ผมเคยเขียน Customize Syntax Highlight
ด้วย Ruby ไปที
ตอนนั้นใช้ Ruby Syntax 1.0.0 ช่วยทำ

วันนี้มีเหตุให้ปัดฝุ่นกลับมาใช้อีกครั้ง
แต่คราวนี้มาในรูปแบบพิสดารกว่าเดิม
เป้าหมายในคราวนี้คือ

ตัวโปรแกรมจะ parse ruby code
output ที่ได้จะอยู่ในรูป scheme code
(syntax highlight ในรูปแบบ scheme code)
โดยโปรแกรมต้องสามารถเรียกจาก emacs ได้โดยตรง

ตัวโปรแกรม ruby เขียนไม่ยาก
แค่ extend Syntax::Convertors::Abstract
class Ruby2SS < Syntax::Convertors::Abstract

def convert(text)
str = "(page-para (code-block (code-line "
regions = []
@tokenizer.tokenize(text) do |tok|
str << to_scheme(tok)
end
str << ')))'
end

def to_scheme(tok)
case tok.group
when :punct :
if tok =~ /"|'/
"(code 'punct \"\\#{tok}\")\n"
else
"(code 'punct \"#{tok}\")\n"
end
when :normal :
if tok =~ /[^\n]*\n([^\n]*)/
")\n (code-line (code 'normal \"#{$1}\") \n"
else
"(code 'normal \"#{tok}\")\n"
end
else
"(code '#{tok.group} \"#{tok}\")\n"
end
end

end


code ที่ได้หน้าตาแบบนี้
(page-para (code-block 
(code-line (code 'ident "order")
(code 'normal " ")
(code 'punct "=")
(code 'normal " ")
(code 'constant "Order")
(code 'punct ".")
(code 'ident "new")
)
(code-line (code 'normal "")
(code 'ident "order")
(code 'punct ".")
(code 'ident "name")
(code 'normal " ")
(code 'punct "=")
(code 'normal " ")
(code 'punct "\"")
(code 'string "")
(code 'string "pok")
(code 'string "")
(code 'punct "\"")
)))


ซึ่งจะเอาไป output ออกเป็น image อีกที (ไม่ขอพูดถึงตอนนี้)

เวลาใช้ก็ต้อง integrate เข้ากับ emacs
โดยผมเลือกเอา el4r มาใช้
โดยต้องเขียน function ขึ้นมา 2 ตัว
ตัวแรกเป็น ruby function ที่อยู่ใน form ที่ emacs สามารถ call ได้
defun(:ruby_syntax_to_scheme,
:interactive => false) { |code|
Ruby2SS.new(Syntax::load('ruby')).convert code
}

ส่วนอีกตัว ก็คือ elisp ที่ทำหน้าที่ดึงข้อมูลจาก buffer แล้วส่งต่อ
ให้ ruby function จากนั้นก็นำผลลัพท์ไปใส่ไว้ใน clipboard
(defun cv-ruby-syntax-to-scheme-slide (beg end)
(interactive "r")
(let ((str (filter-buffer-substring beg end))
(x-select-enable-clipboard t))
(kill-new (ruby-syntax-to-scheme str) 'replace)))
(define-key ruby-mode-map "\C-c\C-p" 'cv-ruby-syntax-to-scheme-slide)


(จริงๆเขียนฝั่ง ruby ตัวเดียวก็ได้ แต่ขึ้เกียจ debug หรือ หัดใช้ el4r มากกว่านี้
ก็เลยเอาแค่ทำงานได้)

Related link from Roti

ECF

หลังจากมี EMF, GEF, GMF
(Graphical Editor Framework,
Eclipse Modelling Framework,
Graphical Modelling Framework)

ตอนนี้ก็ถึงคราว ECF บ้าง
ย่อจาก Eclipse Communication Framework
เป้าหลักๆของ ECF น่าจะทำไว้เพื่อเป็น Facility ของ
Real-time collaborative editing
แบบที่ jcreator มี

Related link from Roti

Eclipse releng

วันนี้นั่งอ่าน presentation ของ Eclipse releng (Release Engineering)
From Developer to Download: A Tour of the Eclipse Platform Build Factory
น่าสนใจดี

process ของ releng เริ่มจาก
developer จะ submit เข้ามาว่า
stable version ล่าสุดของตนคืออะไร
ผ่านทาง map file
ที่มีหน้าตาประมาณนี้

plugin@org.eclipse.ui=I20060117-0800,:pserver:anonymous@dev.eclipse.org:/cvsroot/eclipse

ข้อมูลนี้จะไว้ใช้ build integration builds
ส่วน nightly build จะ fetch map file จาก cvs HEAD ตรงๆเลย

script และ platform (software) ที่จะใช้ในการ build
จะถูกเก็บไว้ใน local CVS ที่อยู่บนเครื่องอีกเครื่องหนึ่ง
เพื่อให้สามารถเปลี่ยน build machine ได้ง่ายๆ (กรณี machine failure)

เมื่อถึงรอบเวลาที่จะทำการ build
build machine (G5 + Yellow Linux)
ก็จะทำดังนี้
  • checkout target code จาก dev.eclipse.org
  • checkout JDK
  • เรียก Ant file เพื่อทำการ build

น่าสนใจนะว่า JDK ก็อยู่ใน repository ด้วย

เสร็จจาก build ก็เป็นเรื่อง test
test แบ่งหัวเรื่องที่ test ออกเป็น
  • JUnit Tests
  • Performance tests
  • Verification for translation
  • Javadoc
  • API scanner
    scan หา internal api ?ไม่แน่ใจว่าตรงนี้หาที่ไหน
    ที่ javadoc หรือที่ไหน

ในส่วนของ JUnit กับ Performance Test
จะทำขนานกัน
(โดยใน Ant script จะใช้คำสั่ง ant parallel task )

JUnit test จะ test บน Windows, Linux, Mac
ส่วน Performance Test จะใช้ 5 เครื่อง
ประกอบด้วย 3 linux, 2 Window
เป็นเครื่องเร็วๆ 3 เครื่อง, เครื่อง cpu ช้าๆ 2 เครื่อง
ผลลัพท์ที่ได้ จะเก็บใน Derby Database Server

เครื่องที่ใช้ใน Performance Test
จะถูก Ghost ทุกๆ อาทิตย์ เพื่อที่จะได้ไม่มีตัวแปรเรื่อง
software configuration ที่จะไปรบกวน ผลการ test

เมื่อผ่านการ test ทั้งหมด
ก็เป็นขั้นการ publish ผลลัพท์
ขั้นตอนก็คือ ใช้ template สร้าง web page
ใน build machine
จากนั้นก็ใช้ rsync sync ข้อมูลกับ public site

ที่ชอบใจก็คือ เขา list ให้ดูเล่นๆว่า
failure ที่เกิดขึ้น มาจากอะไรได้บ้าง
(เขาใช้คำว่า "bloopers" -> "an embarrassing error" )
Usual Suspects
  • สะกดผิด เช่น map file ระบุ version อะไรมาก็ไม่รู้
  • disk space เต็ม
  • Screen savers interupt macro tests
  • Virus Scanner ตื่นขึ้นมาทำงาน ระหว่าง test
  • X window crashes
  • Network fail ระหว่าง checkout

Extraordinary Build Bloopers
  • ฟ้าผ่า
  • ไฟดับ
  • overheated เพราะ แอร์ไม่เย็น


สุดท้ายก็เป็นขั้นตอน release
ที่ต้องจัดการเรื่อง Mirror Servers ทั้งหลาย
รวมทั้ง backup servers
ตัวเลขที่น่าสนใจ ก็คือ
jar file ที่ถูก download จาก eclipse.org โดยตรง
จะตกประมาณวันละ 1,351,152 ครั้ง

Related link from Roti