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

Saturday, September 17, 2005

คนตาบอดที่ป้ายรถเมล์

วันก่อน ระหว่างที่รอรถเมล์กลับบ้าน
ผมเหลือบไปเห็นคนตาบอด 2 คน กำลังรอรถเมล์อยู่
เป็นผู้ชายคน กับผู้หญิงคน
ผู้หญิงลงไปยืนในถนน 1 ก้าว
แวบแรกที่เห็น ก็เกิด บทสนทนาในใจ
"เอ๊ะใช่คนตาบอดหรือเปล่า"
"แต่เขามีไม้เท้าทั้งคู่นี่ ต้องเป็นคนตาบอดแน่เลย"
แวบที่สอง ก็ตามมา
"เราควรจะเข้าไปช่วยเขาดีหรือเปล่า?"
"เขามีใครคอยช่วยอยู่หรือยัง?"
หลังจากปล่อยให้เกิดบทสนทนาในใจอยู่พักหนึ่ง
ผมก็เลยเดินเข้าไปช่วยพาขึ้นรถ
(สำหรับคนที่ไม่เคยพาคนตาบอดเดิน
ขอแนะนำให้ใช้วิธี ให้เขาจับแขนบริเวณข้อศอกเรา
วิธีนี้เพื่อนตาบอดคนหนึ่งของผม เขาแนะนำมา)

ระหว่างที่รอรถเมล์กับเขา
ผมก็รู้สึกว่าเขาช่างเป็นคนที่มีความอดทนเสียเหลือเกิน
ของเรา ขนาดมองเห็น เวลาที่รถเมล์ขาดระยะ
มองไปสุดลูกหูลูกตา แล้วยังไม่เห็นรถเมล์สักคน
เรายังรู้สึกหงุดหงิดเลย แต่นี่เขาต้องรอ 2 ทอด
ทอดที่ 1 รอให้คนมาช่วย ซึ่งไม่รู้ว่าจะมีคนมาช่วยเมื่อไร
ทอดที่ 2 รอรถเมล์มา (ซึ่งมองไม่เห็นอีกต่างหาก)
ดังนั้นในระหว่างที่รอรถเมล์ ผมก็เลยพูดบรรยายให้เขาฟังไปด้วย
ว่าตอนนี้รถเมล์สายนี้เข้านะ สายนี้มาแล้วแต่หมดระยะแล้ว(พวกป้ายแดง)

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

ผมคิดว่า คนกลุ่มหนึ่งที่ป้ายรถเมล์นั้น
มองไม่เห็นคนตาบอด
คนที่มีบทสนทนาภายในกับตัวเองตลอดเวลา
จะมีข้อจำกัดในการเห็นโลกภายนอก
(ข้อนี้ยกตัวอย่างให้เห็นได้จาก คนที่เดินไป
คุยโทรศัพท์มือถือไป ถ้าเราสังเกตอิริยาบทเขา จะเห็นว่าเ
ขาจะเดินตัวลอยๆ ,สายตาจะไม่ focus จับที่ไหนเป็นหลัก
ถ้ามองให้ดี จะเห็นว่า สายตาของเขาบ่งบอกว่า
เขากำลังอยู่ในโลกของการสนทนา)
คนกลุ่มนี้ก็อาจจะกำลังครุ่นคิดถึงปัญหาของตนเองอยู่
(เรื่องงาน, เรื่องแฟน, เรื่องเงิน, ...)

ส่วนอีกกลุ่มหนึ่งมองเห็น
แต่ไม่เกินความคิดเชิงคำถามที่ว่า "แล้วเขาจะขึ้นรถเมล์อย่างไร"
ทำให้ไม่ได้ประเมินต่อไปว่า ตนเองควรจะเข้าไปช่วยเขาดีหรือไม่
คนกลุ่มนี้น่าจะเป็นคนที่ขาด ความสามารถในการคิดในมุมมองของผู้อื่น

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

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

....
ข้อสงสังของผมก็คือ
ในวันนั้น กลุ่มไหนมีปริมาณสัดส่วนเป็นอย่างไร
น่าจะมีใครทำการทดลองเรื่องนี้บ้างนะ
ไม่ใช่ด้วยการสัมภาษณ์นะ เพราะคนเราไม่ตอบ
ตามที่เป็นจริง
แต่ให้ทดลองให้คนตาบอดไปยืนรอรถเมล์
แล้วจับเวลาดูว่าต้องใช้เวลาเท่าไรจึงจะมีคนมาให้ความช่วยเหลือ
ตัวเลขนี้น่าจะเอาไปเป็นส่วนหนึ่งของ "ดัชนีคุณภาพชีวิต" ได้

นึกถึงเรื่องการทดลองนี้แล้วนึกถึง
การทดลองที่ Reader digest เคยทำ
เขาทดลองวางกระเป๋าสตางค์ (ที่มีที่อยู่เขียนไว้ข้างใน) ทิ้งไว้ตามที่ต่างๆ
แล้วแอบดูว่าคนที่เก็บมีลักษณะท่าทางอย่างไร
จากนั้นก็คอยติดตามว่า มีการติดต่อหรือส่งคืนกระเป๋าสตางค์หรือไม่
Reader digest เขาทดลองกับหลายๆประเทศใน Asia
ผลที่ได้ค่อนข้างตรงกันอย่างหนึ่งก็คือ
ถ้าคนที่เก็บได้เป็นคนที่ฐานะไม่ค่อยดี
มันจะได้กระเป๋าพร้อมเงินคืน
ส่วนกรณีที่คนเก็บได้ดูดีมีฐานะ
ก็จะไม่ได้กระเป๋าคืน

ที่ติดใจก็คือ ที่เมืองไทย
มีกระเป๋าใบหนึ่งถูกทดลองทิ้งไว้ใน มหาวิทยาลัยจุฬาฯ
ผลเป็นไปตามที่คาด
กระเป๋านั้นไม่ได้ถูกพบเห็นอีกเลย
(คนเก็บได้เป็นนิสิต)

สำหรับตัวผมเอง
ผมคิดว่าทั้งเรื่องคนตาบอดหรือเรื่องกระเป๋าสตางค์
มีรากเหง้าตัวหนึ่งที่ตรงกันก็คือ "Ignorance"
..

จบดีกว่า ปัญหาพวกนี้คุยกับข้ามคืนก็ไม่จบ

Related link from Roti

Monday, September 12, 2005

Hierarchical Data in Database

ปกติผมจะเก็บ tree structure ใน db ด้วย schema แบบนี้

create table foo (
id long,
....
parent_id long
)


ซึ่งก็ ok แหล่ะ แต่การท่อง tree หรือหา path ของ tree ค่อนข้างกิน io ไปนิด
ซึ่งปกติที่ทำก็คือ load ทั้งหมดมา build เป็น graph structure แล้วเก็บ
มันไว้ใน cache

วันนี้ไปอ่านเจอที่ Gijs Van Tulder เขียนไว้ ในเรื่อง
Storing Hierarchical Data in a Database
ก็เลยได้เปิดหูเปิดตา ว่ามันมี technique อื่นที่ช่วยไม่ให้
การ select หา subtree หรือ path สามารถทำได้ใน select เดียว

ดยเขาจะสร้าง table ให้มี structure แบบนี้

create table foo (
id long,
..
parent_id long,
left_id long,
right_id long
)

ก็คือนอกจากจะมีการเก็บ parent id แล้ว
ก็ยังเก็บค่า left, right ไว้ด้วย
ซึ่งค่านี้ได้จากการ generate โดยการท่อง tree
แบบ Depth-First search

เวลาจะ select หา subtree ก็แค่

select * from foo where left_id between x and y

ค่า x และ y ก็คือ ค่า left_id, right_id ของ node ที่ต้องการหา subtree

...
มีที่น่าสนใจอีกเยอะเลย เช่นจะ select หา path ยังไร,
ผลลัพท์ที่ได้จะสร้างเป็น tree ได้อย่างไร,
การ add note เข้าไปใหม่ จะต้อง update ค่า left, right อย่างไร
ลองไปอ่านดูแล้วกันครับ

Note: อ่านแล้วก็อดนึกถึงคำพูดที่ว่า พวกเด็กที่เรียกไฟฟ้าเขียนโปรแกรมกันทื่อๆ
ก็ถูกของเขาแหล่ะ เพราะเราไม่ได้เรียนมาทาง computer โดยตรง
ก็เลยไม่ได้ผ่านหูผ่านตากับพวก algorithm แบบนี้

Related link from Roti

Commons CLI

วันนี้เขียนโปรแกรม utility เล็กๆ ที่สามารถ run จาก command line ได้
ก็เลยมองหา api ที่ช่วย parse argument
Commons CLI ของ jakarta เป็น api ที่ช่วยในการ parse argument
โดยมีวิธีการใช้ดังนี้

  • ระบุ Options ที่โปรแกรมเรารับได้
    Options opt = new Options();
    opt.addOption("d", true, "extract only specific date");
    opt.addOption("w", true, "extract only week that contain given date");
    opt.addOption("m", true, "extract for specific month,year");

    parameter ตัวที่ 2 ที่เรา pass ให้ method addOption ก็คือ การระบุว่า
    option ตัวนี้ต้องมี argument ตามมาด้วยหรือไม่
    อย่างในตัวอย่างที่เขียนนี้ ผู้ใช้สามารถเรียกใช้โปรแกรมได้ดังนี้
    program -d yymmdd -w yymmdd -y yymm infile outfile

    กรณีที่เราต้องการให้เลือกเฉพาะ option ใด option หนึ่งเท่านั้น
    ก็สามารถเขียนได้ดังนี้
    Options opt = new Options();
    OptionGroup grp = new OptionGroup();

    grp.addOption(new Option("d", true, "extract only specific date"));
    grp.addOption(new Option("w", true, "extract only week that contain given date")
    );
    grp.addOption(new Option("m", true, "extract for specific month,year"));

    opt.addOptionGroup(grp);

    ก็คือเราเปลี่ยนไปใช้ Optiongroup ในการ control ไม่ให้มีการเลือก
    มากกว่า 1 choice พร้อมๆกัน

  • ทำการ parse String[] args โดยระบุ parser ซึ่งเลือกได้ 3 แบบคือ BasicParser, GnuParser, PosixParser
    PosixParser parser = new PosixParser();
    CommandLine cmdline = parser.parse(opt, args);


  • เลือกใช้งาน argument หรือ option ผ่านทาง method getOptionValue
    หรือ getArgs()
    CommandLine cmdline = parser.parse(opt, args);

    File inFile = new File(cmdline.getArgs()[0]);

    if (cmdline.hasOption("d")) {
    Calendar[] ranges = parseDate(cmdline.getOptionValue("d"));
    ...
    }
    if (cmdline.hasOption("w")) {
    filter.setWeek(parseWeek(cmdline.getOptionValue("w")));
    }

Related link from Roti

NetBeans 5.0 Splashscreen

ดูดุดันขึ้นเยอะเลย



Related link from Roti

Hibernate Generic DAO + JDK5.0

อ่านเจอใน blog ของ Hibernate Link
เป็น idea ที่ดีมาก ใช้แต่ 1.4 จนลืมนึกถึง feature generic ของ 1.5 ไปเลย

ตัวอย่างที่เขาใช้
public interface GenericDAO<T, ID extends Serializable> {

T findById(ID id, boolean lock);

List<T> findAll();

List<T> findByExample(T exampleInstance);

T makePersistent(T entity);

void makeTransient(T entity);
}

public interface ItemDAO extends GenericDAO<Item, Long> {

public static final String QUERY_MAXBID = "ItemDAO.QUERY_MAXBID";
public static final String QUERY_MINBID = "ItemDAO.QUERY_MINBID";

Bid getMaxBid(Long itemId);
Bid getMinBid(Long itemId);

}

Related link from Roti

Sunday, September 11, 2005

Eclipse RCP & ClassLoader & Spring

เมื่อก่อนที่เคยเขียน RCP เคยพยายาม integrate
SpringFramework เข้ามาใช้ แต่ติดปัญหาว่า
เรื่องการ load class
โดย spring จะเรียกใช้ current ClassLoader โดยวิธี
Thread.currentThread().getContextClassLoader();

(Hibernate ก็ใช้วิธีนี้เหมือนกัน เข้าใจว่า framework ที่เกิดกับ
serverside นิยมใช้วิธีนี้กัน)
ซึ่งเมื่อนำมาใช้กับ Eclipse จะเกิดปัญหาทันที
เนื่องจาก classloader ที่ได้ มันจะเป็นคนละตัวกับ plugin ที่เราเขียน
ทำให้มองไม่เห็น class ที่เรา provide ให้

วิธีแก้ที่เห็นคนอื่นทำ ก็คือเขาจะ override ClassLoader ของ current thread
ชั่วคราว โดยทำ ณ ขณะที่ทำการ load ApplicationContext
public void start(BundleContext context) throws Exception {
super.start(context);

ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(
this.getClass().getClassLoader());
applicationContext = new ClassPathXmlApplicationContext(SPRING_CONFIGS)
;
} finally {
Thread.currentThread().setContextClassLoader(oldLoader);
}
}


Note: คิดว่าวิธีนี้ยังไม่ support กรณีเราออกแบบให้มีการ merge
spring config file จากหลายๆ plugin เข้ามารวมที่ context เดียว

Related link from Roti

fvLogger, JavaScript Logging

อ่านเจอใน JavaScript Logging
เป็น api แนวเดียวกับ log4j แต่ใช้กับ javascript

วิธีการใช้
include logger.js กับ logger.css
<script type="text/javascript" src="logger.js"></script>
<link rel="stylesheet" type="text/css" href="logger.css" />

บริเวณที่ log information จะใช้ div แบบนี้
<div id="fvlogger">
<dl>
<dt>fvlogger</dt>
<dd class="all"><a href="#fvlogger" onclick="showAll();" title="show all" id="abcdef">all</a></dd>
<dd class="debug"><a href="#fvlogger" onclick="showDebug();" title="show debug" id="showDebug">debug</a></dd>
<dd class="info"><a href="#fvlogger" title="show info" id="showInfo">info</a></dd>
<dd class="warn"><a href="#fvlogger" onclick="showWarn();" title="show warnings" id="showWarn">warn</a></dd>
<dd class="error"><a href="#fvlogger" onclick="showError();" title="show errors" id="showError">error</a></dd>
<dd class="fatal"><a href="#fvlogger" onclick="showFatal();" title="show fatals" id="showFatal">fatal</a></dd>
<dd><a href="#fvlogger" onclick="eraseLog(true);" title="erase">erase</a></dd>
</dl>
</div>




ถ้าไม่ต้องการหรูมากนัก​ ก็ใส่เพียงเท่านี้ก็พอ
<div id="fvlogger"></div>




เวลา log ก็เรียก function debug, info, warn, error, fatal
function foo() {
debug("foo start");
....
}

Related link from Roti

Thursday, September 08, 2005

Link (2005-09-08)



Note:
วันนี้เข้าไปวาดรูปเล่นมา 1 รูป เพลินดี แก้เครียดได้หมือนกัน
http://artpad.art.com/?imiapk946w4

Related link from Roti

Tuesday, September 06, 2005

Ruby กับ Design by Concern

ผมได้ทดลองเขียน program เพื่อทำการ plot graph แสดงการใช้งานของ user
โดยใช้ Ruby language เขียน

ตัวโปรแกรมมี Requirement ดังนี้
  • R1 สามารถอ่านข้อมูลจาก file wtmp ใน aix ได้โดยตรง
  • R2 สามารถสรุปข้อมูลนำมา plot เป็น graph แสดงเวลาการทำงานของ user
    ในช่วงวันที่กำหนดได้
  • R3 แสดงช่วงเวลาที่มีจำนวน concurrent user เกินกว่าค่าที่กำหนด


output หน้าตาประมาณนี้



ออกแบบ class diagram ดังนี้



ใน class diagram จะแสดงว่า Requirement ข้อไหน implement
ใน class อะไรบ้าง

ปัญหาที่เรามักเจอในการ design โปรแกรม ก็คือ Requirement หนึ่งๆ
มักจะกระจายตัวอยู่ใน class (เผอิญตัวอย่างนี้เป็นโปรแกรมเล็กๆ ก็เลยไม่ค่อยเห็นประเด็นนี้เท่าไร)
ทำให้เมื่อมีการเปลี่ยนแปลง requirement เกิดขึ้นต้องกระทบกับหลายๆ class
หรือไม่ก็ ถ้ามีการเพิ่ม requirement ใหม่ๆ เข้ามา Requirement
นั้นก็มักจะทำให้เกิดการเปลี่ยนแปลง class หลายๆ class

ตัว Ruby เอง เปิดโอกาสให้เรา implement class แยกกระจายหลายๆ
ที่ได้ ทำให้เราสามารถจัดกลุ่ม file ตาม concern (requirement)
แทนที่จะแยก 1 file 1 class

ในโปรแกรมที่ผมลองทำ ผมทดลองแยก Requirement R3
ออกเป็นเป็นอีก file
ภายใน file นี้ผมก็ extend class Plotter กับ class UserTimeLine
เฉพาะส่วนที่เกี่ย่วข้องกับ Requirement ที่ 3 นี้เท่านั้น



ซ้ายบนเป็น class UserTimeLine ส่วนซ้ายล่างเป็น class Plotter
ทางขวาเป็น file ที่ implement Requirement R3
เส้นสีแดง แสดงว่ามีการ extend class
ส่วนสีน้ำเงิน แสดง dependency ภายในของ Requirement R3


ข้อดีของวิธีนี้ อืมม์ ไม่ค่อยแน่ใจนัก
มันทำให้เรา concentrate บน Requirement มากขึ้น
เห็น class ที่ต้อง implement feature นั้นๆ อยู่เป็นกลุ่มก้อนเดียวกัน
(อาจจะทำให้ง่ายต่อการจัดการ)
เวลาอ่าน class ต่าง ก็อาจจะอ่านง่ายขึ้น เพราะจะเห็นแต่ส่วนที่ตัวเองเกี่ยวข้องเท่านั้น
(อันนี้อาจเป็นข้อเสียก็ได้ เห็นแต่ส่วนตัวเอง ทำให้ลืมนึกไปว่า
อาจจะมีคนอื่นใช้อยู่ก็ได้ ทำให้แก้ไขไม่ระวัง)

Note: extend ในที่นี้ไม่ได้หมายถึง extends แบบที่เราทำใน Java
แต่มีความหมายเป็นการ Merge เสียมากกว่า

Related link from Roti

Monday, September 05, 2005

ตัวอย่างการ extend class ของ Ruby

ช่วง 2 วันนี้เมามันอยู่กับการเขียน Ruby
โดยพยายามจะเขียนโปรแกรมที่ generate Graph แสดงภาพรวมการ
ทำงานของ User ในระบบ

ตอนนี้เริ่มเขียน version ที่ 2 แล้ว หลังจากปล้ำให้ version แรกทำงานได้แล้ว
ใน version ใหม่นี้ สิ่งที่พยายามจะทำ ก็คือการพยายามใช้ feature
ของ Ruby ให้มากขึ้น (แทนที่จะเขียนโดยใช้กรอบความคิดที่เคยชิน
จากการเขียน Java)
feature หนึ่งที่พยายามจะดู หรือหาว่า จะนำมาใช้อย่างไรก็คือ
การ extend class

ตัวอย่างที่เจอแล้วรู้สึกชอบก็คือ ใน module RMagick
(ซึ่งเป็น api ที่ใช้ในการ manipulate image)

Class RVG (Ruby Vector Graphic) ใน RMagic
จะมี constructor ที่รับตัวเลข 2 ตัว เพื่อใช้
ในการกำหนด ขนาดของ image ที่จะสร้างขึ้น

rvg = RVG.new(width,height)

คำถามของคนที่จะใช้ api นี้ก็คือ เราจะ pass ค่าอะไรเข้าไป, และเป็นหน่วยอะไร
เช่นถ้าผมต้องการรูปขนาด 4x5 นิ้ว ผมควรจะใช้ width, height เป็นเท่าไรดี
และถ้ามีหลายๆหน่วย api ควรจะ implement Unit Converter Function
นี้ไว้ที่ไหนดี

RVG เลือกใช้วิธีที่ดูเข้าทีมาก เขาเปิดโอกาสให้เราเขียนได้ดังนี้

rvg1 = RVG.new(4.in, 5.in) # หน่วยเป็นนิ้ว
rvg2 = RVG.new(30.cm, 15.5.cm) # หน่วยเป็นเซนติเมตร

ที่นี้ ruby จะเข้าใจ syntax นี้อย่างไร
ruby จะมองว่า 4.in ก็คือ การเรียกใช้ method ที่ชื่อ in
ของ Object Fixnum (4 ก็คือ Fixnum Object)
ส่วน 15.5.cm ก็คือ การเรียกใช้ method cm ของ
Float Object นั้น

ประเด็นก็คือ method in หรือ cm
มันไม่ได้ build-in มาใน Fixnum หรือ Float ตั้งแต่ต้น
แต่ ruby เปิดโอกาสให้เรา modify class definition ได้
โดยในกรณีนี้
RMagic กำหนด syntax มาให้เราเรียกใช้ดังนี้

RVG::dpi = 72

ถ้ามีการกำหนดค่านี้เมื่อไร RVG จะเข้าไป add method ใน
Fixnum กับ Float Class ให้เราทันที โดยอาศัยข้อมูล dpi ที่กำหนด
เป็นค่าที่ใช้ในการ convert

ถ้าตามไปดูใน source code ของ RVG จะเห็นวิธีที่เขาเขียน

def dpi=(n)
if !defined?(@dpi)
[Float, Fixnum].each do |c|
c.class_eval do
# the default measurement - 1px is 1 pixel
def px
self
end
# inches
def in
self * ::Magick::RVG.dpi
end
# millimeters
def mm
self * ::Magick::RVG.dpi / 25.4
end
# centimeters
def cm
self * ::Magick::RVG.dpi / 2.54
end
.....

จะเห็นว่ามีการ iterate Float class กับ Fixnum class
เพื่อเพิ่ม method เข้าไป (ผ่านทางคำสั่ง class_eval)

ดูแล้วชอบจริงๆ

Related link from Roti

Saturday, September 03, 2005

Link น่าสนใจ (2005-09-03)

Related link from Roti

Friday, September 02, 2005

ความหมายของคำว่า Maven

ใช้ Maven มาตั้งนาน พึ่งรู้ความหมายของคำนี้
People who love to acquire knowledge on all sorts of topics and, perhaps more importantly, love to tell others about what they've learned (in fact, the word maven comes from Yiddish and means someone who accumulates knowledge).


ไปได้มาจาก David Geary 's Weblog ในหัวข้อเรื่อง Tipping Rails
ที่เล่าถึงประเด็นที่เขาเชื่อว่า Rails จะถึงจุด tipping point ในไม่ช้า

Related link from Roti

Ruby กับการ parse binary file

ช่วงนี้มี project ที่จะลองใช้ ruby parse wtmp file
และทำการ generate Image ที่แสดงลักษณะการใช้งานของ user ออกมา
(จริงๆแล้วมันไม่ต้องเวอร์ขนาดนี้ก็ได้ แต่อยากลอง feature ต่างๆดู)

เริ่มแรกสิ่งที่ต้อง solve ก็คือการ parse wtmp file
ขอยกตัวอย่างเฉพาะเจาะจงลงไปที่ wtmp file ที่ได้จาก AIX แล้วกัน
ใน AIX wtmp file จะมี record size = 648 byte
(AIX version ใหม่ ของเก่าขนาดเล็กกว่านี้เยอะ)
วิธีการที่ผมใช้ก็คือ อ่าน file ทีละ 648 byte เป็น string จากนั้นก็ส่งไป
ให้ parseRecord ทำงาน

def parseFile(fileName)
list = Array.new
begin
File.open(fileName, 'r') do |file|
while (! file.eof)
tmp = parseRecord(file.read(648))
wtmp = Wtmp.new(tmp)
list << wtmp
end
end
rescue
end
return list
end

ตัว parseRecord ก็จะใช้ method unpack เข้ามาช่วย
method นี้จะ return Array ของ object โดยเราต้องกำหนด template
ที่จะใช้ในการ parse

def parseRecord(str)
str.unpack("A256A14A64x2Nnx6Nnna256x36")
end

ความหมายของ template ที่ใช้
  • A256 -> return String ที่ตัดเอา white space ออกแล้ว
    ส่วนค่า 256 ก็คือ ความยาวของ data ที่ต้องการอ่านขึ้นมา ในที่นี้คือ 256 byte
  • x2 -> skip ข้ามไป 2 byte
  • N -> integer ความยาว 4 byte
  • n -> short (2 byte)


Link
Document ของ unpack method ใน String class

Related link from Roti