วันพุธที่ 20 กรกฎาคม พ.ศ. 2554

taglib Authorization by spring security on spring3 richfaces4 jsf2

ในส่วน authorization นี้ พูดในเรื่องของ tag ที่ใช้ในการกำหนดสิทธิ บนหน้า web เป็นหลัก โดยต่อยอดมาจากบนความก่อนหน้าในเรื่อง authentication

authentication by spring security on spring3 richfaces4 jsf2

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

ในที่นี้ ต่อจากหน้า login.xhtml ในคราวที่แล้ว ให้เราสร้างหน้า home.xhtml ขึ้นมา

== home.xhtml ==
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:a4j="http://richfaces.org/a4j"
      xmlns:rich="http://richfaces.org/rich"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:sec="http://www.springframework.org/security/facelets/tags">
   
   status : 
   <sec:isAnonymous > anonymous user -_- </sec:isAnonymous > 
   <sec:isAuthenticated > authenticate user ^_^ </sec:isAuthenticated > 
   
   role : 
   <sec:ifNotGranted roles="ROLE_ADMIN">not admin</sec:ifNotGranted >
   <sec:ifAnyGranted roles="ROLE_ADMIN">admin</sec:ifAnyGranted >
   
   <sec:ifAllGranted roles="ROLE_ADMIN, ROLE_USER">both role (user, admin)</sec:ifAllGranted >
   
</html>

ในส่วนนี้เป็นตัวอย่างง่าย ๆ คือ เราต้องประกาศ

xmlns:sec="http://www.springframework.org/security/facelets/tags"

ขึ้นมาก่อน เพื่อใช้ tag ในการทำเรื่อง authorization ได้
หลังจากนั้น เราก็ใช้งาน หลักการง่าย ๆ คือ ถ้า roles ที่กำหนดไว้ ตรงกับ ชื่อ sec:? นั้น ก็จะทำการแสดงข้อความที่อยู่ด้านใน tag sec:? นั้นๆ ได้ โดยในที่นี้ ลองดูมีอยู่ 5 tag ได้แก่


<sec:isAnonymous />

ใช้เพื่อถามว่าเป็น บุคคลทั่วไป หรือเปล่า

<sec:isAuthenticated />

ใช้เพื่อถามว่าเป็น บุคคลในระบบ หรือเปล่า

<sec:ifAllGranted roles="?" />

ใช้เพื่อถามว่าเป็น บุคคลที่อยู่ในตำแหน่งต่าง ๆ เหล่านี้ทั้งหมดหรือเปล่า

<sec:ifNotGranted roles="?" />

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

<sec:ifAnyGranted roles="?" />

ใช้เพื่อถามว่าเป็น บุคคลที่อยู่ในตำแหน่งใดตำแหน่งหนึ่ง ในที่นี้หรือเปล่า

authentication by spring security on spring3 richfaces4 jsf2

authentication กล่าวเกริ่นแบบไม่แปลให้งงเล่นได้ว่า ในส่วนของ web หรือ application ที่เราได้สร้างขึ้นมานั้น ส่วนที่ทำให้ระบบเราปลอดภัย และจำกัดผู้ไม่เกี่ยวข้องในการใช้งานอันดับแรกเลยก็คนเป็นเรื่องของการแสดงตัวตนผู้ใช้ที่เข้ามาในระบบนั่นเอง

ในส่วนนี้จะมี library ที่จำเป็น และ ใช้งานได้ง่ายดังนี้ โดยจะขอยกยอดมาจาก เนื้อเรื่อง spring framework บทก่อนๆ นะครับ ไม่ค่อยได้เริ่มอะไรใหม่เพราะยาวแล้วจะไม่อยากอ่านกัน
JSF 2.0 and Spring Framework annotation example

ขอแสดงเป็น xml maven แล้วกันนะครับ เพื่อความสะดวก โดยเพิ่มส่วนดังต่อไปนี้เข้าไป (ถ้ามีแล้วก็ไม่ต้องเพิ่มซ้ำนะ)
== pom.xml ==

 org.springframework.security
 spring-security-core
 3.0.5.RELEASE


 org.springframework.security
 spring-security-web
 3.0.5.RELEASE



 org.springframework.security
 spring-security-config
 3.0.5.RELEASE


 org.springframework.security
 spring-security-taglibs
 3.0.5.RELEASE


 org.springframework.security
 taglib-core
 0.4


 org.springframework.security
 facelets-taglib-jsf20-spring-3
 0.5


และเพิ่มในส่วนของ repository เข้าไป เพราะ jar file บางตัว ไม่ได้อยู่ center


 org.springframework.security.taglibs.facelets
 Repository for library Library[spring-security-facelets-taglib-googlecode]
 http://spring-security-facelets-taglib.googlecode.com/svn/repo/


หลังจากนั้น ก็เพิ่ม code ใน file web.xml เพื่อให้ spring security เริ่มทำงานได้

== web.xml ==

 springSecurityFilterChain
 org.springframework.web.filter.DelegatingFilterProxy


 springSecurityFilterChain
 /*


หลังจากนั้น ก็ทำการ setup บน application context file แต่ในที่นี้ เราแยกออกมาเป็นอีก file โดย file applicationContext.xml เราเพิ่ม code ไปว่า

== applicationContext.xml ==
<import resource="/security.xml"  />

== security.xml ==



    
    
        <intercept-url pattern="/login.xhtml" filters="none" />
        <intercept-url pattern="/images/**" filters="none" />
        <intercept-url pattern="/javascript/**" filters="none" />
        <intercept-url pattern="/css/**" filters="none" />
        <intercept-url pattern="/javax.faces.resource/**" filters="none" />
        <intercept-url pattern="/rfRes/**" filters="none" />
        <intercept-url pattern="/**"  access="ROLE_USER" />
        <form-login login-page="/login.xhtml" default-target-url='/home.xhtml'
            always-use-default-target='true' />
        <logout/>
    
    
    

    
        
            
                
                
            
        
    


ในครั้งนี้เราเปลี่ยน tag beans ที่เคยเป็น default มาเป็น tag security เป็น default แทน เพิ่มทำให้ tag ดูง่ายขึ้น แต่ tag beans เปลี่ยนไปนิดหน่อย
เอาเป็นว่า ในที่นี้เรา สร้าง user มาสองคน ชื่อว่า nai_a กับ nai_b ใน tag authentication-manager
ในส่วน tag http เอาไว้กำหนด หน้าที่ต้องการให้สิทธิใดเข้าถึง รายละเอียดคงต้องหาอ่านกันต่อไปนะ

หลังจากนั้น เราก็มาสร้างหน้า login กันดีกว่า เพื่อให้ครบวงจรซะที

== login.xhtml ==
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:a4j="http://richfaces.org/a4j"
      xmlns:rich="http://richfaces.org/rich"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:p="http://primefaces.prime.com.tr/ui">

    <f:view locale="#{localizationBean.locale}">

        <h:head>
            <title>Project Tracking.</title>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
            <link type="text/css" rel="stylesheet" href="css/default.css" />
        </h:head>

        <h:body onload='document.f.j_username.focus();'>

            <center>
                <p:panel header="Login with Username and Password" style="width:400px;">
                    <form name='f' action='j_spring_security_check' method='POST'>
                        <table>
                            <tr><td colspan="2">  </td></tr>
                            <tr><td>User:</td><td><input type='text' name='j_username' value=''/></td></tr>
                            <tr><td>Password:</td><td><input type='password' name='j_password'/></td></tr>
                            <tr><td colspan="2">  </td></tr>
                            <tr align="center">
                                <td colspan="2">
                                    <input name="submit" type="submit"/> 
                                    <input name="reset" type="reset"/>
                                </td>
                            </tr>
                        </table>
                    </form>
                </p:panel>
            </center>
            <br/>
            ROLE_ADMIN nai_a : jimispassword<br/>
            ROLE_USER nai_b : bobspassword<br/>
        </h:body>
    </f:view>
</html>

ในส่วนนี้จะเป็น หน้า login ที่ทำการกรอก user, password ธรรมดา แต่มีการใช้ tag primefaces เข้ามาช่วย ก็ไม่ต้องสนใจ ในที่นี้ แค่ต้องการนำเอา user, password ส่งไปยัง j_spring_security_check เท่านั้น ถ้าถูกต้องก็จะเข้าไปยังหน้า home.xhtml ดังที่เราได้กำหนดไว้
และถ้า กรอกข้อมูลผิด ก็จะยังอยู่ในหน้า login อีกครั้ง เนื่องจากหน้า login ถูกกำหนดเป็นหน้า default กรณียังไม่ทำการ login ที่ file security.xml

วันศุกร์ที่ 24 มิถุนายน พ.ศ. 2554

Hibernate Annotation generate id from oracle function

แก้ไขการ Generate ID จาก ORACLE Sequence มาเป็น เรียก ORACLE Function
แนะนำความต้องทำความรู้จัก Hibernate, Spring Framework เบื้องต้น ก็จะเป็นการดีครับผม

- spring framework 3 quickstart
- Spring Framework and Hibernate on Annotation

เรามาเข้าจุดที่เราสนใจกันจริง ๆ ดีกว่า ในส่วนของ Hibernate Map Class Model กับ Database นั้น
เราจะมีการเขียน Class ในลักษณะนี้ครับ

โดยในตัวอย่างแรกนี้ จะเป็นเรื่องการ Map ID ของ Class ให้มีการดึง Sequence Oracle
มาเป็น id ตอน insert ข้อมูล

Process.java
package x.y.z.projecttracking.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name="PROCESS")
public class Process extends BaseEntity {
    
    public Process(){}
    
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_PROJ_GEN")
    @SequenceGenerator(name = "SEQ_PROJ_GEN", sequenceName = "SEQ_PROJ")
    private Long id;
    
    @Column
    private String code;
    
    @Column
    private String description;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    
    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

ในส่วนนี้จะเป็นเครื่องมือที่ Hibernate มีให้ใช้งานได้ทันที

ส่วนที่สำคัญที่มีผลต่อการ generate id ก็เป็นดังนี้

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_PROJ_GEN")
ในส่วน GeneratedValue นี้จะบอกชนิดของการสร้าง id โดยระบุ strategy คือ ลักษณะการสร้าง id เป็น
Sequence Oracle
(NOTE : กรณี MySQL นั้น ไม่มี Sequence จะใช้แค่ @GeneratedValue เปล่า ๆ เลยแล้วทาง MySQL จะรู้เองว่า ต้องการขอ id ใหม่ ก็จะสร้างเลขใหม่ไม่ซ้ำให้ แต่ ORACLE ไม่มี auto generate เช่นนั้น โดยทั่วไปถึงต้องเรียกใช้ sequence ที่เป็นอีก function เฉพาะใน oracle ให้ทำการสร้าง running -number เพื่องานใด ๆ)

ส่วน generator นั้นก็จะเป็นตัวบอกว่าไปหา ตัวที่ทำการสร้างนั้นที่ใหน ด้วยชื่อ ตามด้านล่างนี้เลยครับ

@SequenceGenerator(name = "SEQ_PROJ_GEN", sequenceName = "SEQ_PROJ")
ในส่วน SequenceGenerator ตัวนี้ถือเป็นเครื่องมือในการดึงข้อมูลจาก Sequence ORACLE ที่เราได้เตรียมไว้แล้ว
โดย name ก็คือชื่อแทนตัว ไว้ให้สำหรับเรียกใช้จากที่อื่น
ส่วน sequenceName ก็จะเป็นตัวระบุว่า Sequence ORACLE ที่เราต้องการใช้งานนั้น มีชื่อว่าอะไร

จบในส่วน เกรินนำ ID Generate By Sequence ไว้เท่านี้ก่อน

เราไปเริ่มแก้ให้เป็น ID Generate By Function กันดีกว่า

เริ่มจาก เราเปลี่ยน Generator ใหม่เป็นดังนี้

Process.java
package x.y.z.projecttracking.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import org.hibernate.annotations.GenericGenerator;

@Entity
@Table(name="PROCESS")
public class Process extends BaseEntity {
    
    public Process(){}
    
    @Id
    @GeneratedValue(generator = "projIdSeqGen")
    @GenericGenerator(
        name = "projIdSeqGen", 
        strategy = "x.y.z.projecttracking.generator.ProjIdSequenceGenerator")
    private Long id;
    
    @Column
    private String code;
    
    @Column
    private String description;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    
    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

มีส่วนที่เปลี่ยนแปลง 2 ส่วน ได้แก่
@GeneratedValue(generator = "projIdSeqGen")
ในส่วน strategy เราไม่ต้องระบุแล้ว เพราะเราจะปรับแต่งเอาเอง โดยใช้ตัว genreator พื้นฐานคือ GenericGenerator

@GenericGenerator(
name = "projIdSeqGen",
strategy = "x.y.z.projecttracking.generator.ProjIdSequenceGenerator")
ในส่วนนี้เราใช้ GenericGenerator แล้วนำมาปรับแต่ง เพิ่มเติมโดย เราจะเห็นว่ามีการระบุ strategy ขึ้นมา
แล้วเราก็เขียน class ซึ่งจะมี code ตามด้านล่างนี้

package x.y.z.projecttracking.generator;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.CallableStatement;
import java.sql.Types;
import java.util.Properties;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.Configurable;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.SequenceHiLoGenerator;
import org.hibernate.type.Type;

/**
 *
 * @author Kan.s
 */
public abstract class FunctionIdSequenceGenerator implements PersistentIdentifierGenerator, Configurable {

    protected final SequenceHiLoGenerator delegate = new SequenceHiLoGenerator();
    protected Constructor constructor;

    @Override
    public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
        return delegate.sqlCreateStrings(dialect);
    }

    @Override
    public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
        return delegate.sqlDropStrings(dialect);
    }

    @Override
    public Object generatorKey() {
        return delegate.generatorKey();
    }

    @Override
    public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
        try {
            String sqlCallFunc = "{ ? = call "+getFunctionName()+"() }";
            System.out.println("SQL call Function() : " + sqlCallFunc);
            CallableStatement call = session.connection().prepareCall(sqlCallFunc);
            call.registerOutParameter(1, Types.VARCHAR);
            call.execute();
            String result = call.getString(1);
            return typedIdForValue(result);
        } catch (Exception e) {
            throw new HibernateException("delegate didn't return a string", e);
        }
    }

    @Override
    public void configure(Type type, Properties params, Dialect d) throws MappingException {
        delegate.configure(Hibernate.STRING, params, d);
        try {
            constructor = type.getReturnedClass().getConstructor(String.class);
        } catch (NoSuchMethodException e) {
            throw new HibernateException("FunctionIdSequenceGenerator is only applicable for types deriving from AbstractPersistenLongId", e);
        }
    }

    protected Serializable typedIdForValue(String val) {
        try {
            return (Serializable) constructor.newInstance(val);
        } catch (InstantiationException e) {
            throw new HibernateException("Cannot create strongly typed id", e);

        } catch (IllegalAccessException e) {
            throw new HibernateException("Cannot create strongly typed id", e);

        } catch (InvocationTargetException e) {
            throw new HibernateException("Cannot create strongly typed id", e);
        }
    }

    public abstract String getFunctionName();
}

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

id ส่วนนี้ ผมจะ return String เลยจัดการแก้ในส่วน Method configure()

ส่วนสำคัญที่ทำหน้าที่สร้าง ID คือ Method generate ในส่วนนี้ผมได้แก้ให้เป็น เรียก Function ORACLE โดย Class ที่นำไปใช้งานจริง จะถูกบังคับให้สร้าง Method getFunctionName() เพื่อระบุชื่อ Function ORACLE ที่ต้องการทำการเรียกใช้งาน

หากต้องการใส่ parameter เข้าไปยัง function ก็ใช้ประมาณนี้ครับ
    String sqlCallFunc = "{ ? = call "+getFunctionName()+"(?, ?) }";
    String param1 = "test";
    Long param2 = "test";
    CallableStatement call = session.connection().prepareCall(sqlCallFunc);
    call.registerOutParameter(1, Types.VARCHAR);
    call.setString(2, param1);
    call.setLong(3, param2 );
    call.execute();
    String result = call.getString(1);
ตามตัวอย่างนี้ก็หวังว่าจะนำไป apply ใช้ประโยชน์ได้นะครับ

ส่วน Class ที่นำไปใช้งานจริงก็เป็นแบบนี้ครับ

package x.y.z.projecttracking.generator;

/**
 *
 * @author Kan.s
 */
public class ProjIdSequenceGenerator extends FunctionIdSequenceGenerator {

    @Override
    public String getFunctionName() {
        return "getProjId";
    }
}

เรามาดูตัวอย่าง PL/SQL ที่ Generate function ORACLE กันบ้างดีกว่า
ในที่นี้เขียนง่าย ๆ เพราะเขียนได้แค่นี้ครับ T_T
ขั้นตอน คือ นำเอาวันเวลาปัจจุบัน มาแล้วเติม prefix เป็น 'PJ'
CREATE OR REPLACE FUNCTION getProjId(
) RETURN VARCHAR2 IS
    dttm_curr VARCHAR2(20);
    cursor c1 IS 
    SELECT TO_CHAR(CURRENT_DATE, 'DDMMYYYYHHMISSSSS') FROM DUAL;
BEGIN
   open c1;      
   fetch c1 into dttm_curr;
   close c1;
   return CONCAT('PJ', dttm_curr); 
END getId4DttmCurr  ;

Result : PJXXXXXXXXXXXXXXXXX
X is number 0-9


กรณี เขียนแบบให้รับ parameter
CREATE OR REPLACE FUNCTION getProjId(prefix IN varchar2
) RETURN VARCHAR2 IS
    dttm_curr VARCHAR2(20);
    cursor c1 IS 
    SELECT TO_CHAR(CURRENT_DATE, 'DDMMYYYYHHMISSSSS') FROM DUAL;
BEGIN
   open c1;      
   fetch c1 into dttm_curr;
   close c1;
   return CONCAT(prefix, dttm_curr); 
END getId4DttmCurr  ;

------------------------------------------------
Thank you for...
credit :
- Custom Id Generators For Typed Id Values With Hibernate
- stackoverflow : How to call a Oracle function from hibernate with return parameter?

วันศุกร์ที่ 17 มิถุนายน พ.ศ. 2554

what ? difference for 'Action' and 'ActionListener' in JSF

action จะ return outcome ซึ่งจะมีผลให้ระบบ navigator system ทำงาน
ว่าทำ action นี้แล้วไปที่ view ไหน (โดยดูจาก outcome ) คล้ายๆกับ findforward ใน Struts น่ะครับ

ส่วน actionListener นั้นเป็น event handler จากการทำ action นี้น่ะครับ
ไม่มีการเปลี่ยน view

อีกอย่าง method signature ของทั้งสองจะต่างกันด้วย โดยตัว method ของ actionListener นั้นจะมี argument ที่เป็น ActionEvent ด้วย เช่น

public void doIt(ActionEvent event){
  ...
}

method signature ของ action นั้นจะไม่มี argument แต่จะ return String ที่เป็น outcome ของ action ออกมาแทน เช่น

public String login() {
  if ( ... ) // login is successful {
    return "success";
  } else {
    return "failure";
  }
}

Note:
actionListener ถูกทำก่อนครับ แล้ว action จะถูกทำทีหลัง

Credit:
http://www.narisa.com/forums/index.php?showtopic=7176

วันพฤหัสบดีที่ 16 มิถุนายน พ.ศ. 2554

JSF2 and Internationalization

ขอรวบรัดนิดนึงครับ ว่าจะเอา ตัวอย่างมาจากบทก่อน เพื่อแก้เพิ่มเนื่องจากเวลาจำกัด
ให้ดูตัวอย่างเริ่มต้นจากด้านล่างนี้ครับ แล้วเราจะมาเพิ่มเติมกันเลย

JSF 2.0 and Spring Framework annotation example

เริ่มแรกให้เราไปเพิ่ม locale และ path file properties ให้ app เรารู้จักก่อน โดยไปเพิ่มที่ file
faces-config.xml
<application>
 <locale-config>
  <default-locale>en</default-locale>
  <supported-locale>th</supported-locale>
 </locale-config>
 <resource-bundle>
  <base-name>message</base-name>
  <var>msg</var>
 </resource-bundle>
</application>

จากตัวอย่าง code ก็จะอธิบายสั้น ๆ ว่า เรากำหนด msg เพื่อให้สะดวกเรียกใช้บนหน้า jsf โดยเวลาเราเรียกใช้จะได้ดังนี้
#{msg['label.username']}

เราจะเพิ่ม file ที่ชื่อ message.properties (มี default locale เป็น en) และ file ที่ชื่อ message_th.properties ทั้ง 2 file นี้จะสร้างไว้ที่ classpath
โดยจะกำหนดค่าในแต่ละ file ดังนี้

message.properties
label.username=User Name
label.password=Password

message_th.properties
label.username=ชื่อผู้ใช้
label.password=รหัสผ่าน

ส่วนต่อมาคือ code ส่วนที่นำไปแปะไว้ที่หน้า web jsf file เพื่อกด click แล้วให้ทำการสลับ ภาษาในหน้านั้น
<h:form>
 <h:commandLink actionListener="#{localizationBean.changeLanguage('th')}" >
  <h:graphicImage value="/images/icon_flag/th.png" styleClass="pic" />
 </h:commandLink>
 / 
 <h:commandLink actionListener="#{localizationBean.changeLanguage('en')}" >
  <h:graphicImage value="/images/icon_flag/en.png" styleClass="pic" />
 </h:commandLink>
</h:form>

ในที่นี้มี h:form เพื่อทำการ refresh ค่าในหน้า page
ตัวอย่างจาก code ก็คือ มีธงชาติ 2 ประเทศ กดอันใหน ก็เป็นภาษานั้น เข้าใจง่ายดี รู้ไม่มีจินตนาการเอาเอง
โดยปกติ แล้วก็ควรจะวางไว้บน header เพื่อที่จะได้ reuse ได้

ส่วนต่อมาคือ class ที่เป็น bean ทำหน้าที่ set locale อยู่เบื้องหลัง code ค่อนข้างจะน้อย ที่สำคัญคือ
ให้ set bean scope เป็น session ไม่งั้น ค่ามันจะหลุดจากการ set locale ครั้งล่าสุด ไปเป็นค่า defaultตลอด
package x.y.z.project.web.jsf.bean;

import java.io.Serializable;
import java.util.Locale;
import javax.faces.context.FacesContext;
import javax.inject.Named;
import org.springframework.context.annotation.Scope;

@Named("localizationBean")
@Scope("session")
public class LocalizationBean implements Serializable {
    
    private Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();

    public void changeLanguage(String language) {
        locale = new Locale(language);
        FacesContext.getCurrentInstance().getViewRoot().setLocale(locale);
    }

    public Locale getLocale() {
        return locale;
    }

    public String getLanguage() {
        return locale.getLanguage();
    }
}

ต่อไปก็เป็นส่วนที่ใช้งานบนหน้า web จริง คือส่วนที่ถูกเปลี่ยนแปลง ดูตัวอย่างเลยดีกว่า
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:a4j="http://richfaces.org/a4j"
      xmlns:rich="http://richfaces.org/rich"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <f:view locale="#{localizationBean.locale}">
  <h:head>
   <title>Project</title>
  </h:head>
  <h:body>
   <f:facet name="header">
    <ui:include src="header.xhtml" />
   </f:facet>

   <rich:panel>
   <h:panelGrid columns="2" styleClass="formhilightlefttable">
    <h:outputLabel value="#{msg['label.username']}" />
    <h:inputText value="#{loginBean.username}"/>
    
    <h:outputLabel value="#{msg['label.password']}" />
    <h:inputText value="#{loginBean.password}"/>
   </h:panelGrid>
   </rich:panel>
  </h:body>
 </f:view>
</html>

ในส่วนนี้ที่สำคัญคือ
ที่ไว้ใช้กำหนด เก็บค่า locale ไว้สำหรับทั้งหน้า

#{msg['label.username']} เป็นตัวอย่างค่าที่ดึงจาก file message.properties ในส่วนนี้จะถูกเปลี่ยน ค่าไป จาก ไทย เป็น อังกฤษ เมื่อมีการคลิ๊กที่ธงชาติ

#{loginBean.username} ตรงนี้เป็นในส่วนติดต่อ กับ bean หลังบ้าน เพื่อใช้เก็บค่า และดำเนินการต่อไป

ui:include src="header.xhtml" ในส่วนนี้ได้ทำการ include file header มาส่วนใหญ่แล้ว ก็จะวางในส่วน menu ต่าง ๆ ไว้ รวมถึง locale ที่เป็นรูปธงไว้ใช้ สลับภาษาไว้บน file นี้ด้วย

วันศุกร์ที่ 6 พฤษภาคม พ.ศ. 2554

JSF 2.0 and Spring Framework annotation example

หลังจากที่เราเรียนรู้ spring ในบทก่อน ๆ กันไปแล้ว มาตอนนี้เราก็มาเพิ่มในส่วนของ user interface กันดีกว่า เหตุผลการใช้คือ ช่วงนี้เค้า ชอบใช้กัน แค่นั้นเอง คือเหตุผลที่เราต้องใช้ -_-"

ในตัวอย่างที่เขียนนี้เป็นแค่หน้ากรอก ขื่อผู้ใช้เข้ามา แล้วลง ชื่อ และ เวลาที่เข้าใช้ใน database

จะช้าไปใย เราก็เริ่มกันเลยดีกว่า
เริ่มจาก create project ในรูปแบบของ web แล้วเราจะได้ โครงสร้าง แล้วเพิ่ม file ต่าง ๆ ลงไปตามนี้
ในที่นี้ create project ด้วย netbean maven ดังนั้นก็ไม่ต้องคิดมากถ้ามีอะไรบางอย่างวางแปลกที่ไป
อย่าไปสนใจ เอาไว้แค่ดูคร่าว ๆ ว่ามี file อะไรบ้างที่จำเป็นแค่นั้นพอ

> Web App
>>> Web Pages
>>>>> WEB-INF
>>>>> - applicationContext.xml
>>>>> - datasource.xml
>>>>> - faces-config.xml
>>>>> - glassfish-web.xml
>>>>> - hibernate.xml
>>>>> - web.xml
>>> - hello.xhtml
>>> - index.jsp
>>> - welcome.xhtml
>>> Source Packages
>>>>> com.mycompany.projecttracking.jsfbean
>>>>> - HellowWorldBean.java
>>>>> com.mycompany.projecttracking.model
>>>>> - BeanEntity.java
>>>>> - WelcomeLog.java
>>>>> com.mycompany.projecttracking.service
>>>>> - WelcomeLogDao.java
>>>>> - WelcomeLogDaoImpl.java
>>> Project Files
>>> - pom.xml

ในตอนแรก เพื่อให้ง่ายต่อผู้ใช้ maven เรามา add depandency กันก่อนดีกว่า ส่วนผู้ที่ไม่ได้ใช้ maven เราเรามาบอกอีกทีว่าใช้ .jar อะไรบ้าง ในขั้นต่อไปนะ



        
            org.springframework
            spring-core
            3.0.5.RELEASE
        
        
            org.springframework
            spring-context
            3.0.5.RELEASE
        
        
            org.springframework
            spring-aop
            3.0.5.RELEASE
        
        
            org.springframework
            spring-aspects
            3.0.5.RELEASE
        
        
            org.springframework
            spring-asm
            3.0.5.RELEASE
        
        
            org.springframework
            spring-jdbc
            3.0.5.RELEASE
        
        
            org.springframework
            spring-beans
            3.0.5.RELEASE
        
        
            org.springframework
            spring-context-support
            3.0.5.RELEASE
        
        
            org.springframework
            spring-instrument
            3.0.5.RELEASE
        
        
            org.springframework
            spring-expression
            3.0.5.RELEASE
        
        
            org.springframework
            spring-webmvc
            3.0.5.RELEASE
        
        
            org.springframework
            spring-tx
            3.0.5.RELEASE
        
        
            org.springframework
            spring-orm
            3.0.5.RELEASE
            jar
        
        
            org.springframework
            spring-test
            3.0.5.RELEASE
        
        
            org.springframework
            spring-web
            3.0.5.RELEASE
        
        
            org.springframework.security
            spring-security-core
            3.0.5.RELEASE
        
        
            org.springframework.security
            spring-security-web
            3.0.5.RELEASE
        
        
            org.springframework.security
            spring-security-config
            3.0.5.RELEASE
        
        
            org.springframework.security
            spring-security-taglibs
            3.0.5.RELEASE
        
        
        
            org.hibernate
            hibernate-core
            3.3.2.GA
        
        
            org.hibernate
            ejb3-persistence
            1.0.1.GA
        
        
            org.hibernate
            hibernate-entitymanager
            3.3.2.GA
        
        
            org.hibernate
            hibernate-annotations
            3.3.1.GA
        
        
            org.hibernate
            hibernate-commons-annotations
            3.3.0.ga
        

        
        
            javax.sql
            jdbc-stdext
            2.0
        
        
        
            javax.transaction
            jta
            1.0.1B
        
        
        
            mysql-connector
            mysql-connector-java-5.1.5-bin
            SNAPSHOT
        
        
        
            log4j
            log4j
            1.2.14
        
        
        
            org.aspectj
            aspectjrt
            1.6.10
        
        
            com.sun.faces
            jsf-api
            2.1.1-b04
        
        
            com.sun.faces
            jsf-impl
            2.1.1-b04
        
        
            jstl
            jstl
            1.1.2
        
        
            javax
            javaee-web-api
            6.0
            compile
        

        
            junit
            junit
            4.8.1
            test
        
       
        
            slf4j
            slf4j-log4j12
            1.5.8
        
        
        
            com.oracle
            ojdbc6
            11.2.0.2.0
        
        
    

กรณีที่ไม่ต้องการให้ run test ตอน deploy ให้เพิ่ม tag ส่วนนี้เข้าไป ใน build
อันนี้ใช้ตอนที่ test ของเรามี environment บางอย่างที่อาจทำให้ error ตอน run แต่ถ้าบางคนไม่เคยเขียน test เลยก็ไม่ต้องใช้ก็ได้นะครับ เข้าใจว่างานเร่งไม่มีเวลาเขียนกัน หรือเขียนไปก็ไม่เคยใช้ เหอ ๆ


 org.apache.maven.plugins
 maven-surefire-plugin
 
  true
 
 
  
   surefire-it
   integration-test
   
    test
   
   
    true
   
  
 


ส่วนผู้ที่ไม่ได้ใช้ maven ก็ add .jar file ดังต่อไปนี้ (จริง ๆ อาจไม่ต้องขนาดนี้ แต่ copy มาจาก jar ที่ได้จาก maven ข้างบน แล้วไม่มีเวลามาตัดให้ว่าอันใหนใช้จริง ต้องขอโทษด้วยครับ ^^")

aspectjrt-1.6.10.jar
ejb3-persistence-1.0.1.GA.jar
hibernate-annotations-3.3.1.GA.jar
hibernate-commons-annotations-3.3.0.ga.jar
hibernate-core-3.3.2.GA.jar
hibernate-entitymanager-3.3.2.GA.jar
javaee-web-api-6.0.jar
jdbc-stdext-2.0.jar
jsf-api-2.1.1-b04.jar
jsf-impl-2.1.1-b04.jar
jstl-1.1.2.jar
jta-1.0.1B.jar
log4j-1.2.14.jar
mysql-connector-java-5.1.5-bin-SNAPSHOT.jar
ojdbc6-11.2.0.2.0.jar
slf4j-log4j12-1.5.8.jar
spring-aop-3.0.5.RELEASE.jar
spring-asm-3.0.5.RELEASE.jar
spring-aspects-3.0.5.RELEASE.jar
spring-beans-3.0.5.RELEASE.jar
spring-context-3.0.5.RELEASE.jar
spring-context-support-3.0.5.RELEASE.jar
spring-core-3.0.5.RELEASE.jar
spring-expression-3.0.5.RELEASE.jar
spring-instrument-3.0.5.RELEASE.jar
spring-jdbc-3.0.5.RELEASE.jar
spring-orm-3.0.5.RELEASE.jar
spring-security-config-3.0.5.RELEASE.jar
spring-security-core-3.0.5.RELEASE.jar
spring-security-taglibs-3.0.5.RELEASE.jar
spring-security-web-3.0.5.RELEASE.jar
spring-test-3.0.5.RELEASE.jar
spring-tx-3.0.5.RELEASE.jar
spring-web-3.0.5.RELEASE.jar
spring-webmvc-3.0.5.RELEASE.jar
antlr-2.7.6.jar
aopalliance-1.0.jar
asm-1.5.3.jar
asm-attrs-1.5.3.jar
aspectjweaver-1.6.8.jar
cglib-2.1_3.jar
commons-collections-3.1.jar
commons-logging-1.1.1.jar
dom4j-1.6.1.jar
ehcache-1.2.3.jar
hibernate-3.2.6.ga.jar
javassist-3.4.GA.jar
persistence-api-1.0.jar
slf4j-api-1.5.8.jar
spring-security-acl-3.0.5.RELEASE.jar
xml-apis-1.0.b2.jar
junit-4.8.1.jar


ขั้นตอนแรกที่จำเป็นต้องทำคือ แก้ไข file : web.xml



    
    
        contextConfigLocation/WEB-INF/applicationContext.xml
    
        org.springframework.web.context.ContextLoaderListener
    
    
     
        org.springframework.web.context.request.RequestContextListener  
      
    
    
        javax.faces.PROJECT_STAGEDevelopment
    
    
        javax.faces.FACELETS_SKIP_COMMENTStrue
    
    
        com.sun.faces.expressionFactorycom.sun.el.ExpressionFactoryImpl

    
        Faces Servlet
        javax.faces.webapp.FacesServlet
        1
    

    
    
        Faces Servlet
        *.xhtml
    
    
    
        30
    
    
        index.jsp
    



และ faces-config.xml ประกาศไว้ก่อนยังไม่ได้ใช้อะไรในตอนนี้มาก




    
        org.springframework.web.jsf.el.SpringBeanFacesELResolver
        
    


ต่อมาเป็น config spring context : applicationContext.xml


           
    <import resource="/datasource.xml" />
    <import resource="/hibernate.xml" />

    <context:annotation-config/>
    <context:component-scan base-package="com.mycompany.projecttracking" />


ต่อมาเป็น config database (ในที่นี้ต่อเข้าเครื่องตัวเองครับ ใช้ oracle 11g xe free) : datasource.xml


  
    
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
        <property name="username" value="SYSTEM" />
        <property name="password" value="system" />
    



ต่อมาเป็น config hibernate : hibernate.xml



    
    
        
    
    
    
        <property name="dataSource" ref="dataSource" />
        
            
                org.hibernate.dialect.OracleDialect
                true
                false
            
        
        <property name="schemaUpdate" value="true"/>
        <property name="packagesToScan" value="com.mycompany.projecttracking.model"/>
    
    

config ในที่นี้ table database จะถูกสร้างเองเมื่อ run web app อันดับต่อไป ก็เป็นส่วน service แต่ในที่นี้เขียน dao แล้วต่อตรงไปเลย แบบว่าให้สั้น ๆ ไว้ก่อน เพราะทำเป็น ตัว demo เล่น ๆ เฉย ๆ ส่วนเรื่องวิธีเขียน service ก่อน เข้า dao ไปดูบทเก่า ก่อนหน้านี้มีเคยเขียนไว้ BaseEntity.java
package com.mycompany.projecttracking.model;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;

@MappedSuperclass
public class BaseEntity implements Serializable {
    
    @Column
    private String createBy;
    
    @Column
    @Temporal(javax.persistence.TemporalType.TIMESTAMP)
    private Date createDate;
    
    @Column
    private String updateBy;
    
    @Column
    @Temporal(javax.persistence.TemporalType.TIMESTAMP)
    private Date updateDate;

    
    public String getCreateBy() {
        return createBy;
    }

    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public String getUpdateBy() {
        return updateBy;
    }

    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }

    public Date getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
    }
    
}
WelcomeLog.java
package com.mycompany.projecttracking.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name="WELCOME_LOG")
public class WelcomeLog extends BaseEntity{
    
    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_WCL_GEN")
    @SequenceGenerator(name = "SEQ_WCL_GEN", sequenceName = "SEQ_WCL")
    private Long id;
    
    @Column
    private String username;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
    
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    
}
WelcomeLogDao.java
package com.mycompany.projecttracking.service;

public interface WelcomeLogDao {
    
    public void log2DB(String name);
    
}
WelcomeLogDaoImpl.java
package com.mycompany.projecttracking.service;

import com.mycompany.projecttracking.model.WelcomeLog;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Repository;

@Repository("welcomeLogDao")
public class WelcomeLogDaoImpl implements WelcomeLogDao{
    
    @Autowired
    private HibernateTemplate hibernateTemplate;

    @Override
    public void log2DB(String name) {
        WelcomeLog wl = new WelcomeLog();
        wl.setCreateBy("SYSTEM");
        wl.setCreateDate(new Date());
        wl.setUsername(name);
        hibernateTemplate.save(wl);
    }
    
}
ในส่วนของ spring ที่ทำหน้าที่ service มีแค่นี้ จากนั้นก็มาในส่วน jsf ส่วนแรกก็คงเป็นหน้า web จะ create เป็น .xhtml hello.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        Hello from Facelets.<br />
        <h:form prependId="false">
        Your name : <h:inputText value="#{hwBean.username}"></h:inputText><br />
           <h:commandButton value=" == OK == "  action="welcome.xhtml"></h:commandButton>
        </h:form>
    </h:body>
</html>
welcome.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:outputText value="#{hwBean.message}" />
        </h:form>
    </h:body>
</html>
ต่อจากนั้นก็สร้าง bean ที่ทำการผูก action ให้หน้า web HelloWorldBean.java
package com.mycompany.projecttracking.jsfbean;

import com.mycompany.projecttracking.service.WelcomeLogDao;
import java.io.Serializable;
import org.springframework.context.annotation.Scope;
import javax.inject.Inject;
import javax.inject.Named;

@Named("hwBean")
@Scope("request")
public class HelloWorldBean implements Serializable {
    
    private String username;
    
    private String message;
    
    @Inject
    private WelcomeLogDao welcomeLogDao;
    
    public void setMessage(String message){
        this.message = message;
    }
    
    public String getMessage(){
        return message;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.message = "Welcome : " + username;
        this.username = username;
        
        welcomeLogDao.log2DB(username);
    }
    
}

เราจะเห็น annotation @Named และ @Inject ในส่วนของ jsf นี้ ซึ่งจริง ๆ เป็น มาตรฐานของ jee เลย แล้ว spring ก็มีวิธีใช้ annotation ตัวนี้แทน @Repository และ @AutoWierd ด้วย ซึ่งจะทำให้เป็นไปในแนวทางเดียวกัน ทั้ง app ที่เราเขียนขึ้น
เอาไว้คราวหน้าจะเขียนเรื่องการใช้ annotation มาตรฐานตัวนี้มาแทน annotation spring เดิม นี้อีกที

หลังจากนั้น ก็ run web app กันเลย
เราก็จะเข้า url : http://localhost:8080/{Web App}/hello.xhtml

ในที่นี้ {Web App} ก็คือชื่อ project ที่เราตั้งไว้
เมื่อเราเข้ามาที่หน้าแรก ก็จะใช้กรอก Your name : [ ]
หลักจากนั้น กดปุ่ม [ == OK == ]
ก็จะย้ายไปที่หน้า welcome.xhtml
Welcome : { ชื่อ ที่กรอกที่หน้า hello.xhtml }

data ก็จะถูก save ลงใน database ที่ชื่อ table WELCOME_LOG

เริ่มต้นง่าย ๆ เพียงเท่านี้ก่อน คราวหน้าคงจะลงไปในส่วน ajax on jsf กัน

วันพุธที่ 4 พฤษภาคม พ.ศ. 2554

URL Repository Java Maven

my url repository maven for get jar or source
-----------------------------------------------------
http://repo1.maven.org/maven2/
http://repository.jboss.org/nexus/content/groups/public-jboss/
http://download.java.net/maven/2/
http://repo2.maven.org/maven2/
http://download.java.net/maven/2/
-----------------------------------------------------

วันอังคารที่ 26 เมษายน พ.ศ. 2554

Spring Framework and Web Service JAX-WS on Annotation

หลังจากที่เราได้รู้เรื่องการติดต่อ Database กับไปแล้วจากบทนี้

Spring Framework and Hibernate on Annotation

ตอนนี้เราก็มารู้เรื่องเกี่ยวกับ Web Service กันต่อเลย แต่จะย่อหน่อย เป็นตัวอย่าง
เพราะจริง ๆ ก็เขียนอยู่แค่นี้เอง

ProjectCheckListWS.java
package th.go.nhso.projecttracking.webservice.service;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import th.go.nhso.projecttracking.webservice.model.ProjectPlanCodeResult;
import th.go.nhso.projecttracking.webservice.model.ProjectPlanCodeRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckInRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckInResult;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckOutRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckOutResult;

@WebService
public interface ProjectCheckListWS {
    public ProjectPlanCodeResult getPlanCode(ProjectPlanCodeRequest planCodeReq);
    public ProjectCheckInResult checkIn(ProjectCheckInRequest checkInReq);
    public ProjectCheckOutResult checkOut(ProjectCheckOutRequest checkOutReq);
}


ProjectCheckListWSImpl
package th.go.nhso.projecttracking.webservice.service;

import javax.jws.WebMethod;
import javax.jws.WebService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import th.go.nhso.projecttracking.service.ProjectCheckListService;
import th.go.nhso.projecttracking.webservice.model.ProjectPlanCodeRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectPlanCodeResult;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckInRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckInResult;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckOutRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckOutResult;

@WebService(serviceName = "ProjectCheckListWebService")
public class ProjectCheckListWSImpl extends SpringBeanAutowiringSupport implements ProjectCheckListWS {

    @Autowired
    private ProjectCheckListService service;

    @Override
    @WebMethod
    public ProjectCheckInResult checkIn(ProjectCheckInRequest req) {
        return service.checkIn(req);
    }

    @Override
    @WebMethod
    public ProjectCheckOutResult checkOut(ProjectCheckOutRequest req) {
        return service.checkOut(req);
    }

    @Override
    @WebMethod
    public ProjectPlanCodeResult getPlanCode(ProjectPlanCodeRequest req) {
        return service.getPlanCode(req);
    }

}


หรือในอีกกรณีคือ ประกาศ endpointInterface ให้กับ ตัว implement แต่วิธีนี้ spring จะสามารถ ผูก service (autowired) ได้ และต้อง extends SpringBeanAutowiringSupport ด้วย มิฉะนั้น service จะ null ได้
ตอนสร้าง service ครั้งแรกไปดูสลับกับ CXF Web Service เลยก็งง ๆ อยู่ เหมือนกันมาก

model ที่ใช้ ส่งค่า parameter นั้นต้องมีการประกาศอะไรเพิ่มเติมนิดหน่อย ตัวอย่าง

BaseProjectCheckRequest.java
package th.go.nhso.projecttracking.webservice.model;

import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

@XmlAccessorType(XmlAccessType.FIELD)
public abstract class BaseProjectCheckRequest implements Serializable {
    
    public String projectCode;
    
    public String projectPlanCode;
    
    public String processCode;
    
    public String processSubCode;
    
    private String postBy;
    
    private String postLocation;

    public String getPostBy() {
        return postBy;
    }

    public void setPostBy(String postBy) {
        this.postBy = postBy;
    }

    public String getPostLocation() {
        return postLocation;
    }

    public void setPostLocation(String postLocation) {
        this.postLocation = postLocation;
    }

    public String getProcessCode() {
        return processCode;
    }

    public void setProcessCode(String processCode) {
        this.processCode = processCode;
    }

    public String getProcessSubCode() {
        return processSubCode;
    }

    public void setProcessSubCode(String processSubCode) {
        this.processSubCode = processSubCode;
    }

    public String getProjectCode() {
        return projectCode;
    }

    public void setProjectCode(String projectCode) {
        this.projectCode = projectCode;
    }

    public String getProjectPlanCode() {
        return projectPlanCode;
    }

    public void setProjectPlanCode(String projectPlanCode) {
        this.projectPlanCode = projectPlanCode;
    }
}

ProjectCheckInRequest.java
package th.go.nhso.projecttracking.webservice.model;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class ProjectCheckInRequest extends BaseProjectCheckRequest {

}

ก็คือ มีการกำหนด xml type เข้าไปด้วย ไม่อย่างนั้นมันจะมีข้อผิดพลาดเกิดขึ้น
@XmlAccessorType(XmlAccessType.FIELD) << ถ้าไม่ใส่ จะเกิดการเรียกสองครั้ง คือ ครั้งแรกจะไปเรียก attribute ก่อน แล้ว มันก็จะไปเรียก get method อีกที แล้วก็ฟ้องว่ามีการประกาศ attribute สองตัวเหมือนกัน ใน class เดียว -_-" อ่าว โง่หนิ !!!
@XmlRootElement << เพื่อบอกว่า ให้เอา attribute ของ root มาเป็นของตัวเองด้วย ไม่งั้นมันจะว่างเปล่า ไม่มี attribute เลย เพราะ class ลูกเราก็ไม่ค่อยได้เขียนอะไรอยู่แล้ว

ส่วนละเอียด service ก็จะเป็นประมาณนี้

ProjectCheckListService.java
package th.go.nhso.projecttracking.service;

import th.go.nhso.projecttracking.webservice.model.ProjectCheckInRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckInResult;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckOutRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckOutResult;
import th.go.nhso.projecttracking.webservice.model.ProjectPlanCodeRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectPlanCodeResult;

public interface ProjectCheckListService {
    public ProjectCheckInResult checkIn(ProjectCheckInRequest request);    
    public ProjectCheckOutResult checkOut(ProjectCheckOutRequest request);
    public ProjectPlanCodeResult getPlanCode(ProjectPlanCodeRequest request);
}


ProjectCheckListService.java
package th.go.nhso.projecttracking.service;

import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import th.go.nhso.projecttracking.dao.ProcessTranDao;
import th.go.nhso.projecttracking.model.ProcessTran;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckInRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckInResult;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckOutRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectCheckOutResult;
import th.go.nhso.projecttracking.webservice.model.ProjectPlanCodeRequest;
import th.go.nhso.projecttracking.webservice.model.ProjectPlanCodeResult;

@Service
public class ProjectCheckListServiceImpl implements ProjectCheckListService {
    private static String STEP_IN = "START";
    private static String STEP_OUT = "FINISH";
    
    @Autowired
    private ProcessTranDao processTranDao;
    
    @Override
    public ProjectCheckInResult checkIn(ProjectCheckInRequest request) {
        ProcessTran t = new ProcessTran();
        this.setReqeq2Tran(t, request);
        processTranDao.insert(t);
        
        ProjectCheckInResult result = new ProjectCheckInResult();
        result.setCode("200");
        result.setDescription("SUCCESS");
        return result;
    }

    @Override
    public ProjectCheckOutResult checkOut(ProjectCheckOutRequest request) {
        ProcessTran t = new ProcessTran();
        this.setReqeq2Tran(t, request);
        processTranDao.insert(t);
        
        
        ProjectCheckOutResult result = new ProjectCheckOutResult();
        result.setCode("200");
        result.setDescription("SUCCESS");
        return result;
    }

    private void setReqeq2Tran(ProcessTran t, ProjectCheckInRequest r) {
        t.setProjectId(Long.MIN_VALUE);
        t.setProjectPlanId(Long.MIN_VALUE);
        t.setProcessId(Long.MIN_VALUE);
        t.setProcessSubId(Long.MIN_VALUE);
        t.setCreateDate(new Date());
        t.setCreateBy(r.getPostLocation());
        t.setStep(STEP_IN);
    }
    
    
    private void setReqeq2Tran(ProcessTran t, ProjectCheckOutRequest r) {
        t.setProjectId(Long.MIN_VALUE);
        t.setProjectPlanId(Long.MIN_VALUE);
        t.setProcessId(Long.MIN_VALUE);
        t.setProcessSubId(Long.MIN_VALUE);
        t.setCreateDate(new Date());
        t.setCreateBy(r.getPostLocation());
        t.setStep(STEP_OUT);
    }

    @Override
    public ProjectPlanCodeResult getPlanCode(ProjectPlanCodeRequest request) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
    
}

ก็เป็น service ธรรมดาที่เราผูกไว้ กับ spring นั่นเอง

Spring Framework and Hibernate on Annotation

แปลกใจตัวเองไม่ได้เขียนอันนี้ขึ้นมา ทั้งที่น่าจะใช้บ่อย Spring Framework และ Hibernate config on Annotation
แต่เอาเหอ ถึงจะขี้เกียจนิดหน่อย แต่มาเริ่มกันดีกว่า ว่าจะทำอะไรก่อนดี บทนี้น่าจะต่อจาก

Java Spring framework 3 Quickstart

แต่ในบทนี้จะไม่พูดถึงเรื่อง coltroller จะพูดเรื่อง ผูก service hibernate กับ spring เป็นหลัก
อันดับแรก ให้สร้าง Web Project หรือ Java Web Application ด้วย Netbean หรือ eclipse(jee) ตามแต่ถนัดนะ อันนี้คงไม่ลง detail ไม่อยากมีภาคสอง(จะยากเกินเดี๋ยวไม่จบ)

จากนั้นก็ไปที่ file : web/WEB-INF/web.xml เริ่มแก้ไข xml ให้เป็นเช่นนี้

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    
    <!-- Spring Framework Config -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>*.htm</url-pattern>
    </servlet-mapping>
    
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>

จากนั้น ก็ config application file กับต่อเลย file : web/WEB-INF/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    
    <import resource="/datasource.xml" />
    <import resource="/hibernate.xml" />
    
    <context:annotation-config/>
    <context:component-scan base-package="th.go.nhso.projecttracking" />
    
</beans>



ตามด้วย config database datasource config file : web/WEB-INF/datasource.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
  
    <bean id="dataSource" 
         class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/projecttracking" />
        <property name="username" value="develop" />
        <property name="password" value="password" />
    </bean>

</beans>


ต่อจากนั้น ก็ hibernate config file : web/WEB-INF/hibernate.xml
ในส่วนนี้จะทำการ create และ update table ใน database ให้เอง 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <constructor-arg ref="sessionFactory" name="sessionFactory" />
    </bean>
    
    <!-- Hibernate session factory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
        <property name="schemaUpdate" value="true"/>
        <property name="packagesToScan" value="th.go.nhso.projecttracking.model"/>
    </bean>
    
</beans>



อันดับต่อไป อันนี้ไม่ค่อยสำคัญมากในหัวเรื่องนี้ view file : dispatcher-servlet.xml
ถ้าใช้ netbean สร้าง web project ดึง spring framework เป็น plugin ก็จะมี file นี้โดยไม่ต้องเขียนเอง ส่วนใหญ่เราจะไม่ค่อยได้ยุ่งกับ file นี้มากนัก ใช้เป็นแค่ทางผ่านไป controller เฉย ๆ เพราะ concept web เดิม เราต้องเขียน serverlet config ผูกกับชื่อ (ถ้าไม่เข้าใจกลับไปดู servlet พื้นฐาน) แต่ในที่นี้เหมือนเราทำรอบเดียว

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
    
    <!--
    Most controllers will use the ControllerClassNameHandlerMapping above, but
    for the index controller we are using ParameterizableViewController, so we must
    define an explicit mapping for it.
    -->
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="index.htm">indexController</prop>
            </props>
        </property>
    </bean>
    
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />
    
    <!--
    The index controller.
    -->
    <bean name="indexController"
          class="org.springframework.web.servlet.mvc.ParameterizableViewController"
          p:viewName="index" />
    
</beans>

นี่ขนาดใช้ annotation ช่วยแระ ยังมี xml file config เหลือขนาดนี้ -_-"

ต่อมาก็คงเป็น DAO class ที่ทำการติดต่อ database เริ่มจาก

BaseDao.java
package th.go.nhso.projecttracking.dao;

public interface BaseDao {
    public void insert(Object... models);
    public Long insert(Object model);
    public void update(Object... models);
    public void delete(Class clazz, Long... ids);
    public Object getById(Class clazz, Long id);
}


ProcessDao.java
package th.go.nhso.projecttracking.dao;

public interface ProcessDao extends BaseDao {
}

BaseDaoImpl.java
package th.go.nhso.projecttracking.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;

public abstract class BaseDaoImpl implements BaseDao {

    @Autowired
    private HibernateTemplate hibernateTemplate;

    @Override
    public Object getById(Class clazz, Long id) {
        return (Object) hibernateTemplate.get(clazz, id);
    }

    @Override
    public void insert(Object... models) {
        for (Object model : models) {
            hibernateTemplate.save(model);
        }
    }

    @Override
    public Long insert(Object model) {
        return (Long) hibernateTemplate.save(model);
    }

    @Override
    public void update(Object... models) {
        for (Object model : models) {
            hibernateTemplate.update(model);
        }
    }

    @Override
    public void delete(Class clazz, Long... ids) {
        for (Long id : ids) {
            Object o = hibernateTemplate.get(clazz, id);
            if (null != o) {
                hibernateTemplate.delete(o);
            }
        }
    }

    public HibernateTemplate getHibernateTemplate() {
        return hibernateTemplate;
    }

    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }
}



ProcessDaoImpl.java
package th.go.nhso.projecttracking.dao;

public interface ProcessDao extends BaseDao {
    
}

BaseEntity.java
package th.go.nhso.projecttracking.model;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;

@MappedSuperclass
public class BaseEntity implements Serializable {
    
    @Column
    private String createBy;
    
    @Column
    @Temporal(javax.persistence.TemporalType.TIMESTAMP)
    private Date createDate;
    
    @Column
    private String updateBy;
    
    @Column
    @Temporal(javax.persistence.TemporalType.TIMESTAMP)
    private Date updateDate;

    
    public String getCreateBy() {
        return createBy;
    }

    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public String getUpdateBy() {
        return updateBy;
    }

    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }

    public Date getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
    }
    
}

Process.java
package th.go.nhso.projecttracking.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="PROCESS")
public class Process extends BaseEntity {
    
    public Process(){}
    
    @Id
    @GeneratedValue
    private Long id;
    
    @Column
    private String code;
    
    @Column
    private String description;

    @Column
    private Long projectId;

    @Column
    private Long projectPlanId;

    public Long getProjectId() {
        return projectId;
    }

    public void setProjectId(Long projectId) {
        this.projectId = projectId;
    }

    public Long getProjectPlanId() {
        return projectPlanId;
    }

    public void setProjectPlanId(Long projectPlanId) {
        this.projectPlanId = projectPlanId;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    
    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
    
}

เราไปดู test กันดีกว่า ทำแล้วก็ต้อง test ได้
จริง ๆ ก็จะมี service รองรับ ก่อนถึง dao แต่ตอนนี้จะ test ว่าที่ทำขึ้นมาสามารถ insert ได้หรือเปล่าไปก่อน
ในส่วน config location อาจต้องแก้ path สำหรับ เรียกดู applicationContext เอง แต่ในที่นี้ ใช้วิธี copy มาที่ source path ก่อน
BaseDaoTest.java
package th.go.nhso.projecttracking.dao.test;

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:applicationContext.xml" })
public abstract class BaseDaoTest {
    
}

ProcessDaoImplTest
package th.go.nhso.projecttracking.dao.test;

import java.util.Date;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import th.go.nhso.projecttracking.model.Process;
import th.go.nhso.projecttracking.dao.ProcessDao;

public class ProcessDaoImplTest extends BaseDaoTest {
    
    @Autowired
    private ProcessDao processDao;

    public ProcessDao getProcessDao() {
        return processDao;
    }

    public void setProcessDao(ProcessDao processDao) {
        this.processDao = processDao;
    }

    @Test
    public void testSomeMethod() {
        Process p = new Process();
        p.setCode("001");
        p.setProjectId(new Long(1));
        p.setProjectPlanId(new Long(1));
        p.setDescription("detail001");
        p.setCreateBy("staff");
        p.setCreateDate(new Date());
        processDao.insert(p);
    }
}

run test file ถ้าไม่มี error ก็ ok ผ่าน คร่าว ๆ ก็น่าจะมีแค่นี้ก่อนครับ

วันพุธที่ 30 มีนาคม พ.ศ. 2554

Convert Object array To JSON List (support JDK 1.4)

-ตัวอย่าง code นี้ทำขึ้นเพื่อ นำไปใส่บน JSP เนื่องจาก การนำ library JSON ที่เป็น file javascript (.js) หรือ java file (.java) ที่มีอยู่ทั่วไป แล้วดีอยู่แล้วไปไว้นั้น เป็นเรื่องยุ่งยาก ณ ตอนนี้ และ อีกอย่างคือ ไม่มีสิทธิในการ restart Server ด้วย จึงจำเป็นต้องเขียนกันตรงนั้นเลย จึงทำขึ้นมาเองวางเอง เ้น้นง่าย เรียก library ต่าง ๆ ให้น้อยที่สุด แต่เขียนให้ดูง่ายที่สุดเช่นกัน

-จุดประสงค์หลัก ในการทำขึ้นมา
เพื่อใช้ return string data จากการ post jsp ในรูปแบบ json โดยข้อมูลเป็น list data ที่ดึงมาจาก Database code ดั้งเดิม(มีคนเขียนไว้ก่อนหน้านี้แล้ว) ไม่มีเวลาไปแก้ เดี๋ยวตายก่อน เป็น JDBC แบบพื้นฐาน (คือ ถ้าเรียน java เบื้องต้น ติดต่อ database ก็อ่าน code ออกกันเลยทีเดียวเลย) ไม่ได้ทำ model รองรับ (เพราะเดี๋ยวจะต้องเขียน library ยาวกว่านี้ยังไม่ว่างพอ) ใส่ Object array ไปเลย โดยให้
obj[0] = id;
obj[1] = name;
ประมาณนี้

แล้วยัดใส่ใน Object array ที่ใช้เป็น list data อีกที
objs[0] = obj;

แล้วค่าที่ได้ ต้องนำไปแสดงบน Browser โดยให้สามารถ render data ใ้ห้อยู่ในรูปปุ่ม หรือ โชว์รูปภาพได้อยู่
และ เนื่องจากนำไป โว
/**
 *    This time only support JDK 1.4
 *    array, string, number
 */
public static class JsonArrayParseUtil {
    public static String START_ARRAY = "[";
    public static String END_ARRAY = "]";
    public static String MEMBER = ",";

    public static String parse(Object obj){
        String result = "";
        if(null == obj){
            return START_ARRAY+END_ARRAY;
        }

        Class type = obj.getClass();
        if(type.isArray()){
            result = arrayJSONStr((Object[])obj);
        }else{
            String data = obj.toString();
            if(null != data){
                data = data.replace("/", "\\/");
                data = data.replace("\"", "\\\"");
                data = data.replace("\n", "\\n");
                data = data.replace("\t", "\\t");
                data = data.replace("\r", "\\r");
            }
            result = "\""+data+"\"";
        }
        return result;
    }

    private static String arrayJSONStr(Object[] datas){
        StringBuffer sb = new StringBuffer();
        sb.append(START_ARRAY);
        for(int i=0; i<datas.length; i++){
            if(i>0){
                sb.append(MEMBER);
            }
            sb.append(parse(datas[i]));
        }
        sb.append(END_ARRAY);
        return sb.toString();
    }
}

เวลานำไปใช้งานจริง ใน JSP file ก็จะได้ประมาณนี้
DBConnection cmd = new DBConnection(); // call self library
    Connection conn = cmd.getConnection(); // call self library
    Object[] results = null;
    Object[] datas = null;
    int maxDataCol = 2;
    stmt = conn.createStatement();
    rs = stmt.executeQuery("SELECT ID, NAME FROM DATATABLE");
    try{
        while(rs.next()){
            datas = new Object[maxDataCol];
            datas[0] = rs.getInt("id");
            datas[1] = rs.getString("name");

            results = ObjArrayUtil.addArray(results, new Object[]{datas});
        }
        cmd.safeClose(rs);
        cmd.safeClose(stmt);     
        cmd.releaseConnection(conn);

        }catch(Exception ex){ 
            ex.printStackTrace();
            cmd.releaseConnection(conn);
        }
        String resultStr = "{ \"aaData\" : " + JsonArrayParseUtil.parse(results) + "}";
        response.setContentType("application/json");
        response.setHeader("Cache-Control", "no-store");
        out.print(resultStr);
        out.flush();

data ที่ได้ ออกมาก็จะหน้าตาประมาณแบบนี้
{"aaData" : [
["1", "kan"],
["2", "iphone"]
]}

ซึ่งในตัวเลยที่มี "-" double quote ครอบนั้น สามารถเอาออกได้ โดยแก้จาก class บรรทัดที่ 20 หลังจาก หลุดจาก isArray แล้วก็ให้เช็คว่าเป็น data เป็นตัวเลขหรือเปล่า แต่ค่อนข้างยุ่งยากเหมือนกัน กรณีที่เราไม่รู้ว่า มันเป็นตัวเลขจริง หรือเป็นตัวเลข String

ต้องแถมอีกตัว นั้นคือ ตัวที่ทำให้เรา add ข้อมูลใน object ไปเรื่อยๆ โดยไม่ต้องกำหนด ความกว้างพื้นที่ใช้งานก่อน

เนื่องจากความเป็นจริง และความสะดวก ของการใช้การดึงข้อมูลแบบ JDBC นี้ มันจะไม่มี list size ออกมาให้เลย เราต้องไป query ครั้งถ้าอยากรู้ว่าเมื่อกี้เราดึงข้อมูลมีเท่าไร หรือใช้วิธีวน loop แบบพิเศษเพื่อนับจำนวน แต่ว่า เราต้องการใส่ข้อมูลแล้วอะ จะรอก็ไม่รู้จะให้เก็บไว้ที่ใหนก่อน เลยเขียน class นี้ขึ้นมาเพื่อความสะดวก สบาย ไม่คำนึง ทั้งหน่วยความจำ หรืออะไรทั้งสิ้น (เอาง่ายว่างั้น)

public static class ObjArrayUtil {
    public static Object[] addArray(Object[] a, Object[] b) {
        if(null == a && null == b){
            return new Object[0];
        }
        Object[] c;
        if(null == b && null != a){
            c = new Object[a.length];
            System.arraycopy(a, 0, c, 0, a.length);
            return c;
        }
        if(null == a && null != b){
            c = new Object[b.length];
            System.arraycopy(b, 0, c, 0, b.length);
            return c;
        }
        return concatArray(a, b);
    }
 
    public static Object[] concatArray(Object[] a, Object[] b) {
        Object[] c = new Object[a.length + b.length];
        System.arraycopy(a, 0, c, 0, a.length);
        System.arraycopy(b, 0, c, a.length, b.length);
        return c;
    }
}

เสร็จแล้ว มีแค่นี้แหละ กากดีจัง เหอ ๆ แค่นี้ก็รอดไปอีก 1 วันแห่งการทำงาน ^^

วันพุธที่ 2 กุมภาพันธ์ พ.ศ. 2554

ซินเจียยู่อี่ ซินนี้ฮวดใช้ 万事如意 新年发财 (2011/02/02)

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

祝:春 节 快 乐,
万 事 如 意 ,恭 喜 发 财!
身 体 健 康,合 家 欢 乐!

恭喜发财 กงสี่ฟาไฉ..ขอให้ร่ำรวย
万事如意 ว่านซื่อหยูอี้ ....สมความปรารถนา
财源广进 ไฉเหยียนกว่างจิ้น...เงินทองไหลมา
年年有余 เหนียนเหนียนโหย่วหยวี๋..เหลือกินเหลือใช้
事事顺利 ซื่อซื่อซุ่นลี่..ทุกเรื่องราบรื่น
金玉满堂 จินยวี้หม่านถัง..ร่ำรวยเงินทอง
一本万利 อิ้เปิ่นว่านลี่...กำไรมากมาย
 大吉大利 ต้าจี๋ต้าลี่...ค้าขายได้กำไร
年年发财 เหนียนเหนียนฟาไฉ...รำรวยตลอดไป
龙马精神 หลงหม่าจินเสิน..สุขภาพแข็งแรง
吉祥如意 จี๋เสียงหยูอี้..สมปรารถนา
好运年年 เห่ายวิ่นเหนียนเหนียน..โชคดีตลอดไป
四季平安 ซื่จี้ผิงอัน..ปลอดภัยตลอดปี

credit : http://guru.google.co.th/guru/thread?tid=62f2c80dcdb09c8a