Wednesday, August 7, 2013

Web dev example : JSON & jQuery Mobile & Bottle.

Bottle is a fast, simple and lightweight WSGI micro web-framework for Python.
jQuery Mobile base on jQuery for mobile device.
jQuery vs. jQuery Mobile vs. jQuery UI

Install bottle:

$ sudo apt-get install python-setuptools
$ easy_install bottle

Demo server deployment

file structure:

BottlejQuery
├── bottleJQuery.py
└── index.html

run command:

$ ./bottleJQuery.py

connect to server:

http://localhost:8080/bottle  

Building simple web server use bottle

bottleJQuery.py

#!/usr/bin/env python
from bottle import route, static_file, debug, run, get, redirect
from bottle import post, request
import os, inspect, json

#enable bottle debug
debug(True)

# WebApp route path
routePath = '/bottle'
# get directory of WebApp (bottleJQuery.py's dir)
rootPath = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))

@route(routePath)
def rootHome():
    return redirect(routePath+'/index.html')

@route(routePath + '/<filename:re:.*\.html>')
def html_file(filename):
    return static_file(filename, root=rootPath)

@get(routePath + '/jsontest')
def testJsonGET():
    print "GET Header : \n %s" % dict(request.headers) #for debug header
    return {"id":2,"name":"Jone"}

@post(routePath + '/jsontest')
def testJsonPost():
    print "POST Header : \n %s" % dict(request.headers) #for debug header
    data = request.json
    print "data : %s" % data 
    if data == None:
        return json.dumps({'Status':"Failed!"})
    else:
        return json.dumps({'Status':"Success!"})

run(host='localhost', port=8080, reloader=True)

Test GET request:

$ curl -i -X GET http://localhost:8080/bottle/jsontest

HTTP/1.0 200 OK
Date: Wed, 07 Aug 2013 16:03:53 GMT
Server: WSGIServer/0.1 Python/2.7.3
Content-Length: 25
Content-Type: application/json

{"id": 2, "name": "Jone"}

bottle debug message:

GET Header :
 {'Host': 'localhost:8080', 'Content-Type': 'text/plain', 'Content-Length': '', 'Accept': '*/*', 'User-Agent': 'curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3'}
localhost - - [08/Aug/2013 00:03:53] "GET /bottle/jsontest HTTP/1.1" 200 25

Test POST request:

$ curl -X POST -H "Content-Type: application/json" -d '{"name":"OopsMonk","pwd":"abc"}' http://localhost:8080/bottle/jsontest
{"Status": "Success!"}

bottle debug message:

POST Header :
 {'Host': 'localhost:8080', 'Content-Type': 'application/json', 'Content-Length': '31', 'Accept': '*/*', 'User-Agent': 'curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3'}
data : {u'pwd': u'abc', u'name': u'OopsMonk'}
localhost - - [08/Aug/2013 00:04:56] "POST /bottle/jsontest HTTP/1.1" 200 22

Building simple web page

Create 4 buttons in the index.html file:

  • jQuery.getJSON : use jQuery.getJSON API.
    bottle debug message:

    GET Header :
     {'Content-Length': '', 'Accept-Language': 'zh-tw,zh;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Host': '127.0.0.1:8080', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0', 'Connection': 'close', 'X-Requested-With': 'XMLHttpRequest', 'Referer': 'http://192.168.1.38/bottle/index.html', 'Content-Type': 'text/plain'}  
    localhost - - [08/Aug/2013 00:42:08] "GET /bottle/jsontest HTTP/1.0" 200 25
  • jQuery.post : use jQuery.post API.
    Unfortunately it's not work, because the content-Type is fixed 'application/x-www-form-urlencoded; charset=UTF-8', that's a problem when using bottle.request.json API to retrieve JSON from request.
    For this perpose we have to overwirte jQuery.post or use jQuery.ajax.
    bottle debug message:

    POST Header :
     {'Content-Length': '22', 'Accept-Language': 'zh-tw,zh;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Host': '127.0.0.1:8080', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0', 'Connection': 'close', 'X-Requested-With': 'XMLHttpRequest', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Referer': 'http://192.168.1.38/bottle/index.html', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
    data : None
    localhost - - [08/Aug/2013 00:55:17] "POST /bottle/jsontest HTTP/1.0" 200 21
  • jQuery.ajax GET, jQuery.ajax POST : use jQuery.ajax API.
    GET debug message:

    GET Header :
     {'Content-Length': '', 'Accept-Language': 'zh-tw,zh;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Host': '127.0.0.1:8080', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0', 'Connection': 'close', 'X-Requested-With': 'XMLHttpRequest', 'Referer': 'http://192.168.1.38/bottle/index.html', 'Content-Type': 'text/plain'}
    localhost - - [08/Aug/2013 01:00:17] "GET /bottle/jsontest HTTP/1.0" 200 25

    POST debug message:

    POST Header :
     {'Content-Length': '22', 'Accept-Language': 'zh-tw,zh;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Host': '127.0.0.1:8080', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0', 'Connection': 'close', 'X-Requested-With': 'XMLHttpRequest', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Referer': 'http://192.168.1.38/bottle/index.html', 'Content-Type': 'application/json; charset=utf-8'}
    data : {u'id': 3, u'name': u'Ping'}
    localhost - - [08/Aug/2013 01:01:40] "POST /bottle/jsontest HTTP/1.0" 200 22

index.html

<!DOCTYPE html>
<html>
<head>
<title>Bottle & jQuery Mobile</title>

<link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>

</head>

<body>
<h3>This is a example using jQuery Mobile and pyhton bottle fromwork. </h3>
<div data-role="controlgroup" data-type="horizontal">
    <a href="#" id="btnGetJSON" data-role="button">jQuery.getJSON</a>
    <a href="#" id="btnPOSTJSON" data-role="button">jQuery.post</a>
    <a href="#" id="btnAJAXGet" data-role="button">jQuery.ajax GET</a>
    <a href="#" id="btnAJAXPOST" data-role="button">jQuery.ajax POST</a>
</div>

<script>
    //button action 
    $("#btnGetJSON").click(function(){
        $.getJSON("jsontest", function(data){
            $.each(data, function(index, value){
                alert("index: " + index + " , value: "+ value);
            });
        });
    });

    $("#btnPOSTJSON").click(function(){
        alert("Not easy work on bottle !!!");
        /*
        It's not work, send JSON via jQuery.post().
        because the content-Type is not 'application/json',
        The content type is fixed :
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        that's a problem on bottle.request.json.
        */

        $.post(
        "jsontest", 
        JSON.stringify({"id":3, "name":"Ping"}), 
        function(ret_data, st){
            $.each(ret_data, function(index, value){
                alert("index: " + index + " , value: "+ value);
            });
            alert("Server return status : " + st); 
        },
        'json'
        );
    });

    $("#btnAJAXGet").click(function(){
        $.ajax
        ({
            url: 'jsontest',
            success: function(data){
                $.each(data, function(index, value){
                    alert("index: " + index + " , value: "+ value);
                });
            },
            /*
            if dataType not set, the Accept in request header is:
            'Accept': '* / *'
            dataType = json :
            'Accept': 'application/json, text/javascript, * /*; q=0.01'
            */
            dataType: 'json'
        });
    });

    $("#btnAJAXPOST").click(function(){
        var post_data = {"id":3, "name":"Ping"};
        $.ajax
        ({
            type: 'POST',
            url: 'jsontest',
            data:JSON.stringify(post_data),
            contentType: "application/json; charset=utf-8",
            dataType: 'json',
            success: function(data){
                $.each(data, function(index, value){
                    alert("index: " + index + " , value: "+ value);
                });
            }
        });
    });
</script>
</body>
</html>

Wednesday, July 31, 2013

Sending HTML Mail Using SMTP With Authorization

Here is a text/plain MIME type parts in official exmaple code.
I remove it from my sample code, because it's not show up in mail at Office Outlook 2010.

#!/usr/bin/env python
"""
File name: sendMail.py
Python send HTML mail using SMTP with authorization

Usage :
./sendMail.py to@gmail.com Subtitle [ FilePath | txt ]
"""

import smtplib
import sys,traceback
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from time import gmtime, strftime

#log file location
log_path = "./mail-log";
#log timestamp format
logger_tstamp = "%Y-%m-%d %H:%M:%S"

#SMPT server
smtp_server = "smtp.gmail.com"
#gmail 465 or 578
smtp_port = 587

#mail from 
user = "from@gmail.com"
pwd = "password"

# log method
class Logger:
    file = 1
    err = 2

class ContentType:
    file = 1
    txt = 2

#select log method 
debug_log = Logger.err
#select content type
content_type = ContentType.txt

def debug(msg):
    if debug_log == Logger.file:
        with open(log_path, 'a') as log_file:
            log_file.write(strftime(logger_tstamp, gmtime()) + msg)
        log_file.close()

    elif debug_log == Logger.err:
        sys.stderr.write(strftime(logger_tstamp, gmtime()) + msg)

    else: 
        print(strftime(logger_tstamp, gmtime()) + msg)

#check argument number
arg_count = len(sys.argv)
if arg_count !=4:
    debug("[Error]: invalid argument\n")
    sys.exit(1)
try:
    mail_to = sys.argv[1]
    subject = sys.argv[2]
    if content_type == ContentType.file:
        content_path = sys.argv[3]
        with open(content_path, 'r') as f_content:
            content = f_content.read()
        f_content.close()
    else:
        content = sys.argv[3]

    serv = smtplib.SMTP(smtp_server, smtp_port)
#    serv.ehlo()
    serv.starttls()
#    serv.ehlo()
    serv.login(user, pwd);
    msg = MIMEMultipart('alternative')
    msg['Subject'] = subject
    msg['From'] = user
    msg['To'] = mail_to
    part1 = MIMEText(content, 'html')
    msg.attach(part1)
    serv.sendmail(user, mail_to, msg.as_string())
    serv.quit()
    debug("[Debug] subject : " + subject + "\n")
except:
    debug("[Error]: send mail error\n")
    debug("[Error]:\n" + traceback.format_exc())
    sys.exit(2);

Reference:
email: Examples
smtplib
How to send email in Python via SMTPLIB

Friday, July 5, 2013

Redirect and Save iptables on Ubuntu 12.04

Redirect port 8080 to 80

sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080  

Check iptables setting

sudo iptables -t nat -L  

Save configure to iptables.rules

sudo iptables-save > /etc/iptables.rules  

Save Solution #1

Configre /etc/network/interfaces

iface eth0 inet dhcp  
    pre-up iptables-restore < /etc/iptables.rules  

Save Solution #2

Configure /etc/network/if-pre-up.d/iptablesload

#!/bin/sh
iptables-restore < /etc/iptables.rules
exit 0

Configure /etc/network/if-post-down.d/iptablessave

#!/bin/sh
iptables-save -c > /etc/iptables.rules
if [ -f /etc/iptables.downrules ]; then
    iptables-restore < /etc/iptables.downrules
fi
exit 0

Change permissions

sudo chmod +x /etc/network/if-post-down.d/iptablessave  
sudo chmod +x /etc/network/if-pre-up.d/iptablesload  

Reference:
Tomcat: redirecting traffic from port 8080 to 80 using iptables
IptablesHowTo

Thursday, June 20, 2013

Setup Raspberry Pi server environment

Update @ 2014/12/27

Install Raspbian "wheezy" image

Download image from Raspberry Pi offical website

Mount HOME to HDD

  • Copy HOME data to disk
    $ sudo mkdir /media/new_home
    $ sudo mount /dev/sda1 /media/new_home
    $ sudo rsync -aXS /home/. /media/new_home/.
    $ sudo umount /media/new_home 
  • fstab
    #get disk UUID
    $ sudo blkid
    /dev/mmcblk0p1: SEC_TYPE="msdos" LABEL="boot" UUID="936C-7122" TYPE="vfat"
    /dev/mmcblk0p2: UUID="c1198422-7a7c-4863-8a8f-45a1db26b4f2" TYPE="ext4"
    /dev/sda1: UUID="2cd990b5-6c27-4933-95d0-fd00b000fe77" TYPE="ext4"
    
    #modify fstab
    $ echo "UUID=2cd880b5-6c27-4933-95d0-fd00b000fe77    /home    ext4    defaults    0    2" | sudo tee --append /etc/fstab
    
    #mount HOMW without reboot.
    $ sudo mount -a

Create a sudo user

#create user with HOME directory 
$ sudo useradd -m oopsmonk
#add user to sudo group 
$ sudo adduser oopsmonk sudo 
#set password 
$ sudo passwd oopsmonk 

Install necessary packages

$ sudo aptitude full-upgrade -y
$ sudo aptitude install tmux vim git python-setuptools -y
$ sudo easy_install pip

SAMBA server

$ sudo aptitude install samba
$ sudo cp /etc/samba/smb.conf /etc/samba/smb.conf.bak
$ sudo vi /etc/samba/smb.conf
enable sucurity user
#   security = user
[rpi]
comment = raspberry-pi
path = /home/oopsmonk/
browseable = yes
writable = yes
read only = no

#add smaba user and restart samba server
$ sudo pdbedit -a -u oopsmonk
$ sudo service samba restart

Configre default editor

$ echo "export EDITOR=vim" >> ~/.bashrc
$ echo "export GIT_EDITOR=vim" >> ~/.bashrc
$ echo "export TERM=screen-256color" >> ~/.bashrc
$ echo "alias tmux='tmux -2'" >> ~/.bashrc
$ source ~/.bashrc

Boot from USB Disk

  1. Use dd or Win32DiskImager dump image to both SD card and USB Disk.
  2. Use gparted to expand root partition to full USB disk.
  3. Change cmdline.txt on sdcard from root=/dev/mmcblk0p2 to root=/dev/sda2
    dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 
    console=tty1 root=/dev/sda2 rootfstype=ext4 elevator=deadline rootwait  
  4. Plug both sdcard and USB disk into Raspberry Pi, bootup system.

exFat support

Defualt cannot mount exFAT filesystem, why exFAT?
  • NTFS performance is so bad.
  • Single file limited to 4GB in FAT32.
Install exFat:
sudo apt-get install exfat-fuse  

FTP server setup

Install vsftpd

$ sudo apt-get install vsftpd  
$ sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.bak  

Modify vsftpd.conf

$ sudo vi /etc/vsftpd.conf  
anonymous_enable=NO
local_enable=YES
write_enable=YES
force_dot_files=YES  #Add at bottom  
Invoke configuration
$ sudo service vsftpd restart    
Reference:
http://www.wikihow.com/Make-a-Raspberry-Pi-Web-Server
http://www.vuplus-community.net/board/threads/howto-make-an-ftp-server-with-your-raspberry.7245/
http://www.instructables.com/id/Raspberry-Pi-Web-Server/step9/Install-an-FTP-server/

BT download server setup

Install transmission-daemon
$ sudo apt-get install transmission-daemon  
Modify config file
$ cd /etc/transmission-daemon  
$ sudo cp settings.json settings.json.bak
$ sudo vi settings.json
#Change download folder
"download-dir": "/home/oopsmonk/BT-Download"
#Accept connection from all ip address
"rpc-whitelist": "*.*.*.*"
#Web GUI login account setting
"rpc-username": "oopsmonk"
"rpc-password": "web-login-pwd"
"umask": 2
umask change from 18 to umask: 2. This will enable all users in the transmission group to also write to the file.
Create download folder and change permission for debian-transmission user.
$ mkdir /home/oopsmonk/BT-Download
$ chmod 777 /home/oopsmonk/BT-Download
Reload config
$ sudo service transmission-daemon reload  
Login to Web GUI
http://localhost:9091/transmission

Nginx proxy setup for BT server

Why should use proxy??
Some firewall allow connect to internet using 80 port only.
In this case, we can use proxy redirect 9091 to 80 port.
$ sudo install nginx
$ sudo service nginx stop  
$ sudo vi /etc/nginx/sites-available/default
server{
        #listen   80; ## listen for ipv4; this line is default and implied
        #listen   [::]:80 default_server ipv6only=on; ## listen for ipv6
...
        location /bt {
                proxy_pass http://127.0.0.1:9091/transmission;
        }
...
}

$ sudo service nginx start  
Test connection:
http://localhost/bt/web/
Do not forget the / in end of URL.
Reference:
Setting Up Transmission’s Web Interface
Linux防健忘日誌No.69-Ubuntu 12.04 安裝及設定transmission-daemon

USB Wifi setup

Check USB wifi driver

$ sudo lsusb  
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 004: ID 0b05:1786 ASUSTek Computer, Inc. USB-N10 802.11n Network Adapter [Realtek RTL8188SU]
Bus 001 Device 005: ID 1a40:0101 Terminus Technology Inc. 4-Port HUB
Bus 001 Device 006: ID 0bc2:5061 Seagate RSS LLC
Here my wifi dongle is ASUS USB-N10.
Supported wifi dongle list can find at http://elinux.org/RPi_VerifiedPeripherals#USB_Wi-Fi_Adapters

Wifi scan test

$ sudo iwlist wlan0 scan | grep ESSID  
                ESSID:"CH_Lee"
                ESSID:"iHome01"
                ESSID:"WALL-E"
                ESSID:"AndyStella"
                ESSID:"My home wifi"
                ESSID:"HC-SY"
                ESSID:"default"  

Configure /etc/network/interfaces

auto wlan0

iface lo inet loopback
iface eth0 inet dhcp

allow-hotplug wlan0
iface wlan0 inet dhcp
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
wpa-ssid "XXXXXX"
wpa-psk "YYYYYY"
iface default inet dhcp

PPPoE setup

Install pppoeconf

$ sudo apt-get install pppoeconf  
$ sudo pppoeconf
Input account and password to config PPPoE.
Satrt DSL connection
$ sudo pon dsl-provider  
Terminate connection
$ sudo poff  
Connection status
$ sudo plog  
or  
$ ifconfig ppp0  

Run MOC issue

Update: 2013/08/14
When run moc get an error as bellow:
$ mocp
Running the server...
Trying JACK...
Trying ALSA...
ALSA lib confmisc.c:768:(parse_card) cannot find card '0'
ALSA lib conf.c:4241:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4241:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1251:(snd_func_refer) error evaluating name
ALSA lib conf.c:4241:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:4720:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM default
Trying OSS...

FATAL_ERROR: No valid sound driver!


FATAL_ERROR: Server exited!
Check if audio driver(snd_bcm2835) is not loaded
$ lsmod
Module                  Size  Used by
xt_TCPMSS               3119  1 
xt_tcpmss               1301  1 
xt_tcpudp               2087  1 
iptable_mangle          1467  1 
ip_tables              11482  1 iptable_mangle
x_tables               16865  5 ip_tables,xt_tcpmss,xt_tcpudp,xt_TCPMSS,iptable_mangle
pppoe                  11317  2 
pppox                   2445  1 pppoe
ppp_generic            25397  6 pppoe,pppox
slhc                    5679  1 ppp_generic
snd_bcm2835            16304  0 
snd_pcm                77560  1 snd_bcm2835
snd_page_alloc          5145  1 snd_pcm
snd_seq                53329  0 
snd_seq_device          6438  1 snd_seq
snd_timer              19998  2 snd_pcm,snd_seq
snd                    58447  5 snd_bcm2835,snd_timer,snd_pcm,snd_seq,snd_seq_device
leds_gpio               2235  0 
led_class               3562  1 leds_gpio
Check device permission.
$ sudo ls -al /dev/snd/*            
crw-rw---T 1 root audio 116,  0 Aug 14 10:49 /dev/snd/controlC0
crw-rw---T 1 root audio 116, 16 Aug 14 10:49 /dev/snd/pcmC0D0p 
crw-rw---T 1 root audio 116,  1 Aug 14 10:49 /dev/snd/seq      
crw-rw---T 1 root audio 116, 33 Aug 14 10:49 /dev/snd/timer
Ha! it's a permission issue.
Change permission:
$ sudo chmod 666 /dev/snd/*
Solved!

Wednesday, June 19, 2013

Install JDK1.4.2(32bit) on Ubuntu 12.04 LTS(64bit)

Here is an error occurred if installed directly:
install.sfx.XXX: not found

Solution:

  • install g++-mltilib and JDK

    $ sudo apt-get install g++-multilib  
    $ chmod +x j2sdk-1_4_2_19-linux-i586.bin  
    $ ./j2sdk-1_4_2_19-linux-i586.bin
    .....
    Do you agree to the above license terms? [yes or no]
    yes
    Unpacking...
    Checksumming...
    0
    0
    Extracting...
    UnZipSFX 5.40 of 28 November 1998, by Info-ZIP (Zip-Bugs@lists.wku.edu).
       creating: j2sdk1.4.2_19/
       creating: j2sdk1.4.2_19/jre/
       creating: j2sdk1.4.2_19/jre/bin/
      inflating: j2sdk1.4.2_19/jre/bin/java
      inflating: j2sdk1.4.2_19/jre/bin/keytool
      inflating: j2sdk1.4.2_19/jre/bin/policytool
    ....
    Creating j2sdk1.4.2_19/lib/tools.jar
    Creating j2sdk1.4.2_19/jre/lib/rt.jar
    Creating j2sdk1.4.2_19/jre/lib/jsse.jar
    Creating j2sdk1.4.2_19/jre/lib/charsets.jar
    Creating j2sdk1.4.2_19/jre/lib/ext/localedata.jar
    Creating j2sdk1.4.2_19/jre/lib/plugin.jar
    Creating j2sdk1.4.2_19/jre/javaws/javaws.jar
    Done.  
    $ sudo mkdir -p /usr/lib/jvm  
    $ sudo mv j2sdk1.4.2_19 /usr/lib/jvm/java-1.4.2_19
  • Java environment configuration.

    $ wget http://webupd8.googlecode.com/files/update-java-0.5b  
    $ chmod +x update-java-0.5b
    $ sudo ./update-java-0.5b  

    Select java-1.4.2_19

    Check java version

    $ java -version
    java version "1.4.2_19"
    Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_19-b04)
    Java HotSpot(TM) Client VM (build 1.4.2_19-b04, mixed mode)  

Compare the same file in two folders and remove it.

有時在整理照片或文件時, 需要比對2個資料匣, 把重覆的檔案拿掉.

Dwonload Source Here

function usage(){
    echo "Find the same file in two folders and remove it."
    echo "usage : ./comp-rm.sh target-dir source-dir"
    echo "remove the same files in target-dir."
}

if [ $# -ne 2 ]; then
    usage
    exit 1
fi

target_dir=$1
source_dir=$2
f_list1=$(find "$target_dir" -type f)
f_list2=$(find "$source_dir" -type f)


for i in $f_list1; do
    echo $f_list2 | grep $(basename $i) >/dev/null && hit_str+=$i";"
done

if [ -z $hit_str ]; then
    echo "list is empty.."
    exit 0
fi

export IFS=";"
count=0
for hit_file in $hit_str; do
    echo "$hit_file"
    let count++
done

echo "Do you want to remove these files ($count)?"
read -p "Press 'Ctrl+C' stop, 'Enter' key to continue..."

for hit_file in $hit_str; do
    echo "removing... $hit_file"
    rm -f $hit_file
done

exit 0

Thursday, June 13, 2013

Add Git log property in build.xml (Apache ANT)

Create git.SHA1 property in build.xml file.

<available file=".git" type="dir" property="git.present"/>
<target name="git.info" description="Store git info" if="git.present">

    <exec executable="git" outputproperty="git.SHA1" failifexecutionfails="false" errorproperty="">
        <arg value="log"/>
        <arg value="--pretty=oneline"/>
        <arg value="-n1"/>
    </exec>
    <condition property="git.version" value="${git.SHA1}" else="unknown">
        <and>
            <isset property="git.SHA1"/>
            <length string="${git.SHA1}" trim="yes" length="0" when="greater"/>
        </and>
    </condition>

    <echo message="print git log : " />
    <echo message="${git.SHA1}" />

</target>

Reference:
How to lookup the latest git commit hash from an ant build script

Tuesday, June 4, 2013

Nginx Error - 413 Request Entity Too Large

nginx version: nginx/1.1.19, OS: Ubuntu12.04
Default nginx accepted body size limitation is 1MB.
You can add client_max_body_size in nginx.conf.
This parameter can put in http, server and location sections of configutation file.

Enlarge body size to 10MB

client_max_body_size 10M  

Or just disable it

client_max_body_size 0  

For example enlarge body size to 10MB
Add to http section:

$ sudo vi /etc/nginx/nginx.conf  
http {

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        client_max_body_size 10M;
        # server_tokens off;

        # server_names_hash_bucket_size 64;
        # server_name_in_redirect off;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;
...
}

Or modify server and location section

$ sudo vi /etc/nginx/sites-available/default  
server {
        #listen   80; ## listen for ipv4; this line is default and implied
        #listen   [::]:80 default ipv6only=on; ## listen for ipv6
        client_max_body_size 10M;
        root /usr/share/nginx/www;
        index index.html index.htm;

        # Make site accessible from http://localhost/
        server_name localhost;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to index.html
                try_files $uri $uri/ /index.html;
                # Uncomment to enable naxsi on this location
                # include /etc/nginx/naxsi.rules
                client_max_body_size 0;
        }

        location /doc/ {
                alias /usr/share/doc/;
                autoindex on;
                allow 127.0.0.1;
                deny all;
        }
....
}  

Reload configuration:

$ sudo service nginx reload  

Done!