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

Tuesday, March 21, 2006

Rails Localization Plugin

Localization Plugin เป็น Plugin ที่ช่วยทำ localize อย่างง่ายๆ
ไม่ต้องไปยุ่งกับโปรแกรม gettext

หลักการ ก็คือเราต้องสร้าง ruby program ใต้
${RAILS_ROOT}/lang
จะแยกเป็น file ตาม language หรือ รวมทุก lang ไว้ใน file เดียวก็ได้
เนื้อหาใน file จะเป็นดังนี้
Localization.define('th') do |l|
l.store 'yes', 'ตกลง'
l.store 'no', 'ไม่ตกลง'
l.store 'hello world', 'สวัสดีครับ'
end

เวลาใช้ใน view ก็เรียกใช้ผ่าน method _
 <body>
<%= _ 'hello world' %>
</body>

การ switch ภาษา จะทำผ่าน Object Localization

Localization.lang = params[:lang] == nil ? :default : params[:lang]


feature ที่ดูน่าสนใจ อยู่ที่ตรงการ Localization.define
ที่เราสามารถทำอะไรแบบนี้ได้
l.store '(time)', lambda { |t| t.strftime('%H:%M') }

ใน view จะเขียนแบบนี้
<%= _ '(time)', Time.now %>

Related link from Roti

Rails 's Date Widget Plugin

ปกติใน rails, ถ้าเราต้องการ entry ข้อมูลที่เป็น Date
rails จะมี helper ที่ชื่อ date_select

date_select("period", "start_date")

ซึ่งได้หน้าตาของ UI ออกมาอย่างนี้



ดูแล้วไม่ถูกใจ user ชาวไทยอย่างแน่นอน
ผมก็เลยหาทางเปลี่ยนไปใช้ jsCalendar แทน
หน้าตาที่ได้ ก็จะเปลี่ยนไปเป็นแบบนี้



โดย jsCalendar มีวิธีการใช้ง่ายๆดังนี้
<input type="text" id="data" name="period[dp_start_date]"/>
<button id="trigger">...</button>
<script type="text/javascript">
Calendar.setup(
{
inputField : "data",
ifFormat : "%d/%m/%y",
button : "trigger"
}
);
</script>


ที่นี้ ถ้าอยากนำมาใช้ใน rails
จะทำอย่างไรให้ดูเป็น rails-style
(เขียนน้อยๆ, default เยอะๆ)

ประเด็นของการทำ widget เอง คงแยกเป็น 2 เรื่อง คือ
  • การ render
  • การ parse request parameter


กรณีของการ render เราจะทำ helper method ขึ้นมาใหม่ตัวหนึ่ง
ให้ชื่อว่า date_picker
มีลักษณะการใช้งานดังนี้
<%= date_picker "period", "start_date" %>

(จะเห็นว่ามีวิธีการใช้ที่เหมือนกับ date_select ของ Rails เลย)

ส่วนการ parse parameter นั้น
จะออกแบบให้ transparent กับ code เดิม
โดยต้องการให้ใช้กับ code ที่ gen จาก scaffold ได้เลย
(code ที่อยู่ใน controller class)

ของเดิม เวลา date_select submit ข้อมูลกลับมา
ข้อมูลที่กลับมาจะอยู่ในรูปแบบนี้

param-name param-value
===========================
start_date(1i) 2006
start_date(2i) 03 (เดือน)
start_date(3i) 20 (วัน)

เราก็จะเลียนแบบวิธีการส่ง parameter แบบเดิมนี้ (rails เรียกว่า multiparameter_attributes)
โดยแทรก filter เข้าไป
เพื่อทำการดักแปลงข้อมูลที่ submit มาจาก form

การใช้งาน filter ออกแบบให้ใช้คำสั่งแบบนี้้
class ApplicationController < ActionController::Base
date_picker_filter
end


เมื่อตกลงใจเรื่อง design ได้แล้ว ก็มาถึงคำถามว่า
จะ implement เป็น plugin ได้อย่างไร

เริ่มด้วยการตั้งชื่อให้ plugin เราก่อน
โดยใช้ชื่อว่า datepicker

directory structure ของ plugin เป็นแบบนี้

+project-name
+app
+...
+vendor
+plugins
+datepicker
+lib
datepicker.rb
init.rb

file init.rb เป็น file ที่จะถูก rails เรียกใช้
มีเนื้อหาดังนี้

require 'datepicker'
ActionController::Base.send :include, DatePicker

การทำงานก็คือ สั่งให้ Class ActionController::Base include
Module Datepicker ของเรา (Mixin)

ใน module Datepicker
จะเริ่มด้วย method self.included
method นี้เป็น callback method
ซึ่งจะถูกเรียกใช้เมื่อมีการเรียก include module ของเรา

logic ที่ Datepicker ทำ ก็คือ
  • สั่ง extend controller ด้วย module ClassMethods
    add class method date_picker_filter ให้เรียกใช้จาก controller ได้
  • เพิ่ม helper date_picker ที่เรียกใช้จากใน view หรือใน controller ได้

module DatePicker

def self.included(controller)
controller.extend(ClassMethods)
controller.helper_method(:date_picker)
end

module ClassMethods
def date_picker_filter(options = {})
before_filter do |c|
ParamUpdator.new(options).update(c.params)
end
end
end

...

def date_picker(object, method, options = {})
...
end


ใน method date_picker_filter
จะมีการ add filter โดยใช้คำสั่ง before_filter)
และใช้ class ParamUpdator ในการ scan และแปลงวันที่ที่อยู่ใน params object

class ParamUpdator
def initialize(options={})
@prefix = options[:prefix] || 'dp_'
@be = options[:ad] || false # buddhist era
@delim = options[:delim] || '/'
@prefix_regexp = Regexp.new("^#{@prefix}")
@delim_regexp = Regexp.new(@delim);
end

def update(hash)
hash.each do |key, value|
if value.class.to_s =~ /^Hash/
update(value)
else
if @prefix_regexp =~ key
update_param(hash, key, value)
end
end
end
end

def update_param(hash, key, value)
darys = parseDate(value)
realName = key[(@prefix.length)..(key.length)]
hash["#{realName}(1i)"] = darys[0]
hash["#{realName}(2i)"] = darys[1]
hash["#{realName}(3i)"] = darys[2]
hash.delete(key)
end

def parseDate(value)
darys = value.split(@delim_regexp);
if darys[2].length <= 2
darys[2] = (darys[2].to_i + (@be ? 1957 : 2000)).to_s
end
darys.reverse
end

end #end ParamUpdator

ประเด็นที่น่าสนใจในส่วนของการแปลง parameter ก็คือ
object params ไม่ได้เป็น instance ของ Hash
แต่เป็น instance ของ HashWithIndifferentAccess

ส่วน date_picker ที่ใช้ render ก็มีหน้าตาแบบนี้

def date_picker(object, method, options = {})
prefix = options[:prefix] || "dp_"
format = options[:format] || "%d/%m/%y"
size = options[:size] || "10"
inputClazz = options[:input_class] || "date_input"
triggerClazz = options[:trigger_class] || "date_trigger"
be = options[:be] || false

maxsize = options[:maxsize] || format.length
id = "#{object}_#{method}"
name = "#{object}[#{prefix}#{method}]"
obj = self.instance_variable_get "@#{object}"
value = obj == nil ? "" : (date_to_string(format, obj.send(method), be))

<<EOS
<input type="text" size="#{size}" maxsize="#{maxsize}"
id="#{id}" name="#{name}"
class="#{inputClazz}" value="#{value}"/>
<button id="trigger_#{id}" class="#{triggerClazz}">...</button>
<script>
Calendar.setup(
{
inputField : "#{id}",
ifFormat : "#{format}",
button : "trigger_#{id}"
}
);
</script>
EOS

end

ประเด็นที่น่าสนใจ ก็คือการ get value จาก Model instance
ที่ใช้ method instance_variable_get
กับ assumption ที่ว่า model instance จะอยู่ในรูป instance variable

เหลือที่ยังไม่ได้ทำ ก็คือ ส่วนของการ validate
ที่จะ implement ด้วย javascript ที่ฝั่ง browser เลย

Related link from Roti

Friday, March 17, 2006

Hibernate(3.2) Transformer

ได้ข่าวมาจาก blog.hibernate.org
Hibernate 3.2 มี Helper ดีๆที่ช่วย convert object[]
เป็น pojo หรือ java.util.Map แล้ว

เดิมเวลาที่เรา select partial column ใน hibernate
Object ที่ return กลับมาก็คือ Object[]
List list = s.createQuery(
"select e.student.name as studentName, " +
" e.course.desription as courseDescription " +
"from Enrollment as e")
.list();
Object[] row1 = (Object[]) list.get(0) // return => object[]


แต่ถ้ามี Transformer ช่วยเราสามารถเขียนแบบนี้ได้เลย
List list = s.createQuery(
"select e.student.name as studentName," +
" e.course.description as courseDescription" +
"from Enrolment as e")
.setResultTransformer( Transformers.aliasToBean(StudentDTO.class))
.list();

StudentDTO dto = (StudentDTO) list.get(0);


นอกจากใช้ได้กับ createQuery แล้ว ยังใช้ได้กับ createCriteria
และ createSQLQuery อีกด้วย

สุดท้าย ก็คือสามารถแปลงเป็น Map object แทนที่จะเป็น bean
.setResultTransformer( Transformers.ALIAS_TO_MAP )

Related link from Roti

Thursday, March 16, 2006

การ debug ruby ด้วย emacs

ปกติใน ruby เราสามารถสั่ง debug ด้วยคำสั่ง
ruby -r debug filename.rb

ซึ่ง ruby จะเข้าสู่ mode debug

ข้อเสียของการ debug แบบนี้ ก็อาจจะเป็นเรื่องมุมมองที่จำกัด (การดู source code)
กับความสะดวกในการ set break point

วันนี้ผมก็เลยลองเอา emacs มาช่วย debug
ก่อนอื่น เราต้องมี file ที่ชื่อ rubydb3x.el หรือ rubydb2x ก่อน
เจ้าตัวที่ชื่อ 3x ใช้กับ emacs ที่ version >= 19.3x ขึ้นไป



ปกติใน emacs มันจะมีโปรแกรมสำหรับใช้ในการ debug อยู่แล้ว
เรียกว่า gud , rubydb ก็ extend จาก gud นี่แหล่ะ

ก่อนจะใช้ก็ต้องมีการ load ขึ้นมาก่อน โดยใช้คำสั่ง
M-x load-library rubydb3x.el
M-x rubydb

หลังจากสั่งมันจะ prompt ขึ้นมาถาม

Run rubydb (like this):

กรณีเครื่องผม ผมมี ruby หลายตัว ก็เลยใส่ path เต็มดังนี้
/usr/local/bin/ruby -r debug filename.rb

filename คือ ruby file ที่ต้องการ debug

เมื่อ start ขึ้นมาแล้ว emacs จะเปิด buffer ใหม่ที่ชื่อ gdb-filename ขึ้นมา
พร้อมกับเปิด buffer แสดง ruby file ที่เราระบุ
และ process จะไปหยุดรออยู่ที่บรรทัดที่ 1 ของ ruby file ที่ระบุ

note: แต่ถ้า shell environment variable ของเรามีการ set ค่า
RUBYOPT=rubygems ไว้
มันก็จะไปหยุดในโปรแกรม rubygems.rb แทน

จากนั้นเราก็สามารถสั่ง debug command ได้จาก buffer ที่แสดง source code
หรือจาก buffer ของ gdb ก็ได้
keyboard binding ที่มาพร้อม rubydb3x ก็มี
  • "\C-b" "Set breakpoint at current line."
  • "\C-s" "Step one source line with display."
  • "\C-n" "Step one line (skip functions)."
  • "\C-r" "Continue with display."
  • "\C-f" "Finish executing current function."
  • "<" "Up N stack frames (numeric arg)."
  • ">" "Down N stack frames (numeric arg)."
  • "\C-p" "Evaluate ruby expression at point."

note: คำสั่งข้างบน ต้องมี prefix key นำด้วย
อย่างกรณีเครื่องผม ต้องกด C-x,C-a นำหน้าก่อน

ที่นี้ก็มาถึงการ customize rubydb เพิ่ม binding หรือคำสั่งที่ต้องการ
อย่างคำสั่งที่ผมใช้บ่อยๆ ตอนใล่ทำความเข้าใจ source code ก็คือ
คึำสั่ง v[ar] l[ocal] ที่ใช้แสดง local variables
เราสามารถเพิ่ม binding เข้าไปได้โดยเขียน elisp แบบนี้

(gud-def gud-view-local "v l" "\C-l" "show local variable")

คำสั่ง gud-def เป็น macro ที่ช่วยในการ define debug command
gud-view-local คือชื่อ command ของเรา (ตั้งชื่อขึ้นมาเอง)
คำสั่งนี้ map เข้ากับ คำสั่ง v l (command ของ ruby)
และ binding เข้ากับ key (C-x, C-a) C-l

มีคำสั่งอยู่หนึ่งคำสั่ง ที่ผมใช้แล้วไม่ work ก็คือ
gud-print ที่ใช้แสดง ruby expression ที่จุดที่ keyboard อยู่
ของผมสั่งแล้ว มันได้ expression กลับมาเป็น method ทั้งก้อนที่กำลังทำงานอยู่
ก็เลยต้อง customize ให้มันแสดงตัวแปรที่ cursor ชี้อยู่
โดย gdb เตรียม variable ตัวหนึ่งไว้ให้เราแล้ว
มีชื่อว่า gud-find-expr-function
โดยปกติมันจะชี้อยู่ที่ function gud-find-c-expr

;; Code for parsing expressions out of C or Fortran code. The single entry
;; point is gud-find-expr, which tries to return an lvalue expression from
;; around point.

(defvar gud-find-expr-function 'gud-find-c-expr)

เราสามารถเปลี่ยนไปชี้ custom function ของเราแทนได้
อย่างของผม ผมเปลี่ยนเป็นการ select inner most expr แทน

(defun gud-custom-find-expr()
(interactive)
(save-excursion
(let ((pos (gud-innermost-expr)))
(buffer-substring-no-properties (first pos) (cdr pos)))))

(setq gud-find-expr-function 'gud-custom-find-expr)

Related link from Roti

Wednesday, March 15, 2006

Native Leak?

App ที่ผมทำให้ลูกค้ารายหนึ่ง
เดิม run อยู่บน IBM P-series
ก็ใช้ได้ปกติดี ตายบ้างเป็นปกติ (ปีละ 2-3 ครั้ง)
เวลาล่ม ก็มีทั้ง user ทำล่ม
เช่น เรียก report ใหญ่ๆในเวลาที่ load online กำลัง peak
พอ report response ช้า ก็ submit เข้ามาใหม่
ส่วนกรณีที่เป็นที่ system ก็คือ
java มันเบื่อขึ้นมา มันก็ core dump ซะเฉยๆ

พอย้ายไปอยู่บน mainframe
คราวนี้ล่มรายอาทิตย์เลย
(โชคดีที่เขาจัดจ้าง team อื่นเข้ามาดูเรื่องนี้โดยเฉพาะ
ไม่ใช่หน้าที่ของบริษัทผม
ผมก็เลยไม่ต้องปวดหัวมากนัก)

ช่วงแรกๆ หลายๆคนที่เกี่ยวข้อง ก็ชี้นิ้วอย่างเดียว
เขียนโปรแกรมอย่างไง
ใช้ technique ถูกต้องหรือเปล่า
system ไม่มีปัญหา ล่มเพราะ app แหง๋ๆ
(แปลกที่ไม่มีใครย้อนไปดูเลยว่า app มัน run มาปีกว่าแล้ว)
ปากเปียกปากแฉะ อธิบายไป
พร้อมกับช่วยจำแนก อธิบายวิธีแก้ปัญหา

ปัญหาก็ค่อยๆแก้ไปเรื่อยๆ
ช่วงนี้เหลือ error หนักๆ ก็คือ
มันจะค่อยมี error out of memory ขึ้นมาอยู่เรื่อยๆ
(ไม่ถี่มาก แต่ก็ถือว่าเป็นปัญหา เพราะเป็นระบบ online
ที่ให้บริการ ณ counter ทั่วประเทศ)

วันนี้ได้ cc จดหมายจาก IBM

Javacore shows that both the Xms and Xmx have been set to 1280 m.
The heapdump shows that only 380 mb of heap is occupied.
Verbose GC shows that at least 80% heap is free when the oom occured.
This looks to be a case of native memory exhaustion.
...
Based on these facts we would reccomend the changing the Xmx and Xms
Heap Settigns as below to ensure that we have enough native memory
left with us for native allocation Reduce the Xmx value to 1024 mb
Reduce the Xms value to 256 mb


ตอนนี้ผู้ต้องสงสัยเบอร์ 1 คือ jdbc type 2
แต่โชคร้ายที่ jdbc type 4 มันก็มี known issue ของมันอยู่
ทำให้ไม่สามารถ switch ไปเฉยๆได้

Note: ข้อเตือนใจ
เขียน app run ใน type 2 ได้
อย่าคาดหวังว่าแค่เปลี่ยน driver type
แล้วมันจะ smooth run ต่อได้นะครับ

ลืมบอกไป
product ผู้สนับสนุน bug อย่างเป็นทางการ ก็คือ websphere 5 ครับ

Related link from Roti

วันนี้นั่งฝึกทำโจทย์ใน Ruby Quiz
มีโจทย์อยู่ข้อ ที่ต้องเขียน method ที่รับ parameter เป็น Number หรือ Range ก็ได้
โดยมีจำนวนไม่จำกัด
เช่น

build(1, 7, 11, 20..24, 32..78)
build(1..2)
build(8, 11..17)

ต้องการแปลงให้เป็น array ชุดเดียว ที่เก็บเลขที่เป็นไปได้ทั้งหมด
ผมก็นั่งเขียนทื่อๆเลย

def build(*args)
list = []
args.each {|arg|
case
when arg.class == Range:
arg.each {|n|
list << n
}
else
list << arg
end
}
list
end

ไปเห็น code ที่คนอื่นเขียนไว้
โจทย์เดียวกัน

def build(*args)
args = args.map {|arg| Array(arg) }.flatten
end


"เด็กเอ๋ยเด็กน้อย ความรู้เจ้ายังด้อยเร่งศึกษา.."

Related link from Roti

ติดตั้ง Ingres2006 บน Gentoo 2006.0

ca-openingres move ไปสังกัด Ingres Corporation อีกแล้ว
คราวนี้ใช้ชื่อเป็น ingres2006
ก็เลย load มาติดตั้งเสียหน่อย

file ที่ให้มาเป็นพวก rpm file
เครื่องผมไม่ได้ลงไว้
ก็เลยติดตั้ง package rpm โดยใช้คำสั่ง
emerge rpm

ในคู่มือของ Ingres กรณีที่ต้องการความสะดวกก็ให้เรียก program ingres_express_install.sh
แต่เครื่องผม มันฟ้อง error ของ rpm ว่าไม่สามารถลงได้ เพราะ /bin/sh ไม่มี
ก็เลยเข้าไปแก้ script ingres_express_install.sh
เพิ่ม --nodeps ลงไปในคำสั่ง rpm

หลังจากสั่ง install โปรแกรมจะฟ้อง error จำนวนหนึ่ง
ประมาณว่า
ingres: iisutm command not found
ingres: ii.... command not found
ให้จดไว้ มา run เองข้างนอก

ก่อน run ก็ให้ login เป็น user ingres (rpm มันสร้างให้แล้ว)
สั่ง setup
export II_SYSTEM=/opt/Ingres/IngresII
export PATH=$PATH:$II_SYSTEM/ingres/bin:$II_SYSTEM/ingres/utility
export LD_LIBRARY_PATH=$II_SYSTEM/ingres/lib


แล้วก็ใล่ run ii... ทั้งหลาย

Related link from Roti

Tuesday, March 14, 2006

เปลี่ยน Caps-lock key เป็น Control key

สำหรับพวกใช้นิ้วก้อยเยอะ
ใน linux console เราสามารถเปลี่ยน Caps-lock key ได้ดังนี้

dumpkeys > my_keymap

แก้ไข file ตรงตำแหน่ง

keycode 58 = Caps_Lock #=> เปลี่ยนเป็น Control

จากนั้นก็ load key โดยใช้คำสั่ง

loadkeys my_keymap

Related link from Roti

Sunday, March 12, 2006

ศึกษา Student (AI Program สมัยแรกๆ)

วันนี้ลองนั่งเขียนโปรแกรมที่ solve algebra equation ดู
พวกที่เป็นโจทย์แบบนี้ -> (x + 2)/ 10 = 8

ลอกวิธีการมาจาก code ในหนังสือ Paradigms of AI Programming
ของ Peter Norvig
โดยเนื้อหาที่เขาพูดถึง ก็คือวิธีการ implement program Student
ที่ Daniel Bobrow ทำ Research Project ไว้ในปี 1964
ตัวโปรแกรม Student ออกแบบมาให้ solve โจทย์แบบนี้


(student '(If the number of customers tom gets is twice the square of
20 % of the number of advertisements he run |.|
and then number of advertisements is 45 |.|
then what is the number of customers Tom gets ?))

;; ที่เขียนข้างบนนี้เป็นโปรแกรมจริงๆนะ run ได้ ไม่ใช่ description

ผมตัดมาให้ดูเฉพาะส่วน solve equation (เฉพาะที่มีตัวแปรเดียว)
หลักการ ก็คือ แปลงโจทย์ให้อยู่ในรูป tree structure ให้ได้ก่อน

=
/ \
(/) 8
/ \
+ 10
/ \
x 2

ตรงนี้ lisp ได้เปรียบในการ represent
เพราะพวก tree structure สามารถเขียนในรูป list ได้ตรงๆเลย
กรณีข้างบน ก็เขียนได้ดังนี้

'(= (/ (+ x 2) 10) 8)

ส่วน ruby ผมลองใช้ class ดู
ซึ่งเขียนแล้ว ก็รู้สึกเยิ่นเย้อนิดๆ (ขนาดมี attr_accessor ช่วย)

class Exp
include ExpHelper

attr_accessor :op, :lhs, :rhs
def initialize(lhs, op, rhs)
@lhs = lhs
@op = op
@rhs = rhs
end

...

end

ที่รู้สึกเยิ่นเย้อ ก็เพราะ lisp จะเขียนแค่นี้

(defstruct (exp (:type list)
(:constructor mkexp (lhs op rhs)))
op lhs rhs)


ขั้นถัดไป ก็คือก็ทำการย้ายข้างให้ ด้านซ้าย มีแต่ตัวแปรอย่างเดียว
กรณีโจทย์ข้างบน ก็จะถูกแปลงเป็น x = (8 * 10) - 2
ใน lisp ที่ norvig เขียน จะมีหน้าตาแบบนี้

(defun isolate (e x)
"Isolate the lone x in e on the left-hand side of e."
;; This assumes there is exactly one x in e
;; and that e is an equation.
(cond ((eq (exp-lhs e) x)
;; case I: X = A -> X = n
e)
((in-exp x (exp-rhs e))
;; case II: A = f(x) -> f(x) = A
(isolate (mkexp (exp-rhs e) '= (exp-lhs e)) x))
((in-exp x (exp-lhs (exp-lhs e)))
;; Case III: f(x) * A = B -> f(x) = B/A
(isolate (mkexp (exp-lhs (exp-lhs e)) '=
(mkexp (exp-rhs e)
(inverse-op (exp-op (exp-lhs e)))
(exp-rhs (exp-lhs e)))) x))
((commutative-p (exp-op (exp-lhs e)))
;; Case IV: A*f(x) = B -> f(x) = B/A
(isolate (mkexp (exp-rhs (exp-lhs e)) '=
(mkexp (exp-rhs e)
(inverse-op (exp-op (exp-lhs e)))
(exp-lhs (exp-lhs e)))) x))
(t ;; Case V: A/f(x) = B -> f(x) = A/B
(isolate (mkexp (exp-rhs (exp-lhs e)) '=
(mkexp (exp-lhs (exp-lhs e))
(exp-op (exp-lhs e))
(exp-rhs e))) x))))

ซึ่งผมชอบที่เขาเขียนตรงที่
  • เขียนแบบไม่มี side-effect (ไม่มี assign statement เลย)
  • Recursive สวยดี

อันนี้ตอนแปลงเป็น ruby ก็นึกอยู่ว่า
จะเขียนแบบ imperative ดีไหม จะได้เปรียบเทียบกันดู
แต่สุดท้าย ก็รู้สึกว่า โจทย์แบบนี้ไม่ควรเขียนแบบนั้น ก็เลยเขียนตาม lisp
โดยให้ method isolate เป็น method ของ class Exp

def isolate(var)
case
when @lhs == var
self
when var.in(@rhs)
Exp.new(@rhs, @op, @lhs).isolate(var)

when var.in(@lhs.lhs)
Exp.new(@lhs.lhs,
"=",
Exp.new(@rhs, self.inverse(@lhs.op), @lhs.rhs)).isolate(var)

when ["*", "+"].include?(@lhs.op)
Exp.new(@lhs.rhs,
"=",
Exp.new(@rhs, inverse(@lhs.op), @lhs.lhs)).isolate(var)

else
Exp.new(@lhs.rhs,
"=",
Exp.new(@lhs.lhs, @lhs.op, @rhs)).isolate(var)
end
end


ตรงเงื่อนไขที่ check ว่า ตัวแปรอยู่ผั่งไหน (จะได้สลับด้านได้ถูก)
ผมอาศัยว่า class ของ ruby เป็น class แบบเปิด (แก้ไขได้)
ก็เลยใช้วิธี modify Symbol Class (ผมใช้ Symbol represent variable)

class Symbol
def in(exp)
to_sym == exp ||
(exp.is_a?(Exp) &&
(to_sym.in(exp.lhs) || to_sym.in(exp.rhs)))
end
end


พอจัด tree เสร็จ
สุดท้ายก็ evaluate ด้านที่อยู่ด้านขวา
ใน ruby ก็ใช้คำสั่ง eval

def solve()
Exp.new(@lhs, '=', eval_equation(@rhs))
end

def eval_equation(exp)
return case
when exp.is_a?(Numeric): return exp
else
eval("#{eval_equation(exp.lhs)} #{exp.op} #{eval_equation(exp.rhs)}")
end
end


ตอน run ก็เขียนดังนี้ (ยังไม่ได้เขียน parser ก็เลยดูไม่ค่อยสวย)

e = Exp.new(Exp.new(Exp.new(:x, '+', 2), '/', 10), '=', 8)
puts "Equation -> #{e}"
puts "Solve -> #{e.isolate(:x).solve()}"

# output
# ------
# Equation -> x + 2 / 10 = 8
# Solve -> x = 78
#

Related link from Roti

upgrade glibc บน Gentoo

มีปัญหากับการลง SBCL(Steel Bank Common Lisp) ซึ่งต้องการใช้ NPTL
ก็เลย emerge glibc ใหม่
ซึ่งก็ไปเจอ message ว่าต้องใช้ USE="nptl nptlonly"
ก็เออออไปตามมัน
ซึ่งก็ไปตายขณะ build โดยฟ้องว่า gcc version เก่าไป
ด้วยความมักง่าย ไม่รู้จักอ่าน (จริงๆแล้วคือค้นใน google)
ก็เลยดุ่มๆไปเรื่อยๆ
upgrade gcc เสร็จ
ใช้ gcc-config switch ไปตัวใหม่ (3.4.5)
ทดสอบโปรแกรมดู พบว่า
โปรแกรม python เกิดปัญหากับ libstdc++.so.5 , run ไม่ได้
ก็เลยพยายาม switch gcc-config กลับ
ปรากฎว่าไม่สำเร็จ
ก็เลยมั่ว set /etc/env.d/xxgcc เอง
set ให้ ld path มันชี้ไปที่ libstdc++ ตัวเก่า
เพื่อให้มัน run python ให้ได้ก่อน (emerge ใช้ python)

จากนั้นก็เลยค้น google เจอ Gentoo Linux GCC Upgrade Guide
พยายามทำไปตามที่เขาว่า แต่ไปตายตรง revdep-rebuild
มันฟ้องว่า บาง ebuild มันหายไป

เมื่อไม่รู้ว่าจะทำอะไรต่อแล้ว
ก็เลยมั่วต่อ สั่ง emerge glibc ตัวใหม่จนสำเร็จ
ทดลอง run SBCL
สำเร็จแฮะ run ได้

แต่เมื่อทดลอง startx
ก็พบ error

x relocation error: X symbol --guard, version GLIBC.2.3.2 not defined in file libc.so.6
with link time reference]

ค้น google ดูพบ forum ที่อุทิศเพื่อเรื่องนี้ (5 หน้า)
x relocation error

หลังจากนั่่งอ่านอยู่พักใหญ่ พยายามทำความเข้าใจ
(ระหว่างนั้นก็สั่ง download Gentoo livecd-2006.0 ไว้ก่อนเลย)
สุดท้ายก็เลือกลงใหม่ดีกว่า
เพราะ คิดว่าถ้ามั่วไปเรื่อยๆ อาจจะเสียเวลามากกว่า ลงใหม่ทีเดียว

Related link from Roti

Friday, March 10, 2006

Virtual Desktop Manager for Mac Os X

หามานาน
สมหวังสักที
Desktop Manager

Related link from Roti

Pattern การ implement Object ใน Prototype.js (1.4.0)

style ปกติที่เราใช้สร้าง javascript object
ก็คือการใช้ function
ตัวอย่าง

function Person(name, age) {
this.name = name;
this.age = age;
this.dump = function() {
return this.name + ", " + this.age;
}
}

var p = new Person("pann", 1);

ซึ่งก็มีหลายคนออกมาชี้ว่า
วิธีนี้ ถ้าเรามี object จำนวนมาก
มันจะเปลือง memory นะ, เพราะ method dump
มันถูก declare ขึ้นมา 1 function ต่อ 1 object
ก็เลยมีการพลิกแพลงออกไปใช้ prototype
(เวลา javascript object ถูกเรียกใช้งาน มันจะมองหา attribute ภายใน
scope ของมันก่อน ถ้าไม่เจอก็จะค้นหาจาก object ที่ pointer prototype ชี้อยู่)

function Person(name, age) {
this.name = name;
this.age = age;
}

Person.prototype = {
dump: function() {
return this.name + ", " + this.age;
}
}


Prototype.js ก็เห็นดีเห็นงามกับแนวทางที่ 2 (อันนี้เป็นสาเหตุ ว่าทำไม library ถึงชื่อ prototype)
แต่ตบแต่ง style เสียใหม่
ได้หน้าตา การประกาศ object ออกมาแบบนี้

var Person = Class.create();

Person.prototype = {
initialize: function(name, age) {
this.name = name;
this.age = age;
},

dump: function() {
return this.name + ", " + this.age;
}
}

ซึ่งก็ดูดีขึ้นนะ (ถ้าเข้าใจว่ามันทำงานอย่างไร)
magic อยู่ที่ function Class

var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}

เมื่อเราเรียก var Person = Class.create()
ตัวแปร Person จะชี้ไปยัง function ที่ return กลับออกมาจาก Class object
และเมื่อเราเรียก Person.new ก็จะเกิดการเรียกใช้ function ที่ชื่อ initialize
ซึ่งถูก declare ไว้ใน Person.prototype object

เมื่อมี object แล้วก็ต้องมีการ extend หรือ inheritance
ใครที่มาจากสาย java แล้วใช้ความคิดแบบ java เข้ามาจับ
รับรองว่าได้ปวดหัวกับการ debug แน่นอน
คำแนะนำที่ดี ก็คือ อย่า design แบบ deep hierachy tree เลย
พวก polymorphic หรือ overloading
ก็อย่าไปนึกถึง

แต่ถ้าอยากลองดู ลองอ่านอันนี้ดู Object Hierarchy and Inheritance in Javascript
ซึ่งดูเหมือนจะเป็น technique แบบเก่า
ถ้าอยากได้แบบใหม่ๆหน่อย ก็ลองอ่านนี่ดู
Javascript Inheritance
แต่ผมไม่ค่อยชอบ technique ที่เขาแนะนำเท่าไร
มันดูแปลกๆในส่วนของการ copyPrototype

ใน Prototype.js ก็มี method extend ให้ใช้เหมือนกัน
แต่ส่วนใหญ่จะใช้ในลักษณะของ Mixin แบบ ruby เสียมากกว่า
โดยการ design จะใช้วิธีเสริม function เข้าไปใน object ที่มีอยู่แล้ว
เช่น

var Enumerable = {
...

findAll: function(iterator) {
var results = [];
this.each(function(value, index) {
if (iterator(value, index))
results.push(value);
});
return results;
},

...
}

Object.extend(Array.prototype, Enumerable);

ทำให้เราสามารถทำแบบนี้ได้


[1,2,3].findAll(function(value) {
return value > 1;
})
// => [2,3]

Related link from Roti

ลานนาจัง กับ ทักษิน

แถาบ้านมีคนญี่ปุ่นอยู่คนหนึ่ง ชื่อ ยูโกะจัง
ทำงานเป็น air hostage อยู่การบินไทย
ยูโกะมีลูกสาวคนหนึ่ง ชื่อ ลานนาจัง
(ชื่อไทยซะด้วย)

ตอนนี้ ลานนาจัง อายุได้ 7 เดือนแล้ว
กำลังน่ารักเลย

วันก่อนภรรยาผมเล่าให้ฟังว่า
ยูโกะจังบ่นให้ฟังว่า

ลานนาจังเกิดวันเดียวกับทักษิน worry นะคะ..
โตขึ้นมาจะเป็นยังไง ...
money money หรือเปล่า, material girl...

Note: กรุณาอ่านออกเสียงแบบคนญี่ปุ่นพูดไทย

Related link from Roti

Form Object ใน Prototype.js (1.4.0)

หลังจากดู presentation A (Re)-Introduction to javaScript
ของคุณ Simon Willison จบแล้ว ก็เกิดความเข้าใจใหม่ๆขึ้นมาอีกมาก

เดิมผมจะมีปัญหากับการอ่าน javascript ที่เขียนแนว Object อยู่มาก
ที่เป็นแบบนี้ ก็เพราะ ผมเอาแนวคิดของ java (class-based) ไปตีความเจ้า javascript (Prototype-based)
ก็เลยเกิดอาการ มึนๆ งงๆ
แต่หลังจากเรียนรู้ ruby, lisp มาระยะหนึ่ง
ซึ่งช่วยขยายกรอบความคิดเกี่ยวกับเรื่อง programing language ขึ้นอีกเยอะ
ก็เลยทำให้หลังจากดู presentation ข้างบนจบ (slides ยาวมาก)
ก็เลยเกิดอาการ Satori เล็กๆขึ้นมา

ผลของ Satori ก็เลยทำให้เกิดความฮึกเหิม ย้อนกลับไปใล่ดู
source code ของ Prototype.js
โดยเลือกดูส่วนที่เกี่่ยวกับการจัดการ Form ก่อน

Object ของ Prototype ที่ใช้จัดการกับ form
ใช้ชื่อตรงตัวเลยว่า Form
ใน source code มีการประกาศไว้ดังนี้

var Form = {
serialize: function(form) {
...
},

getElements: function(form) {
...
},

...
}


method ประกอบด้วย
  • serialize
    return string ที่ใช้ในการ submit form ผ่าน ajax
    ตัวอย่างการใช้งาน
    สมมติ html ดังนี้

    <form id="myform" action="#">
    <input name="input1" type="text" value="xxx"/>
    <input type="checkbox" name="input3" checked="checked" />
    <input name="input2" type="submit"/>
    </form>

    แล้วใช้คำสั่ง Form.serialize

    Form.serialize('myform'); // => input1=xxx&input3=on&input2=

  • getElements
    รับ parameter เป็น form เหมือนกัน
    ผลลัพท์ที่ได้คือ Array ของ Element ที่อยู่ใน form (เฉพาะพวกที่ใช้ input เช่น input, textarea, ...)
  • getInputs
    ค้นหา Element ประเภท input โดย
    รับ parameter เป็น form, ประเภทของ input และ ชื่อของ element นัั้น
    เช่น

    Form.getInputs('myform', 'text', 'input1')

  • disable
    ไว้สั่ง diable form ที่ต้องการ
  • enable
    สั่ง enable form ที่ต้องการ
  • findFirstElement
    อันนี้ดูไม่ค่อยมีประโยชน์เลย รู้สึกว่าจะมีไว้ใช้ใน focusFirstElement
  • focusFirstElement
  • reset


เห็นได้ว่า method ทั้งหมด มี require parameter
เป็น form element หรือ form name ทั้งสิ้น (รับได้ทั้ง 2 แบบ)

Helper object ที่ Form เรียกใช้ มีอยู่ 2 ตัวคือ
  • Form.Element
    มีแค่ 2 method คือ
    • serialize(element)
      อันนี้ถูกเรียกจาก Form.serialize โดยมีเป้าหมายพื่อ encode ข้อมูลเพื่อ submit
    • getValue(element)
      อันนี้ไว้ใช้ get ค่าที่อยู่ใน Element ที่ต้องการ
      รับ parameter เป็น element id หรือ element object ก็ได้

  • Form.Element.Serializers
    อันนี้เป็น lower layer แล้ว ถูกเรียกใช้จาก Form.Element อีกที


ใน Prototype มี Helper Function ที่ชื่อ $F
ซึ่งจริงๆแล้ว ก็คือ short-cut ของ Form.Element.getValue นี่เอง

var $F = Form.Element.getValue;

Related link from Roti

Thursday, March 09, 2006

Synergy

ได้โปรแกรมดีๆมาช่วยงานอีกตัวแล้ว
Synergy เป็นโปรแกรมที่่ช่วยให้เรา share keyboard และ mouse
ระหว่างเครื่องคอมพิวเตอร์ โดยไม่ต้องใช้ hardware
รวมทั้งยัง support การ share ระหว่าง platform ด้วย (windows, Linux/Unix, Mac OS X)

การใช้งานก็ไม่ยาก
เครื่องคอมพิวเตอร์ที่เราจะใช้ keyboard และ mouse
จะ start ใน mode ของ server
ส่วนเครื่องที่เหลือ start ใน mode client

การ start ใน mode server ต้องมี config file ด้วย
หน้าที่ของ config file ก็คือจัดเรียง screen
ว่าเครื่องไหนอยู่ขวา, ซ้าย, บน, ล่าง เมื่อเทียบกับเครื่องอื่น
การจัดเรียงนี้ จะช่วยให้เราลาก mouse ข้ามเครื่องได้

ตัวอย่าง config ของผม

section: screens
g5.local: # my Apple G5
erp: # my Laptop Gentoo
end

section: links
g5.local:
left = erp

erp:
right = g5.local
end

section: options
keystroke(alt+f1) = switchInDirection(left)
keystroke(alt+f2) = switchInDirection(right)
end

Related link from Roti

Wednesday, March 08, 2006

Search Rails api with ri

เดิมเวลาผมจำไม่ได้ว่า method ที่ต้องการมี argument อะไรให้ pass บ้าง
ก็จะ start gem_server แล้วใช้ browser ค้นดู

document ของ rails ในกรณีนี้
ได้มาจากการใช้ program rdoc generate html files ขึ้นมา
ซึ่งปกติเวลาเรา install rails ด้วย gem
มันจะ automatic ทำให้อยู่แล้ว

วันนี้มีอีก solution หนึ่ง
ก็คือการใช้ ri ในการ search
โดยปกติ ri จะค้นหา document ที่อยู่ภายใต้ site-directory
(เครื่องผมอยู่ที่ /usr/share/ri)
กับภายใต้ ~/.rdoc

เราสามารถใช้คำสั่ง rdoc --ri
เพื่อสร้าง output file ที่ ri สามารถใช้ได้
โดย files ที่ได้จะอยู่ภายใต้ directory ~/.rdoc

ตัวอย่างเช่น ถ้าผมต้องการให้ ri สามารถ search activerecord ได้
ก็จะทำการสั่งดังนี้

cd /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib
rdoc --ri


สำเหตุที่ใช้ ri ก็เพราะว่าต้องการเอาไปใช้กับ emacs

Related link from Roti

pack200

ตกข่าวอีกตามเคย
พึ่งรู้ว่า java web start ของ jdk1.5 support gzip format
ที่ได้จากโปรแกรม pack200
(ช่วย compress jar file ให้มีขนาดเล็กลงได้)

อ่านที่นี่
Pack200 and Compression
ตัวอย่างที่ sun แสดงไว้ เขาใช้ servlet เป็นคนเลือก format ในการส่งกลับ
กรณีที่ใช้ apache อย่างเดียว ก็ให้ดูตัวอย่างการ config จาก link นี้
How to use Pack200 for your Java Web Start applications on Apache Web Server

ตอนที่เห็นครั้งแรก ก็นึกว่ามันเป็น compress format ใหม่ๆ
แต่จริงๆแล้วมันก็คือ gzip format ธรรมดานี่แหล่ะ
โดยขั้นตอนการทำงาน เท่าที่ดู (เดา)
ก็คือ
เริ่มจาก มันจะ unpack jar เราก่อน (jar ก็เป็น compress format แบบหนึ่ง)
จากนั้นก็ pack กลับภายใต้ชื่อ file-name.pack
ตรงนี้ไม่รู้ว่า format เป็นอะไร
แต่ถ้าลองเทียบกับ tar format แล้ว
มันจะมีขนาดเล็กว่านิดหน่อย
(ทดสอบโดยใช้คำสั่ง jar xf เพื่อ extract jar file ออกมา
จากนั้นก็ใช้คำสั่ง tar cf กับเนื้อหานั้นๆ)
สุดท้ายก็ compress pack format ด้วย gzip
(file ที่ได้ ก็เลยชื่อ file-name.pack.gz)

ลองทดลองกับ jar file ดังนี้
  • oscar.jar
    (OSGi framework)

    jar size 149469 bytes
    pack200 size 45426 bytes
    jar.gz size 138213 bytes

  • SwingSet2.jar
    เป็น demo ที่ bundle มากับ jdk

    jar size 1432007 bytes
    pack200 size 1148563 bytes
    jar.gz size 1390818 bytes

  • Stylepad.jar
    มากับ demo เหมือนกัน

    jar size 102221 bytes
    pack200 size 65864 bytes
    jar.gz size 96329 bytes

Related link from Roti

รูปเส้นทางขี่จักรยาน trip-น่าน




ตัวอย่างเส้นทางที่ขี่ งามมาก ขี่ไต่ไปตามสันเขา
ภาพที่เห็น รู้สึกจะเป็นแถว กิ่วมรณะ
ที่ได้ชื่อนี้ ก็เพราะว่า สมัยที่ยังมีการปะทะกันอยู่
จุดนี้คือจุดซุ่มโจมตีของ ผกค. (ชัยภูมิเหมาะสมมาก)

พี่ๆที่ไปด้วย เขาลงเรื่องไว้ที่นี่
http://thaimtb.com/cgi-bin/viewkatoo.pl?id=81687
http://www.thaimtb.com/cgi-bin/viewkatoo.pl?id=81340

Related link from Roti

Embeded Java



อาทิตย์ก่อนได้ลองเล่น Snap ซึ่งเป็น product ของ ImSys
ตัวอย่าง features
  • Sun certified J2ME-CLDC
  • Native Java execution
  • 10/100Base-T Etherne
  • 2 Mbytes flash memory
  • 8 Mbytes DRAM


feature ที่ระบุว่าสามารถทำ native java execution
ก็เพราะ cpu ที่ snap board ใช้ (CISC/WISC)
สามารถ program ระดับ microcode ได้
(แก้ไขระหว่าง runtime ได้อีกต่างหาก)
ก็เลยสามารถ implement ให้ interpret java byte code ได้

Note: คำว่า WISC ย่อมาจาก Writable Instruction Set Computing

การพัฒนา java ก็ใช้ developer tool อะไรก็ได้
ขอให้ compile ให้ได้ byte code แล้วกัน
พอได้ class file ก็ใช้ ftp load โปรแกรมลงไป (snap start ftp server ได้)

ตอนแรก กะว่าจะทดลองลง Oscar (OSGi Framework) ดู
ปรากฎว่ามีปัญหาเรื่องขนาด เพราะ OSGi ดูเหมือนจะเล็ก แต่จริงๆแล้วขนาดไม่เบาเลย
ก็เลยเลือกแต่ core กับ shell bundle ลงไป
สุดท้ายไปติดปัญหาว่า snap ไม่ support jar file ก็เลยล้มเลยความตั้งใจ

จากนั้นก็แอบดู model ของการ implement cgi program
ก็ง่ายๆ ตรงไปตรงมาดี หน้าตาประมาณนี้

public class HelloCgi implements HttpCgi {
public void execute(Hashtable variables, OutputStream os) throws .. {
....

}
}

ตอน start server ก็แค่

HttpServer hs = new HttpServer();
Thread t = new Thread(hs)
hs.addCgi(new HelloCgi());
t.start();


ยังเหลือที่อยากลองอีกอย่าง ก็คือ อยากลองดูว่า เราสามารถ implement Jini บน snap ได้หรือไม่?

Related link from Roti

Monday, March 06, 2006

How to be an expert

เห็นที่ bact' ลง link video แสดงการเล่น yoyo ชั้นเซียนแล้ว
ไปอ่านเจอ How to be an expert ก็เลยเอาทำ link ไว้

Related link from Roti