JavaServer Pages (JSP) Part I

  โดย  ประดับเก่ง

 
  เกริ่นนำ
JavaServer Pages (JSP) เป็น web-scripting เทคโนโลยีคล้ายกับ Netscape server-side JavaScript (SSJS) หรือ Microsoft Active Server Pages (ASP) แต่ผิดกันตรงที่หัวใจของ JSP คือ Java ซึ่งเป็นภาษาที่คอนเซ็ปหลักอยู่ที่ออฟเจ็ค (object-oriented style) ซึ่งช่วยทำให้ง่ายต่อการพัฒนาในโปรเจคใหญ่ ๆ ตลอดจนสามารถนำส่วนประกอบต่าง ๆ กลับมาใช้ได้อีก (software reusable) จุดเด่นที่สำคัญของ JSP คือสามารถทำงานได้โดยไม่ขึ้นอยู่กับผู้ผลิตซอฟแวร์รายใดรายหนึ่งโดยเฉพาะ ซึ่งโดยทั่วไปเทคโนโลยีต่าง ๆ มักจะออกมาในลักษณะของผลิตภัณฑ์จากบริษัทผู้ผลิตแห่งใดแห่งหนึ่ง แต่ JSP ใช้ลักษณะของ specification ซึ่งกำหนดโดย Sun Microsystems ดังนั้นผู้ผลิตซอฟแวร์จึงสามารถอ้างอิง specification ที่กำหนดขึ้น ผลิต JSP Container (ตัวที่ใช้ในการรัน JSP) ขึ้นมาใช้กับแฟลตฟอร์มใดก็ได้ 

JSP Containers
JSP Pages (ไฟล์ที่เขียนขึ้นโดยใช้ JSP script และลงท้ายด้วย .jsp) จะถูกรันโดย JSP Container ซึ่งมักจะเป็นส่วนประกอบที่อยู่ใน Webserver หรือ เป็นตัวแอทออนใน Application Server.  โดยทั่วไป JSP Container จะเป็นตัวรับ request จาก client ส่งผ่านไปยัง JSP Page และส่งค่าที่ได้จากการประมวลผลโดย JSP Page กลับไปยัง client.  JSP Container ที่ใช้กันอยู่มีมาจากหลายค่าย ยกตัวอย่างเช่น GNU JSP, Expresso, Tomcat Jakarta, Resin, Weblogic เป็นต้น 
JSP Container ที่เราจะใช้กันคือ Jasper ซึ่งเป็น JSP Container ที่อยู่ใน Tomcat Servlet Engine จากค่าย apache (www.apache.org) ซึ่งทาง Sun ใช้เป็นตัวอ้างอิงในวงการ JSP โดยในอนาคต Tomcat จะเป็นตัวที่ใช้ในการรัน Servlet แทน Jserv ซึ่งใช้กันอยู่ในปัจจุบัน

การติดตั้ง Tomcat
1) Java Development Kit
โปรแกรม Tomcat ถูกเขียนขึ้นโดยใช้จาว่า ซึ่งจะรันกับ Java Development Kit (JDK) เวอร์ชัน 1.1 และ 1.2 ดังนั้นก่อนจะรัน เราจะต้องติดตั้ง JDK1.xxx ตามแฟลตฟอร์มที่เราใช้เสียก่อน
สำหรับวินโดว์และยูนิกส์ให้ไปดาวโหลดที่ http://java.sun.com/jdk/. (บางครั้ง JDK อาจถูกเรียกว่า SDK)
สำหรับ Linux ให้ไปที่ http://www.blackdown.org/.
สมมุติว่าเราใช้วินโดว์ และติดตั้ง JDK1.2.2 ไว้ที่ D:\jdk1.2.2 ให้เซ็ต PATH=d:\jdk1.2.2\bin\ โดยใช้คำสั่งใน Dos (set PATH=d:\jdk1.2.2\bin\;) หรือ คลิกขวาที่ My Computer แล้วเลือกที่ Environment แทป
ที่เราต้องเซ็ต PATH เพราะ Tomcat จะรันโดยใช้คำสั่ง java.exe ซึ่งอยู่ใน bin ไดเรคทรอรี่ของ JDK

2) Jakarta Tomcat
ทาง apache ยังคงทำการพัฒนา Tomcat ให้มีประสิทธิภาพมากเพื่อใช้เป็นมาตราฐานในอนาคต ดังนั้นถ้าเข้าไปที่เวปไซด์ในส่วนของดาวโหลด เราจะเห็นตัวเลือกในการดาวโหลดได้คร่าว ๆ เป็นสองแบบ คือ แบบ souce files ที่ต้องมาคอมไพล์เองโดยใช้เครื่องมือช่วยที่เรียกว่า Ant ซึ่งเป็นส่วนที่นักพัฒนานิยมดาวโหลดกัน กับแบบที่คอมไพล์แล้ว (binary) พร้อมที่จะรันซึ่งเหมาะสำหรับคนที่อยากนำ Tomcat มาใช้งานอย่างเดียว และเป็นอย่างที่เราจะใช้ ดังนั้น ให้ไปที่ http://jakarta.apache.org/downloads/binindex.html แล้วทำการติดตั้ง

สมมุติว่าเราใส่ Tomcat ไว้ที่ D:\jakarta-tomcat ให้เปิด Command Prompt (MSDos) จาก Application Menu หรือใช้ start->run->type cmd แล้วไปที่ไดเร็คทรอรี่ที่เป็น bin ซึ่งในตัวอย่างก็คือ D:\jakarta-tomcat\bin แล้วรันโดยใช้คำสั่ง D:\jakarta-tomcat\bin> startup หลังจากนั้นให้เปิด browser เพื่อเช็คว่า Tomcat ทำงานถูกต้องหรือเปล่า โดยไปที่ http://127.0.0.1:8080/examples/jsp/ 
Note: http://127.0.0.1:8080 คือ ให้ browser ส่ง request ไปที่เครื่องตัวเอง (localhost) ที่พอร์ต 8080 (Tomcat default port)
ถ้าจะปิด Tomcat ก็ให้พิมพ์ D:\jakarta-tomcat\bin\> shutdown ลงไปแทน

Web Applications 
ก่อนที่เราจะเริ่มเขียน JSP สิ่งหนึ่งที่ควรจะทำความเข้าใจคือโครงสร้างของไดเรคทรอรี่ต่าง ๆ สำหรับไฟล์ของเราใน Tomcat ในสมัยก่อนเราไม่ค่อยได้ให้ความสนใจกับการจัดเรียงไฟล์ต่าง ๆ ซักเท่าไหร่ ข้อเสียที่เห็นได้ชัดคือการยากในการจัดเก็บ และการยากในการย้ายไฟล์จากเซฟเวอร์หนึ่ง ไปยังอีกเซฟเวอร์หนึ่ง เพราะโดยทั่วไปแต่ละ webserver จะมีลักษณะการจัดเก็บไฟล์ไม่เหมือนกัน แต่เมื่อ Java Servlet Specification เวอร์ชั่น 2.2 ออกมา เซฟเวอร์ที่ใช้รัน Servlet v2.2 จะถูกบังคับให้มีต้องการสนันสนุนการจัดเก็บไฟล์แบบหนึ่ง ที่เรียกว่า Web Applicaton 

web application  คือกลุ่มของไดเรคทรอรี่และไฟล์ที่อาจจะประกอบด้วย html, jsp, servlet, javabean และอื่น ๆ ซึ่งอยู่รวมกันในลักษณะของระบบไฟล์ (file system) หรือถูกอัดอยู่ในไฟล์เดียวกันโดยจะเรียกว่า Web Archive (.war) ไฟล์ ซึ่งมีประโยชน์ในการโยกย้ายและติดตั้งจากเซฟเวอร์หนึ่งไปยังอีกเซฟเวอร์หนึ่ง

Tomcat เป็น Servlet Engine ที่สนันสนุน Java Servlet API v.2.2 ดังนั้นไฟล์ JSP ที่เราเขียน จะต้องถูกจัดอยู่ในรูปแบบ Web Application นี้ด้วย

ถ้าใครเคยคลุกคลีกับ webserver จะเห็นว่ามีการใช้ไดเรคทรอรี่ในการจัดกลุ่มของไฟล์ไว้ด้วยกันตามหน้าที่ ซึ่งทาง Tomcat ก็ใช้หลักการเดียวกันในการจัดการ web application หลักการก็คือ แต่ละ web appliction จะถูกจัดให้อยู่ในไดเรคทรอรี่ของตัวเอง ที่เรียกว่า context path ยกตัวอย่างเช่น ถ้า web application ของเราถูกแมปกับ context path ที่ชื่อ webapplication1 เวลาดูจาก URL ก็จะเห็นเป็น http://www.mycompany.com/webapplication1/ เป็นต้น

อย่างที่กล่าวมาข้างต้นว่า web application คือกลุ่มของไดเรคทรอรี่และไฟล์ที่ถูกจัดอยู่รวมกันเป็นชั้น ๆ ดังนั้นถ้าเรามีไฟล์ที่ชื่อ helloworld.html :ซึ่งอยู่บนสุด (top-level) ของไดเรคทรอรี่ใน web application เราก็จะสามารถเรียกออกมาดูได้โดยใช้ URL เท่ากับ http://www.mycompany.com/webapplication1/helloworld.html
ในกรณีที่ไฟล์ไม่ได้อยู่บนสุดของไดเรคทรอรี่ใน web application เราก็ยังสามารถเรียกออกมาได้โดยอ้างอิงไปที่ไดเรคทรอรี่ย่อยนั้น ยกตัวอย่างเช่น ถ้ากลุ่มไฟล์ JSP ถูกเก็บไว้ในไดเรคทรอรี่ถัดลงมาที่ชื่อ jsp เราก็สามารถที่จะเรียกไฟล์มาดูโดยใช้ URL เท่ากับ http://www.mycompany.com/webapplication1/jsp/xxx.jsp 

ส่วนประกอบของ Web Application
web application หนึ่ง ๆ อาจประกอบด้วย
1) Servlet
2) JavaServer Pages
3) Utility Classes
4) ไฟล์ปกติ เช่น HTML, images, sounds 
5) JavaBean

แต่ละอย่างจะถูกเก็บไว้ในไดเรคทรอรี่ต่อไปนี้
1) root directory หรือส่วนบนสุด : ไฟล์ *.html, *.jsp มักจะถูกเก็บไว้ส่วนบนสุดของ web application แต่อาจจะมีไดเรคทรอยีย่อยลงมาจาก root เพื่อเก็บ ไฟล์ images ก็ได้ 

2)  WEB-INF/web.xml : ไฟล์ web.xml เป็นส่วนที่ใช้ในการบรรยายส่วนประกอบต่าง ๆ (Web Application Deployment Descriptor) รวมไปถึงการป้อนค่าเริ่มต้นให้กับโปรแกรมต่าง ๆ ที่บรรจุอยู่ใน web application นี้ ยกตัวอย่างเช่น เราสามารถป้อนค่าเริ่มต้นให้กับ Servlet ได้โดยใส่ค่าเข้าไปใน XML Tag ตรงส่วนที่เรียกว่า <init-param> เป็นต้น
(หาอ่านเพิ่มเติมได้จาก JavaServlet Specification v2.2, http://java.sun.com/products/servlet/)

3) WEB-INF/classes/* : เป็นส่วนที่ใช้ในการเก็บไฟล์ *.class ทั้งหมดที่ใช้ใน web application ซึ่งอาจจะเป็นทั้ง Servlet หรือ ไฟล์ class ธรรมดาที่ไม่ได้อยู่ในรูปของ .jar ก็ได้ ยกตัวอย่างเช่น ถ้าไฟล์ของเราอยู่ใน package ชื่อ com.mycompany.mypackage.MyServlet ไฟล์นี้ก็จะถูกเก็บอยู่ในไดเร็คทรอรี่ WEB-INF/classes/com/mycompany/mypackage/MyServlet.class
(คอนเซ็ปเกี่ยวกับ package สามารถอ่านเพิ่มเติมได้จาก JAVA PACKAGE)

4) WEB-INF/lib/*.jar : ไดเร็คทรอรี่นี้ใช้เก็บไฟล์ .jar ที่เราต้องการใช้สำหรับ web application ซึ่งอาจจะเป็นส่วนที่ได้มาจาก third party หรือเป็นพวก Database driver ก็ได้ ประโยชน์อย่างหนึ่งที่เห็นได้ในการเก็บไลบารี่ต่าง ๆ ไว้ที่ไดเร็คทรอรี่นี้คือ การง่ายต่อการติดตั้ง เหตุผลคือ หลังจาก web application ของเราถูกใส่เข้าไปใน server แล้ว ตัว server จะโหลดไฟล์ที่อยู่ในไดเร็คทรอรี่นี้โดยอัตโนมัติ เราจึงไม่ต้องทำการเซ็ต CLASSPATH ให้ยุ่งยาก

ตัวอย่างง่าย ๆ ของโครงสร้าง web application อาจเป็น
/index.html
/howto.jsp
/feedback.jsp
/images/banner.gif
/images/jumping.gif
/WEB-INF/web.xml
/WEB-INF/lib/jspbean.jar
/WEB-INF/classes/com/mycompany/servlets/MyServlet.class
/WEB-INF/classes/com/mycompany/util/MyUtil.class 

Integration with Tomcat 
การติดตั้ง web application สามารถทำได้ 3 วิธีคือ 
1) นำ web application ที่อยู่ในรูปของกลุ่มไดเรคทรอรี่ (ยังไม่ได้ถูกบีบอัด) ไปใส่ไว้ที่ $TOMCAT_HOME/webapps/.  กรณีนี้ Tomcat จะจัด context path ให้ web application เองโดยอิงจากชื่อ subdirectory ที่บรรจุกลุ่มไดเรคทรอรี่ของ web application ไว้ ยกตัวอย่างเช่น เราใส่ web application ไว้ที่ $TOMCAT_HOME/webapps/myapp/ ดังนั้น context path ที่จะใช้ติดต่อกับ web application นี้ก็คือ myapp วิธีนี้เป็นวิธีที่ง่ายที่สุด และเหมาะสำหรับการพัฒนาก่อนนำไปใช้จริง 

2) นำ web application archive (.war ไฟล์) ไปใส่ไว้ที่ $TOMCAT_HOME/webapps/.  วิธีนี้เหมาะสำหรับการติดตั้ง web application ใน production server ที่ใช้งานจริง โดย Tomcat จะแกะและขยายไฟล์ทั้งหมดที่อยู่ใน .war ไฟล์ออกมาแล้วทำการโหลดเข้าหน่วยความจำ 

3) ในกรณีที่ไดเรคทรอรี่ที่เราพัฒนา web application ไม่ได้อยู่ที่ $TOMCAT_HOME/webapps/ เราก็สามารถบอก Tomcat ให้โหลด web application นี้ได้โดยเข้าไปแก้ที่ $TOMCAT_HOME/conf/server.xml ในส่วนของ <Context> ให้ชี้ไปที่ไดเรคทรอรี่ที่เราเก็บ web application ไว้ เช่น ถ้าเราเก็บ web application ไว้ที่ D:\MyWebApplication\ แล้วอยากให้ชื่อ MyWebApp เป็น context path ในการชี้ไปที่ web application ของเรา เราก็แค่แอท 

<Context path="/MyWebApp" docBase="D:\MyWebApplication\" debug="0" reloadable="true"/> 
ไว้ที่บรรทัดถัดมาจาก Context  path อันสุดท้ายที่มีอยู่แล้ว เป็นต้น 
(สมมุติว่าเราติดตั้ง Tomcat ไว้ที่ ไดเรคทรอรี่ D:\jakarta-tomcat ดังนั้น $TOMCAT_HOME จะเป็น D:\jakarta-tomcat) 

My Hello JSP
คอนเซ็ปของ JSP คือการใส่ Servlet code ลงไปใน HTML ไฟล์โดยผ่านทางสคิปต์ที่ขึ้นต้นด้วย <% และลงท้ายด้วย %> ดังตัวอย่างต่อไปนี้

<HTML>
<HEAD>
<TITLE>My Hello JSP</TITLE>
</HEAD>
<BODY>
<H1>Everybody says 
<% String str = "Hello JSP";
     out.println(str); %>
</H1>
</BODY>
</HTML>

อย่างที่กล่าวมาข้างต้น การที่จะรัน JSP ได้ เราจะต้องจัดไฟล์ต่าง ๆ ให้อยู่ใน web application  ดังนั้นถ้าใครอยากรันทดสอบ ก็ให้สร้างไดเรคทรอรี่ขึ้นมาอันหนึ่ง สมมุติว่าชื่อ D:\MyWebApplication\ โดยไดเรคทรอรี่นี้จะเป็น root directory ของ web application ที่เราจะใช้เก็บไฟล์ JSP ทุกไฟล์ที่เราเขียน หลังจากนั้นให้สร้าง subdirectory ที่ชื่อ jsp แล้วก๊อปปี้โค๊ดข้างบนไปใส่ไว้ในไฟล์ที่ชื่อ hellojsp.jsp แล้วเก็บไว้ที่ subdirectory jsp ที่เราเพิ่งสร้างขึ้น ดังรูป

รูปที่ 1 hellojsp.jsp

ถ้าสังเกตดี ๆ จะเห็นอีก subdirectory ชื่อ Web-inf ให้สร้าง subdirectory นี้ขึ้น แล้วใส่ไฟล์ web.xml ซึ่งเป็นไฟล์ที่ใช้บรรยายส่วนประกอบต่าง ๆ (Web Application Deployment Descriptor) ของ web application ของเราเข้าไป ซึ่งถ้าเปิดดู จะไม่เห็นอะไรมากในตอนนี้
หลังจากนั้นให้ไปแก้ $TOMCAT_HOME/conf/server.xml ไฟล์เพื่อลิงค์  Context Path (สมมุติว่าชื่อ MyWebApp) เข้ากับ web application ของเรา พอเสร็จก็ทำการ startup Tomcat, เปิด browser  แล้วพิมพ์  http://127.0.0.1:8080/(your context)/jsp/hellojsp.jsp ผลก็จะออกมาดังรูป 

รูปที่ 2 hellojsp.jsp in action

เกิดอะไรขึ้นบ้าง  ?
ถ้าดูเผิน ๆ จากทาง browser การเรียกไฟล์ JSP คงดูคล้าย ๆ กับการเรียกไฟล์ HTML ธรรมดา ๆ  แต่ความจริงแล้ว  หลังจาก request จาก client ส่งมาถึงเซฟเวอร์, JSP Container จะทำการแปลงไฟล์ JSP ให้กลายเป็น Servlet  source ไฟล์ (ในกรณีที่ JSP ไฟล์ดังกล่าวถูกเรียกเป็นครั้งแรก) ซึ่งไฟล์ Servlet source ที่ได้จะถูกคอมไพล์เป็น .class เพื่อใช้ในการประมวลผล request ของ client แล้วส่งกลับไปให้เซฟเวอร์ในรูปของ outputStream ซึ่งถูกส่งไปที่ client ในท้ายสุด
หลังจากนั้น ถ้า JSP ไฟล์ดังกล่าวถูกเรียกอีก การแปลงไฟล์หรือคอมไพล์จะไม่เกิดขึ้น เพราะ JSP  Container จะใช้ไฟล์ .class ที่เก็บไว้แล้วประมวลผลแทน  ข้อสังเกตคือ การเรียกไฟล์  JSP ครั้งแรกจะรู้สึกว่าช้า แต่ครั้งถัดไป ๆ จะเห็นได้ว่าเร็วขึ้นมาก เพราะได้มีการลดขั้นตอนต่าง ๆ ที่เสียเวลาไป
ใน JSP Specification 1,1 ยังมีการแนะนำให้ JSP Engine รองรับการโหลดไฟล์ JSP ขึ้นมาคอมไพล์ใหม่แบบอันโนมัติ (Reloading) ซึ่งมักจะเกิดขึ้นในกรณีที่ JSP ไฟล์ได้มีแก้ไข แล้วจะต้องมีการคอมไพล์ไฟล์ Servlet ที่กำลังใช้งานอยู่ใหม่


รูปที่  3 การแปลงและคอมไพล์ JSP ไฟล์

จากข้อความที่อธิบายข้างต้น เราสามารถแบ่งระยะเวลาของไฟล์ JSP หนึ่ง ๆ ออกเป็นสองช่วงคือ 
1) Translation Time คือช่วงเวลาที่ JSP ไฟล์ถูกแปลงให้กลายเป็น Servlet ไฟล์และถูกคอมไพล์ให้กลายเป็น .class ไฟล์ ซึ่งจะเกิดก่อนการรับ request จาก client ครั้งแรก
2) Client Request Time คือช่วงเวลาที่ .class ของ JSP ไฟล์ ทำการรับ request จากแต่ละ client แล้วทำการประมวลผล

JSP Translation
จากตัวอย่างไฟล์ hellojsp.jsp เราจะเห็นว่าโค๊ดจะมีแค่สองส่วนคือ ส่วนที่เป็น HTML code และส่วนที่เป็น Java code ซึ่งถูกบรรจุอยู่ใน <%  ... %> 
ในขั้นตอนของการแปลงไฟล์จาก .jsp เป็น Servlet  souce ไฟล์ (.java) ส่วนที่เป็น HTML code จะถูกเปลี่ยนให้อยู่ในรูปที่เทียบเท่ากับ out.print("HTMLCODE");  ไฟล์ hellojsp.jsp อาจถูกแปลงเป็น .java ได้คร่าว ๆ ดังนี้ 

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class _hellojsp1_xjsp extends HttpServlet {

  public void service(HttpServletRequest request, HttpServletResponse response) 
                                     throws ServletException, IOException {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("<HTML>"); 
    out.println("<HEAD>");
    out.println("<TITLE>My Hello JSP</TITLE>");
    out.println("</HEAD>");
    out.println("<BODY>");
    out.println("<H1>Everybody says, ");

    // Java code inside <% 
    String str = "Hello JSP";
    out.println(str);
    // End of java code %>

    out.println("</H1>");
    out.println("</BODY>");
    out.println("</HTML>");
  }
}

* เพื่อที่จะเข้าใจหลักการทำงานของ JSP ผู้อ่านควรมีความเข้าใจ Java Servlet เป็นพื้นฐาน ถ้ามีเวลาว่าง ผู้เขียนอยากให้ช่วยอ่าน Java Servlet tutorial ซักเล็กน้อยเสียก่อน 

Next >>