การ Config Datasource บน JBoss AS7/JBoss EAP 6.x

หลังจากมีโอกาสมาใช้งาน JBoss EAP 6.3 แล้วต้องการกำหนด Datasource สำหรับ MySQL เพื่อทดสอบการใช้งาน Hibernate ทำให้รู้ว่าขั้นตอนในการกำหนด Datasource นั้นแตกต่างจาก JBoss AS 5.x, 6.x พอสมควรเลยขอบันทึกไว้เป็นขั้นตอนดังนี้
1. ทำการ Config Library ก่อนดังต่อไปนี้

1.1 ทำการสร้าง module ขึ้นมาเพื่อ load JDBC Driver ก่อน โดยเข้าไปที่ [jboss-home]\modules
1.2 สร้าง folder com/mysql/main แล้วทำการ copy mysql-connector-java-.jar มาไว้ที่ path นี้
1.3 สร้างไฟล์module.xml โดยมีเนื้อหาดังนี้

			<?xml version="1.0" encoding="UTF-8"?>
			<module xmlns="urn:jboss:module:1.1" name="com.mysql">
				<resources>
					<resource-root path="mysql-connector-java-5.1.30-bin.jar"/>
				</resources>
				<dependencies>
					<module name="javax.api"/>
					<module name="javax.transaction.api"/>  
				</dependencies>
			</module>
		

ส่วนที่สำคัญคือ
name : คื่อส่วน path ตาม Folder ที่สร้างไว้ (ตัด main ออก) ตามตัวอย่างคือ com/mysql/main จะกำหนดเป็น “com.mysql”
resource-root : คือชื่ือ Jar File ที่เราเอามาวางไว้ภายใต้ path com/mysql/main
dependencies : Module library ที่เกี่ยวข้อง เพื่อให้ JBoss load ขึ้นมาด้วย
1.4 บันทึกไฟล์

2. ทำการ Config Datasource

2.1 ทำการแก้ไข [jboss-home]\standalone\configuration\standalone.xml เพื่อทำการเพิ่ม Driver และ Datasource (หาก เลือก profile ในการ deploy ที่ไม่ใช่ standalone.xml ให้แก้ไขตาม profile นั้นๆ)
2.2 ไปที่ section

		<subsystem xmlns="urn:jboss:domain:datasources:1.2">
            <datasources>
                <datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
                    <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
                    <driver>h2</driver>
                    <security>
                        <user-name>sa</user-name>
                        <password>sa</password>
                    </security>
                </datasource>
                <drivers>
                    <driver name="h2" module="com.h2database.h2">
                        <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
                    </driver>
                </drivers>
            </datasources>
        </subsystem>
		

แล้วทำการเพิ่ม MySQL Driver และ Datasource เป็นดังนี้

        <subsystem xmlns="urn:jboss:domain:datasources:1.2">
            <datasources>
                <datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
                    <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
                    <driver>h2</driver>
                    <security>
                        <user-name>sa</user-name>
                        <password>sa</password>
                    </security>
                </datasource>
                <datasource jta="false" jndi-name="java:jboss/datasources/MySQLDS" pool-name="MySQLDS" enabled="false" use-ccm="false">
                    <connection-url>jdbc:mysql://localhost:3306/hibernate?characterEncoding=utf8</connection-url>
                    <driver-class>com.mysql.jdbc.Driver</driver-class>
                    <driver>mysql</driver>
                    <security>
                        <user-name>root</user-name>
                        <password>password</password>
                    </security>
                </datasource>
                <drivers>
                    <driver name="h2" module="com.h2database.h2">
                        <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
                    </driver>
                    <driver name="mysql" module="com.mysql">
                        <driver-class>com.mysql.jdbc.Driver</driver-class>
                        <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
                    </driver>
                </drivers>
            </datasources>
        </subsystem>
		

2.3 ทำการ Stop/Start หรือ Restart JBoss Server ก็สามารถใช้งาน JNDI Datasource ที่สร้างได้แล้ว
หมายเหตุ : ชื่อ jndi-name ต้องขึ้นต้นด้วย java:/ หรือ java:jboss/ เท่านั้น

Read more

Custom Hibernate Sequence and JPA ID generator

หัวข้อนี้ก็ยังหนีไม่พ้น Hibernate อีกตามเคยจากหัวข้อที่แล้ว เรื่องการสร้าง custom order by หัวข้อนี้กลับมาดูเรื่องการ Custom การสร้าง ID และ Sequence แบบฉบับตามใจคนใช้งาน
เริ่มแรกก็ขอคววามช่วยเหลือจาก google ตามเคย หลังจากสอบถามก็ได้ข้อมูลมาสองที่ดังนี้
Customize IdGenerator in JPA, gap between Hibernate and JPA annotations และ
Custom Hibernate Sequence Generator for id field
เมื่อได้แหล่งข้อมูลตัวอย่างแล้วก็นำมาปรับแก้ดังนี้

/*
 * ----------------------------------------------------------------------------
 * Copyright © 2014 by http://www.secondknow.com. All rights reserved.
 * ----------------------------------------------------------------------------
 */
package com.secondknow.hibernate.seq;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.id.SequenceGenerator;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Supot Saelao
 * @version 1.0
 */
public class OracleSeqWithDateFormatGenerator extends SequenceGenerator {
	private static final Logger logger = LoggerFactory.getLogger(OracleSeqWithDateFormatGenerator.class);
	
	private String sql;
	
	@Override
	public void configure(Type type, Properties params, Dialect dialect)
			throws MappingException {
		super.configure(type, params, dialect);
		
		String sequenceName = " TO_CHAR(SYSDATE,'YYYYMMDDHH24MISS') || " + getSequenceName();
		sql = dialect.getSequenceNextValString(sequenceName);
	}

	@Override
	public Serializable generate(SessionImplementor session, Object obj)
			throws HibernateException {
		return generateSeqStr(session);
	}
	
	private String generateSeqStr(SessionImplementor session) {
		try {
			logger.info("Seq statement : {}", sql);
			
			PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
			try {
				ResultSet rs = st.executeQuery();
				try {
					rs.next();
					String seqStr = rs.getString(1);
					logger.info("Seq value : {}", seqStr);
					
					return seqStr;
				} finally {
					rs.close();
				}
			} finally {
				session.getBatcher().closeStatement(st);
			}
		} catch (SQLException sqle) {
			throw JDBCExceptionHelper.convert(session.getFactory()
					.getSQLExceptionConverter(), sqle, "could not get next sequence value", sql);
		}
	}	
}

Read more

How to create custom order by in hibernate Criteria API

สืบเนื่องจากการใช้งาน Hibernate กับฐานข้อมูล oracle แล้วเจอปัญหาเวลาทำการเรียงลำดับข้อมูล (Order by) แล้วภาษาไทยจะเรียงไม่ถูกใจผู้ใช้งาน เช่น
การเรียงข้อมูลจังหวัด

กรุงเทพ
เชียงราย
เชียงใหม่
พะเยา
อ่างทอง

ข้อมูลจะถูกเรียงลำดับดังนี้

กรุงเทพ
พะเยา
อ่างทอง
เชียงราย
เชียงใหม่

ถ้าต้องการเรียงให้ถูกต้องตามหลักภาษาไทย (ภาษาไทยเทพเกิน) ต้องเพิ่ม function NLSSORT เข้าไปด้วยดังนี้

SELECT PROVINCE_NAME 
FROM CONF_PROVINCE
ORDER BY NLSSORT(PROVINCE_NAME, 'NLS_SORT = THAI_DICTIONARY');

ทุกอย่างเหมือนไม่มีอะไรซับซ้อนแต่ปัญหาก็คือ ระบบใช้ Hibernate และก็ Qurey ผ่าน Hibernate Criteria หลังจากสอบถาม google ก็ไปเจอกับแนวทางดังนี้
Read more

True Wifi Auto Login (โปรแกรมสำหรับ Login เข้าใช้ True Wifi หลังหมดเวลา)

ได้มีโอกาสอันดีในการมาสมัครใช้ True Wifi แบบรายเดือนเพราะสถานที่ทำงานไม่มี อินเตอร์เน็ตให้ใช้ ดังนั้นเลยต้องไป ซื้อ SIM True และจัดการสมัคร package 100 ใช้ Wifi แบบไม่จำกัด แต่ปัญหาที่เจอก็คือ ระบบจะตัดสัญญาเมื่อครบกำหนด 90 นาที และต้องทำการ Login ใหม่ซึ่งถ้าใช้งานทั่วไปก็คงไม่มีปัญหาอะไรแต่ถ้าเกิดกำลังดูหนังฟังเพลงกันอยู่ จะทำให้อารมณ์ค้างไม่ใช่เล่น ก็เลยเกิดความคิดจะเขียน script หรือ โปรแกรมอะไรขึ้นมาเพื่อทำให้ทำการต่ออินเตอร์เน็ตเองหลังจากใกล้หมดเวลา ประมาณ นาทีที่ 88, 89 พอลองค้นหาจาก Google ดูแล้วเหมือนไม่เจอโปรแกรมลักษณะนี้ เจอแต่ script ที่ต้องเปิดหน้า browserเอาไว้ (หรือหาไม่เป็นก็ไม่รู้ เลยไม่เจอ) ก็เลยคิดว่า เอาแบบนั้นก็เขียนเองละกัน

1. ขั้นแรกไปศึกษาการ ส่งค่าไปให้ระบบสำหรับทำการ login ก่อน พบว่า จากหน้าจอ login ข้อมูลที่ถูกส่งไปคือ

username==ชื่อผู้ใช้งาน
password=รหัสผ่าน
param=ค่า MAC Address +"|"+ปี+เดือน+วัน+ชั่วโมง+นาที ที่ทำการเข้าระบบ ทำการ เข้ารหัสด้วย base64

ตัวอย่างค่า param

MDA6Mjc6MTA6OTk6YjI6NmN8MjAxMzEwMTgxMzA3Cg==

จากนั้นมาลองเขียน Java โดยทำการ post ค่าดังกล่าวไปในส่วนของการ Login แต่จนแล้วจนรอดก็ไม่ได้ ทั้งๆที่การเข้าหรัสถูกต้องหมด แต่ถ้าเอาค่าจากใน browser มาทดสอบส่งไปสามารถ login ได้ปกติ เลยคิดเอาเองว่า ในขั้นตอนการ generate param นั้นอาจมีการเก็บค่าเพื่อทำการอ้างอิงและตรวจสอบกับค่าที่ส่งจากหน้าจอ ถ้าไม่ตรงกันก็ไม่สามารถเข้าระบบได้

พอสันนิษฐานดังกล่าวก็เปลี่ยนแนวคิด ดังนี้
1. เปิด Connection ไปที่ http://www.google.com ก่อน รอให้ระบบส่งเราเข้าไปยังหน้า Login เอง
2. จากนั้นก็ไป Copy ค่า param จากหน้าจอดังกล่าวมา โดยไม่ต้อง generate เอง
3. ส่งข้อมูลไปทำการ Login

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

Download
Java Version (Windows)
.NET Windows Form version (Update 20/10/2013)

Note: ติดปัญหา และต้องการแนะนำเพิ่มเติม ติดต่อได้ที่หน้า Readme

แก้ไข Richfaces 4.3.0 ให้รองรับปี พ.ศ

แก้ไข Richfaces 4.3.0 ให้รองรับปี พ.ศ
สืบเนื่องจากระบบที่พัฒนามาต้องการให้แสดง ปฏิทินใน Rcihfaces เป็นภาษาไทย จากประสบการณ์ที่ผ่านมาใน Version 3.3.3 Final นั้นจำเป็นต้องไปแก้ไขไฟล์ calendar.js ซึ่งก็สามารถใช้งานได้ดี แต่พอมา Version นี้ก็ไม่แตกต่างกันครับ
คือ ต้องแก้อยู่ดี เพราะปฏิทินนั้น แสดงชื่อเดือน อะไรถูกต้องครับ ยกเว้นพวก ปี วันที่ปัจจุบัน เพราะ เวลากำหนด locale เป็น th_TH แล้ว ตัว ปฏิทินจะสร้าง JavaScript ในหน้าจอดังนี้

<script type="text/javascript">
	RichFaces.ui.Calendar.addLocale("th_TH",
		{"monthLabels":["มกราคม","กุมภาพันธ์","มีนาคม"......"ธันวาคม"]
		,"minDaysInFirstWeek":1
		,"monthLabelsShort":["..ชื่อเดือน .."]
		,"firstWeekDay":0
		,"weekDayLabels":["ชื่อสัปดาห์"]
		,"weekDayLabelsShort":["ชื่อสัปดาห์"] 
		} 
	);

	new RichFaces.ui.Calendar("startDate","th_TH",{
	datePattern":"dd\/MM\/yyyy"
	,"currentDate":new Date(2556,1,26)
	,"style":"z\u002Dindex: 3"
	} 
).load();
</script>

พอส่ง currentDate เป็น พ.ศ ตามที่ Java เข้าใจไปให้ JavaScript ที่นี้ก็ลำบากเลยครับ เวลาเปิดหน้าจอครั้งแรกไม่มีปัญหาอะไรครับ แต่ถ้าคลิกไป คลิกมา ตัวปฏิทินจะแสดงผลเพี้ยนจาก 2556 ไปเป็น 769 บ้าง ตามสถานะการณ์ เนื่องจาก Java พยายามแปลงไปเป็นปีไทย แต่ JavaScript ไม่รู้จัก
หลังจากประสบปัญหานี้ จึงลองไปค้นหาข้อมูลดู ซึ่งไม่เจอใครแก้ปัญหาไว้เลย เลยต้องมางมเอง จากประาบการณ์ ใน Version 3.3.3 Final คิดว่าจะง่าย แต่ ….. ขอโทษที Version 4.3.0 Final พี่แกเปลี่ยน Code อีกแล้วครับ ออกไปเขียนแนว Jquery plugin ต้องงมอีกพอสมควร
เสียเวลามาเกือบทั้งวัด สุดท้ายก็แก้ไขเสร็จเรียบร้อย เลยมาเขียนไว้สักหน่อย การแก้ไขทำตามนี้เลย
Read more