Natty’s blog

Stay hungry. Stay foolish. — Steve Jobs

[Resin] compile simple Java on Resin

Posted by natty on September 23, 2008

โห วันนี้สงสัยต้องรีบกลับไปพักก่อน เพราะมึนสุดๆ เลย เป็นเรื่องที่ไม่น่าจะมึนเลย แต่ติดปัญหานั่นนี่อยู่นั่น

เรื่องมีอยู่ว่า จะพยายามเอา PHP ไปเรียก Jasper report ให้ได้ เพราะอุตส่าห์ทำ environment ตั้งไว้หมดแล้ว ลง resin server เอาไว้แล้วด้วย แต่เหลือแค่ยังไม่ได้ test ว่าใช้งานได้ไหม

และก็เพิ่งมาค้นพบตัวเองว่า ไม่เคยเอา simple java มารันบนนี้เลย แต่เคยเอา PHP ไปเรียก class java สำเร็จแล้ว – -”

ระหว่างทางก็เจอปัญหาปัญญาอ่อนมากมาย ถ้าคิดออกจะบอกเรื่อยๆ

สิ่งที่ทำคือ เอา Java App ที่รันอยู่บน JBoss มารันบน resin แล้ว structure มันก็ไม่เหมือนกัน สรุปคือจริงๆ ก็เหมือนๆ กันทุก container คือ เอา class ไปวางไว้ที่ classes folder ปกติบน JBoss มันจะแยก .java กับ .class เอาไว้คนละ folder (.java in src และ .class in build) แต่โดย default ของ resin มันจะอยู่ใน classes folder หมดเลย ตอนแรกหลงทางไปใหญ่ คิดว่าจะต้อง extends พวก quercus method อะไรด้วย สรุปแล้วไม่มีอะไร งงไปเอง – -”

แต่!!!!!!!!!!!!!! มันจะไม่ compile Java ให้เราเลย หากเราไม่ไปกำหนด webapp-id เอาไว้ใน /var/resin/conf/resin.xml (สำหรับ PHP ไม่มีปัญหาอะไร เพราะมันไม่ต้อง compile .java->.class เลยเอา app ไปวางได้โดยไม่ต้องกำหนดที่ resin.xml)

<web-app id="/test" root-directory="webapps/ROOT/test" archive-path="webapps/ROOT/test.war"/>

อ่อ หากเรารัน web ขึ้นมา แล้วมันบอกว่า permission denied ไม่สามารถเขียนไฟล์ .class ได้ ให้เราไป chown -R ให้ folder ที่จะเขียน .classes ก่อน

และอย่าลืมว่า java ต้องอยู่ใน package มิฉะนั้นมันจะไม่ถูก compile

Why do I have to put my classes in a package?

If I make a class that is not in a package, Resin does not recognize it and does not load it.

Starting with Java 1.4 the use of classes that are not in a package (sometimes called the “default” package”) is deprecated. Java classes should always be in a package. In some version of Java, packageless classes are not going to be supported at all.

The solution is to put all of your classes in a package.

See http://java.sun.com/j2se/1.4/compatibility.html, point #8, second bullet point.

สิ่งของที่อยู่ใน WebContent ของ jboss ก็เอาออกมาอยู่ path นอกสุดได้ตามปกติ

web.xml ก็ config ตามปกติไม่มีอะไรพิสดาร

และสิ่งที่ทำให้แก้นานที่สุดเมื่อกี้คือ พยายามแก้ไฟล์ java และก็รันใหม่ แต่ปรากฎว่าผลก็ยังเหมือนเิดิม เหมือนผีหลอก ตอนหลังจึงได้รู้ว่า มันไม่ยอมทับ file .class เดิม ให้ ก็เลยลองไปลบแล้วให้มัน compile ใหม่ ปรากฎว่าใช้งานได้ แอบงงนิดหน่อย เพราะคิดว่ามันเป็นแบบนี้แค่เป็นบางทีมั้ง ปกติไม่เห็นจะเคยเจอ – -*

และสิ่งที่ผิดที่สุดของวันนี้คือ หาเรื่องเองโดยการนั่งแก้ไฟล์ java ใน notepad แล้วก็เลยตาลาย ไม่เห็นสีสัน เหนื่อยโดยใช่เหตุ เพิ่งมารู้ตัวอีกทีตอนทำเสร็จแล้ว

jboss

jboss

resin

resin

อ่านไปอ่านมา ก็ไม่เห็นจะมีอะไรต้องมึนแล้ว แล้วตูมึนอะไรฟระ – -”

Posted in configuration | Leave a Comment »

[PHP] Class สำหรับ convert DBF to MySQL

Posted by natty on September 22, 2008

เนื่องจากมีโปรเจคหนึ่งที่ต้องทำส่วนหนึ่ง คือส่วนของการ convert DBF to MySQL เลยเอามาฝากกัน ที่ทำเพราะว่า ระบบนี้จะเป็นต้องอ่าน DBF file ใหม่ๆ ทุกวันเพื่อนำมาใช้ในระบบ แต่ developer อยากใช้กับ MySQL database ดังนั้นเก๋จึงทำ class ที่อ่าน dbf file มาลง mysql ให้เค้า โดยทำเป็นตารางที่หน้าตาเหมือนกันใน MySQL ให้

ระบบนี้มี table ชื่อเดิม แต่ต้องอัพเดทข้อมูลตาม dbf file ทุกวัน ดังนั้นเก๋จึงไม่ได้ทำ function ที่ให้สร้าง MySQL table หน้าตาเหมือนๆ กัน ทำแค่ให้ insert ข้อมูลลงไปใน MySQL ได้ นั่นหมายความว่า หากต้องการนำ class นี้ไปใช้ ต้องสร้าง table ที่หน้าตาเหมือนกับ dbf file ไว้ก่อน แล้วค่อยเอา class นี้ไปช่วยในการ insert ข้อมูลให้

ใจจริง อยากทำ function ที่ให้ create table เหมือนกัน แต่ติดที่ว่า วิธีการอ่าน dbf file คือเก๋ใช้ dbase function ซึ่งเป็นการ read wirte dbf file แต่ไม่ใช่การ query ด้วยคำสั่ง SQL ดังนั้นจึงคิดวิธีการ get property ของ field ไม่ออก ว่าต้องทำยังงัย ก็เลยคิดว่า หากวันนึงอยากใช้ create table function ขึ้นมาจริงๆ คงต้องใช้ ODBC เข้าช่วย เพราะคำสั่ง SQL สามารถ get property ได้

มีไฟล์ที่เกี่ยวข้อง 3 ไฟล์ คือ DBFMySQL.php, iDBFMySQL.php และ test.php

เก๋กำหนดให้มี interface เอาไว้ จะเห็นว่า createTable โดน comment ไว้ แสดงว่ามันจะ coming soon ฮ่าๆ


<?php

/*created by Nattanicha Rittammanart
* Created date: Sep 22, 2008 */

interface iDBFMySQL{
    /*
        fromDBF: dbf with the right path
        toMySQL: define table name in MySQL
        myDB: MySQL database to put MySQL table
        myUser: MySQL username
        myPass: MySQL password
        myHost: MySQL Host

    */

    /**/
    public static function convertDBFToMySQL($fromDBF, $toMySQL, $myDB, $myUser, $myPass, $myHost);
    public static function readDBFToArray($fromDBF);
    public static function persistToMySQL($arrTable, $toMySQL, $myDB, $myUser, $myPass, $myHost);
    //public static function createMySQLTable($arrTable, $toMySQL, $myDB, $myUser, $myPass, $myHost);
}

function convertDBFToMySQL เป็นฟังก์ชั่นที่ให้ผู้ใช้เรียกใช้เพื่อ convert dbf to mysql โดยตรง โดยฟังก์ชั่นอื่น เป็นฟังก์ชั่นที่ใช้งานร่วมกับมันเท่านั้น ก็คือใส่ parameters ให้ครบ มันก็จะให้สิ่งที่ต้องการออกมา ฟังก์ชั่นนี้ เรียก readDBFToArray และ persistToMySQL มาใช้งาน


    public static function convertDBFToMySQL($fromDBF, $toMySQL, $myDB, $myUser, $myPass, $myHost){
        $arrTable = array();
        $arrTable = DBFMySQL::readDBFToArray($fromDBF);
        DBFMySQL::persistToMySQL($arrTable, $toMySQL, $myDB, $myUser, $myPass, $myHost);
    }

function readDBFToArray รับ path ของ dbf file เพื่อเอามาใส่ลง array โดยจะ return เป็น array 2 ชุดใหญ่ คือ ชุด column name กับชุดของ data โดยชุดของ data ก็จะเก็บเป็น array ย่อยลงไปอีก โดย array นี้จะ prepare เอาไว้ให้ function persistToMySQL เป็นคนเรียก


public static function readDBFToArray($fromDBF){
		// open in read-only mode
		$db = dbase_open($fromDBF, 0);
		$records = array();

		if ($db) {
		// read some data ..
			$record_numbers = dbase_numrecords($db);
			$field_numbers = dbase_numfields($db);
			for ($i = 1; $i <= $record_numbers; $i++) {
				$row = dbase_get_record_with_names($db, $i);
				array_push($records,array_values($row));
			}
			$columnHeader = array_keys($row);
			$arrTable = array($columnHeader,$records);
			dbase_close($db);
		}
		return $arrTable;
	}

function persistToMySQL รับ array มาจาก readDBFToArray เพื่อนำมาลง MySQL table ใน field ที่ถูกต้อง

public static function persistToMySQL($arrTable, $toMySQL, $myDB, $myUser, $myPass, $myHost){

		$connect = mysql_connect($myHost, $myUser, $myPass) or die("cannot connect to database");
		mysql_select_db($myDB, $connect);

		$columnHeader = $arrTable[0];
		$records = $arrTable[1];

		for($i=0;$i<sizeof($columnHeader)-1;$i++){
				$temp = $columnHeader[$i].",";
				$prepareFields=$prepareFields.$temp;
		}

		$prepareFields = substr($prepareFields, 0, -1);

		for($j=0;$j<sizeof($records);$j++){
			$prepareRecords='';
			for($i=0;$i<sizeof($records[$j])-1;$i++){
				$temp = "'".trim($records[$j][$i])."'".",";
				$prepareRecords=$prepareRecords.$temp;
			}
			$prepareRecords = substr($prepareRecords, 0, -1);
			$sql = "INSERT INTO $toMySQL ($prepareFields) VALUES ($prepareRecords)";
			$test = mysql_query($sql);
		}
	}

วิธีการใช้งานก็ง่ายมากๆ เลย ทำแบบนี้

<?php

/*created by Nattanicha Rittammanart
* Created date: Sep 22, 2008 */

include 'DBFMySQL.php';

$fromDBF='D:\PO_McDonald\INVORDD0.DBF';
$toMySQL='INVORDD0';
$myDB='db_mcdonald';
$myUser='root';
$myPass='root';
$myHost='localhost';

DBFMySQL::convertDBFToMySQL($fromDBF, $toMySQL, $myDB, $myUser, $myPass, $myHost);

?> 

ในอนาคตอาจจะมีการเพิ่มเติมฟังก์ชั่นเข้าไปอีกค่ะ หากเป็นประโยชน์ก็ช่วยบอกด้วยนะคะ

ดาวน์โหลด sourcecode ได้ที่นี่ ค่ะ

Posted in Foxpro, PHP | 1 Comment »

[PHP] Enable DBase function

Posted by natty on September 22, 2008

พอดีกำลังจะทำ class สำหรับ DBF MySQL ประมาณว่า convert กันไปกันมา เลยจะอ่าน DBase file แต่พอดีตะกี้ใช้แล้ว มัน error แบบนี้

Fatal error: Call to undefined function dbase_open() in C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\DBFMySQL\DBFMySQL.php on line 27

พอไป search หาวิธีแก้ ตามลิงค์นี้ เค้าก็บอกว่า ต้องไปแก้ใน php.ini ให้มัน enable dbase dll ก่อน

extension=php_dbase.dll

เท่านี้แหละที่อยากบอก ;)

Posted in PHP | Leave a Comment »

[grails] WebAlbum: ตอนที่ 2 จบ domain class

Posted by natty on September 22, 2008

จาก ตอนที่ 1 เราแค่บอกว่าเราจะทำ domain class อะไรบ้าง แต่ก็เขียนไปแค่อันเดียว blog นี้เราจะเขียน domain class ที่เหลือให้หมดเลย หลักๆ จะอิงจาก tutorial เพราะจะได้เรียนรู้ไปในตัว

ครั้งที่แล้วมี User ไปแล้ว คราวนี้ก็เหลือ Album, Picture, Image

มาที่ Album กันก่อน

class Album implements Comparable {
    User user
    String caption
    String description
    SortedSet pictures
    Integer picturesCount = 0
    Date dateCreated = new Date()
    Date lastUpdated = new Date()

    static belongsTo = User
    static hasMany = [ pictures : Picture ]

    static optionals = [ 'description' ]

    static constraints = {
        caption(size: 1..40, blank: false)
        description()
    }

    int compareTo(obj) {
        obj.id.compareTo(id)
    }
} 

การ implement interface comparable คือเพื่อจะทำให้ object ภายใน Class Album สามารถ sort ได้ compare ได้ เนื่องจากเป็นลิสต์ของ album

static belongsTo = User
static hasMany = [ pictures : Picture ]

ตัวแปร belongsTo บอกว่า Album มีความสัมพันธ์กับ User แปลง่ายๆ ว่า Album ขึ้นกับ User เป็นความสัมพันธ์อีกด้านหนึ่ง และ Album ก็มีหลาย pictures จากตัวแปร hasMany

int compareTo(obj) {
        obj.id.compareTo(id)
    }

ด้านบนเป็น method เอาไว้ compare object ด้วยการใช้ id

ต่อมาก็เป็น Picture.groovy

class Picture implements Comparable {</pre>
User user
 Album album
 SortedSet images
 String file
 Integer operation
 String caption
 String description
 String contentType
 Integer width
 Integer height
 Date dateCreated = new Date()
 Date lastUpdated = new Date()
 Boolean confirmDelete

 static belongsTo = [ User, Album ]
 static hasMany = [ images : Image ]

 static optionals = [ 'caption', 'description' ]

 static transients = [ 'file', 'operation', 'confirmDelete' ]

 static constraints = {
 caption(size: 0..40)
 description()
 }

 static mapping = {
 images cascade: 'all-delete-orphan', inverse: true
 description type: 'text'
 user index: 'pictures_user_index', unique: false
 album index: 'pictures_album_index', unique: false
 }

 static final Integer NoOp = 0
 static final Integer RotateClockWise90 = 1
 static final Integer RotateAntiClockWise90 = 2
 static final Integer Rotate180 = 3
 static final Integer Flip = 4
 static final Integer Flop = 5

 int compareTo(obj) {
 obj.id.compareTo(id)
 }
}

ซึ่งหน้าตาก็คล้ายๆ กับ domain class อื่นๆ แต่มีส่วนที่ต่างคือส่วนที่เกี่ยวกับการ mapping และการประกาศ final variable

static mapping = {
        images cascade: 'all-delete-orphan', inverse: true
        description type: 'text'
        user index: 'pictures_user_index', unique: false
        album index: 'pictures_album_index', unique: false
    } 

ตรงนี้เป็นเรื่องของ GORM เกี่ยวกับ mapping DSL คือตั้งแต่ grails version 1.0 มาแล้วนั้น domain class สามารถถูก map เป็น schema อื่นๆ ได้โดยผ่าน ORM DSL เช่น เราอาจจะเปลี่ยนชื่อ table name ชื่อ column เรา enable caching ให้กับ mapping closure ได้ด้วย

image cascade น่าจะเป็นการกำหนด cascade ให้กับ image ของ picture object นี้ ให้เป็น all-delete-orphan และให้ inverse ได้ มี description ชนิด text มีการทำ index ไปที่ user และ album ที่เกี่ยวกับกับ picture นี้ ([variable] index)

เพิ่งแอบเห็น พี่ชาญวิทย์แปลเรื่อง mapping ไว้ให้แล้ว อ่านง่ายดี ภาษาเฉพาะทางสำหรับ mapping ใน GORM (ตอน 1)

ต่อไปก็เป็น Class Image ไม่ต้องอธิบายอะไรกันมากแล้ว คล้ายๆ กับที่ผ่านมา

class Image implements Comparable {
    User user
    Picture picture
    Integer size
    byte[] data
    String contentType
    Integer width
    Integer height
    Date dateCreated = new Date()
    Date lastUpdated = new Date()

    static belongsTo = [ User, Picture ]

    static mapping = {
        picture index: 'images_index', unique: true
        size index: 'images_index', unique: true
        data type: 'binary'
    }

    static constraints = {
        data(maxSize: MAX_SIZE)
    }

    static final Integer MAX_SIZE = 10 * 1024 * 1024

    static final Integer Original = 1
    static final Integer Large = 2
    static final Integer Medium = 3
    static final Integer Small = 4

    static final Integer[] Widths =  [ 0, 0, 500, 250, 100 ]
    static final Integer[] Heights = [ 0, 0, 500, 250, 100 ]

    static final String[] Names = [ '', 'Original', 'Large', 'Medium', 'Small' ]

    int compareTo(obj) {
        size.compareTo(obj.size)
    }

    String filename() {
        Image.filename(picture.id, size)
    }

    static String filename(long id, int size) {
        if (size == Original) {
            return "${id}-${Names[size]}.jpg"
        }
        else {
            return "${id}-${Names[size]}.png"
        }
    }
} 

ผ่านมา 2 ตอนแล้วไม่ได้รันซะที (ฮา) พยายามต่ออีกนิด มา generate controller และ view ให้กับ domain class ของเรากันดีกว่า เราจะใช้คำสั่ง grails generate-all กับ User, Album, Picture ส่วนกับ Image ไม่ต้อง เพราะมันจะเป็นแค่รูปๆ ที่แสดงขึ้นมา

grails generate-all User

หลังจากนั้นไปปรับไฟล์ grails-app/conf/DataSource.groovy นิดหน่อย เพื่อให้เก็บ HSQLDB เป็นไฟล์ใน db/devDB

eenvironments {
	development {
		dataSource {
			dbCreate = "update" // one of 'create', 'create-drop','update'
			url = "jdbc:hsqldb:file:db/devDB"
		}
	}

การเปลี่ยน url เพื่อเข้าใช้งาน อันนี้จะทำหรือไม่ทำก็ได้ หากเราไม่อยากเข้าผ่าน http://localhost:8080/WebAlbum แต่อยากเข้าผ่าน http://localhost:8080/natty อะไรแบบนี้ ก็ให้เพิ่ม web-jetty.xml

<?xml version="1.0"  encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
  "http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">
  <Set name="contextPath">/</Set>
</Configure>

เฮ้อ เรื่องเยอะ กว่าจะได้รัน ซัดเลยจ้ะ grails run-app แล้วลองเข้าไปเยี่ยมเยียนดูซิ เรากำลังได้ application แล้ว

Posted in grails | Leave a Comment »

วิธีการ install UnixODBC

Posted by natty on September 18, 2008

เนื่องจาก Windows server ไม่เพียงพอ ทำให้เก๋ต้องหาวิธีการใช้ ODBC บน Linux เฮ้อ… ปัญหาแต่ละอย่าง

ไปเจอมาตัวนึง ชื่อ UnixODBC แต่เท่าที่อ่าน list ก็ไม่เห็นว่าจะใช้กับ dbf ได้ แต่ก็ยังหน้าด้าน install มันอยู่

ก่อนอื่น ดาวน์โหลด package นี้ มา แล้วเอาไปวางที่ไหนก็ได้ใน Linux จากนั้นก็แตกซิบ แล้วเข้าไปที่ path นั้นเพื่อทำการ install ดังนี้

gunzip unixODBC*.tar.gz
tar xvf unixODBC*.tar

cd unixODBC*
./configure

มาถึงขึ้นนี้ บางท่านอาจจะ install ไม่ผ่าน เพราะติดปัญหา QT headers ให้ทำการ yum install avahi-qt3-devel ก่อน แล้ว ./configure ใหม่อีกรอบ จึงจะทำคำสั่งต่อไป

(*ถ้า ไม่มี permission การทำ ./configure ให้พิมพ์ chmod +x configure ก่อน)

make
make install

ก็จะได้ unixODBC อยู่ที่ /usr/local/etc/unixODBC ค่ะ

Posted in configuration | Leave a Comment »

ติดต่อ network drive ผ่าน Apache+PHP

Posted by natty on September 18, 2008

สองวันมานี้ เขียน PHP เรียก Foxpro แต่ทำ test บนเครื่องตัวเองก่อน โดยเอา dbf มาวางไว้บนเครื่อง ติดต่อผ่าน ODBC บน Windows พอทำโปรแกรมเสร็จ ก็จะเอาไปใช้จริง จึงจะเปลี่ยน path ไปเรียก dbf ซึ่งเป็น network drive ปรากฎว่าไม่สามารถทำได้ กลายเป็น table does not exist ไปซะงั้น

ปัญหาเกิดจาก Apache server ไม่ได้รับอนุญาติให้ติดต่อไปยัง network drive นั้น สิ่งที่เราต้องทำคือ

เข้าไปยัง control panel/Administrative tool/service แล้ว dbclick ที่ Apach service เลือกไปที่ log on tab ให้เลือกที่ this account แล้ว browse ไปยัง user ที่มีสิทธิ์เข้า network drive ดังกล่าว ใส่ user password ที่ถูกต้อง และอย่าลืม restart Apache ใหม่ เพียงเท่านี้ก็จะสามารถติดต่อไปยัง network drive ได้แล้ว แต่หาก restart Apache ใหม่แล้ว Apache ไม่สามารถ start ได้ แสดงว่า user/pass ที่คุณใส่เข้าไปไม่ถูกต้อง ให้กลับไปแก้ก่อน

แต่อย่าลืมว่า เวลาใส่ path ใน ODBC ต้องใส่เป็น UNC path มิฉะนั้นจะมองไม่เห็น

Posted in configuration | 1 Comment »

[grails] WebAlbum: ตอนที่ 1

Posted by natty on September 17, 2008

หลังจากที่พยายามจะทำ WebAlbum ให้เสร็จ แต่สรุปว่าก็ยังไม่เสร็จจนได้ ต้องกลับไปเริ่มที่ ลง plugin eclipse ก่อน – -”

แต่ก็ได้อะไรมาไม่มากก็น้อย เก็บบันทึกไว้ก่อนแล้วกัน

พยายามจะสร้าง WebAlbum ตาม tutorial โดยมี domain class ทั้งหมดคือ User, Album, Picture, Image โดยมีรายละเอียดดังนี้


class User {
    String name
    String motto
    String salt
    String password
    String passwordConfirmation
    SortedSet albums
    SortedSet pictures
    Integer albumsCount = 0
    Integer picturesCount = 0
    Date dateCreated = new Date()
    Date lastUpdated = new Date()

    static hasMany = [albums: Album, pictures: Picture]
    static transients = ['passwordConfirmation']
    static constraints = {
    name(size: 1..40, blank: false, unique: true)
    motto(size: 0..80, nullable: true)
    salt(maxSize: 40, blank: false, nullable: false)
    }

} 

สำหรับ class user อธิบายได้ว่า มี properties ต่างๆ เช่น name, motto…

property ที่เป็น date เมื่อ new date(); ขึ้นมา จะทำให้เกิดเป็น drop down ของวันที่ให้ในหน้า page

 static hasMany = [albums: Album, pictures: Picture]

hasMany คือตัวแปรที่บอกว่า มีความสัมพันธ์กับแบบ one to many คือ user มีได้หลาย Albums และหลาย Pictures

static transients = ['passwordConfirmation']

transient คือตัวแปรที่บอกว่า จะไม่ถูก persist ลง database

name(size: 1..40, blank: false, unique: true)
motto(size: 0..80, nullable: true)
salt(maxSize: 40, blank: false, nullable: false)

เป็นการกำหนดค่าให้กับ properties ของเราว่าจะแสดงอย่างไร เช่น สำหรับ name ให้มีขนาดไม่เกิน 40 characters ห้ามเป็นค่าว่าง และต้องเป็น unique ด้วย

โดย User page สำหรับ input จะหน้าตาดีประมาณนี้เลย

วันนี้พอแค่นี้ก่อน หิวข้าวแล้ว และขอไปลง plugin ให้สำเร็จก่อน ปวดตาเหลือเกินแลัวกับ edit plus ตอนต่อไปค่อยมาต่อ domain class อื่นๆ กัน ยาวเกินไปเดี๋ยวคนอ่านจะขี้เกียจอ่าน (ฮา ข้ออ้าง)

ท่าทางจะอีกยาวไกลกับ WebAlbum on grails…

Posted in grails | Leave a Comment »

PHP+Foxpro: การ query date

Posted by natty on September 17, 2008

จากตอนที่แล้ว เรื่องของ Apache + PHP + GD lib + Foxpro + JPGraph ก็มาดูกันต่อในเรื่องการ query date ของ foxpro

จะยกตัวอย่างฟิลด์ date ที่เก็บเป็น yyyy/mm/dd

สมมติว่าเรา query date ขึ้นมา


$sql = "SELECT data_date FROM tablename;
$rs=odbc_exec($conn,$sql);
        if (!$rs)
            {exit("Error in SQL");}
        while (odbc_fetch_row($rs))
        {
            $data_date = odbc_result($rs,"data_date");
        }

หากเราต้องการนำข้อมูล date ดังกล่าวไป query ต่อ สิ่งที่ต้องทำคือ ต้องเปลี่ยน format ของ date นั้นเป็น mm/dd/yyyy เนื่องจากเรากำลังจะใช้คำสั่งในการ query date ของ foxpro คือคำสั่ง ctod (character to date) ซึ่งโดยปกติหากเราต้องการใช้ format ของวันที่ยังคงเป็น yyyy/mm/dd เราจะมีการใช้คำสั่ง set century on ก่อน แต่เนื่องจากเราไม่ได้ใช้คำสั่งนี้ใน PHP เราเลยต้อง parse date ดังกล่าวออกมาเป็น character เพื่อประกอบกับไปให้ได้ตาม mm/dd/yyyy และใช้คำสั่ง ctod ได้

(*หมายเหตุ ใน foxpro มีคำสั่งที่คล้าย ctod คือ dtoc (date to character))

สิ่งที่เราจะได้จาก $data_date คือข้อมูล date โดยที่เราสามารถ parse ออกมาได้ดังนี้


$dateparse = date_parse($data_date);
$month = $dateparse ["month"];
$day = $dateparse ["day"];
$year = $dateparse ["year"];

หากเราต้องการนำข้อมูล date ดังกล่าวไป query ต่อแบบไม่ใช้ set century on ทำได้ดังนี้


$sql = "SELECT * FROM tablename WHERE data_date >= ctod('" .$month. "/" .$day. "/" .$year. "')";

เพียงเท่านี้เราก็จะหมดปัญหาการ query date ของ foxpro

Posted in Foxpro, PHP | Leave a Comment »

Grails บน Windows ภาคสอง (simple domain class + controller)

Posted by natty on September 15, 2008

เฮ้อ เขียน blog แล้วรู้สึกผิด สัญญากับพี่แซนด์ว่าจะดู application ใน OFBiz ให้ แล้วไหงหนูมาบ้า grails อยู่ตรงนี้ – -”

ต่อๆ …

ก็ต่อจากคราวที่แล้วที่สร้าง application ไปแล้ว ก็ยังคงอ่าน quick start ต่อ มาถึงเรื่อง Datasource configuration ซึ่งดูไปดูมาก็เหมือนใน rails ที่เราจะมี standard environment หลังจากที่เราทำการสร้างแอพขึ้นมา (Development, TestData, and Production) โดยมันจะอยู่ในไฟล์ DataSource.groovy และโดย default แล้วมันก็จะใช้ HSQLDB เหมาะสำหรับการ test

และก็ลอง create domain class โดยการใช้คำสั่ง

cd my-project
grails create-domain-class

มันจะ prompt ให้เราใส่ชื่อ domain class เก๋ก็ใส่ Book มันก็จะ create Book.groovy มาให้ใน grails-app/domain ก็คือทำตามที่เค้าว่าทุกอย่าง

Note1: อย่าตั้งชื่อให้เหมือนคำที่เป็น keyword ใน database เช่น ชื่อ Group
Note2: หากตั้งชื่อเป็นตัวใหญ่นำหน้า 2 ตัวติดกัน จะทำให้เกิด error บนหน้า page แต่ตั้งเป็น MyCar แบบนี้อ่ะได้ มันไม่ติดกัน

Domain class เป็น persistent artifact สำหรับ persist ลง database ซึ่งมันก็คือ GORM นั่นเอง คราวนี้เราลองมาใส่ field ใน Book.groovy ของเรา

class Book {
	String title
	String author
}

ก่อนจะใส่ title กับ author เราจะเห็นแค่ class Book ตั้งอยู่เปล่าๆ ใน Book.groovy เท่านั้น
และเราก็มาสร้าง Test data กัน โดยที่ง่ายที่สุดให้ไปดูที่ <..>/grails-app/conf/BootStrap.groovy แล้วสร้างไว้ใน init

class BootStrap {

     def init = { servletContext ->
     // Create some test data
        new Book(author:"Stephen King",title:"The Shining").save()
        new Book(author:"James Patterson",title:"Along Came a Spider").save()
     }
     def destroy = {
     }
} 

สร้าง Controller

เค้าบอกว่า controller เนี่ยเป็นตัวกลางของ Grails applications ที่ควบคุม web requests และ URLS ของ request map ไปสู่ controller class และ closure ภายใน class ซึ่งก็เหมือนกันบ rails อีก

ให้เรารันคำสั่ง grails create-controller และพิมพ์ชื่อของ controller เข้าไป เช่นหากเราใส่ชื่อ Book มันก็จะได้ BookController.groovy แล้วเราก็จะเปลี่ยนให้มันใช้ dynamic scaffold ซึ่งจะ gen application ให้เราตอน runtime (คือถ้าไม่ทำเป็น dynamic เราก็สามารถ scaffold แบบ manual ได้เช่นกัน โดยใช้คำสั่ง grails generate-all ซึ่งมันจะไม่ replace default scaffolding ให้) โดยเราจะทำดังนี้ แต่ต้องแน่ใจว่า Book ต้องขึ้นต้นด้วย B ใหญ่

class BookController {
	def scaffold = Book
}

สตาร์ท Grails

เริ่มงาน app ของเรา โดยใช้คำสั่ง

grails run-app

มันจะ start ใน Jetty servlet ที่พอร์ต 8080 หากอยาก start ใน พอร์ตอื่นให้ใช้คำสั่ง grails -Dserver.port = 9090 run-app และหากอยากดูลิสต์ของหนังสือที่เราสร้างขึ้น ก็เข้าที่

http://localhost:8080/my-project/book/list

เค้าว่ากันว่ามันจะช้าใน development mode กว่าจะไปแต่ละ page ก็ใช้เวลาหลายนาที หลังจากนี้เราอาจจะเพิ่ม maximum heap size โดย set ที่ {{JAVA_OPTS}} ที่ตัวแปร ‘-Xmx512m’ เพื่อให้ maximum heap เป็น 512

วูปี้ๆ สำเร็จ โหย หน้าตามันดีกว่า rails เยอะแยะมากมายจริงๆ เล้ยยยยยย

อีกลิงค์น่าสนใจของ Grails

Posted in grails | 1 Comment »

Requirement change/ Maintenance บ่อยจัง

Posted by natty on September 15, 2008

เป็นเรื่องน่าเบื่อสำหรับคน IT เกี่ยวกับเรื่อง requirement change และ เรื่องการ maintenance โดยเฉพาะอย่างยิ่ง เมื่อเรามีมุมมองว่า มันยังไม่จำเป็นนี่นา มันสำคัญขนาดนั้นเชียวหรือ? ถ้าเราถาม user เค้าก็จะต้องบอกเราว่า ด่วนสุดๆ จำเป็นมากๆ ไม่มีไม่ได้

วันๆ นึงคนทาง IT เรา ได้รับฟังปัญหาแบบนี้ไม่ใช่จาก user คนเดียว เราเองก็ต้องแอบจัด priority ในหัวเราเอง ว่าอันไหนทำก่อนทำหลัง เพราะไม่มีทางที่เราจะทำทั้งหมดนั้นทันอยู่แล้ว requirement หรือการ maintenance บางอย่าง คุณ user อาจะดูว่าง่าย ไม่เห็นจะมีอะไรเลย แต่เราคน IT รู้ดีว่า ทำอะไรต้องใช้เวลาเท่าไหร่ เช่น อยู่ดีๆ  user เดินมาบอกว่า อยากเปลี่ยนให้มีเงื่อนไขคนทำงาน 15 วันแล้วรับเงินเดือน เค้าก็จะคิดแค่ว่า แค่เปลี่ยนเลขจาก 30 เป็น 15 ไม่เห็นจะมีอะไร ทำ 2 วันก็เสร็จ แต่จริงๆ แล้วมันก็ไม่ใช่แค่นั้นน่ะสิ เราต้องเช็คเงื่อนไขตั้งมากมาย เพราะมันกระทบกับคนอื่นทั้งบริษัท และเป็นนโยบายด้วย

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

เนื่องจากบริษัทนี้ มีบริษัทในเครือมากกว่า 10 บริษัท และมี IT ศูนย์กลางที่เดียวที่เป็นคน provide software ต่างๆ ให้บริษัทในเครืออื่นๆ และบริษัทในเครือก็จะไม่มีแผนก IT ที่พัฒนา software แต่จะมีเฉพาะคนคอย support ดังนั้นหน้าที่ด้าน software และการ maintenance หลักๆ จะตกเป็นของ IT ศูนย์ใหญ่ดังกล่าว และสิ่งที่ชอบมากคือ แม้จะเป็นบริษัทในเครือ ก็จะต้องเสียค่า maintenance เป็นรายเดือนให้กับศูนย์กลาง เวลาไป support ก็ต้องคิดค่าใช้จ่ายเสมือนเราไปจ้างข้างนอกมา ต้องการ requirement ใหม่ก็ต้องผ่านการประชุมจากอีก หลายๆ site เพราะต้องทำกันเป็นมาตรฐานไม่ใช่ใครคิดจะขออะไรก็ได้ ทำให้ทุกอย่างของ IT ไม่ใช่ของฟรีที่ได้มากง่ายๆ สำหรับผู้ใช้อีกต่อไป

แนวคิดการทำแบบนี้ ทำให้ลดความกดดัน และปริมาณงานใน IT ไปได้มาก เพราะการที่มีกระบวนการตรวจสอบเยอะ และยังไปเกี่ยวกับเรื่องเงินด้วยนั้น ทำให้ความอยากของคนลดลงเป็นปกติ สำหรับบริษัทดังกล่าวทำวิธีการนี้ได้ง่ายหน่อยเพราะมีบริษัทในเครือ แต่สำหรับที่ที่มี IT ที่เดียว รองรับบริษัทเดียว แน่นอนว่าจะต้องมีปัญหาเรื่อง user ขอ requirement เรี่ยราด อยากได้อะไรก็ขอๆ กำหนดให้เสร็จภายใน 2 วัน (ทำเหมือนมาซื้อของ) ซึ่งการที่ IT โดน interrupt งานมากไป ก็เห็นได้ชัดว่าทำให้กระบวนการทำงานโดยรวมช้าลง และทำให้นักพัฒนาเองก็เสียความรู้สึก หากไอ้สิ่งที่ทำนั้นไม่มีค่ามากพอที่ควรทำ ไม่ได้ผ่านการ prove มาก่อน แถมยังโดนกดดันให้ทำเสร็จเร็วๆ อีก โดยส่วนมากปัญหานี้ในบริษัททั่วไป ก็แก้โดยทำพวก requirement change หรือการทำแบบฟอร์มการขอ maintenance และมาวิเคราะห์ว่าปัญหาไหนควรได้รับการแก้ไข ซึ่งก็ยิ่งทำให้กระบวนการทำงานช้าลง

จริงๆ วิธีที่ได้ผล คือ ต้องทำให้มาเกี่ยวข้องกับเรื่องเงินให้ได้ คน IT ไม่ใช่ราคาถูกๆ ที่ใครจะใช้ทำอะไรก็ได้ พวกเรามีค่าวิชาชีพ งานของพวกเราออกไป เป็นเงินเป็นทอง ดังนั้น หากนำนโยบายด้านการเงินมาใช้กับหน้าด่าน requirement และ maintenance ของเรา ก็น่าจะทำให้อะไรดูเป็นรูปเป็นร่าง เป็นต้นว่า…

หากใครต้องการ requirement ใหม่ ก็กำหนดเวลาการ prove เพื่อเสนอว่า เค้าจะต้องเสียเงินเท่าไหร่ให้กับ IT เพราะทั้งหมดเป็นค่าใช้จ่าย เราต้องจ้างโปรแกรมเมอร์ทำ แทนที่จะได้ทำอย่างอื่นก็ต้องมาทำงานของเค้า คิดเป็นตัวเงินของแต่ละงานออกมา แล้วให้กลายเป็นค่าใช้จ่ายของหน่วยดังกล่าว สิ้นปีก็มาสรุปกันว่า แผนกไหน มีค่าใช้จ่ายด้าน IT เท่าไหร่ ซึ่งสิ่งนี้จะไม่ได้แสดงให้เห็นแค่ตัวเงินเท่านั้น แต่จะทำให้เห็นว่า IT มีงานเยอะแค่ไหนที่จะต้อง support user และมีการ support ในเรื่องอะไร กระทบงานหลักอย่างไร ทำไมงานบางอย่างจึงล่าช้า เค้าจะได้รู้ว่า คนเรามีเวลาเท่าเดิมแต่งานเพิ่มขึ้น ยังงัยก็ต้องเลือกเอาว่าจะทำอะไร ทำพร้อมกันไม่ได้อยู่แล้ว

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

Posted in Work experience | 1 Comment »