เกริ่นนำ
ในการเขียน Java Application (Standalone) หรือ Servlet (Server side)
ที่ต้องมีการ initialize ค่าตัวแปรต่าง ๆ ที่เราไม่สามารถใส่ค่าที่แน่นอนได้ในช่วง
compile time เช่น ชื่อของไดร์เวอร์ที่โปรแกรมใช้สำหรับติดต่อกับเดต้าเบส,
ค่าของ host ที่โปรแกรมเราจะติดต่อ หรือแม้กระทั่งค่าของ port ที่โปรแกรมจะใช้
สิ่งหนึ่งที่จะขาดไม่ได้คือ properties file.
Properties file คือ ไฟล์ที่ใช้เก็บชื่อ*และค่า (key-value
pairs) ของตัวแปรต่าง ๆ ที่เราอาจต้องทำการแก้ไขในภายหลัง ซึ่งมักจะเกิดขึ้นในช่วงหลังจากที่เราได้ทำการคอมไพล์ไฟล์
java ให้กลายเป็น .class แล้ว. properties file จะช่วยให้เราสามารถเปลี่ยนค่าตัวแปรต่าง
ๆ ก่อนที่จะทำการรันโปรแกรม ซึ่งค่าของตัวแปรเหล่านี้จะถูกโหลดเข้าไปในโปรแกรมในช่วง
runtime แทนที่จะต้อง hardcode ลงไปในโปรแกรม และต้องทำการ recompile ใหม่ทุกครั้งที่มีการแก้ไขค่าตัวแปรเหล่านี้
* ชื่อ ในที่นี้ก็คือ กุญแจ (key) ใน Hashtable เพราะชื่อและค่าของตัวแปรต่าง
ๆ ที่เก็บอยู่ใน properties file จะถูกตีความและเก็บไว้ใน java.util.Properties
ซึ่งเป็น subclass ของ java.util.Hashtable
การเขียน Properties file
จริง ๆ แล้ว properties file ก็คือ text ไฟล์ธรรมดา ๆ แต่นักพัฒนาส่วนมากมักนิยมใช้นามสกุลที่เป็น
xxx.properties เพื่อความชัดเจนในการจำแนกไฟล์นี้ออกจากไฟล์อื่น
ๆ (ในปัจจุบัน properties file ได้เริ่มมีการเปลี่ยนลักษณะการเก็บ โดยใช้
XML format แทน ซึ่งจะกล่าวถึงในบทความต่อ ๆ ไป)
โดยทั่วไป หนึ่งบรรทัดใน properties file จะใช้เก็บชื่อและค่า (key-value
pairs) ของหนึ่งตัวแปร ซึ่งแต่ละบรรทัดจะจบด้วย line terminator (\n,
\r หรือ \r\n) โดยจะถูกใส่ลงไปอัตโนมัติโดย text editor เมื่อเรากด enter
เพื่อขึ้นบรรทัดใหม่. บรรทัดไหนที่ว่าง หรือขึ้นต้นด้วย
#
หรือ ! จะถูกข้ามไปโดยอัตโนมัติ ซึ่งปกติเราจะใช้สำหรับการเขียน
comment
properties file จะถูกตีความโดยคลาส
java.util.Properties
สิ่งที่ใช้เป็นตัวบอกคลาสดังกล่าว เพื่อแยกแยะชื่อและค่าของตัวแปร ซึ่งถูกเก็บอยู่ในแต่ละบรรทัด
ออกจากกันก็คือ
=,
:,
หรือ
whitespace(" ") ยกตัวอย่างเช่น
# properties file for jarticles website
serverName=www.jarticles.com
port=7001
protocol=https
หรือ
# properties file for jarticles website
serverName:www.jarticles.com
port:7001
protocol:https
หรือ
# properties file for jarticles website
serverName www.jarticles.com
port 7001
protocol https
หรือแม้กระทั่ง
# properties file for jarticles website
serverName=www.jarticles.com
port:7001
protocol https
Note: คลาส java.util.Properties จะแยกชื่อและค่าออกจากกันโดยใช้=,
:,
หรือ
whitespace(" ") ที่เจอตัวแรกเท่านั้น
ถ้าปรากฎว่ามีตัวพวกนี้ปรากฎขึ้นมาอีกหลังจากนั้น ตัวที่เหลือพวกนี้จะกลายเป็นส่วนหนึ่งของค่า
(value) ของตัวแปรไป ยกตัวอย่างเช่น
# properties file with more than 1 "="
myhost=http://127.0.0.1=localhost
จะได้ (key, value) = ("myhost", "http://127.0.0.1=localhost")
ซึ่งในกรณีนี้ เราสามารถนำมาใช้ประโยชน์ในการเก็บตัวแปรที่มีค่ามากกว่าหนึ่งค่าได้
โดยการแยก http://127.0.0.1=localhost ออกจากกันอีกทีหนึ่ง โดย search ไปที่ตัว
= แล้วแยกสองค่านี้ออกจากกัน
ในกรณีที่ชื่อและค่าของตัวแปรไม่สามารถเขียนอยู่ในบรรทัดเดียวได้ หรือถ้าต้องการเขยิบลงไปอีกบรรทัดหนึ่ง
เราก็สามารถใช้
\ ใส่ลงไปที่ท้ายสุดของบรรทัดแรก
เพื่อเป็นตัวบอกว่าบรรทัดต่อไปยังคงเป็นส่วนหนึ่งของบรรทัดปัจจุบัน (โดยช่องว่างหลัง
\
และก่อนตัวอักษรตัวแรกของบรรทัดต่อไป จะถูกตัดออกไปทั้งหมด) ยกตัวอย่างเช่น
# properties file for jarticles website
serverName=www.jarticles.com
port=\
7001
protocol=https
จะได้
(key, value) = ("serverName", "www.jarticles.com")
(key, value) = ("port", "7001")
(key, value) = ("protocol", "https");
ในกรณีที่มีช่องว่างที่อยู่ข้างหน้าหรือข้างหลัง ชื่อ (key) และ ค่า (value)
ของตัวแปร ช่องว่างเหล่านี้จะถูกตัดออกไปโดยอัตโนมัติ ยกตัวอย่างเช่น
# properties file for jarticles website
serverName=www.jarticles.com
port
= 7001
protocol=https
จะได้
(key, value) = ("serverName", "www.jarticles.com")
(key, value) = ("port", "7001")
(key, value) = ("protocol", "https")
* ตรงบรรทัด port=7001 จะสังเกตเห็นว่ามีช่องว่างอยู่ข้างหน้าและหลังคำว่า
port และ 7001 แต่จะถูกตัดออกไป
อีกสองตัวอย่าง ซึ่งนำมาจาก JDK API
Truth = Beauty
Truth:Beauty
Truth
Beauty
3 อันข้างบนจะได้ผลเหมือนกันคือ
(key, value) = ("Truth","Beauty")
หรือ
fruits
apple, banana, pear, \
cantaloupe, watermelon, \
kiwi, mango
จะได้ (key, value) = ("fruit", "apple, banana,
pear, cantaloupe, watermelon, kiwi, mango")
Advance properties file
ถ้าใครเคยดู properties file ของ apache
webserver หรือ jserv servlet engine
จะเห็นอะไรคล้าย ๆ อย่างนี้
support .Z .z .tgz .gz .zip
servlet.dbdemo.initArgs=\
username=paul,\
password=Youtellme1,\
owner=www.jarticles.com
หลังจากที่
java.util.Properties ทำการตีความแล้ว
จะได้ผลดังนี้
(key, value) = ("support", ".Z .z .tgz .gz
.zip")
(key, value) = ("servlet.dbdemo.initArgs",
"username=paul,password=Youtellme1,owner=www.jarticles.com")
เราจะเห็นว่า ใน value จะประกอบด้วยค่าหลาย ๆ ค่า
ซึ่ง format จะขึ้นอยู่กับนักพัฒนาที่ออกแบบระบบนั้น ๆ ในการดึงค่าทั้งหมดออกมา
เราจะต้องทำการตีความ value ที่ได้เอง ซึ่งจะทำได้โดยใช้
java.util.StringTokenizer
(ตัวอย่างการเขียนจะอยู่ในส่วนของ
programming ข้างล่าง)
How to program
ในการเขียนโปรแกรมเพื่อโหลดชื่อและค่าของตัวแปรต่าง ๆ ที่อยู่ใน propeties
file เข้ามาในโปรแกรม มีขั้นตอนหลัก ๆ อยู่ 2 ขั้นดังนี้ คือ
1) โหลด properties file เข้ามาอยู่ในรูปของ InputStream
2) ใช้ java.util.Properties.load(InputStream input) อ่าน InputStream
ที่ได้เข้ามาที่คลาส Properties เพื่อทำการตีความ
สมมุติว่าเรามีไฟล์ชื่อ server.properties
ซึ่งเก็บค่าตัวแปร 4 ตัว ดังต่อไปนี้
# our first properties file
ServerType=standalone
Timeout=300
KeepAlive=On
MaxKeepAliveRequests=100
โปรแกรมที่ง่ายที่สุด สำหรับโหลดไฟล์ดังกล่าว ก็จะเป็นอย่างโปรแกรมข้างล่าง
(PropertiesEx1.java)
import java.io.*;
import java.util.Properties;
public class PropertiesEx1 {
public static void main(String args[]) {
Properties props = null;
try {
FileInputStream input = new
FileInputStream("D:\\Mystuff\\server.properties");
props = new Properties();
props.load(input);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("ServerType=" + props.getProperty("ServerType"));
System.out.println("Timeout=" + props.getProperty("Timeout"));
System.out.println("KeepAlive=" + props.getProperty("KeepAlive"));
System.out.println("MaxKeepAliveRequests=" + props.getProperty("MaxKeepAliveRequests"));
}
}
*ส่วนที่เป็น HighLight เป็นส่วนที่แสดง
2 ขั้นตอนหลัก อย่างที่กล่าวมาข้างต้น
ก่อนอื่น เราต้องการโหลดไฟล์ server.properties ซึ่งอยู่ที่ D:\Mystuff\server.properties
ดังนั้นเราจึงใช้ FileInputStream ซึ่งเป็น subclass ของ InputStream
ในการอ่านไฟล์ จะสังเกตเห็นว่า String ที่เป็น input argument
สำหรับ FileInputStream จะเป็น "D:\\Mystuff\\server.properties"
แทนที่จะเป็น
"D:\Mystuff\server.properties"
เหตุผลคือ
ตัว \(backslash)
เป็นตัวที่ต้องมีการ escape sequence ในจาว่า ดังนั้นเราต้องใช้ \\
แทน
จากนั้นเราทำการสร้าง object ของ java.util.Properties
แล้วทำการโหลด server.properties ผ่านฟังก์ชั่น Properties.load(InputStream
inStream) โดยเมื่อไฟล์ของเราผ่านการตีความโดยคลาส Properties แล้ว เราสามารถอ่านค่าต่าง
ๆ ที่อยู่ในไฟล์ server.properties โดยผ่านฟังก์ชั่น Properties.getProperty(String
key)
ข้อควรจำคือ ในการอ่านค่าต่าง ๆ โดยใช้ฟังก์ชั่น
getProperty(String
key) เราจะต้องใช้ key ที่เหมือนกับชื่อของตัวแปรที่อยู่ใน properties
file ของเรา (case sensitive) เพราะ java.util.Properties เป็น subclass ของ
Hashtable ยกตัวอย่างเช่น
System.out.println("ServerType=" + props.getProperty("ServerType"));
จะได้ ServerType=standalone
แต่ System.out.println("ServerType=" + props.getProperty("serverType"));
จะได้ ServerType=null
ถ้าในกรณีที่ server.properties อยู่ package เดียวกับคลาสที่ใช้ในการอ่านตัวมัน
เช่น D:\Mystuff\PropertiesEx1.class และ D:\Mystuff\server.properties เราก็จะไม่ต้อง
hardcode ที่อยู่ของไฟล์ server.properties โดยให้ FileInputStream
โหลดที่
FileInputStream input = new FileInputStream("server.properties");
ซึ่ง default คือ ให้โหลดที่ current directory
ที่ PropertiesEx1.class อยู่แทน
java.util.StringTokenizer
บางทีในตัวแปรหนึ่ง เราอาจต้องการเก็บค่ามากกว่าหนึ่งค่า
เช่นตัวแปร support ที่อยู่ในไฟล์ support.properties
# support.properties file
support .Z .z .tgz .gz .zip
จะเห็นว่า ตัวแปร support จะมีค่า 5 ค่า
คือ .Z, .z, .tgz, .gz และ .zip
ขั้นตอนการเขียนโปรแกรมให้อ่าน support.properties
สามารถทำได้ดังนี้คือ
1) อ่านไฟล์ server.properties เข้ามาที่ FileInputStream
เหมือนปกติ
2) ให้คลาส Properties ทำการโหลด
และตีความ
3) หลังจากขั้นตอนที่สอง จะได้ (key, value)
ออกมาเป็น ("support", ".Z .z .tgz .gz .zip") ซึ่งทั้ง 5 ค่าของ
support
ยังติดกันอยู่โดยมี
"
" เป็นตัวขั้น ดังนั้นให้ใช้
java.util.StringTokenizer แยกค่าดังกล่าวออกจากกัน
โดยเซ็ต delimiter = " " ดังโปรแกรมข้างล่าง (PropertiesEx2.java)
import java.io.*;
import java.util.Properties;
import java.util.StringTokenizer;
public class PropertiesEx2 {
public static void main(String args[]) {
Properties props = null;
try {
FileInputStream input = new FileInputStream("support.properties");
props = new Properties();
props.load(input);
} catch (Exception e) {
e.printStackTrace();
}
String supportStr = props.getProperty("support");
StringTokenizer st = new StringTokenizer(supportStr,
" ");
while (st.hasMoreTokens()) {
System.err.println("File format
supported=" + st.nextToken());
}
}
}
StringTokenizer จะทำการแยกสตริง supportStr ออกเป็นส่วน
ๆ โดยใช้ " " เป็นตัวแบ่ง ซึ่งในกรณีของเรา จะได้สตริง 5 ตัว แต่ละตัวที่ได้ก็คือ
ค่าทั้ง 5 ของ support
ในการแยกสตริงออกจากกันโดยใช้ StringTokenizer เราสามารถดูว่ายังมีสตริงเหลือจากการแยกอยู่หรือเปล่าโดยใช้ฟังก์ชั่น StringTokenizer.hasMoreTokens() ซึ่งถ้า return ค่าที่เป็น true
ออกมา เราก็สามารถอ่านค่าของสตริงตัวที่เหลือนั้นได้ โดยใช้ฟังก์ชั่น StringTokenizer.nextToken()
ซึ่งค่าที่ออกมาจะอยู่ในรูปของ String
ตัวอย่างอีกอันของ advance properties file ที่เคยกล่าวมาข้างต้น
servlet.dbdemo.initArgs=\
username=paul,\
password=Youtellme1,\
owner=www.jarticles.com
จะเห็นว่า servlet.dbdemo.initArgs ไม่ได้เป็นแค่
key ที่เก็บค่าเพียง 1 ค่า แต่ตัวมันเองกลับเป็นตัวที่เก็บชื่อและค่าอีก 3
คู่เอาไว้ ซึ่งก็คือ
servlet.dbdemo.initArgs => username=paul
=> password=Youtellme1
=> owner=www.jarticles.com
ขั้นตอนการเขียนโปรแกรม เพื่อทำการตีความ อาจเป็นดังนี้คือ
1) แยก (key, value) โดยใช้ Properties.load(InputStream
inStream) ออกมาเป็น ("servlet.dbdemo.initArgs","username=paul,password=Youtellme1,owner=www.jarticles.com")
ก่อน
2) หลังจากนั้น ให้ใช้ StringTokenizer แยกชื่อและค่าออกมา
โดยใช้ตัว "," เป็นตัวแยก ก็จะได้
string1 = "username=paul"
string2 = "password=Youtellme1"
string3 = "owner=www.jarticles.com"
3) ทำการแยก key, value ของแต่ละ string
ออกจากกันโดย StringTokenizer โดยใช้ตัว "=" เป็นตัวแยกอีกที ซึ่งจะได้
(key, value) = ("usernameusername", "paul")
(key, value) = ("password, "Youtellme1")
(key, value) = ("owner", "www.jarticles.com")
ตัวอย่างของโปรแกรมที่ใช้โหลดไฟล์ servletInfo.properties
จะเป็นอย่างข้างล่างนี้ (PropertiesEx3.java)
import java.io.*;
import java.util.*;
public class PropertiesEx3 {
public static void main(String args[]) {
Properties props = null;
Vector servletList = new Vector();
try {
FileInputStream input = new FileInputStream("servletInfo.properties");
props = new Properties();
props.load(input);
} catch (Exception e) {
e.printStackTrace();
}
for (Enumeration e = props.keys() ; e.hasMoreElements() ;)
{
// key = "servlet.dbdemo.initArgs"
// value = "username=paul,password=Youtellme1,owner=www.jarticles.com"
String key = (String) e.nextElement();
String value = (String) props.get(key);
ServletInfo servletInfo = new ServletInfo(key);
StringTokenizer st = new StringTokenizer(value,
",");
String nvPair = null;
while ( st.hasMoreTokens() ) {
// nvPair => username=paul or password=Youtellme1
or owner=www.jarticles.com
nvPair = st.nextToken();
StringTokenizer st1 =
new StringTokenizer(nvPair, "=");
String tmpName = null;
String tmpValue = null;
int i=0;
while ( st1.hasMoreTokens() ) {
if (i == 0) {
tmpName = st1.nextToken();
i++;
} else {
tmpValue = st1.nextToken();
}
}
servletInfo.addProperty(tmpName, tmpValue);
}
servletList.add(servletInfo);
}
int vSize = servletList.size();
if (vSize > 0) {
ServletInfo servletInfoTemp = null;
for (int i=0; i<vSize; i++) {
servletInfoTemp = (ServletInfo) servletList.elementAt(i);
servletInfoTemp.listProperties();
}
}
}
}
class ServletInfo {
String servletName = null;
Hashtable props = null;
public ServletInfo(String name) {
servletName = name;
props = new Hashtable();
}
public void addProperty(String name, String value) {
props.put(name, value);
}
public void listProperties() {
if ( props.isEmpty() ) {
System.out.println("ServletInfo: " + servletName +
" has no property");
} else {
String key = null;
System.out.println("ServletInfo: " + servletName +
" has propertie(s)...");
for (Enumeration e = props.keys() ; e.hasMoreElements()
;) {
key = (String) e.nextElement();
System.out.println("(key,
value) = (\"" + key + "\",\"" + props.get(key) + "\")");
}
}
}
}
Copyright (C)
2000 www.jarticles.com.
|