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

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?

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

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 นี้ด้วย

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 กัน

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/
-----------------------------------------------------

ปลาน้อยไร้สาระ

ปล่อยของ