ป้ายกำกับ

วันพฤหัสบดีที่ 3 พฤศจิกายน พ.ศ. 2559

JavaScript onload vs onpageshow

บันทึกไว้เพื่อเตือนความจำ

The onpageshow event is similar to the onload event, except that it occurs after the onload event when the page first loads. Also, the onpageshow event occurs every time the page is loaded, whereas the onload event does not occur when the page is loaded from the cache.

Reference

วันจันทร์ที่ 3 ตุลาคม พ.ศ. 2559

Writing Java Desktop Application with Android Studio

วันนี้พอจะมีเวลาและอยากจะเขียนโปรแกรมที่ติดค้างมานานแล้ว เป็นโปรแกรมภาษา Java และจะเขียนแบบง่ายๆเป็น Desktop Application แต่เนื่องจากห่างหายจากการเขียนแบบนี้มานานมาก เมื่อก่อนเคยใช้ NetBeans เขียนเป็นประจำ แต่ว่า ตั้งแต่บริษัท Sun Microsystems ขายกิจการไปให้บริษัท Oracle Corporation (ปีคศ. 2010) มันเหมือนเรารู้สึกเคว้งคว้างไปด้วย ไม่รู้ว่าอนาคตของภาษา Java จะเป็นอย่างไร ตั้งแต่นั้นก็ไม่ได้ใช้ NetBeans อีกเลย ก็หันมาใช้ Eclipse อยู่พักหนึ่ง แต่ก็ไม่ค่อยถูกใจมากนัก สุดท้าย 2-3 ปีมานี้ หันมาจับ Android Studio เสียส่วนใหญ่ วันนี้ก็เลยคิดอยากจะเขียน Java Desktop Application ด้วย Android Studio ขึ้นมา

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

Create New Module

ปกติแล้ว Project ที่เราสร้างด้วย Android Studio นั้น จะเป็น Android Application ซึ่งเมื่อสร้างแล้วจะได้ Module แรกชื่อ app ขึ้นมา ในกรณีนี้ เราจะไม่ใช้ Module นี้ บางความเห็นที่ค้นๆมาบอกว่าให้ลบทิ้ง แต่ผมคิดว่าไม่จำเป็นต้องลบทิ้ง เผื่อว่าเราอาจจะเปลี่ยนใจพัฒนาเป็น Android Application ก็จะได้ไม่ต้องสร้างใหม่ให้ยุ่งยาก ก็ปล่อยไว้แบบนั้นไปก่อน

เมื่อต้องการสร้าง Java Application เราจะใช้วิธีสร้าง Module ใหม่ โดยเลือกเมนู File/New/New Module แล้วเลือก Module แบบ Java Library ดังภาพ
จากนั้นก็กรอก Library name (ชื่อของ Module) Java package name (ชื่อ Package หลัก) และ Java class name ชื่อคลาสไฟล์ที่จะถูกสร้างขึ้นไฟล์แรก

Set Main Class

เมื่อสร้าง Module เสร็จเรียบร้อยแล้ว เราจะได้คลาสไฟล์แรกมาด้วย ถ้าต้องการให้ไฟล์นี้เป็นโปรแกรมหลัก ให้เพิ่มฟังก์ชัน main() ไว้ที่ไฟล์นี้ เช่น
package com.example;

public class MyClass {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}
เมื่อเขียน main() เสร็จแล้วควร Build ด้วยเมนู Build/Make Module ... เสียก่อน จากนั้น เราจะสามารถรันโปรแกรมได้จากเมนู Run/Run... จะปรากฎป๊อบอัพเมนูให้เลือกรัน MyClass ดังภาพ
หากเคยเลือกรันไปแล้วครั้งหนึ่ง จะปรากฏชื่อคลาสตรงปุ่มข้างๆปุ่มรันที่ Toolbar ดังภาพ
แต่หากไม่ปรากฎชื่อคลาสหลักเพื่อจะรันโปรแกรม เราก็สามารถสร้างใหม่เองได้ด้วยการเลือก Edit Configurations... แล้วเพิ่ม Configuration แบบ Application ด้วยการกดปุ่มเครื่องหมาย + ตรงมุมบนซ้าย
กำหนด Name และ Main class ให้ถูกต้อง ก็จะได้ Configuration สำหรับรันโปรแกรมหลัก

Create Jar File with Dependencies

ปกติแล้วเมื่อ Build Module แล้วจะได้ Jar ไฟล์ของโปรแกรมอยู่ในโฟลเดอร์
<โปรเจ็คโฟลเดอร์>/<โมดูลโฟลเดอร์>/build/libs
เช่น AndroidStudioProjects/MyApplication/lib/build/libs เป็นต้น
แต่หากโปรแกรมมีการเรียกใช้ Library อื่นๆด้วย Jar ไฟล์ของ Library ที่จำเป็นต้องใช้งานร่วมกันนั้น อาจจะไม่ได้รวมอยู่ใน Jar ไฟล์เดียวกัน ซึ่งจะทำให้ไม่สะดวกในการนำไปใช้งานก็เป็นได้ ซึ่งเราสามารถเพิ่ม Task สำหรับการสร้าง All-in-One Jar ไฟล์ได้ ด้วยการแก้ไขไฟล์ build.gradle script ของโมดูลนี้ โดยเพิ่ม Task ชื่อ fatJar ดังนี้
version='1.0' 
task fatJar(type: Jar) {
manifest {
        attributes 'Implementation-Title': 'MyClass Jar File Example',
        'Implementation-Version': version,
        'Main-Class': 'com.example.MyClass'
    }
    baseName = project.name + '-all'
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}
 เมื่อเพิ่ม Task ชื่อ fatJar เข้าไปแล้ว และสามารถ Sync Gradle ได้อย่างถูกต้อง เราจะพบชื่อ Task นี้ในหน้าต่างของ Gradle Properties ดังภาพ
ซึ่งสามารถเรียกใช้ได้ด้วยการ Double-Click ที่ชื่อ หรือคลิ๊กเมาส์ปุ่มขวาแล้วเลือกเมนูรันได้เลย ซึ่งผลลัพธ์จะได้ Jar ไฟล์อยู่ในโฟลเดอร์เดียวกับ Jar ไฟล์ปกติ แต่ชื่อไฟล์จะมี -all เพิ่มเข้ามา

อ้างอิง

วันพุธที่ 27 กรกฎาคม พ.ศ. 2559

Bridge setup for KVM

ช่วงนี้ได้ทดลองใช้งาน KVM บน Ubuntu เนื่องจากกำลังทดลองทำ Cluster เล็กๆสำหรับ Apache Cassandra จากเมื่อก่อนที่เคยใช้แต่ VirtualBox มาตลอด เพราะสะดวกดี ใช้ได้ทั้งบน Windows, OS X และ Linux พอมาลอง KVM ก็พบว่าการบริหารจัดการ VM ด้วย Virtual Machine Manager (VMM) ทำให้เราสะดวกมากขึ้น เนื่องจากผมก็ไม่ค่อยจะชอบจำ Command-line เท่าไรนัก และ VMM ก็สามารถ Remote ไปจัดการที่เครื่องใดก็ได้ในขณะที่ VirtualBox นั้น จะไม่มีเครื่องมือที่เป็น Remote มาให้ (มี RemoteBox อยู่แต่ไม่ได้ลองใช้)

หลังจากที่พยายามศึกษาการใช้งาน และทำการ Convert image ไฟล์จาก VDI Format ของ VirtualBox มาเป็น QCOW2 Format ของ Qemu อยู่พักใหญ่ เนื่องจากมีอยู่หลายไฟล์ และแต่ละไฟล์มีขนาดค่อนข้างใหญ่ ทำให้เนื้อที่ฮาร์ดดิสต์เต็ม เลยต้องจัดการข้อมูลเก่าๆอยู่นาน สุดท้ายก็ได้ทดลองรัน VM ขึ้นมาได้ แต่แล้วก็ติดปัญหาว่า VM ไม่สามารถมองเห็นจากภายนอกได้ เนื่องจาก VMM มี UI ให้เราสร้างแต่ Virtual Network ที่เป็น NAT หรือ Router เท่านั้น (เห็นบางแห่งแนะนำว่าเลือกเป็น Bridge ได้ แต่สำหรับ VMM บน Ubuntu 14.04 ที่ผมใช้อยู่ไม่มีเมนูให้เลือก) สุดท้ายก็เลยต้องลงมือทำด้วย Command-line ซึ่งก็เลยเป็นที่มาของการบันทึกนี้เพื่อกันลืมครับ

ขั้นตอน

1.ติดตั้ง bridge-utils
2.แก้ไขไฟล์ /etc/network/interfaces ซึ่งสำหรับกรณีที่ใช้ DHCP ของผมเป็นดังนี้ (ค่า bridge_ports ให้ใส่ชื่อ interface ของ LAN, ค่า post-up ใช้ในกรณีที่ DHCP แจก IP ตาม MAC Address ของ LAN)
auto lo
iface lo inet loopback

auto br0
iface br0 inet dhcp
    bridge_ports eth0
    bridge_stp off
    bridge_fd 0
    bridge_maxwait 0
    post-up ip link set br0 address f4:6d:04:08:f1:5f
3.แก้ไขไฟล์ /etc/sysctrl.conf ดังนี้
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0
4.restart network ใหม่

หมายเหตุ 1

พบว่า UFW มี Bug(UFW blocks libvirt bridged traffic) ทำให้ Guest VM ไม่สามารถใช้งาน Bridge ได้ ซึ่งมีคนแนะนำให้สั่ง
sysctl -p /etc/sysctl.conf
แล้วจะทำให้ใช้งานได้ โดยเราสามารถเพิ่มคำสั่งนี้ไว้ใน /etc/rc.local เพื่อให้ทุกครั้งที่ boot เครื่องใหม่ก็จะใช้งานได้เป็นปกติ

หมายเหตุ 2

มีคนรายงานปัญหาว่า หากคำสั่ง sysctl -p /etc/sysctl.conf แล้วมี error ดังนี้
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: No such file or directory
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-ip6tables: No such file or directory
อาจเกิดจากการที่ module bridge ในบางกรณี ไม่สร้าง net.bridge.* ให้ ซึ่งเขาแนะนำให้ติดตั้ง module br_netfilter เพิ่ม โดยสั่ง
modprobe br_netfilter
หากใช้งานได้ ให้เพิ่มชื่อ br_netfilter ไว้ใน /etc/modules ก็จะใช้งานได้ โดยไม่ต้องสั่ง sysctl -p อีกต่อไป

ตรวจสอบ module br_netfilter ว่ามีหรือไม่ได้ด้วยคำสั่ง
 egrep -r -i "nf-filter" /lib/modules/$(uname -r)/kernel/net/*

อ้างอิง


วันอังคารที่ 22 กันยายน พ.ศ. 2558

การบังคับให้ Browser บันทึกไฟล์

ในบางครั้งที่เราทำลิ้งค์ไปยังไฟล์ใดๆ โดยเฉพาะไฟล์ที่เปิดใช้งานได้ด้วย browser เช่น ไฟล์เสียง และ ไฟล์วีดีโอ แต่ในบางกรณีก็มีข้อจำกัด เช่น server ของเราอาจจะไม่สามารถให้บริการไฟล์ได้อย่างต่อเนื่อง (streaming) หรือผู้ใช้บางรายใช้ browser ที่ไม่รองรับ format ของไฟล์บางชนิด เราจึงอาจจะต้องการให้ผู้ใช้บันทึกไฟล์ไว้ แทนที่จะเปิดดูในขณะนั้น

การจะใช้คำสั่ง JavaScript จัดการให้ browser บันทึกไฟล์อัตโนมัติ ก็จะทำไม่ได้ เนื่องจากจะติดปัญหาเรื่องความปลอดภัย เพราะหากเปิดช่องตรงนี้ไว้ จะทำให้เว็ปที่ไม่ปลอดภัยสามารถแอบปล่อยไวรัสไว้ในเครื่องของผู้ใช้งานได้ง่ายๆ

วิธีการที่ดีที่สุดคือการใช้ Content-Disposition ใน header ของ HTTP โปรโตคอล โดยวิธีการนี้ต้องอาศัยการเขียนโปรแกรมทางฝั่ง server เพื่อรองรับการตอบกลับแบบนี้ ซึ่งทำได้ทุกภาษาเช่น PHP, ASP.NET, Perl/CGI หรือ JSP เป็นต้น แต่ในกรณีทั่วไปที่เราใช้บริการฟรี server เราอาจจะไม่สามารถเขียนโปรแกรมใดๆได้ จึงอาจจะเป็นข้อจำกัดของวิธีการนี้

รูปแบบของ Content-Disposition

รูปแบบของ Content-Disposition สำหรับ HTTP ได้กำหนดไว้ใน RFC6266 (อ้างอิง 1) โดยสรุปคือ
Content-Disposition: <value> [; <param>]
โดยที่ <value> มีค่าเป็น (ไม่สนใจตัวอักษรใหญ่-เล็ก)

  1. inline หมายถึง แสดงผลอัตโนมัติ โดยทั่วไปไม่ต้องระบุก็จะแสดงอัตโนมัติเป็นปกติ
  2. attachment หมายถึง ผู้ใช้ควบคุมการแสดงผล
ซึ่งหมายความว่า หากต้องการบังคับให้ผู้ใช้บันทึกไฟล์ เราจะต้องกำหนด Content-Disposition มีค่าเป็น attachment นั่นเอง


ส่วน <param> มีค่าเป็น (ไม่สนใจตัวอักษรใหญ่-เล็ก)

  1. filename = <name> หมายถึงชื่อไฟล์ <name>
  2. filename* = <name> หมายถึงชื่อไฟล์ <name> ที่เข้ารหัสแบบที่ไม่ใช่ ISO-8859-1
หมายเหตุ: บาง borwser อาจจะไม่รองรับค่า filename* จึงควรจะส่งค่าทั้งสองแบบ
Content-Disposition: attachment;
                          filename="EURO rates";
                          filename*=utf-8''%e2%82%ac%20rates

ตัวอย่าง

ภาษา PHP

<?php
header('Content-disposition: attachment; filename=movie.mpg');
header('Content-type: video/mpeg');
?>

ภาษา JSP

<% //Set the headers.
response.setHeader("Content-Disposition", "attachment; filename=downloaded.pdf");
response.setContentType("application/x-download");
%>

อ้างอิง

  1. IETF, RFC6266
  2. W3, HTTP/1.1 Appendices
  3. IANA, Content Disposition Values and Parameters

วันศุกร์ที่ 12 มิถุนายน พ.ศ. 2558

ทดลองติดตั้ง Team Foundation Server 2013

หลายปีมาแล้วที่ผมพยายามจะทดลองใช้ Team Foundation Server(TFS) ตั้งแต่สมัย Visual Studio 2008 แต่สุดท้ายก็เลิกใช้ เนื่องจากมันใช้ทรัพยากรค่อนข้างมาก ซึ่งตอนนั้นผมไม่ได้มี Windows Server ดูแลประจำอยู่ ก็เลยหันไปใช้ Subversion บน Ubuntu แทน และเนื่องจากตอนนั้นก็ต้องการเพียงแค่เรื่องของ Version Control เท่านั้นด้วย ก็เลยลงตัว แต่ผมก็ยังติดตามดูความสามารถของโปรแกรมตัวนี้อยู่เรื่อยๆ เพราะคิดว่าสักวันคงต้องได้ใช้มันแน่ๆ

ผ่านไปหลายปี หลังจากดูๆมานาน และหลายปีแล้วที่มหาวิทยาลัยสงขลานครินทร์ลงทุนซื้อลิขสิทธิ์ซอฟต์แวร์ของค่าย Microsoft ไว้ทุกๆปี ก็เลยคิดว่าน่าจะเอามาใช้ให้คุ้มค่า 2-3 วันมานี้ก็เลยได้เวลาทดลองติดตั้งดู ซึ่งผมได้ทดลองติดตั้งไว้ใน Virtual Machine โดยลืมไปว่ามันค่อนข้างใช้ทรัพยากรนะ ทีแรกผมกำหนด RAM ไว้เพียง 4GB พอเริ่มต้นติดตั้ง ก็ต้องหยุดเพื่อเพิ่ม RAM เข้าไปอีก จะใช้มากหรือน้อยก็อยู่ที่ว่าเราจะเลือกติดตั้งแบบใด ซึ่งมีอยู่ 3 แบบคือ
  1. Basic สำหรับติดตั้งบนเครื่องระดับ Windows Client เช่น Windows 8.1 เป็นต้น โดยไม่จำเป็นต้องมี Microsoft SQL Server แต่มันจะติดตั้งรุ่น Express ให้ และจำกัดขนาดทีมได้ไม่เกิน 5 คน ซึ่งจะเทียบเท่ากับ Team Foundation Server Express ที่ Microsoft ให้ดาวโหลดใช้ได้ฟรี
  2. Standard สำหรับติดตั้งบนเครื่องระดับ Windows Server เช่น Windows Server 2012 เป็นต้น โดยติดตั้งทุกอย่างอยู่บนเครื่องเดียว ซึ่งจะต้องมี Microsoft SQL Server อยู่ด้วย (ต้องการ RAM อย่างน้อย 8GB) และให้เลือกทำงานร่วมกับ SharePoint ได้ด้วย (ต้องการ RAM อย่างน้อย 10GB) ซึ่งผมเลือกที่จะไม่ลง SharePoint (ยังไม่อยากปวดหัว เอาไว้โอกาสหน้านะ)
  3. Advance สำหรับติดตั้งบนเครื่องระดับ Windows Server โดยที่ SQL Server หรือ SharePoint  Server อยู่ที่เครื่องอื่น
สำหรับการติดตั้งแบบ Standard นั้น เราควรจะติดตั้ง SQL Server ให้เรียบร้อยเสียก่อน โดยจะต้องเลือกติดตั้ง Reporting Service ไว้ด้วย ซึ่งถ้าหากเราไม่ได้ใช้ SharePoint ก็จะต้องเลือกติดตั้ง Reporting Service แบบ Native Mode 

เมื่อเสร็จสิ้นการติดตั้ง เราจะสามารถเข้าถึง TFS ได้ที่ URL http://<servername>:8080/tfs

Eclipse Plug-In

ส่วนหนึ่งที่ผมตัดสินใจทดลองติดตั้ง TFS ดูอีกสักครั้ง ก็เพราะค้นพบว่ามี Eclipse Plug-In สำหรับใช้ทำงานร่วมกับ TFS อยู่ด้วย ซึ่งหากทีมพัฒนาซอฟต์แวร์ของเราใช้ IDE คนละตัวกัน โดยเฉพาะ IDE สองขั้วตรงกันข้ามอย่าง Visual Studio และ Eclipse แต่ถ้ายังสามารถทำงานร่วมกันในระบบเดียวได้ ก็จะทำให้การทำงานราบรื่นขึ้น

สำหรับการติดตั้ง Plug-In นี้ไม่ได้ยุ่งยากอะไรเลย เพียงแค่ Add Repository ของ Plug-In ไว้ โดยใส่ URL คือ http://dl.microsoft.com/eclipse/tfs ดังภาพ

จากนั้นก็เลือกติดตั้ง
เท่านี้ก็ติดตั้งได้เรียบร้อย

Netbeans Plug-In

สำหรับผู้ที่ใช้ Netbeans นั้น เท่าที่ผมค้นมา พบว่ามีการร้องขอให้ทาง Microsoft ทำ Plug-In ออกมาเหมือนกัน ตั้งแต่เมื่อปี 2010 แล้ว แต่ก็ไม่ได้รับการตอบสนอง และมีโครงการ NetBeans Plugin for Team Foundation Server ที่พัฒนามานานแล้วแต่ยังไม่เสร็จสักที สุดท้ายก็มีกระทู้ใน StackOverflow แนะนำให้เลี่ยงไปใช้วิธีอื่น เช่น ใช้ SVN Bridge เป็นต้น แต่ก็จะได้ความสามารถเพียงแค่เรื่องของ Version Control เท่านั้น

สรุป

สำหรับผู้ที่สนใจจะนำเอากระบวนการพัฒนาซอฟต์แวร์ตามหลักการมาใช้ในทีม เครื่องมืออย่าง Team Foundation Server นี้ก็เป็นสิ่งที่น่าสนใจไม่น้อย หากไม่มีงบเพียงพอที่จะซ์้อ สามารถใช้รุ่น Express ก็ได้ และเดี๋ยวนี้ทาง Microsoft ก็มีบริการแบบ Online ที่เรียกว่า Visual Studio Online ให้สามารถทำงานได้เช่นกัน