Friday, April 01, 2005

XDoclet2 Terminate?

วันนี้อ่านพบ Hibernate 3.0 release แล้ว
ก็เลยตามดูว่า xdoclet มีความคืบหน้าในเรื่อง
การ support dtd ตัวใหม่ของ Hibernate 3.0
แค่ไหน
เท่าที่ตามเข้าไปดูใน xdoclet site
พบว่ามีสัญญาณที่ไม่ดีสำหรับ project นี้
โดยเฉพาะตัว xdoclet2 (ที่โปรยหัวไว้ 2 ปีมาแล้ว)
ใน cvs source ไม่พบว่ามี code ขยับมา 2 เดือนแล้ว
นอกจากนี้ก็ยังพบว่า ปีก่อนช่วงเดือน
june-november code ไม่ขยับเลย

เป็นอันปิดบัญชีสำหรับ xdoclet ไปได้แล้ว
ดังนั้นใน case Hibernate 3.0 ถ้าเกิดต้องใช้
ขึ้นมาคงต้องเข้าไปแก้ hibernate-module เองแล้ว
(ดีที่ architecture ของ xdoclet 1.x นั้น
ดีพอใช้อยู่แล้ว การเขียน module หรือ customize
module ก็เลยไม่หนักหนานัก)

Related link from Roti

รูปเชียนภู่กันจีน

ไปสัปดาห์หนังสือแห่งชาติมา
ได้หนังสือภาพจีนมาหลายเล่ม
เลย scan รูปให้ดูกัน


 Posted by Hello


 Posted by Hello


 Posted by Hello

Related link from Roti

ลง ingres r3 บน ubuntu

เมื่อวานเข้าไป load ingres database
(ซึ่งปัจจุบันเปลี่ยนเป็น opensource แล้ว)
เอามาลงบน notebook ของตัวเอง

ตัว package ของ ingres บน linux
อยู่ในรูป rpm
จึงต้องใช้ alien ในการแปลงให้เป็น debian-package ก่อน
โดยตัวที่จำเป็นในการลงก็มี
  • ca-ingres_3.0.1-110_i386.deb -> core package
  • ca-ingres-dbms_3.0.1-110_i386.deb -> database manager
  • ca-ingres-das_3.0.1-110_i386.deb -> เพื่อให้ต่อ jdbc ได้

เนื่องจาก ingres ไม่ยอมให้ root เป็นคน start
จึงต้อง สร้าง user ingres และ group ingres เสียก่อน
จากนั้นก็ install package โดยใช้ command dpkg
จะได้ file อยู่ใน directory /opt/CA/IngresII
เนื่องจากผมยังไม่คล่อง linux มากนักเลยยังไม่รู้วิธี
ที่จะ overwrite user, group ใน debian package
ก็เลยใช้วิธี manual โดยใช้ chgrp, chown เอง

เมื่อลงเสร็จแล้วก็ให้
export II_SYSTEM=/opt/CA/IngresII
export PATH=$PATH:$II_SYSTEM/ingres/bin:$II_SYSTEM/ingres/utility
export LD_LIBRARY_PATH=$II_SYSTEM/ingres/lib

สั่ง setup database ด้วยคำสั่ง
(ใครที่ไม่เคยใช้ ingres ควรจะอ่านคู่มือ
เสียก่อน เนื่องจากมีหัวข้อที่ต้อง setup
พอสมควร)
iisudbms

สั่ง setup DAS
iisudas

เท่านี้ก็เสร็จสิ้นขบวนการ
ในการใช้งานเราจะสั่ง start server ด้วยคำสั่ง ingstart
และ stop ด้วยคำสั่ง ingstop

Related link from Roti

Thursday, March 31, 2005

Article Hibernate vs. Rails

หลังจากเริ่มเป็น Issue มาได้พักหนึ่ง
ในที่สุดก็มีคนเขียนบทความเปรียบเทียบระหว่าง
Hibernate กับ Active Record (Rails)
TheServerSide.com - Hibernate vs. Rails: The Persistence Showdown

เริ่มต้นก็เน้น Pattern ที่แตกต่างของทั้ง 2
ActiveRecord is "an object that wraps a row in a database table or view, encapsulates database access and adds domain logic on that data"[Fowler, 2003]. This means the ActiveRecord has "class" methods for finding instances, and each instance is responsible for saving, updating and deleting itself in the database. It's pretty well suited for simpler domain models, those where the tables closely resemble the domain model. It is also generally simpler then the more powerful, but complex Data Mapper pattern.
Data Mapper Pattern

The Data Mapper is "a layer of mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself"[Fowler, 2003]. It moves the responsibility of persistence out of the domain object, and generally uses an identity map to maintain the relationship between the domain objects and the database. In addition, it often (and Hibernate does) use a Unit of Work (Session) to keep track of objects which are changed and make sure they persist correctly.

Related link from Roti

Wednesday, March 30, 2005

Ruby on Rails #2

ลองมาดูที่ Object Relation Mapping ก่อน
ตัว ruby on rails ใช้ Active Record

ลองดูหลักการเบื้องต้นก่อน
  • การ map เข้ากับ table
    ใช้หลักง่ายๆก็คือ ชื่อ class ในรูปเอกพจน์ map กับ ชื่อ table ในรุปพหูพจน์
    class Person < ActiveRecord::Base
    end
    class person map เข้ากับ table persons
    และ column ทุก column ใน table persons
    automatic map เป็น property ของ class Person
    Note: จะเห็นได้ว่าตัว class person ต้อง extend base class ที่ชื่อ ActiveRecord
  • การ map Relationship
    class Project < ActiveRecord::Base
    belongs_to :portfolio
    has_one :project_manager
    has_many :milestones
    has_and_belongs_to_many :categories
    end
    • belongs_to เป็นได้ทั้ง many-to-one หรือ one-to-one
      โดย primary key ของ portfolios => foreign key ของ Projects
    • has_one มีลักษณะเป็น one-to-one
      โดย primary key ของ project => foreign key ของ project_managers
    • has_many เป็น one-to-many
    • has_and_belongs_to_many เป็น many-to-many โดยต้องมี table
      หนึ่งสำหรับเก็บ pk ของทั้ง categories และ projects
      โดย default table name จะใช้ชื่อว่า categories_projects (เรียงตามลำดับอักษร)
  • Aggregate Mapping (hibernate เรียก component mapping)
    เป็นการ map attribute ตัวเดียวหรือหลายๆตัวเป็น object
    class Customer < ActiveRecord::Base
    composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
    composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
    end
    • customer.balance จะมี datatype เป็น Money
      โดย attribute ที่ชื่อ balance จะถูก pass เป็น
      parameter ที่ชื่อ amount ใน Money Constructor Method
    • ส่วน customer.address จะมี datatype เป็น Address
      โดยใช้ attribute address_street, address_citry
      ในการ initialize Address object

  • Validation
    class Account < ActiveRecord::Base
    validates_presence_of :subdomain, :name, :email_address, :password
    validates_uniqueness_of :subdomain
    validates_acceptance_of :terms_of_service, :on => :create
    validates_confirmation_of :password, :email_address, :on => :create
    end
    • presence_of -> Require
    • uniqueness_of -> ค่าที่ set ต้อง unique ใน table
    • acceptance_of -> case พิสดารหน่อย ไม่มีการเก็บลง table จริง
      แต่ต้องการให้มีการ pass ค่ามา
      (เห็นเขายกตัวอย่าง use case กรณี หน้าจอที่ต้อง tick accept license)
    • confirmation_of ->case พิสดารเช่นกัน เขายกตัวอย่างพวก password
      confirmation ที่ค่า 2 ค่าต้องตรงกัน
  • Self Relate RelationShip (พวกที่ structure เป็น tree)
    class Item < ActiveRecord::Base
    belongs_to :list
    acts_as_list :scope => :list
    end

  • Etc. -> Callback, Observers, Inheritance, Transaction

จะเห็นได้ว่า Powerfull เหลือเฟือเหลือใช้
เป็นทางเลือกอีกทางที่น่าสนใจ

Related link from Roti

Ruby on Rails #1

พักนี้อ่านเจอ blog ที่พูดถึง ruby on rails เยอะ
ก็เลยอยากรู้ว่าดีแค่ไหน

เท่าที่อ่านดู
ruby เป็น script language
rails เป็น Web Application framework
ดูจาก diagram ข้างล่างแล้วมี concept เป็น MVC แบบเดียวกับ struts เลย
ส่วนที่ต่างกัน ก็คงจะเป็นตรงที่ไม่ต้องเขียน Configuration file (พวก Action mapping)
แล้วก็ออกแบบมาสำหรับ web application ที่มี backend
เป็น database โดยเฉพาะ
Request cycle in Rails
การ install
  • ใช้ synaptic install ruby1.8, libzlib-ruby, libyaml-ruby, libdrd-ruby, liberb-ruby, rdoc
  • download rubygem (ทำหน้าที่เหมือน apt-get)
  • extract rubygem.tar.gz, cd เข้าไปใน directory ที่ extract สั่ง "ruby setup.rb"
  • สั่ง "gem install rails --remote"
  • สั่ง "gem install postgres-pr" (กรณีใช้ postgresql เป็น backend)


ทดสอบสร้าง Hello world
  • สั่ง "rails test" เพื่อทำการสร้าง project
    ในขั้นนี้ rail จะ generate file ให้เราจำนวนหนึ่ง
  • สามารถทดลอง run ได้โดยสั่ง "ruby script/server" ใต้ project directory แล้วใช้ browser ชี้ไปยัง http://localhost:3000/
  • สร้าง controller โดยใช้คำสั่ง "ruby script/generate controller Hello"
    จะได้ file app/controllers/Hello_controller.rb
  • edit file Hello_controller.rb
    class HelloController < ApplicationController
    def index
    render_text "hello world"
    end
    end

  • ทดลองเรียก http://localhost:3000/Hello


Note: จะเห็นได้ว่าตัว Rail จะ map ชื่อcontroller เข้ากับ url ให้โดยอัตโนมัติ
แต่ยังไม่ได้ใช้ view เข้ามา render


ทดสอบต่อ Database
เข้าไป config file "config/database.yml"
development:
adapter: postgresql
database: pphetra
host: localhost
username: pphetra
password: xxxxxxx

test:
adapter: postgresql
database: pphetra
host: localhost
username: pphetra
password: xxxxxxx

production:
adapter: postgresql
database: pphetra
host: localhost
username: pphetra
password: xxxxxxx

Note: แค่เห็นว่าเตรียมแยก develop, test, production ให้ก็รู้สึกชอบใจแล้ว
สร้าง table "Categories"
create table Categories (
id integer primary key not null default nextval('id'),
title varchar(255) not null,
description text
)

สั่ง "ruby script/generate model Category"
สั่ง "ruby script/generate controller Category"
จะได้ file app/controllers/category_controller.rb
ให้แก้เนื้อหาใน file เป็น
class CategoryController < ApplicationController
scaffold:Category
end

จากนั้นเรียก url http://localhost:3000/category
จะได้ page ที่สามารถทำ create, update, delete, list
ได้
Note: rail จะ mapping ตัว model กับ table โดย model
ต้องอยู่ในรูป เอกพจน์ ส่วนชื่อ table ต้องอยู่ในรูป พหูพจน์


Note: การ insert เข้า Postgres ยังมีปัญหาในส่วนของ generate id อยู่ ยังไม่ได้เปิดคู่มือหาจริงๆจัง

ง่ายดีเหมือนกัน แต่ใช้ assumption ที่ต้องรู้กัน เยอะพอสมควร
ส่วนที่แสดงเป็นตัวอย่างอย่างง่าย แต่ถ้าทำใช้จริง
ก็จะต้องทำส่วน view plug เข้าไป เพื่อที่จะ
customize ให้มีหน้าตาที่ user รับได้

Related link from Roti

Monday, March 28, 2005

วันนี้เห็น stack trace ของ blogger

วันนี้ post ข้อมุลแล้วเกิด exception เลยได้เห็น stack trace ของ blogger
ก็เลยใช้วิชาเดาเข้ามาช่วย

ตัว blogger ใช้ tomcat 4.x เป็น app server
ตัว web server นั้นไม่รู้ รู้แต่ว่าใช้ jk เป็นตัวแจก request ให้ tomcat
และน่าจะใช้ sticky session ด้วยเพราะมี cookie ตัวหนึ่งที่เก็บค่า serverId
(ใครที่ยังไม่เคยใช้ tomcat cluster อ่านเพิ่มเติมได้ที่นี่ link)
จากนั้นก็เข้าสู่ framework ของ google
(มีบางส่วนเป็น pyra framework) ที่วางลักษณะ
เป็น pipe chain กับ Handler chain
เท่าที่เห็นก็มี
  • IdentityCookiePipe ไม่รู้ว่าเก็บอะไร เพราะ encode ไว้ (ข้อมูลอยู่ใน cookie ที่ชื่อ I)
  • StatsPipe -> สถิติ
  • ProfileLogPipe ->น่าจะ update lastupdate, กับ logging
  • PyraJspDispatchPipe ->framework ของ Pyra ซึ่งมี handler chain
  • AuthenticatedHandler
  • ActionHandler -> ตัวนี้น่าจะเก็บ post ลง database
  • PublishHandler -> ตัวนี้น่าจะ generate cache page เก็บไว้

java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start(Native Method)
at com.pyra.blogger.Blog.publishAsync(Blog.java:1938)
at com.pyra.blogger.frontend.PublishHandler.perform(PublishHandler.java:133)
at com.google.servlet.handlers.ActionHandler.execute(ActionHandler.java:27)
at com.google.servlet.handlers.AuthenticatedHandler.execute(AuthenticatedHandler.java:106)
at com.google.servlet.pipe.HandlerDispatchPipe.invoke(HandlerDispatchPipe.java:82)
at com.pyra.blogger.frontend.PyraJspDispatchPipe.invoke(PyraJspDispatchPipe.java:71)
at com.google.servlet.pipe.ServletPipe.invokeNextPipe(ServletPipe.java:118)
at com.pyra.blogger.frontend.ProfileLogPipe.invoke(ProfileLogPipe.java:48)
at com.google.servlet.pipe.ServletPipe.invokeNextPipe(ServletPipe.java:118)
at com.google.servlet.pipe.StatsPipe.invoke(StatsPipe.java:64)
at com.google.servlet.pipe.ServletPipe.invokeNextPipe(ServletPipe.java:118)
at com.google.servlet.pipe.PendingRequestPipe.invoke(PendingRequestPipe.java:31)
at com.google.servlet.pipe.ServletPipe.invokeNextPipe(ServletPipe.java:118)
at com.google.servlet.pipe.HttpRedirectPipe.invoke(HttpRedirectPipe.java:43)
at com.google.servlet.pipe.ServletPipe.invokeNextPipe(ServletPipe.java:118)
at com.pyra.blogger.frontend.IdentityCookiePipe.invoke(IdentityCookiePipe.java:137)
at com.google.servlet.pipe.ServletPipe.invokeNextPipe(ServletPipe.java:118)
at com.google.servlet.exceptionhandling.ExceptionHandlerPipe.invoke(ExceptionHandlerPipe.java:99)
at com.google.servlet.pipe.ServletPipe.invokeNextPipe(ServletPipe.java:118)
at com.google.servlet.pipe.LocaleContextPipe.invoke(LocaleContextPipe.java:116)
at com.google.servlet.BaseServlet.doGet(BaseServlet.java:89)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2415)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:172)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:223)
at org.apache.jk.server.JkCoyoteHandler.invoke(JkCoyoteHandler.java:261)
at org.apache.jk.common.HandlerRequest.invoke(HandlerRequest.java:360)
at org.apache.jk.common.ChannelSocket.invoke(ChannelSocket.java:604)
at org.apache.jk.common.ChannelSocket.processConnection(ChannelSocket.java:562)
at org.apache.jk.common.SocketConnection.runIt(ChannelSocket.java:679)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:619)
at java.lang.Thread.run(Thread.java:534)

Related link from Roti

Transitive dependencies management

Ivy เป็น tool ตัวหนึ่งที่ช่วยจัดการ dependency library

สมมติว่า project เรา depend on project X
และ project X depend on project Y
เวลาเราเชียน build script ด้วย maven
เราต้องระบุว่า project เรา depend กับทั้ง
proejct Y และ Y

แต่สำหรับ Ivy แล้ว เราจะระบุ dependency
แค่กับ project X เท่านั้น ส่วน x จะไป
depend กับใคร ตัว Ivy จะ solve ให้เอง

feature Transitive dependencies ช่วย
ให้เราจัดการ dependency ได้ง่ายขึ้น
อย่าง project ล่าสุดของผม ใน maven ผม
ต้องระบุ dependency กับ jar ทั้งหมด
40 file แต่ถ้าเป็น Ivy, dependency ที่ต้องระบุ
จะเหลือแค่ 15 file

Related link from Roti

ตัวอย่าง Tapestry Ajax component

มีคนทดลองทำ Ajax style component อยู่ที่ XTile example
ดูตามที่เขาเขียนแล้ว ok เลย
xtile เป็น invisible component
ทำหน้าที่เป็นตัว call xmlhttprequest
เข้าไปยัง Page Listener

ชอบที่ call ตรงเข้า page listener เลย
ทำให้ source code ไม่กระจัดกระจาย

Related link from Roti