วันพุธที่ 9 พฤศจิกายน พ.ศ. 2554

ทำ Cron บน Web Application ง่ายๆ ด้วย Spring + Quartz

บน Linux เวลาต้องการที่จะรันโปรแกรม หรือ Script โดยตั้งเวลาให้รัน ทุกๆ เช้าหรือทุกๆ ชั่วโมงคงทำได้ไม่ยากเพราะบน Linux มี Cron อยู่ แต่ถ้าต้องการ Schedule รันโปรแกรมบน Windows คงต้องใช้ Windows Task Scheduler แต่ถ้าต้องการรัน Cron บน Web Application ล่ะจะต้องทำยังไง?
สำหรับ Java นั้นมี Library สำหรับจัดการ Schedule อยู่ชื่อว่า Quartz เอาไว้จัดการ Schedule ต่างๆ ใน Application ได้
แต่เนื่องด้วยถ้าใช้ Quartz อย่างเดียวมันคงไม่หล่อเท่าไร ดังนั้นเราจึงต้องเอามา Integrate กับ Spring ซะเพราะหลายคนส่วนใหญ่ก็ใช้งาน Spring กันอยู่แล้ว
ก่อนอื่นเรามาดู dependency กันก่อนครับว่าจะใช้ Quartz กับ Spring นั้นต้องใช้อะไรบ้าง

pom.xml

<dependencies>

 <dependency>
          <groupid>opensymphony</groupid>
   <artifactid>quartz</artifactid>
   <version>1.6.3</version>
 </dependency>
 
 <dependency>
   <groupid>commons-collections</groupid>
   <artifactid>commons-collections</artifactid>
   <version>3.2.1</version>
 </dependency>
 
 <dependency>
  <groupid>org.springframework</groupid>
  <artifactid>spring</artifactid>
  <version>3.0.5.RELEASE</version>
 </dependency>
 
 <dependency>
  <groupid>org.springframework</groupid>
  <artifactid>spring-web</artifactid>
  <version>3.0.5.RELEASE</version>
 </dependency>
 
  </dependencies>

จากนั้นให้สร้างไฟล์ที่เราต้องการจะรันเป็น Schedule


HelloQuartz.java

public class HelloQuartz {
    public void hello() {
        System.out.println("hello quartz");
    }
}
เมื่อมีองค์ประกอบครบแล้ว เราก็มา config applicationContext.xml กัน applicationContext.xml

<bean id="helloQuartz" class="com.example.scheduler.HelloQuartz" />
 
 <bean id="helloJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
  <property name="targetObject" ref="helloQuartz" />
  <property name="targetMethod" value="hello" />
 </bean>
 
 <bean id="helloJob" class="org.springframework.scheduling.quartz.CronTriggerBean">
  <property name="jobDetail" ref="helloJobDetail" />
  <property name="cronExpression" value="0/5 * * * * ?" />
 </bean>
 
 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="triggers">
   <list>
    <ref bean="helloJob" />
   </list>
  </property>
 </bean>
จาก code ด้านบน เรากำหนดให้ bean ที่ต้องการรันเป็น HelloQuartz และกำหนด method ที่จะรันให้เป็น hello แล้วสั่งรันทุกๆ 5 วินาที ซึ่งเราสามารถกำหนด expression ได้ตาม pattern ของ Cron ครับ
Continue Reading...

วันพฤหัสบดีที่ 27 ตุลาคม พ.ศ. 2554

แก้ปัญหา Failed to add the host to the list of known hosts ใน cygwin

บน cygwin เวลาเราจะ ssh ไปที่อื่นแล้วเกิด warning "Failed to add the host to the list of known hosts (/home/[username]/.ssh/known_hosts)." นั้นเนื่องมาจากใน windows เราไม่มี folder home อยู่แต่ home ของเราจะอยู่ที่ /cygdrive/c/Users/[username] แทนครับ

วิธีแก้ไข เราต้องไปแก้ไขไฟล์ที่บอก path ว่า home เราอยู่ที่ /cygdrive/c/Users/[username] โดยให้ไปแก้ไขที่ไฟล์

C:\path\to\cygwin\etc\passwd


โดยเปลี่ยน text ภายในไฟล์จาก /home/[username] เป็น /cygdrive/c/Users/[username] แทน จากนั้นให้ลอง ssh ใหม่จะเห็นว่าสามารถ ssh ได้แล้วโดยไม่เตือน warning ดังกล่าว

ที่มา - http://ekawas.blogspot.com
Continue Reading...

วันพุธที่ 31 สิงหาคม พ.ศ. 2554

Install Gitorious บน Ubuntu 10.04 LTS

ก่อนอื่นต้องติดตั้ง package ที่จำเป็นก่อน
apt-get install -y build-essential apache2 apache2-dev libapache2-mod-xsendfile git-core git-doc mysql-server mysql-client libmysqlclient15-dev phpmyadmin ruby-dev rubygems libopenssl-ruby libdbd-mysql-ruby libmysql-ruby libexpat-dev libcurl4-openssl-dev postfix apg geoip-bin libgeoip1 libgeoip-dev imagemagick libmagick++-dev libpcre3 libpcre3-dev zlib1g zlib1g-dev zip unzip libyaml-dev libonig-dev memcached irb aspell libaspell-dev aspell-en
update gem ให้เป็น version ล่าสุดเพราะว่ามี default gem version ต่ำเกินไป
gem install rubygems-update
cd /var/lib/gems/1.8/bin
./update_rubygems
ติดตั้ง ruby gems ที่ต้องจำเป็น
gem install -b --no-ri --no-rdoc rmagick chronic geoip daemons hoe echoe ruby-yadis ruby-openid mime-types diff-lcs json ruby-hmac rake stompserver passenger rails raspell
gem install -b --no-ri --no-rdoc -v 1.0.1 rack
gem install -b --no-ri --no-rdoc -v 1.3.1.1 rdiscount
gem install -b --no-ri --no-rdoc -v 1.1 stomp
ทำ symlinks ให้กับ ruby
ln -s /usr/bin/ruby1.8 /usr/bin/ruby
ติดตั้ง Sphinx
mkdir src
cd src
wget http://sphinxsearch.com/downloads/sphinx-0.9.8.1.tar.gz
tar -xvzf sphinx-0.9.8.1.tar.gz
cd sphinx-0.9.8.1/
./configure --prefix=/usr && make all
sudo make install
clone Gitorious
mkdir -p /var/www/gitorious.osdev.co.th
chmod ugo+rwx /var/www/gitorious.osdev.co.th
cd /var/www/gitorious.osdev.co.th/
git clone git://gitorious.org/gitorious/mainline.git gitorious
Configure Services

สร้าง init script ไฟล์ git-daemon
vim /etc/init.d/git-daemon
#! /bin/sh
### BEGIN INIT INFO
# Provides:          Gitorious GIT-Daemon
# Required-Start:
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: GIT-Daemon server daemon
# Description:       Starts the GIT-Daemon needed by Gitorious
### END INIT INFO

# Author: Fabio Akita fabioakita@gmail.com

RETVAL=0
PROG="git-daemon"
GITORIOUS_ROOT=/var/www/gitorious.osdev.co.th/gitorious
GIT_DAEMON="/usr/bin/ruby $GITORIOUS_ROOT/script/git-daemon -d"
LOCK_FILE=/var/lock/git-daemon
PID_FILE=$GITORIOUS_ROOT/tmp/pids/git-daemon.pid

do_check_pid() {
if [ -f $PID_FILE ]; then
PID=`cat $PID_FILE`
RUNNING=`ps --pid $PID | wc -l`
else
PID=0
RUNNING=0
fi
}

runlevel=`runlevel | awk '{print $2}'`

start()
{
do_check_pid
if [ $RUNNING != 2 ] ; then
echo -n "Starting $PROG: "
/bin/su - git -c "$GIT_DAEMON"
sleep 10
if [ -f $PID_FILE ] ; then
echo "success"
RETVAL=0
else
echo "failure"
RETVAL=1
fi
else
echo -n "$PROG already running"
RETVAL=1
fi
[ "$RETVAL" = 0 ] && touch $LOCK_FILE
echo
}

stop()
{
do_check_pid
echo -n "Stopping $PROG: "
if [ $RUNNING != 2 ] ; then
echo -n "Stopping $PROG"
else
PROGPID=`cat $PID_FILE`
kill -TERM $PROGPID
fi
RETVAL=0
# if we are in halt or reboot runlevel kill all running sessions
# so the TCP connections are closed cleanly
if [ "x$runlevel" = x0 -o "x$runlevel" = x6 ] ; then
PROGPID=`cat $PID_FILE`
kill -9 $PROGPID > /dev/null
fi
[ "$RETVAL" = 0 ] && rm -f $LOCK_FILE && rm -f $PID_FILE
echo
}

case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
condrestart)
if [ -f $LOCK_FILE ] ; then
if [ "$RETVAL" = 0 ] ; then
stop
# avoid race
sleep 10
start
fi
fi
;;
*)
echo $"Usage: $0 {start|stop|restart|condrestart}"
RETVAL=1
esac
exit $RETVAL
สร้าง init script ไฟล์ git-ultrasphinx
vim /etc/init.d/git-ultrasphinx
#! /bin/sh
### BEGIN INIT INFO
# Provides:          Gitorious Ultrasphinx
# Required-Start:
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Ultrasphinx daemon
# Description:       Starts the Ultrasphinx daemons needed by Gitorious
### END INIT INFO

# Author: Fabio Akita fabioakita@gmail.com

RETVAL=0
GITORIOUS_ROOT=/var/www/gitorious.osdev.co.th/gitorious

START_CMD="cd $GITORIOUS_ROOT && rake ultrasphinx:daemon:start RAILS_ENV=production"
STOP_CMD="cd $GITORIOUS_ROOT && rake ultrasphinx:daemon:stop RAILS_ENV=production"
RESTART_CMD="cd $GITORIOUS_ROOT && rake ultrasphinx:daemon:restart RAILS_ENV=production"
STATUS_CMD="cd $GITORIOUS_ROOT && rake ultrasphinx:daemon:status RAILS_ENV=production"
LOCK_FILE=/var/lock/git-ultrasphinx
PID_FILE=$GITORIOUS_ROOT/db/sphinx/log/searchd.pid

case "$1" in
start)
echo `date` " Starting git-ultrasphinx" >> "$GITORIOUS_ROOT/log/git-ultrasphinx.log"
/bin/su -- git -c "$START_CMD"
;;
stop)
echo `date`" Stopping git-ultrasphinx" >> "$GITORIOUS_ROOT/log/git-ultrasphinx.log"
/bin/su -- git -c "$STOP_CMD"
;;
status)
echo `date`" Status For  git-ultrasphinx" >> "$GITORIOUS_ROOT/log/git-ultrasphinx.log"
/bin/su -- git -c "$STATUS_CMD"
;;
restart)
echo `date`" ReStarting git-ultrasphinx" >> "$GITORIOUS_ROOT/log/git-ultrasphinx.log"
/bin/su -- git -c "$RESTART_CMD"
;;
*)
echo $"Usage: $0 {start|stop|restart|status}"
RETVAL=1
esac
exit $RETVAL
สร้าง init script ไฟล์ git-poller
vim /etc/init.d/git-poller
#!/bin/sh
# Start/stop the git poller
#
### BEGIN INIT INFO
# Provides: git-poller
# Required-Start: stomp
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 1
# Short-Description: Gitorious poller
# Description: Gitorious poller
### END INIT INFO

GITORIOUS_ROOT=/var/www/gitorious.osdev.co.th/gitorious
echo  `date` " Starting git-poller" >> "$GITORIOUS_ROOT/log/git-poller.log"
/bin/su -- git -c " cd $GITORIOUS_ROOT; RAILS_ENV=production script/poller $@"

สร้าง init script ไฟล์ stomp
vim /etc/init.d/stomp
#!/bin/sh
# Start/stop the stompserver
# WATCH THE LINE WRAPPING BELOW
### BEGIN INIT INFO
# Provides: stomp
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 1
# Short-Description: Stomp
# Description: Stomp
### END INIT INFO
test -f /usr/bin/stompserver || exit 0
. /lib/lsb/init-functions
case $1 in
start) log_daemon_msg Starting stompserver stompserver
start-stop-daemon --start --name stompserver --startas /usr/bin/stompserver \
--background --user git
log_end_msg $?
;;
stop) log_daemon_msg Stopping stompserver stompserver
start-stop-daemon --stop --name stompserver
log_end_msg $?
;;
restart) log_daemon_msg Restarting stompserver stompserver
start-stop-daemon --stop --retry 5 --name stompserver
start-stop-daemon --start --name stompserver --startas /usr/bin/stompserver \
--background --user git
log_end_msg $?
;;
status)
status_of_proc /usr/bin/stompserver stompserver && exit 0 || exit $?
;;
*) log_action_msg Usage: /etc/init.d/stomp {start|stop|restart|status}
exit 2
;;
esac
exit 0
copy ไฟล์จาก gitorious ultrasphinx plugin ไปไว้ที่ aspell library
cp /var/www/gitorious.osdev.co.th/gitorious/vendor/plugins/ultrasphinx/examples/ap.multi /usr/lib/aspell/
สร้าง gitorious logrotate file
vim /etc/logrotate.d/gitorious
โดยมี content
/var/www/gitorious.osdev.co.th/gitorious/log/*log {
missingok
notifempty
sharedscripts
postrotate
/etc/init.d/git-daemon restart > /dev/null 2>/dev/null || true
/etc/init.d/git-ultrasphinx restart > /dev/null 2>/dev/null || true
/bin/touch /var/www/gitorious.osdev.co.th/gitorious/tmp/restart.txt > /dev/null 2>/dev/null || true
endscript
}
สร้าง git system user
adduser --system --home /home/git/ --group --shell /bin/bash g
chown -R git:git /home/git
passwd git #ใส่ password ตามต้องการ
sudo usermod -a -G git www-data
sudo chown -R git:git /var/www/gitorious.osdev.co.th
set init script ให้รันเมื่อ start up
cd /var/www/gitorious.osdev.co.th/gitorious
chown -R git:git config/environment.rb script/poller log tmp
chmod -R g+w config/environment.rb script/poller log tmp
chmod ug+x script/poller

chown git:git /etc/init.d/git-ultrasphinx
chown git:git /etc/init.d/git-daemon
chown git:git /etc/init.d/stomp
chown git:git /etc/init.d/git-poller
chmod 755 /etc/init.d/git-ultrasphinx
chmod 755 /etc/init.d/git-daemon
chmod 755 /etc/init.d/stomp
chmod 755 /etc/init.d/git-poller
update-rc.d stomp defaults
update-rc.d git-daemon defaults
update-rc.d git-ultrasphinx defaults
update-rc.d git-poller defaults
Config Apache2

ติดตั้ง passenger module สำหรับ apache
/var/lib/gems/1.8/bin/passenger-install-apache2-module
สร้างไฟล์ passenger.load สำหรับ module ของ apache
vim /etc/apache2/mods-available/passenger.load
โดยมี content (ส่วนนี้อาจเปลี่ยนไปตาม passenger version)
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-3.0.8/ext/apache2/mod_passenger.so
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-3.0.8
PassengerRuby /usr/bin/ruby1.8
Enable module ที่จำเป็น
/usr/sbin/a2enmod passenger
/usr/sbin/a2enmod rewrite
/usr/sbin/a2enmod ssl
Enable SSL site
/usr/sbin/a2ensite default-ssl
Restart Apache
/etc/init.d/apache2 restart
สร้าง site gitorious
vim /etc/apache2/sites-available/gitorious

ServerName gitorious.osdev.co.th
DocumentRoot /var/www/gitorious.bluequartz.net/gitorious/public

# Enable X-SendFile for gitorious repo archiving to work
XSendFile on
XSendFileAllowAbove on

# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
#
# The following directives define some format nicknames for use with
# a CustomLog directive (see below).
#
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

CustomLog /var/log/apache2/gitorious_access.log combined
TransferLog /var/log/apache2/gitorious_access.log
ErrorLog /var/log/apache2/gitorious_error.log

สร้าง site gitorious-ssl
vim /etc/apache2/sites-available/gitorious-ssl


SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
BrowserMatch ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
DocumentRoot /var/www/gitorious.osdev.co.th/gitorious/public

# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
#
# The following directives define some format nicknames for use with
# a CustomLog directive (see below).
#
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

CustomLog /var/log/apache2/gitorious_ssl_access.log combined
TransferLog /var/log/apache2/gitorious_ssl_access.log
ErrorLog /var/log/apache2/gitorious_ssl_error.log
Disable default SSL site และ enable the Gitorious sites
/usr/sbin/a2dissite default
/usr/sbin/a2dissite default-ssl
/usr/sbin/a2ensite gitorious
/usr/sbin/a2ensite gitorious-ssl
สร้าง repository directory
su git
cd ~
mkdir .ssh
touch .ssh/authorized_keys
chmod 700 .ssh
chmod 600 .ssh/authorized_keys
mkdir repositories
mkdir tarballs
mkdir tarballs-work
chmod ug+rwx repositories
echo "export PATH=$PATH:/var/www/gitorious.osdev.co.th/gitorious/script" > ~/.bashrc
Configure Gitorious
cd /var/www/gitorious.osdev.co.th/gitorious
mkdir tmp/pids
cp config/database.sample.yml config/database.yml
cp config/gitorious.sample.yml config/gitorious.yml
cp config/broker.yml.example config/broker.yml
แก้ไขไฟล์ config/gitorious.yml
vim /var/www/gitorious.osdev.co.th/gitorious/config/gitorious.yml
#  Remove every section but production
# Be sure if you paste a generated secret key using apg -m 64 that you clean up
# any line breaks. The secret key that is pasted should be on a SINGLE line.
production:

# ให้รันคำสั่ง apg -m 64 แล้วนำผลลัพธ์ที่ได้มา paste ที่ cookie_secret
cookie_secret: urbecpajicEvpigdiOlqueDracpevyerbEmDecetsendeehurEjShucJox8Ojit4WaWrinyienderdalyaifugNeahatdashyunnamFucwerdolfyedyeactEjEajIb5IksEytkurewsEetAiwirtEkfedtheebyatNoganyoofBabikOcyemEshvovHasojTropsyengyegidzyggEdamCasdijHegdeasfudCovmenpobdywajcycsuchDabr2NadUlHighEisyoilmojliawgOdAcyimpifCaiWumtangEvzueshjajOtFuciddEbWhoivedesjetockVemOdEvnooquoncoapCaQuavnelkoijfudcatfezUlreylbUd

# The path where git repositories are stored. The actual (bare) repositories resides
# in repository_base_path/#{project.slug}/#{repository.name}.git/:
repository_base_path: "/home/git/repositories"

# Stuff that's in the html . custom stats javascript code etc
extra_html_head_data:

# System message that will appear on all pages if present
system_message:

# Port the ./script/gitorious script should use which should be
# the same port that the web server is running on. In Some development
# environments you may see 3000 for the value. For production
# servers you should use the same port as your web server.
gitorious_client_port: 80

# Host the ./script/gitorious script should use:
gitorious_client_host: gitorious.bluequartz.net

# Host which is serving the gitorious app, eg "gitorious.org"
gitorious_host: gitorious.bluequartz.net

# System User which is running git daemon
gitorious_user: git

# Email spam on server errors to:
# You probably want this during development or when you are trying to debug
# an installation that is not working correctly.
exception_notification_emails: mike.jackson@bluequartz.net

# Mangle visible e-mail addresses (spam protection)
mangle_email_addresses: true

# Enable or Disable Public Mode (true) or Private Mode (false)
public_mode: true

# Define your locale
locale: en

# Where should we store generated tarballs?
# (should be readable by webserver, since we tell it to send the file using X-Sendfile)
archive_cache_dir: "/Users/git/tarballs"

# Which directory should we work in when we generate tarballs, before moving
# them to the above dir?
archive_work_dir: "/Users/git/tarballs-work"

# is it only site admins who can create new projects?
only_site_admins_can_create_projects: false

# Should we hide HTTP clone urls?
hide_http_clone_urls: false

# Is this gitorious.org? Read: should we have a very flashy homepage?
is_gitorious_dot_org: false

# Pick a default license
#default_license: GNU Affero General Public License (AGPLv3)
แก้ไขไฟล์ config/database.yml
vim /var/www/gitorious.osdev.co.th/gitorious/config/database.yml
# Remove every section but production
# Make sure the 'production' section is the following
production:
adapter: mysql
database: gitorious
username: git
password: [secret]
host: localhost
encoding: utf8
reconnect = true
สร้าง SQL User
su git
mysql -uroot -p
mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '******';
mysql> GRANT ALL PRIVILEGES ON * . * TO  'git'@'localhost' IDENTIFIED BY  '******' WITH GRANT OPTION MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0;
mysql> GRANT ALL PRIVILEGES ON `gitorious` .* TO 'git'@'localhost';
mysql> quit
Create the Gitorious Databases
ให้ใช้ user root ไปที่ cd /var/www/gitorious.osdev.co.th/gitorious แล้วรันคำสั่ง
bundle install
จากนั้นให้ใช้ user git รันคำสั่ง
su git
cd /var/www/
chmod go-w gitorious.osdev.co.th
cd gitorious.osdev.co.th/gitorious
vim Rakefile
ให้เพิ่ม require 'thread' เข้าไปด้านบนสุดของส่วน require
bundle exec rake db:create RAILS_ENV=production
bundle exec rake db:migrate RAILS_ENV=production
สร้าง Gitorious Admin User
cd /var/www/gitorious.osdev.co.th/gitorious
vim vendor/rails/activesupport/lib/active_support.rb
ให้เพิ่มส่วน require 'thread' เข้าไปด้านบนสุดของส่วน require
env RAILS_ENV=production /usr/bin/ruby1.8 script/create_admin
ให้ใส่ admin email และ password ที่ต้องการ

Configure Sphinx Search Daemon
vim /var/www/gitorious.osdev.co.th/gitorious/config/ultrasphinx/default.base
แก้ไขในส่วน
# Daemon options
searchd
{
# What interface the search daemon should listen on and where to store its logs
address = 0.0.0.0
port = 3312
.......
ให้เป็น
# Daemon options
searchd
{
# What interface the search daemon should listen on and where to store its logs
address = localhost
port = 3312
.......
load configurations เข้าไปใน ruby files
cd /var/www/gitorious.osdev.co.th/gitorious
bundle exec rake ultrasphinx:bootstrap RAILS_ENV=production
bundle exec rake ultrasphinx:spelling:build RAILS_ENV=production
Set permissions อีกครั้งเพื่อความชัวร์
cd /var/www/gitorious.osdev.co.th/gitorious
chown -R git:git config/environment.rb script/poller log tmp
chmod -R g+w config/environment.rb script/poller log tmp
chmod ug+x script/poller
Setup Cron Job
su git
crontab -e
* */1 * * * cd /var/www/gitorious.bluequartz.net/gitorious && rake ultrasphinx:index RAILS_ENV=production
reference http://www.bluequartz.net/
Continue Reading...

วันจันทร์ที่ 18 กรกฎาคม พ.ศ. 2554

แปลงเลขอาราบิกให้เป็นเลขไทยใน LibreOffice/OpenOffice.org

จาก post http://www.leknarm.com/2010/10/openofficeorg-writer.html ผมได้เขียนมาโครง่ายๆ และได้ทำเป็น extension ไว้ให้ลองเล่นกันแล้ว แต่ว่ามี bug อยู่จำนวนมาก และจากงาน OpenOffice.org Codefest#9 ก็ได้มีเพื่อนๆ ช่วยกันพัฒนา extension นี้จนสมบูรณ์ขึ้นมา

แต่ระหว่างที่ผมกำลัง review และปรับปรุง extension อยู่นั้นก็ได้เกิดเหตุการที่ไม่คาดฝันขึ้น มีบุคคลนึงเอา extension จาก post ที่กล่าวมาเอาไป publish ที่ OpenOffice.org Extensions เฉยเลย
http://extensions.services.openoffice.org/en/project/thai-number-converter (ไม่ขอทำเป็น link นะครับเพราะ SEO) น่าเศร้าใจเป็นที่สุด

แต่ตอนนี้ทางทีมก็ได้ publish extension ที่แก้ไขและปรับปรุงเรียบร้อยแล้วไว้ที่ http://extensions.services.openoffice.org/project/convertdigit ทุกท่านสามารถ download และทดลองใช้ได้เลยครับ หากมี bug หรือ comment ในส่วนไหนสามารถ comment ได้ที่ extension หรือที่ blog นี้ก็ได้ครับ
Continue Reading...

วันพฤหัสบดีที่ 19 พฤษภาคม พ.ศ. 2554

การ embedded view ลงใน content บน drupal

โจทย์คือต้องการแสดงผลของ view หลายๆ view ในหน้าเดียวจะทำอย่างไร?

ก่อนอื่นผมได้ลองหา module เกี่ยวกับการ embedded view แล้วหาเท่าไรก็หาไม่เจอ ที่เจอก็ใช้ยากหรือใช้ไม่ได้เลย คราวนี้ก็เลยต้องมาหา API ในการเรียกใช้มันแทน ปรากฏว่าง่ายกว่าที่คิดเยอะ ทำให้รู้เลยว่าทำไมเขาไม่ทำเป็น module กัน

วิธีการ embedded มีดังนี้

<?php
  $viewName = 'viewname';
  print views_embed_view($viewName);
?>

แค่นี้เราก็สามารถ embedded view เข้าไปใน page หรือ story ได้แล้วครับ

ที่มา - thedrupalblog.com
Continue Reading...

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

การทำ Menu/Toolbar Merging บน LibreOffice

จาก http://www.leknarm.com/2009/07/openofficeorg-menubar-merging.html ผมได้เขียนวิธีการทำ Menubar/Toolbar Merging ไว้แต่การที่จะกำหนด MergePoint ได้นั้นจะต้องรู้ก่อนว่า Menubar นั้นๆ หรือ Toolbar นั้นๆ มี UNO function ชื่อว่าอะไร

แต่ document เกี่ยวกับ UNO function หรือ DispatchHelper นั้นหาได้ยากยิ่ง UNO function Russian document (ยิ่งกว่าหา M$ เถื่อน) และถึงจะหามาได้ก็ไม่รู้อยู่ดีว่า UNO function ที่ได้มานั้นเป็นการทำงานส่วนไหน

ถ้าการหา document นั้นมันยากเราก็เข้าไปดูใน LibreOffice เลยสิเพราะเนื่องจากโครงสร้างของ Menu/Toolbar ของ LibreOffice นั้นเป็นลักษณะ xml ที่ Override กันอยู่เป็นชั้นๆ ซึ่งลองหาดูก็พบ Menu/Toolbar configuration อยู่ที่ LibO_HOME/Basis/share/config/soffice.cfg/modules/[program]

หากลองเข้าไปดูจะเห็นโครงสร้างของ Menu/Toolbar เป็นชั้นๆ ซึ่งเราสามารถนำ UNO function ที่กำหนดอยู่ในนั้นมากำหนดเป็น MergePoint ใน extensions เราได้เลยครับ
Continue Reading...

วันจันทร์ที่ 18 เมษายน พ.ศ. 2554

การ search & replace ใน MySQL

เผอิญต้อง search & replace ใน MySQL เลยไปค้นดูปรากฏว่าใช้แค่บรรทัดเดียวคือ

update [table_name] set [field_name] = replace([field_name],'[string_to_find]','[string_to_replace]');

ที่มา mediacollege
Continue Reading...

วันพฤหัสบดีที่ 24 กุมภาพันธ์ พ.ศ. 2554

Thai labels for LibreOffice และ OpenOffice.org

วันนี้ขอมาเขียนสั้นๆ ครับเนื่องจากได้เคยทำ extensions label ไว้นานแล้ว เป็น extensions ที่ติดตั้ง size กระดาษที่เป็น label ที่มีขายอยู่ในประเทศไทย สามารถ download ได้จาก

http://extensions.services.openoffice.org/project/thailabels

ซึ่งที่ได้ทำไปมีอยู่ 2 ยี่ห้อครับคือ labellion และตราช้างนะครับ
ลองเอาไป download ใช้กันดูนะครับ
Continue Reading...

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

วิธีการ debug LibreOffice/OpenOffice.org ด้วย Visual Studio

การ debug LibO หรือ OOo บน Windows นั้นมีอยู่หลายวิธีด้วยกันแต่ผมจะขอนำวิธีการใช้ Visual Studio มาเล่าให้ฟังละกันครับ เพราะว่ามันสามารถตอบโจทย์การ debug ครั้งนี้ได้

ขอเล่าวิธีการทั่วไปหากต้องการ debug ก่อนครับ ก่อนอื่นหากเราต้องการ debug code ในส่วนใดเราจำเป็นต้อง build module นั้นๆ ด้วย parameter debug=true ก่อนแล้ว copy output ไปไว้ที่ LibO/OOo Installation folder (copy <module name>/<output folder>/bin/*.dll ไปไว้ที่ <LibO Root>/program/)

โจทย์ในครั้งนี้คือ ต้อง debug code ในส่วนตอนเริ่มโปรแกรม ซึ่งการ Attach to process ธรรมดาไม่สามารถใช้งานได้ เนื่องจากกว่าเราจะได้ Attach to process, program มันก็ถูกรันไปเรียบร้อยแล้ว


อีกทั้ง LibO และ OOo ตัว binary จริงๆ นั้นไม่ใช่ exe ไฟล์แต่เป็น bin(soffice.bin) ซึ่งตามปรกติแล้ว Visual Studio ไม่สามารถเปิดไฟล์ชนิดดังกล่าวได้ ดังนั้นเราจึงจำเป็นต้อง rename soffice.bin ให้กลายเป็น .exe ซะหรือจะใส่นามสกุลเพิ่มไปเลยก็ได้ (soffice.bin.exe)


จากนั้นให้ไปเพิ่ม path ที่จำเป็นเข้าไปใน Windows ด้วยซึ่ง path ที่ต้องเพิ่มมีดังนี้
  1. <LibO Root>\URE\bin
  2. <LibO Root>\Basis\program
เมื่อเพิ่ม path เรียบร้อยแล้ว ก็สามารถ debug ได้ตามปรกติเลยครับ
Continue Reading...

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

แก้ปัญหา Font ภาษาไทยซ้อนกันบน OpenOffice.org และ LibreOffice

จากปัญหา http://www.leknarm.com/2009/11/openofficeorg-calc-windows-7.html

จากตอนแรกเดาปัญหาว่าเกิดจากการที่ตัว OpenOffice.org หรือ LibreOffice เมื่อใช้งาน UI font ของ Windows จะทำให้แสดงผลได้ผิดพลาดเนื่องจาก font ที่ใช้เป็น UI ของ Windows นั้นไม่มี gryph ไทยซึ่งก็จริงตามนั้น แต่หากใน OOo หรือ LibO ไม่ได้ใช้ Use system font for user interface มันควรจะใช้ได้แต่ดันไม่ได้

ปัญหาที่แท้จริงนั้นเกิดจาก default font ที่ใช้เป็น UI ของ OOo และ LibO นั้นใช้เป็น Andale Sans UI ซึ่งเป็น font ของ StarOffice แต่เนื่องจากเราไม่ได้ใช้ version StarOffice จึงทำให้เราไม่มี font ดังกล่าว

ปัญหานี่สามารถแก้ไขได้ 2 วิธีคือ
  1. ไปแก้ที่ default font ใน project officecfg โดยให้ default font นั้นเป็น font อื่นที่มี gryph ครบทุกภาษา
  2. แก้ replacement table ซึ่งมีขั้นตอนดังนี้
    1. ไปที่เมนู Tools > Option > LibreOffice(OpenOffice.org) > Fonts 
    2. เลือก Apply replacement table แล้ว replace font จาก Andale Sans UI ไปเป็น Tahoma หรือ font อื่นๆ ที่ชอบ 
    3. แล้วกดเครื่องหมายถูกด้านขวาจะได้ font replace ไปอยู่ในกล่องด้านล่าง
    4. คลิกเครื่องหมายถูกหน้า Always แล้วกด OK

 
แค่นี้เราก็สามารถใช้งานภาษาไทยบน LibO และ OOo ได้อย่างมีความสุขแล้ว

อ่อลืมบอกไป ต้องไม่ใช้ Use system font for user interface ด้วยนะครับโดยปิดที่
Tools > Option > LibreOffice > View แล้วเลือกเครื่องหมาถูกหน้า Use system font for user interface ออกครับ
Continue Reading...

วันศุกร์ที่ 28 มกราคม พ.ศ. 2554

ระบบ Build ของ LibreOffice

เนื่องจากได้ปล้ำกับ LibreOffice มาสัปดาห์นึง เพิ่งจะเข้าใจมัน(ถึงจะไม่หมดก็เหอะ) ก็เลยเอามา blog ไว้เผื่อท่านอื่นๆ จะได้เข้าใจบ้าง

ใน LibreOffice นั้นมี wiki บอกวิธีการ build อยู่ที่ http://wiki.documentfoundation.org/Development/Native_Build

โดยในส่วน getting the source นั้นเขียนไว้ดังนี้

$ mkdir git
$ cd git
$ git clone git://anongit.freedesktop.org/libreoffice/bootstrap libo
Cloning into libo...
Remote: Counting objects: 76845, done.
remote: Compressing objects: 100% (17328/17328), done.
remote: Total 76845 (delta 60786), reused 74045 (delta 58579)
Receiving objects: 100% (76845/76845), 15.82 MiB | 1.17 MiB/s, done.
Resolving deltas: 100% (60786/60786), done.
$ cd libo

ถ้าดูจาก file size ที่ได้มา 15.82 MiB นั้นดูเหมือนจะน้อยเกินไปสำหรับ source code ของ LibreOffice ซึ่งในส่วนที่ get มานั้นเป็นแค่ bootstrap ของ LibreOffice เท่านั้น

LibreOffice นั้นเก็บ source code ทั้งหมดไว้เป็น 19 repository หรือ 20 ถ้ารวมส่วนของ l10n ไปด้วยซึ่งถ้าจะ git ออกมาทีละ repo ก็สามารถทำได้ แต่ว่า LibreOffice นั้นมี shell script ที่สามารถดึง source code ทั้งหมดออกมาได้ทีเดียว เรียกว่า g

ซึ่ง g ที่ว่านั้นอยุ่ใน bootstrap ที่เพิ่ง git ออกมานั่นเอง ทั้งนี้เมื่อเรา ได้ bootstrap มาแล้วเราสั่ง ./autogen.sh มันจะ check environment ทั้งหมดของเครื่องเราว่าสามารถที่จะ build LibreOffice ได้หรือไม่ และถ้าผ่านส่วนนั้นมาแล้ว มันจะไปเรียก g เพื่อ clone repository ทั้ง 19 repo มาไว้ที่เครื่องเราต่อไป

ดังนั้น source code ทั้งหมดนั้นเราจะใช้ git ในการ checkout หรือทำงานเฉยๆ ไม่ได้เพราะว่ามันต้องทำทีละ 19 repo แต่จากที่บอกไว้ทีแรก เราใช้ g ในการทำงานแทนได้ เพราะ g จะทำงานเหมือนกับคำสั่ง git แต่ว่าทำให้ทั้ง 19 repo เลยทีเดียว
Continue Reading...

วันศุกร์ที่ 14 มกราคม พ.ศ. 2554

First child CSS Selector

วันนี้จะมาเขียนเรื่องการใช้งาน CSS Selector ในส่วนของ First child element ซึ่งหมายถึงการระบุว่าจะทำงานกับ element แรกเท่านั้นดังตัวอย่าง

<html>
  <head>
    <title>CSS Selector</title>
  </head>
  <body>
    <div>
      <p>div1 line1</p>
      <p>div1 line2</p>
      <p>div1 line3</p>
    </div>
    <div>
      <p>div2 line1</p>
      <p>div2 line2</p>
      <p>div2 line3</p>
    </div>
  </body>
</html>

หากโจทย์ต้องการให้กำหนดสีให้กับ <p> แรกของแต่ละ div จะทำได้โดยใช้ :first-child ต่อหลัง tag ดังตัวอย่าง

<style type="text/css">
  div > p:first-child { color: red;}
</style>

เท่านั้น div1 line1 และ div2 line1 ก็จะมีตัวอักษรสีแดงแล้วครับ
Continue Reading...

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

Function Bahteng บน OpenOffice.org Calc

เห็นมีคนถามมาเยอะว่าใน OpenOffice.org Calc มีฟังก์ชัน bahttext แต่ไม่เห็นมีฟังก์ชัน bahteng เลย ใน excel ยังมีเลยทำไมใน OpenOffice.org ถึงไม่มี

คำตอบคือใน excel นั้นฟังก์ชัน bahteng เป็น add-in function ที่มีคนทำขึ้นมาเสริมเข้าไปในตัวโปรแกรมซึ่งใน excel เพียวๆ นั้นไม่มีฟังก์ชัน bahteng

ใน OpenOffice.org Calc ก็เหมือนกันกับ excel คือตัวโปรแกรมนั้นไม่ได้มีฟังก์ชัน bahteng แต่มี Extension ที่เสริมเข้าไปให้ใช้งาน bahteng ได้แต่ไม่ได้ใช้ชื่อ bahteng จะใช้ชื่อ moneytext แทน

Extension ดังกล่าวมีชื่อว่า numbertext  ซึ่งเมื่อติดตั้ง extension แล้วจะมี function โผล่มาให้ใช้งาน 2 ฟังก์ชันคือ
  1. numbertext
  2. moneytext
โดยในส่วน bahteng ที่เราจะใช้กันนั้นจะเป็นฟังก์ชัน moneytext ซึ่งถ้าจะใช้งานก็ให้พิมพ์ลงใน Cell ว่า

=MONEYTEXT(25;"THB";"en")

ฟังก์ชันจะทำเลข 25 ให้กลายเป็นคำว่า twenty-five baht

ฟังก์ชัน numbertext และ moneytext นั้นสามารถพิมพ์ออกมาเป็นตัวอักษรได้ถึง 40 กว่าภาษา ซึ่งนั่นก็เป็นสาเหตุที่ว่าทำไมต้องกำหนดภาษาและสกุลเงินในฟังก์ชันนั่นเอง

numbertext นอกจากจะเป็น extension ของ OpenOffice.org ยังมีรูปแบบ อื่นๆ ให้ใช้กันได้อีกเช่น jar เป็นต้น สามารถดูรายละเอียดเพิ่มได้ที่ http://numbertext.org
Continue Reading...

วันศุกร์ที่ 7 มกราคม พ.ศ. 2554

ใช้งาน cookie บน javascript กับภาษาไทย

เผอิญได้โจทย์มาว่าใน contact form user นั้นควรกรอกแค่ครั้งเดียว ครั้งต่อไปเข้ามาหน้า contact ก็ไม่ควรต้องกรอกอีก ซึ่งผู้ที่จะกรอกนั้นจะเป็น anonymous ดังนั้นวิธีการที่จะทำให้หน้า contact จำค่าได้ก็คงต้องใช้ cookie

ปัญหาคือหน้า contact form นั้นอยู่ใน drupal ซึ่งเมื่อเปิด page cache แล้วมันจะทำให้ใช้งาน cookie สำหรับ anonymous ไม่ได้ ดังนั้นเลยต้องเลี่ยงไปใช้ cookie ของ javascript แทน

จาก tutorial http://www.w3schools.com/JS/js_cookies.asp ก็ดูเหมือนจะทำงานได้ดีแต่พอเรานำไปใช้จริงและกรอกค่าใน form เป็นภาษาไทย ผลลัพธ์ก็คือ ภาษาไทยเละ ดังนั้นเราจึงต้อง customize code กันนิดหน่อยเพื่อให้ใช้งานภาษาไทยได้

original code

function getCookie(c_name) {
  if (document.cookie.length > 0) {
    c_start = document.cookie.indexOf(c_name + "=");
    if (c_start != -1) {
      c_start = c_start + c_name.length+1;
      c_end = document.cookie.indexOf(";", c_start);
      if (c_end == -1) 
        c_end = document.cookie.length
      return unescape(document.cookie.substring(c_start,c_end));
    }
  }
  return "";
}

function setCookie(c_name, value,expiredays) {
  var exdate = new Date();
  exdate.setDate(exdate.getDate()+expiredays);
  document.cookie = c_name+ "=" +escape(value)+((expiredays==null) ? "" : ";expires="+exdate.toUTCString());
}

customized code

function getCookie(c_name) {
  if (document.cookie.length > 0) {
    c_start = document.cookie.indexOf(c_name + "=");
    if (c_start != -1) {
      c_start = c_start + c_name.length+1;
      c_end = document.cookie.indexOf(";", c_start);
      if (c_end == -1) 
        c_end = document.cookie.length
      return decodeURIComponent(document.cookie.substring(c_start,c_end));
    }
  }
  return "";
}

function setCookie(c_name, value,expiredays) {
  var exdate = new Date();
  exdate.setDate(exdate.getDate()+expiredays);
  document.cookie = c_name+ "=" +encodeURIComponent(value)+((expiredays==null) ? "" : ";expires="+exdate.toUTCString());
}

เพื่อให้ใช้งานภาษาไทยได้ ต้องใช้ function encodeURIComponent แทน function escape แต่ปัญหายังไม่หมดแค่นี้ เมื่อเรา getCookie ที่ภายใน text นั้นมี space เข้ามาใช้งาน space ที่เคยพิมพ์เข้าไปจะกลายเป็นเครื่องหมาย + แทน ดังนั้นเราต้อง replace มันก่อนที่จะนำไปใช้งานด้วย

getCookie('c_name').replace(/\+/g, " ")

ซึ่ง /\+/ นั้นหมายถึงเครื่องหมาย + และ g หมายถึงให้ replace ทั้ง string
Continue Reading...

วันพฤหัสบดีที่ 6 มกราคม พ.ศ. 2554

แก้ปัญหา warning: Attempt to assign property of non-object ใน drupal เมื่อใช้งาน Google Adwords

 เมื่อมีการเปิดการใช้งาน google adwords ในหน้าใดก็ตาม จะเกิด error  warning: Attempt to assign property of non-object in ...... theme.inc ขึ้นที่หน้าดังกล่าว พอลองไปดูที่ตำแหน่งดังกล่าวจะเป็นส่วนของ language ซึ่งไม่น่าจะเกิด error ได้เลย

ปัญหาดังกล่าวจริงๆ แล้วเกิดขึ้นที่ module google adwords ซึ่งดันไปเรียกใช้งาน global variable ชื่อว่า language ตัวเดียวกันทำให้เกิด warning ขึ้น

วิธีการแก้ไขคือให้ไปที่ module google_adwords แก้ไข google_adwords.module ใน function _google_adwords_view_adwords ให้แก้ไขตาม code ด้านล่าง โดยเปลี่ยนชื่อตัวแปร $language ให้เป็นตัวอื่นซะ

if (arg(0) != 'admin' && $track > 0) {
  if($node->google_adwords['enabled'] <> 0) { 
   
    $label = $node->google_adwords['label'];
    $id = variable_get('google_adwords_conversion_id', 0);
    $google_conversation_language = variable_get('google_adwords_conversion_language', $language->language); //change variable from $language to $google_conversation_language
    $format = variable_get('google_adwords_conversion_format', '2');
    $color = variable_get('google_adwords_conversion_color', 'FFFFFF');
    $google_js = variable_get('google_adwords_external_script', 'https://www.googleadservices.com/pagead/conversion.js');
    
    $output = '';
    
    $output .= "\n" . '<!-- Google Code for Conversion Page -->' . "\n";
    $output .= '<script language="JavaScript" type="text/javascript">' . "\n";
    $output .= '<!--' . "\n";
    $output .= '    var google_conversion_id = '. $id .';' . "\n";
    $output .= '    var google_conversion_language = "'. $google_conversation_language .'";' . "\n"; //change variable from $language to $google_conversation_language
    $output .= '    var google_conversion_format = "'. $format .'";' . "\n";
    $output .= '    var google_conversion_color = "'. $color .'";' . "\n";
    $output .= '    var google_conversion_label = "'. $label .'";' . "\n";
    $output .= '//-->' . "\n";
    $output .= '</script>' . "\n";
    $output .= '<script language="JavaScript" src="' . $google_js . '"></script>' . "\n";
    $output .= '<noscript>' . "\n";
    $output .= '    <img height="1" width="1" border="0" src="https://www.googleadservices.com/pagead/conversion/' . $id . '/?label='. $label .'&amp;guid=ON&amp;script=0"/>' . "\n";
    $output .= '</noscript>' . "\n";
       
    return $output;
    
  }
}

ที่มา http://goo.gl/EvPvO
Continue Reading...

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

แก้ปัญหาชื่อภาษาไทยบน Alfresco เละใน Version 3.4 บน Windows

เมื่อลง Alfresco 3.4 บน Windows แล้วเมื่อสร้าง Space หรือไฟล์ที่มีชื่อภาษาไทยแล้ว ชื่อไฟล์ดังกล่าวจะกลายเป็น ????

ปัญหาดังกล่าวเกิดจาก default character set ใน MySQL ไม่ได้ถูกตั้งค่าให้เป็น UTF-8 เหมือน Version ก่อนๆ ทำให้ Table ที่สร้างขึ้นมาไม่ได้มี Collation เป็น UTF-8 เมื่อมีการ Insert ภาษาไทยเข้าไปใน Table จึงทำให้ภาษาไทยเละ

วิธีการแก้ไขคือเราต้อง alter table ให้มันกลายเป็น UTF-8 ซะก่อนซึ่งจะให้ alter ที่ละ table มันก็จะเหนื่อยไปนิด ดังนั้นก็เลยเขียน php เพื่อที่จะ alter table ให้กลายเป็น UTF-8 ทั้งหมดซะ

<?php
  $db = mysql_connect('localhost','username','password');
  if(!$db) echo "Cannot connect to the database - incorrect details";
  mysql_select_db('alfresco');
  mysql_query('ALTER DATABASE CHARACTER SET utf8');
  $result=mysql_query('show tables');
  while($tables = mysql_fetch_array($result)) {
    foreach ($tables as $key => $value) {
      mysql_query("ALTER TABLE $value CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci");
    }
  }
?>

เมื่อรันแล้วให้ลอง restart alfresco ซักทีนึงแล้วก็ลองดูว่าภาษาไทยสามารถใช้งานได้หรือยัง ถ้ายังให้ไป set default character ใน MySQL ให้เป็น UTF-8 ตาม link นี้ครับ http://www.leknarm.com/2010/01/set-mysql-utf8.html
--
Tantai Thanakanok. Open Source Development Co., Ltd.
Tel: +66 38 311816, Fax: +66 38 773128, http://www.osdev.co.th/
Continue Reading...

Blogroll

About