Showing posts with label Easy Servlet. Show all posts
Showing posts with label Easy Servlet. Show all posts

December 25, 2011

ကလပ်စ်လုဒ်ဒါ (ClassLoader)

အီးဇီးဆာဗလက် ခေါင်းစဉ်ဖြင့် ဆာဗလက် အခြေခံ အကြောင်းများကို ဖော်ပြလာခဲ့သည်မှာ ယခုဆိုလျှင် ၉ကြိမ်မြောက်ကို ရောက်ရှိခဲ့ပြီ ဖြစ်၏။ ရေးမည်ဆိုလျှင် အများကြီး ကျန်ရှိပါသေးသည်။ သို့ပါသော်လည်း အခြားသော အကြောင်းအရာများကိုလည်း ဆက်ချင်ပါသေးသောကြောင့် အီးဇီးဆားဗလက်ကို ဤနေရာတွင် တခန်းရပ်ပါမည်။ နောက်ဆုံးဆိုတော့ ရွေးရခက်ပါသည်။ Security အကြောင်းကို ပဲဆက်ရေးရင်ကောင်းမလား၊ Database နဲ့ပတ်သက်တဲ့ အကြောင်းကိုရေးရင် ကောင်းမလား အမျိုးမျိုးစဉ်းစား ခဲ့ပါသည်။

Security နှင့်ပတ်သက်ပြီး၊ အခြားသော Java EE အကြောင်းဖော်ပြရင်း၊ နောက်ပြီး Security ခေါင်းစဉ်အနေဖြင့်လည်း ဖော်ပြချင်ပါသဖြင့်၊ အခြေခံ အီးဇီး ဆာဗလက်တွင် တကူးတက ဖော်ပြရန်မလိုပေ။ Database ကလည်း အရင်ဆုံး ဆာဗလက်ဘက်မှ မဟုတ်ပဲ အရင်ဆုံး အခြေခံ SQL၊ Database မှစ၍ JDBC၊ JDO နှင့် JPA အစရှိသည့်Java နှင့် ပတ်သက်သော Database အကြောင်းအရာများကို နောက်နှစ်ထဲတွင် ဖော်ပြရန်လည်း စီစဉ်ထားပါသည်။

ဆာဗလက်အတွက် ဘာများကျန်သေးသလဲဟု စဉ်းစားရာတွင်၊ လွန်စွာမှအရေးပါသော Multithread အကြောင်းကို သွားသတိရပါသည်။ ဆာဗလက်သည် Multithread ကို အခြေခံ၍အသုံးပြုထား၏။ သတိမမှုပဲ ရေးမိပါက မမျှော်လင့်သော ဒုက္ခများ ကျရောက်တတ်ပေသည်။ ထို့အတွက် ဤနေရာတွင် ClassLoader အကြောင်းကို ဖော်ပြပြီး ဆာဗလက်ကိုရေးသားလျှင် သတိထားသင့်သည့် အကြောင်း အရာများကို ဖော်ပြသွားပါမည်။

ကလပ်စ်လုဒ်ဒါ (ClassLoader)

ကလပ်စ်လုဒ်ဒါသည် ကလပ်စ်တစ်ခုအား မံမိုရီအပေါ် ခေါ်ယူသည့်အခါတွင်၎င်း၊ ထိုကလပ်စ်မှ အသုံးပြုမည့် အချက်အလက် (resource) များကို ရှာဖွေသည့် အခါတွင်၎င်း အသုံးပြုသော အော့ဘဂျက် တစ်ခုဖြစ်ပြီး၊ java.lang.ClassLoader အား အမွေဆက်ခံ၍ ရေးသားထားသော ကလပ်စ်၏ အင်စတန့်စ် တစ်ခုဖြစ်၏။ Java ကလပ်စ်များအားလုံးသည် အပလီကေးရှင်း စတင်ချိန်တွင်၊ ကလပ်စ်လုဒ်ဒါများ၏ ခေါ်ယူမှု့ကြောင့် မံမိုရီအပေါ်ကို ရောက်ရှိလာပြီး၊ ထိုကလပ်စ်များမှလည်း မိမိအားခေါ်ယူသည့် ကလပ်စ် လုဒ်ဒါများကို သိရှိနိုင်ပါသည်။ အဘယ်ကြောင့်ဆိုသော် Java တွင် အသုံးပြုသော ကလပ်စ်များ အားလုံးသည် java.lang.Class အား အမွေဆက်ခံထားကြပြီး၊ java.lang.Class ၏ getClassLoader မက်သတ်ကို အသုံးပြုခြင်းအားဖြင့် မိမိအား ခေါ်ယူခဲ့သော ကလပ်စ်လုဒ်ဒါကို သိရှိနိုင်ခြင်းဖြစ်၏။

ကလပ်စ်လုဒ်ဒါသည် သစ်ပင် (Tree) ဖွဲ့စည်းပုံကို အသုံးပြုထားပြီး၊ ပတ်သက်ရာ မိဘ လုဒ်ဒါတစ်ခုမှ အမွေဆက်ခံ ထားနိုင်ပါသည်။ ကလပ်စ်လုဒ်ဒါအားလုံး၏ ပင်မကလပ်စ်လုဒ်ဒါအား Bootstrap Class Loader ဟုခေါ်ဆိုပြီး၊ Java ဒြပ်မဲ့စက် (JVM) တွင် တိုက်ရိုက်ရေးသားထားပြီး၊ အပလီကေးရှင်းတစ်ခုအား စတင် အလုပ်လုပ်စေသည့်အခါတွင် ဦးစွာခေါ်ယူမည့် ကလပ်စ် လုဒ်ဒါဖြစ်၏။ Bootstrap ကလပ်စ် လုဒ်ဒါသည် Java ၏ အခြေခံ လိုင်ဘရီကလပ်စ်များ၊ jre/lib/ext ၏ အောက်တွင် ဖြည့်စွက်ထားသော ပြင်ပလိုင်ဘရီ များမှ ကလပ်စ်များကို ခေါ်ယူရာတွင် အသုံးပြုနိုင်ပါသည်။

Bootstrap ကလပ်စ်လုဒ်ဒါ၏ အောက်တွင် တည်ရှိသည်မှာ၊ စစ္စတမ် ကလပ်စ်လုဒ်ဒါဖြစ်ပြီး Bootstrap ကလပ်စ် လုဒ်ဒါ၏ တစ်ခုတည်းသော သားသမီးကလပ်စ်လုဒ်ဒါ (Sub ClassLoader) ဖြစ်၏။ စစ္စတမ်ကလပ်စ် လုဒ်ဒါသည် CLASSPATH အတွင်းရှိ ကလပ်စ်များအား ခေါ်ယူရာတွင်၎င်း၊ ရီဆို့ (Resource) ဖိုင်များအား ရှာဖွေရာတွင်၎င်း အသုံးပြုသော ကလပ်စ် လုဒ်ဒါဖြစ်ပြီး၊ Java အပလီကေးရှင်း တစ်ခုတွင် လွန်စွာမှ အရေးပါအရာရောက်သော နေရာတွင် တည်ရှိပါသည်။ တဖန် ကိုယ်တိုင်ရေးသားထားသော ကလပ်စ်လုဒ်ဒါ တစ်ခုသည် အထွေအထူး သတ်မှတ်ရေးသားထားခြင်း မရှိပါက စစ္စတမ်ကလပ်စ်လုဒ်ဒါ၏ သားသမီး အဖြစ်သတ်မှတ်ခံရမည် ဖြစ်ပြီး၊ လွန်စွာမှအခြေခံကျသော ကလပ်စ်လုဒ်ဒါဟုလည်း ပြောနိုင်ပေသည်။


ကလပ်စ်လုဒ်ဒါများတွင် အထက်အောက် ပတ်သက်မှု့သည် လွန်စွာမှအရေးကြီး၏။ ပုံမှန်အားဖြင့် ကလပ်စ်လုဒ်ဒါတစ်ခုသည် ကလပ်စ်တစ်ခုအား လုဒ်လုပ်ရာတွင် ဦးစွာ မိမိ၏ အထက်မှ၊ ကလပ်စ်လုဒ်ဒါအား တာဝန်ပေး၍ အခေါ်ခိုင်းမည်ဖြစ်၏။ မိမိ၏ မိဘအားအားကိုး၍ မရမှသာ ကိုယ်တိုင်ခေါ်ယူမည် ဖြစ်သည်။ ကလပ်စ်အားလုံးသည် စစ္စတမ်ကလပ်စ်လုဒ်ဒါအား အမွေဆက်ခံ၍ ရေးသားထားသည် ဖြစ်ပါသောကြောင့်၊ CLASSPATH တွင်ရှိသော ကလပ်စ်များအားလုံးအား စစ္စတန်ကလပ်စ်လုဒ်ဒါမှ ခေါ်ယူနိုင်ခြင်း ဖြစ်၏။

တဖန် အပလီကေးရှင်းများသည် မိန်းသရဒ်  (Main Thread) အပါအဝင် မည်သည့် သရဒ်  (Thread) အပါအဝင် Context Class Loader တစ်ခုကို ပိုင်ဆိုင်ကြ၏။ Context ကလပ်စ်လုဒ်ဒါ ဆိုသည်မှာ ထိုသရက် အတွင်းရှိ ကလပ်စ်များကို လုဒ်လုပ်ရန် တာဝန်ရှိပါသည်။ ဤထူးခြားချက်ကို အသုံးပြုခြင်း အားဖြင့် သရက်တစ်ခု အတွင်းမှ မိမိ၏ မိဘတွင် လုဒ်မလုပ်နိုင်သော ကလပ်စ်များအား လုဒ်လုပ်နိုင်ရန် ဒီဇိုင်းရေးဆွဲနိုင်ပါသည်။ Context Class Loader များသည် မိမိ၏ မိဘနေရာတွင်ရှိသော သရဒ်များတွင် သတ်မှတ်ရေးသားရန် လိုအပ်ပါသည်။ အကယ်၍ သရဒ် တစ်ခုအား စတင်ခေါ်ယူရာတွင် Context Class Loader ကို ရေးသားထားခြင်းမရှိပါက လက်ရှိ သရဒ်၏ Context ကလပ်စ် လုဒ်ဒါအား အသုံးပြုသွားမည် ဖြစ်သည်။ Context ကလပ်စ်လုဒ်ဒါအား သတ်မှတ်ရာတွင် java.lang.Thread ၏ setContextClassLoader အား အသုံးပြုနိုင်ပြီး၊ ကလပ်စ်လုဒ်ဒါအား ခေါ်ယူရာတွင် getContextClassLoader မက်သတ်ကို အသုံးပြုနိုင်ပါသည်။


Tomcat ၏ ကလပ်စ်လုဒ်ဒါ

Tomcat သည် သရဒ် (Thread) ပေါင်းများစွာဖြင့် ဖွဲ့စည်းထားသော ရှုပ်ထွေးသော Java အပလီကေးရှင်း တစ်ခုဖြစ်၏။ အဓိကအားဖြင့် အောက်ပါအတိုင်း ကလပ်စ်လုဒ်ဒါများအား ဖွဲ့စည်းထား၏။



Tomcat တွင်၊ ပါဝင်သော ဝဘ်အပလီကေးရှင်းတိုင်းတွင် ကလပ်စ်လုဒ်ဒါများကို သတ်မှတ်ထားကြပါသည်။  ကလပ်စ်လုဒ်ဒါများက ရှာဖွေရန်နေရာများသည် WEB-INF/classes ၏ အောက်ရှိ ကလပ်စ်ဖိုင်များနှင့် WEB-INF/lib ၏အောက်ရှိ jar ဖိုင်များဖြစ်ကြ၏။ ဆာဗလက်အား အလုပ်လုပ်စေသော သရဒ်၏ Context ကလပ်စ် လုဒ်ဒါသည်လည်း အထက်ပါ ကလပ်စ် လုဒ်ဒါပင်ဖြစ်၏။

ဝဘ်အပလီကေးရှင်း အသီးသီး၏၊ မိဘနေရာတွင်ရှိသော လုဒ်ဒါသည် Shared ကလပ်စ် လုဒ်ဒါဖြစ်၏။ shared/classes ၏ အောက်ရှိ ကလပ်စ်ဖိုင်များနှင့် shared/lib ၏ အောက်ရှိ jar ဖိုင်များရှိ ကလပ်စ်များကို ခေါ်ယူအသုံးပြုနိုင်၏။ Shared ကလပ်စ် လုဒ်ဒါအား ဝဘ် အပလီကေရှင်း အားလုံးမှ ဘုံအသုံးပြုနိုင်ပါသဖြင့် shared/classes နှင့် shared/lib အောက်ရှိ ကလပ်စ်နှင့် ရင်းမြစ်များအား ဝဘ်အပလီကေးရှင်းများ အားလုံးမှ အသုံးပြုနိုင်ခြင်း ဖြစ်၏။

Catalina ကလပ်စ် လုဒ်ဒါသည် Tomcat ဆာဗာအား ခေါ်ယူရာတွင် အသုံးပြုသော လုဒ်ဒါဖြစ်၏။ ထို ကလပ်စ် လုဒ်ဒါအား အခြားသော ဝဘ် အပလီကေးရှင်းများမှ ခေါ်ယူအသုံးပြုနိုင်စွမ်း မရှိပေ။ ဤနည်းအားဖြင့် Context ကလပ်စ် လုဒ်ဒါတစ်ခုမှ ခေါ်ယူထားသော ကလပ်စ်များ၊ ရင်းမြစ်များအား အခြားသော အပလီကေးရှင်းများမှ အသုံးပြုခြင်းမရှိစေနိုင်ရန် စီမံထားနိုင်ပါသည်။

Common ကလပ်စ် လုဒ်ဒါသည်၊ Catalina နှင့် Shared ကလပ်စ် လုဒ်ဒါများ၏ မိဘနေရာတွင်ရှိသော ကလပ်စ် လုဒ်ဒါများ ဖြစ်ကြ၏။ တနည်းအားဖြင့် Tomcat နှင့် အားလုံးသော ဝဘ်အပလီကေးရှင်း များတွင် ဘုံအနေဖြင့် အသုံးပြုနိုင်မည် ဖြစ်၏။ Tomcat ၏ common/lib၊ common/classes နှင့် common/endorsed ၏ အောက်ရှိ ရင်းမြစ်များနှင့် ကလပ်စ်များကို ရှာဖွေ အသုံးပြုနိုင်ပါမည်။

နောက်ဆုံးတွင် Tomcat ၏ Class Loader များမှ ခေါ်ယူအသုံးပြုမည့် အစီအစဉ်ကို ဖော်ပြသွားပါမည်။ ပုံမှန် အပလီကေးရှင်းများတွင် ကလပ်စ် လုဒ်ဒါများသည် မိမိ၏ မိဘများ၏ ကလပ်စ်လုဒ်ဒါများကို ဦးစွာအသုံးပြုမည်ဖြစ်သော်လည်း၊ Tomcat ၏ ခေါ်ယူပုံမှာ တမူထူးခြားပါသည်။

Tomcat ၏ ဝဘ်အပလီကေးရှင်းများတွင် ကလပ်စ်များကို ရှာဖွေရာတွင် အောက်ပါ အစီအစဉ် အတိုင်း ခေါ်ယူသွားမည် ဖြစ်၏။
  1. မိမိ၏ ကိုယ်ပိုင် ကလပ်စ် လုဒ်ဒါ
  2. Common  ကလပ်စ် လုဒ်ဒါ
  3. Shared  ကလပ်စ် လုဒ်ဒါ
  4. စစ္စတမ်  ကလပ်စ် လုဒ်ဒါ
  5. Bootstrap  ကလပ်စ် လုဒ်ဒါ


Web Application များတွင် ကလပ်စ်များအား ဘုံအနေဖြင့် အသုံးပြုရာတွင် သတိထားရန် အချက်များ

Tomcat ဆာဗာကို အသုံးပြုသည့်အခါတွင် shared နှင့် common ၏ အောက်ရှိ ကလပ်စ်ဖိုင်များ၊ jar ဖိုင်များနှင့် ရင်းမြစ်ဖိုင်များသည် မည်သည့် ဝဘ်အပလီကေးရှင်းက မဆို အသုံးပြုနိုင်ပါသည်။ သို့ရာတွင် ဤကဲ့သို့ ကလပ်စ်တစ်ခုအား ဘုံအဖြစ် အသုံးပြုရာတွင် သတိပြုစရာ အချက်တစ်ခုရှိ၏။ ကလပ်စ် ကိန်းရှင် (Class Variables) များပင် ဖြစ်၏။

အင်းစတန့်စ် အဖြစ်အသုံးပြုမည် ဆိုပါက၊ ခေါ်ယူခဲ့သော သရဒ် (Thread) အသီးသီးတွင် အသစ်ခေါ်ယူအသုံးပြုမည် ဖြစ်ပါသဖြင့် ဘုံအဖြစ် သရဒ် (Thread) များ အကြားတွင် အသုံးပြုပါရင်လည်း ပြဿနာမရှိပါ။ သို့ရာတွင် ကလပ်စ် ကိန်းရှင်များမှာမူ ကလပ်စ်အား လုဒ်လုပ်သည့်အခါတွင် တစ်ခါတည်းသာ ခေါ်ယူမည် ဖြစ်ပါသဖြင့်၊ သရဒ် များအကြားတွင် ကိန်းရှင်တစ်ခုတည်း ကိုသာ အသုံးပြုမိနေမည် ဖြစ်၏။

ကျွှန်တော်တို့ နမှုနာ ကလပ်စ်တစ်ခုကို shared ထဲတွင် ရေးသားကြည့်ပါမည်။ ရုတ်တရက်ကြည့်လျှင် အင်စတန့်စ် လယ်ဗယ်ဖြင့် အသုံးပြုနေသရောင် မြင်ရမည်ဖြစ်သော်လည်း၊ ကလပ်စ် ကိန်းရှင်ကို အသုံးပြုထားပါသည်။ ထို့အတွက် setText မက်သတ်ကို အသုံးပြုမည် ဆိုပါက ဘုံအနေဖြင့် အသုံးပြုနေသည့် သရဒ်များအား အကျိုးသက်ရောက်မှု့ ပေးနိုင်မည် ဖြစ်၏။ ဤနမှုနာသည် ကိုယ်တိုင် ရေးသား ထားပါသဖြင့် ရှောင်တိမ်းနိုင်မည် ဖြစ်သော်လည်း၊ အခြားသူများက ရေးသားထားသော ကလပ်စ်ဖြစ်ပါက၊ Framework ၏ ကလပ်စ် တစ်ခုဖြစ်ခဲ့ပါက ဘုမသိဘမသိနှင့် အသုံးပြုနေနိုင်ပါသည်။
package shared;

public class Label{
 private static String text="";

 public synchronized void setText(String text){
  this.text=text;
 }

 public String getText(){
  return(text);
 }
}
တဖန် ကလပ်စ်တစ်ခုအား ကလပ်စ်လုဒ်ဒါအမျိုးမျိုးအပေါ်တွင် ထားရှိမိသည့်အခါမျိုးတွင် ပို၍ သတိထားသင့်ပါသည်။ ဥပမာ both.Sample ဟု ကလပ်စ်တစ်ခုကိုရေးပြီး၊ အထက်ပါ Label ကလပ်စ်နှင့် အတူ Shared ကလပ်စ် လုဒ်ဒါက မြင်နိုင်သောနေရာတွင် ထားရှိပါမည်။ တဖန် ထိုကလပ်စ်ကို ဝဘ်အပလီကေးရှင်း တစ်ခုအတွင်းတွင်လည်း ထားကြည့်ပါမည်။ ပြီးလျှင် အောက်ပါ မက်သတ်ကို Label ကလပ်စ် အတွင်းတွင် ဖြည့်စွက်ရေးသားကြည့်ပါမည်။ မည်သည့် အဖြေရရှိမည် ထင်သနည်း။
public boolean classTest() {
 ClassLoader cl = Thread.currentThread().getContextClassLoader();
 Class sampleClass;
 try {
  sampleClass = cl.loadClass("both.Sample");
  return (sampleClass.equals(both.Sample.class));
 } catch (ClassNotFoundException e) {
  e.printStackTrace();
 }
    return false;
}
false ဖြစ်၏။ ရုတ်တရက်ကြည့်မည် ဆိုလျှင်၊ both.Sample ၏ ကလပ်စ်အား နှိုင်းယှဉ်ပြီး၊ တူညီပါက true ကို ပြန်ပေးပြီး၊ မတူညီပါက false ဟု ပြန်ပေးနေ၏။ sampleClass နှင့် both.Sample.class သည် တစ်ခုတည်းသော ကလပ်စ်ဖြစ်ခြင်းကြောင့် တူသရောင်ရောင်ရှိသော်လည်း လုဒ်လုပ်သော လုဒ်ဒါမှာ မတူညီသောကြောင့် false ဟု ပြန်ပေးခြင်း ဖြစ်၏။

ဒုတိယ စာကြောင်းတွင် getContextClassLoader သည် အပလီကေးရှင်း ကလပ်စ် လုဒ်ဒါကို ခေါ်ယူပေးနိုင်ပြီး၊ အဆိုပါ cl ကလပ်စ် လုဒ်ဒါကို အသုံးပြု၍ ခေါ်ယူသော ကလပ်စ်သည် ဝဘ် အပလီကေးရှင်း အတွင်းမှ ကလပ်စ်ကို လုဒ်လုပ်မည်ဖြစ်၏။

တဖန် စာကြောင်း ၄ကြောင်းမြောက်ရှိ both.Sample.class သည် Common ကလပ်စ်လုဒ်ဒါမှ လုဒ်လုပ်ထားသော both.Sample ၏ ကလပ်စ်ကို ပြန်ပေးမည်ဖြစ်၏။ ဤနည်းအားဖြင့် ကလပ်စ် အမည်မှာ တူညီသော်လည်း မံမိုရီအပေါ်ကို ကလပ်စ်လုဒ်ဒါနှစ်ခုဖြင့် ၂ကြိမ်ခေါ်ယူထားသောကြောင့်၊ ကလပ်စ် နှစ်ခုဖြစ်နေပြီး တူသလားဟုမေးသောအခါ မတူပါဟု အဖြေရရှိခြင်း ဖြစ်၏။

ဤနမှုနာတွင် သိပ်ပြီးပြဿနာမရှိသော်လည်း သတိမမှုမိပဲ အသုံးပြုမိပါက မမျှော်လင့်သော အခက်အခဲများနှင့် ကြုံတွေ့တတ်ပါသည်။

ဆာဗလက်သည် မာလတီသရဒ် (Multi Thread) အပလီကေးရှင်းများ ဖြစ်ကြပါသဖြင့်၊ သရဒ်များအကြားတွင် ဘုံကလပ်စ်များအား အသုံးပြုရာတွင်၎င်း၊ ကလစ်လုဒ်ဒါများ၏ သဘောသဘာဝကို နားလည်ပြီး သရဒ်များကြားတွင် ကလပ်စ်များအား ခွဲခြားအသုံးပြုနိုင်ရန်၎င်း စီမံ၍ မာလတီသရဒ်၏ အကျိုးကို ကောင်းစွာအသုံးချသင့်ပါသည်။

ဤအခန်းဖြင့် အီးဇီးဆာဗလက် ခေါင်းစဉ်ကို အဆုံးသတ်ပါမည်။ ရှေ့ဆက်၍ အီးဇီးဂျေအက်စ်ပီ နှင့် အီးဇီးဒေတာဘေစ် အခန်းများကို ဖော်ပြပြီးသည့်နောက်တွင် Struts, Spring အစရှိသည့် အခြေခံ Framework များနှင့် EJB3 ကို အစပြုသော Java EE ကို ဆက်လ က်မိတ်ဆက်ရေးသား သွားပါဦးမည်။


ကိုးကား
http://www.techscore.com/tech/Java/JavaEE/Servlet/supplement-1/


မြန်မာမှုပြုထားသော စကားလုံးများ

  • သရဒ် : Thread
  • မာလတီ သရဒ် : Multi Thread
  • ကလပ်စ် လုဒ်ဒါ : Class Loader

လေးစားစွာဖြင့်
မင်းလွင်

November 29, 2011

Access Control List

ကျွှန်တော် အီးဇီးဆာဗလက် ခေါင်းစဉ်ဖြင့် ဆာဗလက်၏ အခြေခံအကြောင်းအရာများကို ရေးသားလာခဲ့သည်မှာ ၇ကြိမ်ရှိခဲ့ပြီဖြစ်သည်။
  • ဆာဗလက်တစ်ခုကို မည်သို့ရေးသားရမည်၊ 
  • ဆာဗလက်အတွင်းမှ ပါရာမီတာများကို မည်သို့ရယူမည်၊ 
  • ဆာဗလက်အချင်းချင်း မည်သို့ပူးပေါင်းဆောင်ရွက်နိုင်သည်၊ 
  • ဆာဗလက်မှ အသုံးပြုသူကို မည်သို့မှတ်သားနိုင်သည်၊ 
  • ဆာဗလက်အတွင်းမှ အသုံးပြုလိုသည့်လော့ဂျစ်နှင့် မဆိုင်သော အကြောင်းအရာများကို မည်သို့ခွဲခြားပြီးရေးသားနိုင်သည်၊ 
  • ရေးထားသော အပလီကေးရှင်းကို စောင့်ကြည့်ပြီး အလိုအလျှောက်ရေးသားနိုင်သည်၊
အစရှိသည်တို့ကို လေ့လာခဲ့ပြီးဖြစ်၏။  ဆာဗလက်နှင့်ပတ်သက်၍ နောက်ရေးသားရန်မှာ ဤအခန်းအပါအဝင် နှစ်ခန်းသာကျန်တော့၏။ ဤတစ်ခေါက်တွင် Access Control List အကြောင်းကို ရေးသားပြီး နောက်တစ်ခန်းတွင်၊ Security နှင့်ပတ်သက်ပြီး ရေးသားရန်စီစဉ်ထားပါသည်။

Access Control List ဆိုသည်မှာ

သာမန် ကွန်မြူနတီ ဘလောဂ် များမှအစ ဝဘ်အပလီကေးရှင်းများသည် သတင်းအချက်အလက်၊ အရင်းအနှီးများကို ပိုင်ဆိုင်၏။ မည်သူမဆို အချက်အလက်များကို အလွယ်တကူပြောင်းလည်း နိုင်၍မဖြစ်ပေ။ ထို့အတွက် ထိုအရင်းအနှီးများကို မည်သူက ဘယ်အအထိအသုံးပြုနိုင်သည် ဆိုသည်ကို သတ်မှတ်ထားရန်လိုအပ်၏။ ဤကဲ့သို့ အဆင့်အလိုက် ဆက်သွယ်အသုံးပြုနိုင်ခွင့်ကို သတ်မှတ်ရေးသားထားသည့် စာရင်းကို Access Control List သို့မဟုတ် ACL ဟုခေါ်ပါသည်။ Enterprise Application အတော်များများသည် ACL ကို အသုံးပြု၍ အသုံးပြုနိုင်ခွင့်ကို ထိမ်းသိမ်းထားနိုင်ခြင်းဖြစ်၏။

JavaEE တွင်လည်း သတ်မှတ်ထားသော အသုံးပြုသူက၊ ခွင့်ပြုထားသော လုပ်ပိုင်ခွင့်များကို ရရှိစေရန် သတ်မှတ်ရေးသားနိုင်ပါသည်။ ထိုသို့ရေးသားရာတွင် web.xml ကို အသုံးပြုရေးသားနိုင်ပြီး၊ ထိုနေရာတွင် အောက်ပါ အချက်အလက်များကို သတ်မှတ်ရေးသားရန် လိုအပ်ပါသည်။

  1. အသုံးပြုသူအမျိုးအစား (ROLE) သတ်မှတ်ချက်
  2. အသုံးပြုခွင့်ကန့်သတ်ထားသော ရင်းမြစ်များ
  3. ကန့်သတ်ထားသော ရင်းမြစ်များနင့် အသုံးပြုခွင့်ရသော Role ၏သက်မှတ်ချက်
  4. ကန့်သတ်ထားသော ရင်းမြစ်များအား ဆက်သွယ်ခွင့်စစ်ဆေးရေးစနစ် သက်မှတ်ချက်
မည်သူက ဘယ်အဆင့်အထိအသုံးပြုနိုင်သည်၊ တဖန် စစ်ဆေးရေးစနစ်တွင် အသုံးပြုသော လျှို့ဝှက်နံပါတ် သိမ်းဆည်းရေးစနစ်မှာမှု အပလီကေးရှင်း ဆာဗာအပေါ်တွင် မှုတည်၍ အမျိုးမျိုးကွဲပြားခြားနား၏။ သို့ရာတွင် ဤဘလောဂ်တွင်မှု အခြေခံ JavaEE လုံခြုံရေးစနစ်ကို အသုံးပြု၍ ရှင်းလင်းရေးသားသွားပါမည်။

<web-app>
  ...
  <security-constraint>
    ...
  </security-constraint>
  <login-config>
    ...
  </login-config>
  <security-role>
    ...
  </security-role>
</web-app>
အထက်ပါ နမှုနာ web.xml တွင် ရေးသားထားသည့်အတိုင်း အနည်းဆုံး အချက်သုံးချက်ကို သက်မှတ်ရန်လိုအပ်ပါသည်။

အမည် အသုံးပြုနိုင်ပုံ ရှင်းလင်းချက်
security-constraint ကန့်သတ်ချက်ရှိသော ဆားဗစ်များနှင့်၊ ထိုဆာဗစ်များအား ဆက်သွယ် အသုံးပြုနိုင်သည့် အသုံးပြုသူ အမျိုးအစားများကို သတ်မှတ် ရေးသားရန် လိုအပ်ပါသည်။
login-config ကန့်သတ်ချက်ရှိသည့် ဆာဗစ်များကို အသုံးပြုလိုသည့်အခါတွင် အသုံးပြုမည့် စစ်ဆေးရေးစနစ်ကို သတ်မှတ်ရေးသား ရန်လိုအပ်ပါသည်။
security-role ဤစစ္စတမ်တွင် အသုံးပြုမည့် အသုံးပြုသူအမျိုးအစားများကို သတ်မှတ်ရေးသားရန် လိုအပ်ပါသည်။

အထက်ပါ Tag များကို taglib နှင့် resource-ref Tag များ၏ နောက်တွင် ရေးသားရန်လိုအပ်ပါသည်။

<security-role>

<security-role>တဂ်အတွင်းတွင် အသုံးပြုသူအမျိုးအစား(Role) ကို သတ်မှတ်ရေးသားနိုင်၏။ ရေးသားပုံနမှုနာမှာ အောက်ပါအတိုင်းဖြစ်၏။
<security-role>
  <role-name>admin</role-name>
</security-role>
<security-role>
  <role-name>support</role-name>
</security-role>
<role-name>တွင် အသုံးပြုမည့် အဆင့်၏ အမည်ကို သတ်မှတ်ရေးသားရပါမည်။ ထို့အပြင် <security-role>တဂ်အတွင်းတွင် <role-name> တစ်ခုသာ ရေးသားနိုင်ပြီး၊ Role အမျိုးမျိုးကို အသုံးပြုလိုပါက၊ အသုံးပြုလိုသည့် Role တိုင်းကို <security-role>တဂ် တစ်ခုချင်း ရေးသားရန်လိုအပ်ပါသည်။ တဖန် မည်သူက ဘယ်အဆင့်ရှိသည်ဆိုသည်ကို သတ်မှတ်ပုံမှာ အသုံးပြုသည့် အပလီကေးရှင်း ဆာဗာအပေါ်တွင် မှုတည်ပြီး အမျိုးမျိုး ကွဲခြားပါသည်။

<security-constriant>

<security-constriant>တဂ်သည် အသုံးပြုခွင့်ကို ကန့်သတ်လိုသည့် ရင်းမြစ်များနှင့်၊ ကန့်သတ်သားသော ရင်းမြစ်များအား အသုံးပြုနိုင်သည့်၊ အသုံးပြုသူအမျိုးအစားများကို သတ်မှတ်ရန် အသုံးပြုပါသည်။ ကန့်သတ်လိုသည့် ရင်းမြစ်များကိုရေးသားရန် <web-resource-collection>၊ အသုံးပြုနိုင်သည့် အသုံးပြုသူအမျိုးအစားများအား သတ်မှတ်နိုင်သည့် <auth-constraint>နှင့်၊ အသုံးပြုသူနှင့် အကြားတွင်အသုံးပြုမည့် ဆက်သွယ်ရေးစနစ်ကို သတ်မှတ်ပေးနိုင်သည့် <user-data-constraint> တို့ဖြင့် ဖွဲ့စည်းထားပါသည်။

<security-constraint>
  <web-resource-collection>
    <web-resource-name>admin page</web-resource-name>
    <url-pattern>/admin/*</url-pattern>
    <url-pattern>/root/*</url-pattern>
    <http-method>GET</http-method>
  </web-resource-collection>
  <web-resource-collection>
    <web-resource-name>support page</web-resource-name>
    <url-pattern>/support/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>admin</role-name>
  </auth-constraint>
  <user-data-constraint>
    <transport-guarantee>NONE</transport-guarantee>
  </user-data-constraint>
</security-constraint>
<web-resource-collection> အတွင်းတွင် <web-resource-name><url-pattern><http-method> အစရှိသည့် တဂ် ၃မျိုးကို ရေးသားနိုင်၏။ <web-resource-name> မှာ ကန့်သတ်လိုသည့် ရင်းမြစ်၏ အမည် ဖြစ်၏။ <web-resource-collection>တစ်ခုအတွင်းတွင်<web-resource-name> တစ်ခုတည်းသာ ရေးသားနိုင်ပါသည်။

<url-pattern>အထဲမှာတော့ အပလီကေးရှင်းအတွင်းတွင် ဖော်ပြနိုင်သည့် ရင်းမြစ်၏ လိပ်စာပုံစံကို ရေးသားရန်လိုအပ်ပါသည်။ <web-resource-collection> တစ်ခုအတွင်းတွင် <url-pattern>ကို တစ်ခုထက်မက ရေးသားနိုင်ပြီး၊ <servlet-mapping> နှင့်<filter-mapping>တို့တွင် အသုံးပြုခဲ့သော <url-pattern> နှင့် ရေးသားပုံမှာ အတူတူပင်ဖြစ်၏။

<user-data-constraint> သည် အသုံးပြုသူနှင့် အကြားတွင် လိုအပ်သည့် ဆက်သွယ်ရေးပုံစံ ကို သတ်မှတ်ပေးနိုင်ပြီး၊ <security-constraint> တစ်ခုအတွင်းတွင် <user-data-constraint>တစ်ခုတည်းသာ အသုံးပြုနိုင်၏။ တဖန် <user-data-constraint> အတွင်းတွင် <transport-guarantee> ကို ရေးသားရန် လိုအပ်ပြီး၊ ဆက်သွယ်ရေးစနစ်၏ လုံခြုံရေးစနစ်ကို ဖော်ပြရေးသားရန်လိုအပ်ပါသည်။ အသုံးပြုနိုင်သည်မှာ အောက်ပါအတိုင်းဖြစ်၏။

အမျိုးအစား ရှင်းလင်းချက်
NONE လုံခြုံရေး စနစ်အား သတ်မှတ်တောင်းဆိုခြင်း မရှိပါ။
INTEGRAL ပြုပြင်ပြောင်းလည်း ရေးသား၍မရနိုင်သော ဆက်သွယ်ရေး စနစ်ဖြင့် ဆက်သွယ်ရန် လိုအပ်ပါသည်။
CONFIDENTIAL ကြားဖြတ်ပြီး ခိုးနားမထောင်နိုင်သော ဆက်သွယ်ရေးစနစ်ဖြင့် ဆက်သွယ်ရန် လိုအပ်ပါသည်။

INTEGRAL သို့မဟုတ် CONFIDENTIAL ကို အသုံးပြုပါက၊ SSL (HTTPS) ဖြင့် ဆက်သွယ်ရန် လိုအပ်ပါသည်။ တဖန် <user-data-constraint> ကို ရေးသားထားခြင်းမရှိပါက အလိုအလျှောက် NONE ကို သတ်မှတ်သွားမည် ဖြစ်၏။ အထက်ပါ နမှုနာထဲတွင် NONE ဟု သတ်မှတ် ရေးသား ထားပါသော်လည်း၊ မရေးသားထားရင်လည်း NONE ဟု သတ်မှတ်သွားမည် ဖြစ်၏။

<login-config>

<login-config>တွင် အသုံးပြုသူအား စစ်ဆေးသည့် Login စနစ်ကို သတ်မှတ်ရေးသားရပါမည်။
<login-config>
  <auth-method>BASIC</auth-method>
  <realm-name>admin page</realm-name>
</login-config>
<web-app> တစ်ခုတွင် <login-config>တစ်ခုတည်းသာ ရေးသားနိုင်၏။ တစ်နည်းဆိုရသော် ဝဘ်အပလီကေးရှင်း တစ်ခုတွင် Log in လုပ်နည်း တစ်နည်းသာ အသုံးပြုနိုင်သည်ဟု ဆိုနိုင်၏။ <login-config> အတွင်းတွင် မဖြစ်မနေ ရေးသားရန် လိုအပ်သည်မှာ <auth-method> ဖြစ်ပြီး၊ <auth-method> အတွင်းတွင် စစ်ဆေးရေးပုံစံကို ရေးသားရပါမည်။

အမည် ရှင်းလင်းချက်
BASIC HTTP Basic နည်းစနစ်ကို အသုံးပြုပါသည်။
FORM HTML Form ကို အသုံးပြု၍ အသုံးပြုသူကို စစ်ဆေးမည်ဟု သတ်မှတ်နိုင်ပါသည်။
DIGEST HTTP Digest နည်းစနစ်ကို အသုံးပြုပါသည်။ BASIC နှင့် DIGEST တွင် ကွာခြားသည်မှာ အသုံးပြုသူနှင့် Password အား စကားဝှက်ကို အသုံးပြု၍ ဆက်သွယ်ခြင်းဖြစ်၏။ DIGEST ကို အသုံးပြုပါက စကားဝှက်နည်းကို အသုံးပြု၍ ဆက်သွယ်နိုင်မည် ဖြစ်၏။
CLIENT-CERT SSL(HTTPS) တွင် အသုံးပြုသော Digital Signature ကို အသုံးပြု၍ အသုံးပြုသူ အားခွဲခြား သတ်မှတ်နိုင်ပါသည်။


<realm-name> တဂ်သည် BASIC နည်းစနစ်ကို အသုံးပြုသည့်အခါတွင် အသုံးပြုနိုင်သည့် တဂ်တစ်မျိုးဖြစ်ပြီး၊ log in dialog အတွင်း ဆက်သွယ်သည့် နယ်မြေနေရာတွင် တဂ်ထဲတွင် ရေးသားထားသော အမည်ကို ဖော်ပြပေးမည် ဖြစ်သည်။
ဤအခန်းအတွက် လက်တွေ့လေ့ကျင့်ခန်းများကို နောက်ရက်များတွင် ရေးသားပါမည်။

လေးစားစွာဖြင့် 
မင်းလွင် 

ကိုးကားများ
http://www.techscore.com/tech/Java/JavaEE/Servlet/10/

November 10, 2011

Servlet Listener

ကျွှန်တော်သည် မြန်မာအိုင်တီပရိုတွင် ဤလွယ်ကူသော ဆာဗလက်များ အကြောင်းကို အခန်းဆက်ဖြင့် ရေးသားနေခဲ့ပါသည်။ သို့ရာတွင် အလုပ်များနေသည်ကတစ်ကြောင်း မြန်မာအိုင်တီပရိုဘလောဂ်၏ စာလုံးအပြောင်းအလည်းကြောင့် စာမရေးဖြစ်သည်ကတစ်ကြောင်း၊ အစရှိသည့်အကြောင်းကြောင်းကြောင့် ကျွှန်တော့်ဘလောဂ်ကို တစ်ခြားတနေရာရာကို ရွှေ့ရန်ဆုံးဖြတ်ခဲ့ပါသည်။ သည်လိုနှင့် Google ၏ Blogspot ကို သွားတွေ့ပြီး ဤနေရာတွင် စာရေးသားသွားရန် ဖြစ်လာခဲ့ပါသည်။

အစတွင် မြန်မာအိုင်တီပရိုတွင် ရေးသားခဲ့သော ရေးလက်စ အကြောင်းအရာများကို ရွှေ့နေခဲ့ပါသည်။ ပြီးခဲ့သော Servlet ဖြင့် AOP ကို ရေးပြီးချိန်တွင် မြန်မာအိုင်တီပရိုတွင် ရေးခဲ့သော အကြောင်းများ ကုန်ပြီဖြစ်ပါသဖြင့် အခန်းဆက်ကို စတင်ရေးသားသွားမည် ဖြစ်သည်။

ဤတစ်ခေါက်ရေးသားသွားမည့်အကြောင်းမှာ ServletListener အကြောင်းပဲဖြစ်ပါသည်။ FrameWork များကိုအသုံးများလာသောယနေ့ခေတ်မှာ ServletListener များကို ကိုယ်တိုင်ရေးသားသူနည်းပါမည် ဟုထင်ပါသည်။

ServletListener ကင်းစောင့်သူများနှင့် တူ၏။ စောင့်ကြည့်နေသော Event တစ်ခုဖြစ်ပေါ်လာပါက သူလုပ်စရာရှိသည့် အလုပ်တာဝန်များကို သူ့အလိုလို ထမ်းဆောင်သွားမည် ဖြစ်သည်။ ServletListener များအား စောင့်ကြည့်နေသော Object များအပေါ်တွင် မှုတည်၍ အဓိကအားဖြင့် ၃မျိုးခွဲခြားနိုင်၏။
  1. ServletContext
  2. ServletRequest
  3. HttpSession

ServletContextListener

ServletContextListener များသည် ServletContext ၏ Event များကို စောင့်ကြည့်နေပြီး WebApplication ၏ Servlet များ၏ Lifecycle ပြောင်းလည်းမှု့များတွင် အသုံးပြုနိုင်၏။ ဥပမာ အားဖြင့် WebApplication တစ်ခုတွင် ကုဒ်စာရင်းများကို အသုံးပြုသည်ဆိုကြပါဆို့။ ထိုကုဒ်များကို ဆာဗလက်များထဲတွင် တိုက်ရိုက်လည်းရေမထားချင်ဘူး။ ဘာလို့လည်းဆိုတော့ အသုံးပြုမည့် လော့ဂျစ်နဲ့မဆိုင်တဲ့အတွက် အစတစ်ခါ ခေါ်ယူထားပြီးလျှင် အကြိမ်ကြိမ် အသုံးပြုသွားလိုပါသည်။ ပြောင်းလည်းရန်လည်း အစီအစဉ်မရှိ။ ထိုအခါမျိုးတွင် ServletContextListener သည် လွန်စွာအစဉ်ပြေ၏။

ဆာဗလက်ကို initialize လုပ်သည့်အခါတွင် အလုပ်လုပ်နိုင်မည့် Listener တစ်ခုကိုရေထားပြီး၊ ထို Listener မှ လိုအပ်သော ကုဒ်များကို Properties များမှ၎င်း၊ ဒေတာဘေစ်များမှ၎င်း ခေါ်ယူပြီး မံမိုရီပေါ်တင်ထားပါက ထိုဆာဗလက် တသက်တာ တောက်လျှောက်အသုံးပြုသွားနိုင်မည် ဖြစ်၏။
ဤServletContextListener သည် Servlet LifeCycle များကို စောင့်ကြည့်နေပါသဖြင့် Servlet မစတင်ခင်၊ ဒါမှမဟုတ် Servlet ပြီးဆုံးသည့်အခါများတွင် လုပ်ဆောင်စေလိုသည့်အရာများကို ရေးသားထားပါက စောင့်ကြည့်နေသော Servlet ကစတင်သည့်အခါတွင်၎င်း၊ ပြီးဆုံးသည့်အခါတွင်၎င်း သူ့အလိုလိုအလုပ်လုပ် နိုင်မည်ဖြစ်၏။

တဖန် ServletContextAttributeListener သည် ServletContextAttribute များကို စောင့်ကြည့်နေပါသဖြင့် Application Scope တွင် အချက်အလက်အသစ်များကို သိမ်းဆည်းသည့်အခါတွင်၎င်း၊ အချက်အလက်များကို ပြောင်းလည်းသည့်အခါတွင်၎င်း၊ အချက်အလက်များကို ဖျက်သိမ်းရာတွင်၎င်း သိရှိပြီး အလုပ်လုပ်နိုင်ပါသည်။

အင်တာဖေစ်လုပ်ဆောင်ချက်များအသုံးပြုပုံ/ရှင်းလင်းချက်များ
javax.servlet.ServletContextListenercontextInitializedWeb Application များကို စတင် ခေါ်ယူသည့် အခါတွင် ခေါ်ပါသည်။ Filter နှင့် Servlet#init ကို မခေါ်ယူခင် ခေါ်ယူပါသည်။
contextDestroyedWeb Application များကို အဆုံးသတ်သည့် အခါ များတွင် ခေါ်ယူပါသည်။ Filter နှင့် Servlet#destroy ၏ နောက်တွင် ခေါ်ယူပါသည်။
javax.servlet.ServletContextAttributeListener attributeAdded ServletContext တွင် ServletContext#serAttrivute လုပ်ဆောင်ချက်ဖြင့် Attribute တစ်ခုကို ဖြည့်စွက်သည့်အခါတွင် ခေါ်ယူပါလိမ့်မည်။
attributeReplaced ServletContext#serAttrivute လုပ်ဆောင်ချက်ဖြင့်  ရှိရင်းစွဲ Attribute တစ်ခုကို အစားထိုး ပြောင်းလည်းသည့်အခါတွင် ခေါ်ယူပါလိမ့်မည်။
attributeRemoved ServletContext#removeAttribute ဖြင့် Attribute တစ်ခုကို ဖျက်စီးပစ်သည့်အခါတွင် ခေါ်ယူပါလိမ့်မည်။


ServletRequestListener

ServletRequestListener များသည် ServletRequest Object များ၏ Event များကို စောင့်ကြည့်နေသော Listener များဖြစ်ကြ၏။ ServletRequestListener နှင့် ServletRequestAttributeListener Interface များမှ အသုံးပြုနိုင်ပါသည်။

ServletRequestListener ဖြင့် ServletRequest Object ၏ စတင်ခေါ်ယူသည့်အခါတွင်၎င်း၊ နောက်ဆုံး အသုံးမလိုတော့သည့် အခါ မံမိုရီအပေါ်မှ ဖျက်စီးပစ်သည့်အခါတွင်၎င်း လုပ်ဆောင်ချက်များကို ဖြည့်စွက်ရေးသားနိုင်၏။ တဖန် ServletRequestAttributeListener အင်တာဖေစ်ကို အသုံးပြုခြင်းအားဖြင့် ServletRequest Object တွင် Attribute များကို ဖြည့်စွက်သည့်အခါတွင်၎င်း၊ အစားထိုး ပြောင်းလည်းသည့် အခါတွင်၎င်း၊ ဖျက်စီးပစ်သည့်အခါတွင်၎င်း လုပ်ဆောင်ချက်များကို ဖြည့်စွက်ရေးသားနိုင်သည်။


အင်တာဖေစ်လုပ်ဆောင်ချက်များအသုံးပြုပုံ/ရှင်းလင်းချက်များ
javax.servlet.ServletRequestListener requestIntialized HTTP Request Object အား init လုပ်သည့်အခါတွင် ခေါ်ယူပါမည်။
Request အားခေါ်ယူပြီး Filter များအား မခေါ်ယူခင် ခေါ်ယူပါမည်။
requestDestroyed HTTP Request Object အား အသုံးမလိုတော့၍ ဆာဗာမံမိုရီအပေါ်မှ ဖျက်စီးပစ်သောအခါတွင် ခေါ်ယူပါမည်။  Filter များအား ခေါ်ယူပြီနောက်တွင် ခေါ်ယူပါမည်။
javax.servlet.ServletRequestAttributeListener attributeAdd ServletRequest#setAttributeဖြင့် Attribute တစ်ခုအား အသစ် ဖြည့်စွက်လိုက်သည့် အခါတွင် ခေါ်ယူပါမည်။
attributeReplaced ServletRequest#setAttributeဖြင့် ရှိရင်စွဲ Attribute တစ်ခု၏ တန်ဖိုးအား ပြောင်းလည်း သတ်မှတ်သည့် အခါများတွင် ခေါ်ယူပါမည်။
attributeRemoved ServletRequest#removeAttributeဖြင့် ရှိရင်းစွဲ Attribute တစ်ခုအား ဖျက်ပစ်လိုက်သည့်အခါတွင် ခေါ်ယူမည် ဖြစ်၏။ သို့ရာတွင် Attribute မှာ ဖျက်စီးပြီးဖြစ်သည့် အခါတွင်၎င်း၊ မရှိခဲ့ရင်၎င်း ခေါ်ယူမည် မဟုတ်ပေ။



HttpSessionListener

အမည်အတိုင်း HttpSession Object ၏ ပြောင်းလည်းမှု့ အခြေအနေများကို စောင့်ကြည့်ပြီး အလုပ်လုပ်နိုင်သော Listener များဖြစ်ကြ၏။

HttpSession Object အား စတင်ခေါ်ယူသည့်အခါတွင်၎င်း၊ ဖျက်စီးပစ်သည့်အခါတွင်၎င်း HttpSessionListener အင်တာဖေစ်ကို အသုံးပြုနိုင်၏။ တဖန် HttpSession တွင် Attibute များအား သတ်မှတ်သည့်အခါတွင်၎င်း၊ ပြောင်းလည်းသည့် အခါတွင်၎င်း၊ ဖျက်စီးသည့် အခါတွင်၎င်း HttpSessionAttributeListener အင်တာဖေစ်ကို အသုံးပြုနိုင်ပါသည်။

 ထို့ထက်မက Application Server များအား Cluster အနေဖြင့်အသုံးပြုသည့် စစ္စတမ်များတွင် ဆက်ရှင်များအား ဆာဗာတစ်ခုမှ အခြားဆာဗာတစ်ခုအား လက်ကမ်းပြောင်းလည်းပေးသည့် အခါတွင်၎င်း၊ ဆာဗာအား ခေတ္တနားသည့်အခါ ဆက်ရှင်အချက်အလက်များအား ခေတ္တသိမ်းဆည်းသည့်အခါ မျိုးတွင်၎င်း ဆာဗာအား ပြန်လည်စတင်၍ ယခင်အသုံးပြုခဲ့သော ဆက်ရှင်အချက်အလက်များအား ပြန်လည်ခေါ်ယူသည့်အခါမျိုးတွင်၎င်း HttpSessionActivationListener အင်တာဖေစ်ကို အသုံးပြုနိုင်၏။


အင်တာဖေစ်လုပ်ဆောင်ချက်များအသုံးပြုပုံ/ရှင်းလင်းချက်များ
java.servlet.http.SessionListener sessionCreated Session အား စတင်ခေါ်ယူသည့်အခါမျိုးတွင် ခေါ်ယူပါမည်။ Request မှ getSession(true) အား ခေါ်ယူသည့်အခါတွင် ခေါ်ယူပါသည်။
sessionDestroyed HttpSession#invalidate ဖြင့် Session အား ဖျက်စီးသည့် အခါမျိုးတွင်၎င်း၊ session timeout ဖြစ်သည့်အခါတွင်၎င်း  ခေါ်ယူပါသည်။
Servlet 2.3 တွင် Session အား ဖျက်စီး ပစ်ပြီးနောက် ခေါ်ယူခဲ့သော်လည်း 2.4 မှစ၍ Session အား ဖျက်စီးပစ်မည့် လုပ်ဆောင်ချက်အား မခေါ်ယူခင် ခေါ်ယူရန် ပြောင်းလည်းခဲ့၏။
java.servlet.http.SessionAttributeListener attributeAdded HttpSession#setAttribute အား အသုံးပြု၍ ဆက်ရှင်တွင် တန်ဖိုးအသစ်တစ်ခု သိမ်းဆည်းသည့် အခါတွင် ခေါ်ယူပါသည်။
attributeReplaced HttpSession#setAttribute အား အသုံးပြု၍ ဆက်ရှင်တွင် ရှိပြီးသား တန်ဖိုးတစ်ခုအား ပြောင်းလည်း သတ်မှတ်သည့် အခါတွင် ခေါ်ယူပါသည်။
attributeRemoved HttpSession#removeAttributeဖြင့် ဆက်ရှင်တွင် ရှိသော တန်ဖိုးတစ်ခုအား ဖျက်ထုတ်ပစ်သည့် အခါတွင် ခေါ်ယူပါသည်။
java.servlet.http.SessionActivationListener sessionWillPassivate Session Object အား Active အနေအထားမှ ရပ်နားပစ်သည့်အခါ၊ Serialized မလုပ်ခင် အခါမျိုးမှာ ခေါ်ယူပါသည်။
Application Server အားရပ်နား သည့်အခါ မျိုးတွင် ဆက်ရှင် အချက်အလက်များအား Serialize လုပ်၍ သိမ်းဆည်းပါသည်။ ထို့အပြင် Application Server အများကို Cluster အနေနှင့် အသုံးပြု၍၊ ဆာဗာတစ်ခုမှ အခြားသော ဆာဗာတစ်ခုအား ဆက်ရှင်အချက်အလက်များကို ကူးပြောင်းပေးရန် Serialize လုပ်သည့်အခါများတွင် ခေါ်ယူပါသည်။ 
sessionDidActivate ဆက်ရှင်အား ပြန်လည် Active ဖြစ်စေသည့်အခါ၊ Serialize လုပ်ထားသော ဆက်ရှင်အား ဆက်ရှင် Object အနေအဖြင့် ပြန်လည်ခေါ်ယူသောအခါ များတွင် ခေါ်ယူပါသည်။
Cluster ကို အသုံးပြုထားသော စစ္စတမ်များတွင် ဆာဗာတစ်ခုမှ Serialize လုပ်၍ လက်ဆင့်ကမ်းလာသော အချက်အလက်ဖိုင်ကို မံမိုရီအပေါ်ခေါ်ယူ၍ ဆက်ရှင်Object အဖြစ်ပြန်လည် အသုံးပြုသည့် အခါတွင် ခေါ်ယူပါသည်။
java.servlet.http.SessionBindingListener valueBound ဆက်ရှင်တွင် တန်ဖိုးတစ်ခု bind လုပ်သည့်အခါမျိုးတွင် ခေါ်ယူပါမည်။ HttpSession#setAttribute ကို ခေါ်ယူသည့်အခါမျိုးတွင် ခေါ်ယူပြီး၊ SessionAttributeListener#attributeAdded ၏ ရှေ့တွင် ခေါ်ယူပါသည်။
valueUnbound ဆက်ရှင်တွင် တန်ဖိုးတစ်ခု unbind လုပ်သည့်အခါမျိုးတွင် ခေါ်ယူပါမည်။ HttpSession#setAttribute ဖြင့် ရှိပြီးသား Attribute တစ်ခုအား အစားထိုးသည့်အခါ၊ HttpSession#removeAttribute ကို ခေါ်ယူသည့်အခါမျိုးတွင် ခေါ်ယူပြီး၊ SessionAttributeListener#attributeAdded နှင့် SessionAttributeListener#attributeRemoved ၏ ရှေ့တွင် ခေါ်ယူပါသည်။


လက်တွေ့ရေးသားကြည့်ခြင်း

လက်ရှိ အသုံးပြုနေသူများကို စောင့်ကြည့်ရေတွက်နိုင်သည့် အပလီကေးရှင်းတစ်ခုကို လစ္စနာကို အသုံးပြု၍ ရေးကြည့်ပါမည်။ ဤနမှုနာအတွက် ရေးသားသွားမည်မှာ အောက်ပါအတိုင်း ဖြစ်၏။
  • UserCountListener
  • CounterBean
  • UserCounterServlet
  • episode6.html

UserCountListener သည် ဆက်ရှင်များကို စောင့်ကြည့်နေသော ဆက်ရှင်လစ္စနာ ဖြစ်ပြီး၊ အတွင်းပိုင်းတွင် လူဦးရေကို မှတ်သားနိုင်သော CounterBean Object ကို ပိုင်ဆိုင်၏။ ဆက်ရှင်တစ်ခုကို စတင်ခေါ်ယူအသုံးပြုပါက CounterBean ၏ increase ကို ခေါ်ယူပြီး ဆက်ရှင်တွင် မှတ်သားထားမည် ဖြစ်သည်။ တဖန် ဆက်ရှင်အား အသုံးမပြုတော့ပါက CounterBean ၏ decrease ကို ခေါ်ပြီး လူဦးရေကို လျှော့မှတ်ပါမည်။

UserCountListener.java
public class UserCountListener implements HttpSessionListener {
 
 private CounterBean counter = new CounterBean();

 @Override
 public void sessionCreated(HttpSessionEvent event) {
  this.counter.increase();
  event.getSession().setAttribute("user-count", this.counter);
 }

 @Override
 public void sessionDestroyed(HttpSessionEvent event) {
  this.counter.decrease();
 }
}

CounterBean သည် အသုံးပြုသူအရေအတွက်ကို မှတ်သားနိုင်သော Object ဖြစ်၏။ ဆာဗလက်လစ္စနာများသည် Single Thread Object များဖြစ်ပြီး အခြားသော Thread အမျိုးမျိုးမှ ခေါ်ယူအသုံးပြုလေ့ရှိ၏။ ထို့အတွက် အမှတ်မမှားစေရန် Thread Save ဖြစ်စေရန် သတိထားပြီး ရေးသားရန်လိုအပ်၏။ အသုံးပြုနေသော နမှုနာတွင် syncronized ကို အသုံးပြု၍ Thread များအကြားတွင် တူညီသော အခြေအနေကို ထိမ်းသိမ်းထားနိုင်ရန် စီမံနေခြင်းဖြစ်သည်။

public class CounterBean {
 private int count = 0;
 
 public int getCount() {
  return this.count;
 }
 
 public synchronized void increase() {
  this.count ++;
 }
 
 public synchronized void decrease() {
  this.count --;
 }
}

တဖန် ဆာဗလက်လစ္စနာ ကလပ်စ် တစ်ခုကို ရေးသားပြီးလျှင် web.xml တွင်ရေးသားရန်လိုအပ်ပါသည်။ Filter များ သတ်မှတ်ချက်ပြီးလျှင် Listener ၏သတ်မှတ်ချက်ကို ရေးသားရန်လိုအပ်ပြီး၊ ရေးသားပုံမှာ အောက်ပါအတိုင်းဖြစ်၏။ လစ္စနာ တဂ်အတွင်းတွင် အသုံးပြုလိုသည့် ကလပ်စ်ကို ရေးသားရုံဖြင့် အသုံးပြုနိုင်မည် ဖြစ်၏။ UserCounterListener သည် HttpSessionListener ကို ပံ့ပိုးထားပါသဖြင့် ဤ Web Application တစ်ခုလုံးတွင် လစ္စနာ အနေဖြင့် အသုံးပြုနိုင်ပါမည်။
 <listener>
  <listener-class>com.episode6.UserCountListener</listener-class>
 </listener>

Web Application အတွင်း Session တစ်ခုကို ခေါ်ယူတိုင်း အထက်ပါ လစ္စနာက အလိုအလျှောက် အလုပ်လုပ်သွားမည် ဖြစ်၏။ ထိုနည်း၎င်း ဆက်ရှင်တစ်ခုကို ဖျက်သိမ်းတိုင်းလည်း အလုပ်လုပ်သွားပါမည်။ တကယ်ဆိုရင် အဓိကလိုအပ်သည်မှာ UserCountListener.java နှင့် CounterBean.java တို့သာ ဖြည်၏။ သို့ရာတွင် လစ္စနာ၏ အလုပ်လုပ်ပုံကို သိရှိစေရန်  UserCounterServlet.java နှင့်  episode6.html ကိုလည်း ရေးသားထားပါသည်။

UserCounterServlet.java သည် ဆက်ရှင်အတွင်းတွင် ရှိသော CounterBean ကို ခေါ်ယူပြီး လက်ရှိလူဦးရေကို စာမျက်နှာပေါ်တွင်ရေးသားနေပြီး၊ episode6.html သည် UserCounterServlet.java ကိုခေါ်ယူသော လင့်ခ်ကိုသာရေးသားထားပါသည်။

@Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {
  CounterBean counter = (CounterBean) req.getSession(true).getAttribute(
    "user-count");
  if (null != counter) {
   resp.getWriter().write(
      new StringBuilder("ယခုအသုံးပြုနေသူ ဦးရေမှာ ")
      .append(counter.getCount())
      .append("ယောက် ဖြစ်ပါသည်။").toString());
  } else {
   resp.getWriter()
      .write("ဘရောင်ဇာသတ်မှတ်ချက်ကို ဆက်ရှင်အားလက်ခံနိုင်ရန် သတ်မှတ်ထားရန်လိုအပ်ပါသည်။");
  }
 }

အထက်ပါ ဆာဗလက်အတွင်းတွင် req.getSession(true) ဖြင့် ဆက်ရှင်ကို စတင်ခေါ်ယူနေ၏။ ထိုအခါ စောင့်ကြည့်နေသူ လစ္စနာက အလုပ်ထလုပ်ပြီး အသုံးပြုသူကို တိုးမှတ်ပါမည်။ ပြီးလျှင် ရှိပြီးသား ဆက်ရှင်တွင် user-count အမည်ဖြင့် သိမ်းထားမည်ဖြစ်၏။ တဖန် ဆာဗလက်ထဲမှ ရရှိလာသော ဆက်ရှင်မှ getAttribute("user-count") ဟု သိမ်းထားသော CounterBean Object ဖြစ်သော counter ကို ခေါ်ယူ၏။ counter က null မဟုတ်ခဲ့ပါက counter.getCount() ဖြင့် အသုံးပြုနေသူကို ခေါ်ယူဖော်ပြနေခြင်း သာဖြစ်၏။


လက်တွေ့စမ်းသပ်ကြည့်ခြင်း

လက်တွေ့ရေးထားသော နမှုနာကို စမ်းကြည့်ပါဦးမည်။


အထက်ပါစာမျက်နှာမှ လစ္စနာ နမှုနာ ဆိုသည့်လင့်ခ်ကို နှိပ်ကြည့်ပါက ရေးသားထားသော ဆာဗလက်ကို ခေါ်ယူမည်ဖြစ်ပြီး၊ ဆက်ရှင်ကိုပါ တစ်ခါတည်း ခေါ်ယူမည်ဖြစ်သောကြောင့် ဆက်ရှင်လစ္စနာကို အလုပ်လုပ်စေမည် ဖြစ်ပါသည်။ လင့်ခ်ကို နှိပ်၍တွေ့မြင်ရသော စာမျက်နှာမှာ အောက်ပါအတိုင်းဖြစ်၏။


နမှုနာ Source များကို မြန်မာအိုင်တီပရို ကျူတိုရီရယ်ပရိုဂျက်တွင် လေ့လာနိုင်ပါသည်။

ကိုးကားများ
http://www.ne.jp/asahi/hishidama/home/tech/java/servlet/listener.html
http://docs.oracle.com/javaee/6/api/overview-summary.html

လေးစားစွာဖြင့်
မင်းလွင်

November 5, 2011

Servlet Filter ဖြင့် AOP

ကျွှန်တော်သည် Java EE ၏ အခန်းဆက်အနေဖြင့် Servlet အကြောင်းကို စတင်ရေးသားခဲ့သည်မှာ ၅ကြိမ်မြောက်ကို ရောက်ခဲ့ပါပြီ။ အခြေခံ Servlet ကို ရေးသားပုံမှအစ၊ Request Parameter များကိုအသုံးပြုပုံ၊ ထိုမတဆင့် Cookies နှင့် Session ကို အသုံးပြုပြီး Client နှင့် Server အကြားမှာ အဆင်ပြေပြေ ဆက်သွယ်အသုံးပြုနိုင်ပုံနှင့် RequestDispatcher ၏ include နှင့် forward ကိုအသုံးပြုခြင်း အားဖြင့် Servlet တစ်ခုနှင့် တစ်ခုဆက်သွယ်ပြီး အသုံးပြုနိုင်ပုံတို့ကို လေ့လာခဲ့ပါသည်။ ယနေ့ ဘလောဂ်ဖြင့် Servlet 1.3 တွင် စတင်ပါဝင်ခဲ့သော Filter အကြောင်းကို ဖော်ပြသွားပါမည်။

Intercepting Filter Pattern



J2EE ၏ ထင်ရှားသော Design Pattern တစ်ခုဖြစ်ပါသည်။ Application တစ်ခုတွင်ပါဝင်သော Program များတွင်၊ အဓိကကျသော Business Logic များအပြင်၊ Security, Log နှင့် DB Access အစရှိသော နေရာတကာတွင်ပါဝင်သည့် လုပ်ဆောင်ချက်များကိုလည်း ရေးသားရလေ့ရှိ၏။ ထိုနေရာတကာတွင် ပါဝင်သော အခြေခံလုပ်ဆောင်ချက်များသည် များသောအားဖြင့် ဘယ်လိုပရိုဂရမ်များမှာမဆို လုပ်ဆောင်နေ ကြသည်မှာ အတူတူပင် ဖြစ်၏။ ဤကဲ့သို့သော တူညီသည့် အခြေခံလုပ်ဆောင်ချက်များကို ပရိုဂရမ်တိုင်းတွင်မရေးသားတော့ပဲ၊ Filter အနေနဲ့ပြင်ပတွင် တစ်ခုတည်းသာ ပြင်ဆင်ထားပြီး၊ Business Logic များကို ခေါ်ယူရာတွင် ထို Filter များကို ဖြတ်ပြီးမှ ခေါ်ယူစေခြင်း ဖြစ်သည်။



Security၊ Log နှင့် DB Access အစရှိသော Filter များကို Filter Chain အနေနဲ့ပြင်ဆင်ထားပြီး၊ Business Logic ကို ခေါ်ယူသောအခါ Filter Chain အတွင်းတွင်ရှိသော Filter များကို အလုပ်လုပ်ဆောင်စေပြီးမှ Target ဖြစ်သော Business Logic ကို လုပ်ဆောင်စေသော Pattern မျိုးကို Intercepting Filter Pattern ဟုခေါ်ပါသည်။

Aspect Oriented Programming




အထက်ပါ Intercepting Filter တွင်ဖော်ပြထားသော Class များကြားတွင် တည်ရှိသော တူညီသည့်လုပ်ဆောင်ချက်များကို Business Logic နှင့် ခွဲထုတ်ခြင်း အတွေးအခေါ်ကို ကျင့်သုံးခြင်းအားဖြင့် Program များကို နားလည်ရလွယ်ကူစေခြင်း၊ Source Code များကို နည်းပါးစေခြင်းအားဖြင့် Maintenance အပိုင်းကို တိုးတက်စေခြင်းကို ဦးစားပေးသည့် အမြင်မျိုးကို Aspect Oriented ဟုခေါ်ပါသည်။

ထင်ရှားသည့် Aspect Oriented Programming ဘာသာရပ်များမှာ Aspect C++, Aspect J, Aspect R နှင့် JBossAOP တို့ဖြစ်ကြ၏။ တဖန် Aspect Oriented အမြင်ကို ထည့်သုံးထားသော Framework များမှာ Aspectcocoa, AspectWerkz, Spring Framework နှင့် Seasor Framework တို့ဖြစ်ကြ၏။

Filter ဖြင့် Web Application တွင် AOP

Java EE တွင် Interceptor၊ DI နှင့် AOP Framework များကို အသုံးပြုပြီး AOP Application များကို ရေးသားနိုင်ရန်ပြင်ဆင်ထားပါသည်။ တဖန် Servlet ၏ Filter နှင့် FilterChain Interface များကို အသုံးပြုပြီး Intercepting Filter Pattern ကို ရေးသားခြင်း အားဖြင့် Aspect လုပ်ဆောင်ချက်များကို Filter များအဖြစ်ခွဲထုတ်ခြင်းအားဖြင့် AOP ကို လည်းပုံဖော်နိုင်မည် ဖြစ်သည်။

Java Web Application တွေမှာ Filter ကို အသုံးပြုနိုင်ဖို့အတွက် ဦးစွာအသုံးပြုမည့် Filter Class ကို ရေးသားရန်လိုအပ်ပါတယ်။ ပြီးလျှင် web.xml တွင် Filter အမည်နှင့် class ကို သတ်မှတ်ရေးသားပြီး၊ မည်သည့် url ကိုခေါ်ဆိုသည့်အခါမျိုးတွင် Filter ကို အသုံးပြုပါမည်ဟု filter-mapping ကို သတ်မှတ်ရန်လိုအပ်ပါသည်။

ကျွှန်တော်တို့ ပြီးခဲ့သော နမှုနာများတွင် မြန်မာစာကို အသုံးပြုနိုင်ရန် HTTPServletResponse တွင် Encoding ကို UTF-8 အဖြစ် အမြဲသတ်မှတ်နေခဲ့ပါသည်။ ဤနမှုနာတွင် Encoding ကို သတ်မှတ်ပေးသော Filter တစ်ခုကိုရေးကြည့်ပါမည်။

EncodingFilter
public class EncodingFilter implements Filter {
    
    private String encoding = null;

    @Override
    public void destroy() {
        this.encoding = null;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {
        res.setCharacterEncoding(encoding);
        chain.doFilter(req, res);
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        this.encoding = config.getInitParameter("encoding");
    }

}


Filter အဖြစ်အသုံးပြုလိုသည့် Class များသည် Filter Interface ကို implement လုပ်၍ ရေးသားရန်လိုအပ်ပါသည်။ တဖန် ထို Class များသည် Filter ၏ init, doFilter, destroy လုပ်ဆောင်ချက်များကို override လုပ်ရန်လိုအပ်ပါသည်။

init
Filter#init လုပ်ဆောင်ချက်သည် Filter Class ကို JVM ဆီသို့ စတင် Load လုပ်သောအခါ တွင် တစ်ကြိမ်တည်းသာ အခေါ်ခံရမည့် လုပ်ဆောင်ချက်ဖြစ်ပါသည်။ Filter အတွက်အခြေခံ လိုအပ်ချက်များကို ဤနေရာတွင် ရေးသားသင့်ပါသည်။ ဤနမှုနာထဲတွင် FilterConfig#getInitParameter ကို အသုံးပြု၍ Filter ၏ encoding init-parameter ကို ရယူပြီး private member ဖြစ်သော encoding တွင် အစားထိုးနေပါသည်။

doFilter
Filter#doFilter သည် Filter ကိုအသုံးပြုသော Request များကို ခေါ်ယူသည့်အခါတွင် အသုံးပြုမည့် အဓိက လုပ်ဆောင်ချက်တစ်ခု ဖြစ်ပါသည်။ Request လာသည့်အခါတိုင်း အခေါ်ခံရမည့် လုပ်ဆောင်ချက်ဖြစ်ပြီး၊ Filter မှ Business Logic များအပေါ်တွင် လုပ်ဆောင်ပေးလိုသည့် လုပ်ဆောင်ချက်များကို ဤနေရာတွင် ရေးသားရမည်ဖြစ်၏။

ဤနမှုနာထဲတွင် ServletResponse#setCharactersetEncoding လုပ်ဆောင်ချက်ဖြင့် Character Set Encoding အမျိုးအစားကို private member encoding ဖြင့် သတ်မှတ်ပေးနေပါသည်။ ဤကဲ့သို့ သတ်မှတ်ပေးခြင်းအားဖြင့် ServletResponse တွင် utf-8 အမျိုးအစား Character Set ကို အသုံးပြုနိုင်မည်ဖြစ်၏။ ပြီးပါက FilterChain#doFilter ကိုခေါ်ယူပြီး Filter Chain အတွင်းတွင်ရှိသော အခြားသော Filter များကို အလုပ်လုပ်စေမည်ဖြစ်သည်။

destroy
Filter#destroy လုပ်ဆောင်ချက်သည် JVM အပေါ်မှ Filter Class ကို ဖျက်စီးပစ်သောအခါတွင် တစ်ကြိမ်တည်းသာ အခေါ်ခံရမည့် လုပ်ဆောင်ချက်ဖြစ်ပြီး၊ ဤနမှုနာထဲတွင် member ဖြစ်သော encoding အား null ဖြင့် အစားထိုးနေပါသည်။ Filter Class ကို ရေးသားပြီးပါက၊ web Application တွင် Filter အဖြစ်အသုံးပြုနိုင်ရန် web.xml တွင် အောက်ပါအတိုင်း သတ်မှတ်ရေးသားရန် လိုအပ်ပါသည်။

web.xml
    <filter>
        <filter-name>enc-filter</filter-name>
        <filter-class>com.episode5.EncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    
    <filter-mapping>
        <filter-name>enc-filter</filter-name>
        <url-pattern>/enc/*</url-pattern>
        <url-pattern>/filter/*</url-pattern>
    </filter-mapping>

ဦးစွာ filter tag အတွင်းတွင် အသုံးပြုမည့် filter အမည်နှင့် class အပြင် အသုံးပြုမည့် init-param များကို သတ်မှတ်ရေးသားပါသည်။ ပြီးပါက filter-mapping tag အတွင်းတွင် filter ကို အသုံးပြုမည့် url-pattern နှင့် mapping ကို သတ်မှတ်ရေးသားပါသည်။ အထက်ပါ နမှုနာထဲတွင် /enc/* ဟုရေးသားထားပါသဖြင့် /enc/ ၏အောက်မှ Servlet များကို ခေါ်ယူရာတွင် အထက်ပါ Filter ကို ဖြတ်ပြီး ခေါ်ယူအသုံးပြုနိုင်မည် ဖြစ်သည်။

ပြီးလျှင် Filter ကို သုံးပြီးခေါ်ယူရန်အတွက် Servlet တစ်ခုကို ရေးကြည့်ပါဦးမည်။ Encoding ကို ရေးမထားပဲ "မင်္ဂလာပါ မြန်မာအိုင်တီပရို" ဟု ဖော်ပြနေသော Servlet တစ်ခုကို ရေးပြီး HTML စာမျက်နှာမှ Filter ကို မသုံးပဲတစ်မျိုး၊ Filter ကို သုံးပြီးတခါခေါ်ကြည့်ပါမည်။

TargetServlet

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.getWriter().println("မင်္ဂလာပါ မြန်မာအိုင်တီပရို။");
    }


HttpServletResponse ၏ Writer ကို ခေါ်၍ "မင်္ဂလာပါ မြန်မာအိုင်တီပရို။" ဟု ရေးသားနေခြင်းသာဖြစ်၏။ ဦးစွာ Filter ကို အသုံးမပြုပဲ TargetServlet ကို ခေါ်ကြည့်ပါမည်။ No Filter လင့်ခ်တွင် အောက်ပါအတိုင်း ရေးသားထားပါသည်။

<li><a href="/target">No Filter</a></li>


Encoding ကို မရေးသားထားသောကြောင့် မြန်မာစာလုံးများကို ဖော်ပြနိုင်ခြင်းမရှိပါ။

<li><a href="/enc/target">Encoding Filter</a></li>

အထက်ပါလင့်ခ်တွင် /enc/target ဟု ခေါ်ယူနေပါသဖြင့် Encoding Filter ၏ filter-mapping တွင် ရေးသားထားသော /enc/* နှင့် ကိုက်ညီသော ပုံစံဖြစ်ပါသဖြင့် TargetServlet ကို ခေါ်ယူသည့်အခါတွင် Encoding Filter ကို ဖြတ်၍ ခေါ်ယူမည် ဖြစ်သည်။ တဖန် EncodingFilter ၏ doFilter လုပ်ဆောင်ချက်အတွင်းတွင် ServletResponse#setCharacterEncoding ဖြင့် Encoding ကို utf-8 ဟု သတ်မှတ်ထားသောကြောင့် မြန်မာစာကို ဖော်ပြပေးနိုင်မည်ဖြစ်သည်။ လက်တွေ့စမ်းကြည့်ပါမည်။



မြန်မာစာလုံးကို ဖော်ပြပေးနိုင်သည်ကို တွေ့မြင်ရမည် ဖြစ်သည်။

Encoding သည် Business Logic ဖြစ်သော "မင်္ဂလာပါ မြန်မာအိုင်တီပရို။" ကိုဖော်ပြရာတွင် တိုက်ရိုက်မပတ်သက်သော်လည်း မြန်မာစာကို ဖော်ပြရန်အတွက် လိုအပ်ပါသည်။ အလားတူ မြန်မာစာကို သုံးတဲ့ Servlet တိုင်းမှာ လိုအပ်ပါသည်။ အဲ့ဒီလို Business Logic နှင့်မပတ်သက်သော Encoding သက်မှတ်ချက်ကို ပြင်ပသို့ခွဲထုတ်ပြီး မြန်မာစာကိုဖော်ပြ နေသော Servlet ကို ခေါ်ယူတိုင်း Encoding Filter ကို ကြားဖြတ်အလုပ်လုပ်ခိုင်နေခြင်းသည် Filter ဖြစ်ပါသည်။

Filter များကို အသုံးပြုခြင်း

EncodingFilter ၏ doFilter လုပ်ဆောင်ချက်၏ ပါရာမီတာများတွင် FilterChain ၏ instance ကိုလည်း ရယူနေသည်ကို သတိပြုမိမည်ဖြစ်သည်။ တဖန် လုပ်ဆောင်ချက်ပြီးလျှင် FilterChain#doFilter ကို ခေါ်ယူနေသည်ကို သတိပြုမိမည်ဖြစ်မည်။ Servlet Container သည် web.xml တွင်သတ်မှတ်ထားသော Filter များကို FilterChain အဖြစ်ပြောင်းယူထားပါသည်။ ပြီးလျှင် ပဋ္ဌမဆုံး Filter မှ စတင်ပြီး doFilter ကိုခေါ်ယူမည်ဖြစ်သည်။ Filter အတွင်းတွင် လုပ်ဆောင်ပြီးပါက FilterChain#doFilter ကိုပြန်ခေါ်နေပါသည်။ FilterChain မှ တဆင့်ပြီးတဆင့် Filter များကို ကုန်အောင်ခေါ်ယူမည်ဖြည်သည်။ နောက်ဆုံးခေါ်စရာ Filter မရှိတော့မှ Target Servlet ကို ခေါ်ယူမည်ဖြစ်သည်။

ဤနည်းအားဖြင့် Filter များကို တစ်ပြိုင်နက်တည်း အသုံးပြုလိုပါက၊ လွယ်လင့်တကူ အသုံးပြုနိုင်မည် ဖြစ်သည်။ နမှုနာအဖြစ် HTML Header များကို ရေးသားနေသော အပိုင်းနှင့် မာတိကာသို့ လင့်ခ်ကို ရေးသားနေသောအပိုင်းကို HeaderFilter အဖြစ်ရေးသားပြီး EncodingFilter နှင့် တစ်ပြိုင်နက်တည်း အသုံးပြုကြည့်ပါမည်။

HeaderFilter

    @Override
    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {
        PrintWriter writer = res.getWriter();
        writer.println(getHeaderHtml(req.getParameter("id"), req.getParameter("title")));
        chain.doFilter(req, res);
        writer.println(getFooterHtml());        
    }


HeaderFilter#doFilter လုပ်ဆောင်ချက်ထဲတွင် Common Class တစ်ခုဖြစ်သော HtmlStringUtil#getHeaderHtml နှင့် getFooterHtml ကို ခေါ်ယူပြီး HTML စာသားများကို ရယူနေပါသည်။ ရယူထားသော စာသားများကို PrintWriter မှ တဆင့် ServletResponse တွင် ဖြည့်စွက်ရေးသားနေစေခြင်းပင်ဖြစ်၏။

HtmlStringUtil

    /**
     * အထက်ပိုင်း HTML စာသားများကိုရယူခြင်း
     * @param title
     * @return
     */
    public static String getHeaderHtml(String episode, String title) {
        StringBuilder sb = new StringBuilder("<!DOCTYPE html>");
        sb.append("<html>");
        sb.append("<head>");
        sb.append("<meta charset='UTF-8'>");
        sb.append("<link rel=\"stylesheet\" href=\"/theme/episode.css\" type=\"text/css\" />");
        sb.append("<title> ").append(episode).append("</title>");
        sb.append("</head>");
        sb.append("<body style='font-family:Myanmar3'>");

        sb.append("<div style='float:right'><a href='/page/"+ episode +".html'>မာတိကာသို့</a></div>");
        sb.append("<h3>");
        sb.append(title);
        sb.append("</h3>");
        return sb.toString();
    }
    
    /**
     * အောက်ပိုင်း HTML စာသားများကို ရယူခြင်း
     * @return
     */
    public static String getFooterHtml() {
        return "</body></html>";
    }

HTML စာသားများကို ပြန်ပေးနေခြင်းသာဖြစ်၏။ ပုံမှန်ဆိုလျှင် JSP ကို သုံး၍ ဖော်ပြစေသည်က များသောပါသော်လည်း၊ လက်ရှိ JSP အကြောင်းကို မဖော်ပြရသေးသောကြောင့် ဤကဲ့သို့ ရေးနေခြင်းဖြစ်ပါသည်။

web.xml

    <filter>
        <filter-name>head-filter</filter-name>
        <filter-class>com.episode5.HeaderFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>head-filter</filter-name>
        <url-pattern>/filter/*</url-pattern>
    </filter-mapping>


HeaderFilter ကို အသုံးပြုနိုင်ရန် အထက်ပါအတိုင်း သက်မှတ်ချက်ကို web.xml တွင် ဖြည့်စွက်ရေးသားရန် လိုအပ်ပါသည်။ EncodingFilter ၏သတ်မှတ်ချက်တွင် url-pattern တွင် /filter/* ဟု ရေးသားထားသည်ကို မှတ်မိဦးမည်ထင်သည်။ ဤ HeaderFilter တွင်လည်း အလားတူ သက်မှတ်ထားပါသဖြင့် /filter/* ဟုခေါ်ယူသောအခါ EncodingFilter ကို ခေါ်ယူပြီးသည့်နောက် HeaderFilter ကို ခေါ်ယူလိမ့်မည် ဖြစ်သည်။

<li><a href="/filter/target?id=episode5&title=Header Filterကိုအသုံးပြုထားခြင်း">
               Encoding And Header Filter</a></li>


အထက်ပါအတိုင်း TargetServlet အား id နှင့် title request parameter များဖြင့် ခေါ်ယူကြည့်ပါမည်။

 

အထက်ပါအတိုင်း ခေါင်းစဉ်နှင့် မာတီကာသို့ လင့်ခ်ကို ရေးသားနိုင်သည်ကို တွေ့မြင်ရမည်ဖြစ်သည်။ ဤနည်းအားဖြင့် Filter များကို အသုံးပြု၍ Business Logic နှင့်တိုက်ရိုက်မသက်ဆိုင်သော်လည်း ရေးသားရန်လိုအပ်သည့် အပိုင်းများကို Filter အဖြစ်ရေးသားပြီး လိုအပ်သည့်အခါ ကြားဖြတ် လုပ်ဆောင် စေခြင်း အားဖြင့် Business Logic အပိုင်းကို တတ်နိုင်သလောက် ရှင်းလင်းစွာရေးသားနိုင်မည် ဖြစ်သည်။

ကိုးကား

http://java.sun.com/blueprints/corej2eepatterns/Patterns/Intercepti...
http://legacy.techscore.com/tech/J2EE/Servlet/6.html

November 4, 2011

Servlet Include နှင့် Forward

Web Application များကို ရေးသားရာတွင် Servlet တစ်ခုအတွင်းမှ၊ အခြေအနေအရ အခြားသော Servlet များကို ခေါ်ယူအသုံးချလိုသောအခါမျိုး၊ ဒါမှမဟုတ် လက်ရှိလုပ်ငန်းများကို အခြားသော Servlet ကို လုံးလုံးတာဝန်လွှဲလိုသည့်အခါမျိုး ကြုံလာရတတ်ပါသည်။ ယခင်က Servlet API တွင် ServletContext#getServlet ဒါမှမဟုတ် ServletContext#getServlets ကို အသုံးပြု၍ Servlet များကို ခေါ်ယူ အသုံးပြုနိုင်ခဲ့၏။

သို့ရာတွင် Version 2.1 နောက်ပိုင်းတွင် အထက်ပါနည်းကို အသုံးပြုခြင်းသည် မလိုလားအပ်သော ပြဿနာများဖြစ်ပေါ်တတ်ပါသဖြင့် Support လုပ်ခြင်းမရှိတော့ပါ။ လက်ရှိ API တွင်လည်း အထက်ပါ လုပ်ဆောင်ချက်များပါဝင်သော်လည်း ခေါ်ယူသည့်အခါတိုင်း null ကို သာပြန်ပေးစေပါသည်။ သို့ရာတွင် အထက်ပါ လုပ်ဆောင်ချက်များအစား include နှင့် forward လုပ်ဆောင်ချက်များကို ပြင်ဆင်လာခဲ့ပါသည်။

include သည် Servlet တစ်ခုအတွင်းမှ၊ တစ်စိတ်တစ်ပိုင်ကို အခြားသော Servlet၊ HTML၊ JSP ကို အလုပ်လုပ်စေလိုသောအခါတွင် အသုံးပြုပြီး၊ forward သည် အခြားသောနေရာသို့ လုံးဝတာဝန်လွှဲလိုက်ခြင်း ဖြစ်သည်။ HTTP တွင် Redirect ဟုခေါ်သော Function ပိုင်ဆိုင်သော်လည်း၊ ထိုနည်းမှာ Client ထံသို့အရင် ပြန်ပို့ပြီးမှ အခြားသောနေရာသို့ တဆင့်ပြန်သွားခြင်းဖြစ်ပါသည်။ သို့ရာတွင် Servlet ၏ forward သည် Client ဆီသို့မပြန်တော့ပဲ စေလိုရာသို့ တိုက်ရိုက်သွားရောက်စေခြင်း ဖြစ်ပါသည်။

ယခုဘလောဂ်ဖြင့် Servlet ၏ include နှင့် forward ကို ဖော်ပြပြီး၊ default စာမျက်နှာသတ်မှတ်ချက်များ ဖြစ်သော error-page များ သတ်မှတ်ပုံကို ဖော်ပြသွားပါမည်။


အသုံးချလိုသည်များကို include


include ရော forward ပါ javax.servlet.RequestDispatcher interface မှတဆင့် လုပ်ဆောင်စေသော လုပ်ဆောင်ချက်များ ဖြစ်ကြပါသည်။ အထက်ပါ interface ကို ServletRequest#getRequestDispatcher ၊ ServletContext#getRequestDispatcher နှင့် ServletContext#getNameDispatcher လုပ်ဆောင်ချက်တို့မှ တဆင့် ခေါ်ယူနိုင်ပါသည်။ ကွဲခြားသည်မှာ ခေါ်ယူလိုသည့် HTML, JSP နှင့် Servlet တို့အားခေါ်ယူသည့် ခေါ်ယူနည်းပဲဖြစ်ပါသည်။


ServletRequest getRequestDispatcher လက်ရှိ နေရာမှ နိုင်းယှဉ် Path သို့မဟုတ်WWW Application Root မှ အတိအကျ Path ဖြင့် ခေါ်ယူရပါမည်။
ServletContext getRequestDispatcher WWW Application Root မှ အတိအကျ Path ဖြင့် ခေါ်ယူရပါမည်။
getNamedDispatcher Resource ၏ သတ်မှတ်သားသော အမည်ဖြင့် ခေါ်ယူနိုင်မည် ဖြစ်သည်။

လက်တွေ့ include ကို သုံးပြီး နမှုနာ တစ်ခုကိုရေးကြည့်ပါမည်။ နမှုနာအဖြစ် IncludeServlet.java နှင့် ခေါ်ယူအသုံးချမည့် ToBeInclude.java ကို အသုံးပြုသွားပါမည်။ ဤနမှုနာထဲတွင် include လုပ်ပုံကို သိသာစေရန်၊ IncludeServlet အထဲမှနေ၍ ToBeInclude ကို include လုပ်ပြီး၊ HTML စာမျက်နှာတွင် လိုအပ်သောအချက်အလက်များကို ရေးခိုင်ကြည့်ပါမည်။

IncludeServlet#doGet
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
    
        // response တွင် ယူနီကုဒ် Encoding အဖြစ် သတ်မှတ်ခြင်း
        response.setCharacterEncoding("UTF-8");
        PrintWriter writer = response.getWriter();
        writer.println(getHeaderHtml(EPISODE, "Include ကို စမ်းသပ်ရေးသားခြင်း"));
        
        // RequestDispatcher ကို HttpServletRequest မှ ခေါ်ယူခြင်း
        RequestDispatcher disp = request.getRequestDispatcher("/ToInclude");
        // Dispatcher မှ Servlet ကို ခေါ်ယူ အလုပ်လုပ်စေခြင်း
        disp.include(request, response);
        
        writer.println(getFooterHtml());        
    }

ထုံးစံအတိုင်း HttpServletResponse ၏ Object ဖြစ်သော response ၏ Encoding ကို ယူနီကုဒ် (UTF-8) ဟု သတ်မှတ်ပါသည်။ ပြီးလျှင် PrintWriter ၏ instance ကို ခေါ်ယူပြီး၊ HTML ၏ အထက်ပိုင်းစာသားများကို ရေးသားစေပါသည်။ getHeaderHtml အကြောင်းကိုမှု ယခင်အခန်းတွင် ဖော်ပြခဲ့ပါ၍ ယခုအခန်းတွင် အသေးစိတ် မရှင်းပြတော့ပါ။
အဓိကမှာ include လုပ်ရန်အတွက် RequestDispatcher ၏ instance ကို HttpServletRequset#getRequestDispatcher လုပ်ဆောင်ချက်ဖြင့် ခေါ်ယူနေပါသည်။ HttpServletRequset#getRequestDispatcher ၏ ပါရာမီတာတွင် "/ToInclude" ဟု လမ်းကြောင်းအပြည့်အစုံ(full path) ရေးပြီး ခေါ်ယူနေပါသည်။ ပြီးပါက RequestDispatcher#include ကို ခေါ်ယူ၍ ToBeInclude Servlet အား ခေါ်ယူအလုပ်လုပ်စေခြင်း ဖြစ်သည်။

နောက်ဆုံးတွင် getFooterHtml ဖြင့် HTML ၏ အောက်ပိုင်းစာသားများကို ရယူပြီး ရေးသားနေစေခြင်းဖြစ်ပါသည်။

ToBeInclude#doGet
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        // Class ၏အမည်ကို ရယူခြင်း
        String className = this.getClass().getSimpleName();
        // response တွင် ယူနီကုဒ် Encoding အဖြစ် သတ်မှတ်ခြင်း
        resp.setCharacterEncoding("UTF-8");
        PrintWriter writer = resp.getWriter();

        writer.println("<p>");
        writer.println("ဤစာကြောင်းသည် " + className + " Class အတွင်းမှရေးသားထားခြင်း ဖြစ်ပါသည်။<br />");
        writer.println("</p>");

        @SuppressWarnings("unchecked")
        Enumeration<String> attrNames = req.getAttributeNames();
        
        if(null != attrNames) {
            writer.println("<h3>Servlet များအကြားတွင် လက်ဆင့်ကမ်း လာသော အချက်အလက်များ</h3>");
            writer.println("<p>");
            
            // လက်ဆင့်ကမ်းလာသော Attribute မကုန်မချင်း
            while(attrNames.hasMoreElements()) {
                // Attribute အမည်
                String name = attrNames.nextElement();
                writer.print(name);
                writer.print(" : ");
                // Attribute တန်ဖိုး
                writer.print(req.getAttribute(name));
                writer.print("<br />");
            }
            writer.println("</p>");
        } 

    }

IncludeServlet အထဲမှ ခေါ်ယူသောအခါ ရေးသားမည့် လုပ်ဆောင်ချက်ဖြစ်၏။ မည်သည့် Class အတွင်းမှ ရေးသားသွားသည် ဆိုသည်ကို ဖော်ပြနိုင်ရန် Class ၏ အမည်ကို ရယူပါသည်။ ပြီးလျှင် Encoding သတ်မှတ်ပြီး PrintWriter ကို ခေါ်ယူပြီး၊ "ဤစာကြောင်းသည် " + className + " Class အတွင်းမှရေးသားထားခြင်း ဖြစ်ပါသည်။" ဟု ရေးသားစေပါသည်။

တဖန် Servlet တစ်ခုမှ အခြားသော Servlet များကို ခေါ်ယူရာတွင် အချက်အလက်များကို လက်ဆင့်ကမ်းပေးပို့နိုင်ပါသည်။ အသုံးပြုကြသည့် လုပ်ဆောင်ချက်များမှာ HttpServletRequest#setAttribute ဖြင့် ပေးပို့လိုသည့် အချက်အလက်များကို ရေးသားပြီး၊ HttpServletRequest#getAttribute ဖြင့် ပေးပို့လိုက်သော အချက်အလက်များကို ရယူနိုင်ပါသည်။ တဖန် HttpServletRequest#getAttributeNames ဖြင့် ပေးပို့လိုက်သော အချက်အလက်၏ အမည်များကို ရယူနိုင်ပါသည်။

ဤနမှုနာတွင် IncludeServlet အတွင်းမှ အချက်အလက်များကို မပေးပို့လိုက်ပါသော်လည်း include လုပ်သောအခါတွင် ServletContainer က ပေးပို့လိုက်သော အချက်အလက်များကို ကြည့်ရှုလိုပါသဖြင့် getAttributeNames ဖြင့် အမည်များကို ရယူကာ၊ while ဝါကျကို အသုံးပြုပြီး အမည်များမကုန်မချင်း အမည်တစ်ခုစီနှင့် တန်ဖိုးများကို ဖော်ပြနေစေခြင်း ဖြစ်ပါသည်။



IncludeServlet ကို ခေါ်ယူကြည့်သောအခါ အထက်ပါအတိုင်း တွေ့မြင်ရမည် ဖြစ်သည်။ IncludeServlet အတွင်းမှ setAttribute နှင့် အချက်အလက်များကို မရေးသားထားပါ သော်လည်း include ကို အသုံးပြုပါက အထက်ပါ အချက်အလက်များကို ServletContainer မှ ဖြည့်စွက်ရေးသား သွားသည်ကိုသိရှိနိုင်ပါသည်။


မျက်နှာလွှဲခဲပစ် forward

Servlet တစ်ခုအတွင်းမှ အခြားသော Servlet တစ်ခုဆီသို့ လုံးဝတာဝန်လွှဲပြောင်းလိုသော အခါမျိုးတွင် forward ကို အသုံးပြုရပါသည်။ include ကဲ့သို့ပင် forward သည်လည်း RequestDispatcher interface ၏ လုပ်ဆောင်ချက်တစ်ခုဖြစ်သည်။ include နှင့် forward ၏ကွဲခြားချက်ကို သိရှိနိုင်စေရန် IncludeServlet တွင်အသုံးပြုခဲ့သော ToBeInclude ကို ပြန်လည်အသုံးပြုပါမည်။

ဤနမှုနာတွင် ForwardServlet အထဲမှ ToBeInclude ဆီသို့ forward လုပ်ကြည့်ပါမည်။

ForwardServlet#doGet
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        req.setAttribute("param1", "အခန်း၄ မှ ပါရာမီတာ ၁ ဖြစ်၏။");
        req.setAttribute("param2", "အခန်း၄ မှ ပါရာမီတာ ၂ ဖြစ်၏။");
        req.setAttribute("param3", "အခန်း၄ မှ ပါရာမီတာ ၃ ဖြစ်၏။");

        //  Dispatcher ကို အမည်ဖြင့် ခေါ်ယူ၍ တာဝန်လွှဲ ခိုင်းစေခြင်း
        getServletContext().getNamedDispatcher("ToInclude").forward(req, resp);

    }

ဤ Servlet အတွင်းတွင် HttpServletRequest#setAttribute ကို အသုံးပြု၍ လက်ဆင့်ကမ်းလိုသော အချက်အလက်များကို သတ်မှတ်နေပါသည်။ နောက်ဆုံးတွင် getServletContext ဖြစ် ServletContext ကို ခေါ်ယူပြီး၊ ServletContext#getNamedDispatcher ဖြင့် ToBeInclude ၏ RequestDispatcher ကို ခေါ်ယူပါသည်။ ရရှိလာသော RequestDispatcher မှတဆင့် forward လုပ်လိုက်ခြင်းအားဖြင့် ToBeInclude ကို အလုပ်လက်ဆင့်ကမ်းလိုက်ခြင်းဖြစ်သည်။


အထက်ပါအတိုင်း setAttribute ဖြင့် လက်ဆင့်ကမ်းလိုက်သော အချက်အလက်များကို ToBeInclude ထဲမှ ခေါ်ယူနိုင်ကြောင်းကို တွေ့မြင်နိုင်မည် ဖြစ်သည်။ forward ကို အသုံးပြုရာတွင် include ကို အသုံးပြုစဉ်ကကဲ့သို့ javax.servlet.include အချက်အလက်များ မပါဝင်သည်ကို သတိပြုမိမည် ဖြစ်သည်။

ဤအခန်းတွင် include နှင့် forward ကို အသုံးပြု၍ Servlet တစ်ခုနှင့်တစ်ခု ဆက်သွယ်ပြီး အလုပ်လုပ်နိုင်ပုံနှင့် Servlet တစ်ခုနှင့် တစ်ခုအကြား အချက်အလက်များကို လက်ဆင့်ကမ်းလိုသည့်အခါများတွင် setAttribute, getAttribute နှင့် getAttributeNames လုပ်ဆောင်ချက်များကို အသုံးပြုနိုင်ပုံတို့ကို လေ့လာခဲ့ပါသည်။


Error Page များကို အသုံးပြုခြင်း

နောက်ဆုံးတွင် Java EE Web Application များတွင် Error Page များကိုအသုံးပြုပုံကို ဖော်ပြပါမည်။ Error Page ဆိုသည်မှာ HTTP Error သို့မဟုတ် Servlet Container အတွင်း Exception များဖြစ်ပေါ်သောအခါ ဖော်ပြပေးမည့် Web စာမျက်နှာများဖြစ်ပါသည်။ Java EE Application များတွင် Application အတွင်း၌ အမှားများပေါ်ပေါက်သောအခါမျိုးတွင် သင့်တော်သလို စာများနှာများကို ဖော်ပြပေးနိုင်ရန် Error Page များကို အသုံးပြု၍ စီစဉ်ထားနိုင်ပါသည်။

Application အတွင်းတွင် အမှားအဖြစ် သတ်မှတ်မှတ်လိုသောအခါ forward ကို အသုံးပြု၍ Error Page များကို လမ်းညွှန်နိုင်သလို၊ Exception အဖြစ် Throw လုပ်ပြီး၊ သတ်မှတ်ထားသော Error Page များကို ဖော်ပြနိုင်ပါသည်။ Error Page များ၏ သတ်မှတ်ချက်ကို web.xml တွင် ရေးသားရပေမည်။

web.xml
  <error-page>
    <error-code>404</error-code>
    <location>/error/error.html</location>
  </error-page>
  <error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/error/error.html</location>
  </error-page>

အထက်ပါအတိုင်း error-page tag အတွင်းတွင် Error Page ၏ သတ်မှတ်ချက်ကို ရေးသားရပါမည်။ HTML Error Code များကို သတ်မှတ်ရေးသားလိုပါက error-code tag အတွင်းတွင် ဖြစ်ပွားနိုင်သော error code ကို ရေးသားပြီး၊ ထို error code ဖြစ်ပေါ်ပါက ဖော်ပြပေးရမည့် စာမျက်နှာကို location အတွင်းတွင် ဖော်ပြရပါမည်။ ဤကဲ့သို့ရေးသားထားပါက HTML Error 404 ဖြစ်ပေါ်ပါက /error/error.html စာမျက်နှာကို ဖော်ပြပေးမည်ဖြစ်သည်။

ထို့နည်းတူ Java Exception များကိုလည်း exception-type တွင်သတ်မှတ်နိုင်ပါသည်။ ဤကဲ့သို့ Exception အမျိုးမျိုးကို သင့်တော်ရာ Error Page များကို ဖော်ပြပေးခြင်းအားဖြင့် အသုံးပြုသူကို သင့်တော်မည့် လမ်းညွှန်မှု့များကို ပေးနိုင်မည်ဖြစ်သည်။



အထက်ပါအတိုင်း Application အတွင်းတွင်မရှိသော HTML စာမျက်နှာကို Request လုပ်ကြည့်သောအခါ ပြင်ဆင်ထားသော အမှားစာမျက်နှာကို ဖော်ပြပေးနိုင်မည်ဖြစ်သည်။

Application များတွင် အမှားမှာ မကင်းပေ။ အကြောင်းအမျိုးမျိုးကြောင့် အမှားဆိုသည်မှာ ပေါ်ပေါက်လာနိုင်ပါသည်။ အမှားတစ်ခုပေါ်ပေါက်လာပါက ရှေ့ဘာဆက်လုပ်ရမှန်းမသိသော Application များသည် Application ကောင်းဟု မသတ်မှတ်နိုင်ပေ။ နောက်ဆုံးအခါမျိုးအထိ အသုံးပြုသူကို မျက်စေ့မလယ်စေအောင် လမ်းညွှန်ပေးနိုင်သော Application မျိုးကို Application ကောင်းဟု သတ်မှတ်နိုင်မည် ဖြစ်သည်။

လက်တွေ့စမ်းသပ်ရန်
http://mmitp-servlet-tutorial.appspot.com/


ကိုးကား
http://download.oracle.com/javaee/5/api/javax/servlet/http/HttpServ...
http://download.oracle.com/javaee/5/api/javax/servlet/ServletContex...
http://download.oracle.com/javaee/5/api/javax/servlet/RequestDispat...
http://legacy.techscore.com/tech/J2EE/Servlet/5.html


လေးစားစွာဖြင့်
မင်းလွင်

November 3, 2011

ကွတ်ကီးနှင့် ဆက်ရှင်

ယနေ့ခေတ် အသုံးပြုနေသော HTTP ပရိုတိုကောသည် အနေအထားကို ဖော်ပြနိုင်ခြင်း မရှိပေ။ အစဉ်မပြတ် ရရှိလာသော Request များအတွင်းတွင် တစ်စုံတစ်ယောက်ထံမှ Request များဖြစ်သည်ဟု Server မှ သိရှိနိုင်ရန် ပြင်ဆင်ထားခြင်းမရှိပေ။ Privacy ကို ကြွေးကြော်သူများက ထိုအရာကို သင့်တော်သည့်လုပ်ဆောင်ချက်ဟု ကျေနပ်ကြမည်ဖြစ်သော်လည်း၊ Web Programmer အတော်များများအတွက်တော့ ခေါင်းကိုက်စရာ အချက်တစ်ခု ဖြစ်ပါသည်။ အဘယ်ကြောင့်ဆိုသော် Web Application တစ်ခုသည် အနေအထားကို သိရှိရန်လိုအပ်သောကြောင့် ဖြစ်ပါသည်။ Web Application တစ်ခုသည်၊ အသုံးပြုသူနှင့်အပြန်အလှန် ဆက်သွယ်ရာတွင် အသုံးပြုသူ၏အချက်အလက်များကို Request များကြား၌ မှတ်သားထားရန် လိုအပ်ပါသည်။
ဥပမာအားဖြင့် Online Shopping Cart တစ်ခုဆိုလျှင်၊ အသုံးပြုသူက စာမျက်နှာအတော်များများကို သွားကြည့်၊ ဝယ်ယူရန်သိမ်းဆည်းထားသော ပစ္စည်းများကို မှတ်သားထားရန်လိုအပ်ပါသည်။ လိုအပ်လာပါက နောက်ရက်ပေါင်းအတန်ကြာအထိ မှတ်ထားရမည်ဖြစ်သည်။ အကယ်၍ Server က အခြေအနေများကို မှတ်သားခြင်းမပြုနိုင်ပါက မည်သူကမည်သည့်ပစ္စည်းကို ဝယ်ယူမည်ဆိုသည့်အချက်ကို သိရှိနိုင်မည်မဟုတ်ပေ။
ထို့ကြောင့် Enterprise Application များတွင် Server က အခြေအနေကို မှတ်မိနိုင်ရန် နည်းအမျိုးမျိုးကို အသုံးပြုနေခဲ့ကြသည်။ ထင်ရှားသောနည်းများမှာ အောက်ပါအတိုင်းဖြစ်ကြသည်။
  • User Authentication
    အသုံးပြုသူ Log in ချိန်တွင် ရရှိသော အချက်အလက်များကို ပြန်လည်အသုံးပြုသော နည်းဖြစ်၏။ သို့ရာတွင် Log in ကိုတောင်းဆိုသော ဆိုက်မှသာလျှင် အသုံးပြုနိုင်သောနည်း ဖြစ်ပါသည်။ အသုံးပြုသူက Log In ချိန်တွင် Browser က အမည်နှင့် Pass Word ကို မှတ်သားထားပြီး၊ ထိုဆိုက်အတွင်းမှ အခြားသောစာမျက်နှာများကို ကူးပြောင်းချိန်တွင် မှတ်သားထားသော အချက်အလက်များကို ဆက်လက်၍ Request လုပ်နိုင်ပါသည်။ ထို့အတွက် Server ဘက်တွင် HttpServletRequest#getRemoteUser လုပ်ဆောင်ချက်ဖြင့် အသုံးပြုသူကို သိရှိနိုင်ပေသည်။ ထိုအသုံးပြုသူဖြင့်၊ Session ကို အသုံးပြုပြီး Server ဘက်မှ Request များ၏ အနေအထားကို သိရှိနိုင်ပေသည်။
  • Hidden Input Fields
    နောက်တစ်နည်းမှာ Servlet က စာမျက်နှာများကို ရေးသားရာတွင်၊ FORM Tag ၏ လျှို့ဝှက် IMPUT  များကို အသုံးပြုနိုင်ပါသည်။ အသုံးပြုသူနှင့် အပြန်အလှန် ဆက်သွယ်ထားသော အချက်အလက်များကို၊ နောက်စာမျက်နှာတစ်ခု ရေးသားချိန်တွင် Hidden Input Fields များကို အသုံးပြု၍ ရေးသားစေပြီး၊ ထိုစာမျက်နှာမှ Request လာသောအခါ ယခင်ရေးမှတ်လိုက်သော အချက်အလက်များကို ပြန်လည်ရယူခြင်းအားဖြင့် Server ဘက်မှ လက်ရှိအခြေအနေကို မှတ်သားခြင်း ပြုလုပ်နိုင်ပေသည်။Servlet မှ Form မှ Input တန်ဖိုးများကို HttpServletRequest#getParameterValues ဖြင့် ရယူနိုင်ပါသည်။
  • URL ကို ပြုပြင်ရေးသားခြင်း
    အထက်ပါနည်းနှင့် ခပ်ဆင်ဆင်နည်းဖြစ်ပါသည်။ Servlet မှ စာမျက်နှာတစ်ခုကို ရေးသားရာတွင်၊ အသုံးပြုလိုသည့် Link များ၏ URL တွင် လိုအသော အချက်အလက်ကို ရေးသားစေခြင်းဖြင့်၊ Server မှ လက်ရှိအခြေအနေကို မှတ်သားနိုင်ပါသည်။ Servlet မှ Request Parameter ၏ တန်ဖိုးများကို HttpServletRequest#getPathInfo ဖြင့် ရယူနိုင်ပါသည်။
  • Cookie
    တဖန် Server မှ အသုံးပြုသူ၏ Browser  အပေါ်တွင် သက်မှတ်ထားလိုသည့်အချက်အလက်များကို HTTP Header တွင်ဖြည့်စွက်သောနည်း ကိုလည်း အသုံးပြုပါသေးသည်။ ဤကဲ့သို့သော Server နှင့် Client ၏ Browser ၏ အကြားတွင် အချက်အလက်များကို အပြန်အလှန်အသုံးပြုနေသည့် Memory နေရာသို့မဟုတ် ပရိုတိုကောကို ကွတ်ကီး ဟုခေါ်ဆိုပေသည်။ ကွတ်ကီးအကြောင်းကို နမှုနာနှင့်တကွ ဖော်ပြသွားပါဦးမည်။
  • Session
    ကွတ်ကီးကို အသုံးပြု၍ Server နှင့် Client အကြားတွင် အချက်အလက်များကို အပြန်အလှန်အသုံးပြုနိုင်ပါသည်။ သို့ရာတွင် Server နှင့် Client အကြားတွင် အချက်အလက်များကို အသွားအပြန်အသုံးပြုသည့် ပမာဏမှာ များပြားလေလေ၊ အဲ့ဒီအချက်လက်များကို အကြိမ်တိုင်အပြန်အလှန် သယ်ယူပို့ဆောင်နေရသည်မှာ အတော်ပင် အဓိပ္ပါယ်မဲ့လှပေသည်။ ရှားပါးလှသည့် ကွန်နက်ရှင်းကို ပမာဏများများနှင့် အသုံးပြုလေလေ Performance ကျဆင်းလေလေ ဖြစ်ပေလိမ့်မည်။ ထို့အတွက် Client ဖြင့် လိုအပ်သောအချက်အလက်များကိုသာ အဆက်အသွယ်လုပ်ပြီး၊ လိုအပ်သော အချက်အလက်များကို Server အပေါ်တွင် ခေတ္တသိမ်းဆည်းထားနိုင်ရန် နေရာများကို ပြင်ဆင်လာကြ ပါသည်။ ထိုကဲ့သို့သော မံမိုရီနေရာများကို session ဟုခေါ်ပါသည်။
User Authentication နှင့်ပတ်သက်၍၊ နောက်ပိုင်း Security နှင့်အတူဖော်ပြရန် စီစဉ်ထားပါသောကြောင့်၎င်း၊ Hidden Input Fields များကို အသုံးပြုခြင်းနှင့် URL ကို ပြုပြင်ရေးသားခြင်းမှာ၊ ယနေ့တိုင် ဖော်ပြခဲ့သော ရေးသားပုံကိုအသုံးပြုပါက အလွယ်တကူရေးသားနိုင်သောကြောင့် ဤဘလောဂ်တွင် ဖော်ပြတော့မည်မဟုတ်ပေ။


Cookie
 Cookie ဆိုသည်မှာ HTTP ကို အသုံးပြု၍ Web Server နှင့် Browser တို့၏ကြားတွင် အနေအထားကို ထိမ်းသိမ်းပေးထားနိုင်သော ပရိုတိုကော သို့မဟုတ်၊ အနေအထားကို သိမ်းဆည်းပေးထားသော အရာများကို ဆိုလိုပါသည်။ Servlet သည် javax.servlet.http.Cookie ကို အသုံးပြု၍ Cookie ၏ အချက်အလက်များကို အသုံးချနိုင်ပါသည်။ Server မှ Response လုပ်သည့်အခါ Http Header တွင် စာကြောင်းအဖြစ် ဖြည့်စွက်ခြင်းအားဖြင့် Cookie အချက်အလက်များကို Browser နှင့် Server ကြားတွင် အသုံးပြုနေခြင်း ဖြစ်ပါသည်။ Http Header တွင် ဖြည့်စွက် အသုံးပြုနေသည့် စာကြောင်းမှာ အောက်ပါအတိုင်းဖြစ်ပါသည်။
Set-Cookie: NAME=VALUE; Max-Age=DATE; path=PATH; domain=DOMAIN_NAME; version=VERSION; comment=COMMENT; secure

NAME=VALUE ကွတ်ကီး၏ အမည်နှင့်တန်ဖိုး ဖြစ်ပါသည်။ ဥပမာ ID=56ဆိုပါက၊ ID သည် ကွတ်ကီး၏အမည် ဖြစ်ပြီး၊ 56 သည် တန်ဖိုးဖြစ်၏။
Cookie class ၏Constructor သည် Cookie(String,String) ကဲ့သို့ အမည်နှင့် တန်ဖိုးကို တပါတည်း ရေးသားပြီး၊ တန်ဖိုးကိုပြောင်းလည်းလိုပါက Cookie#setValue လုပ်ဆောင်ချက်ကို၎င်း၊ အမည်ကိုရယူရာ၌ Cookie#getName ကို ၎င်း၊ တန်ဖိုးကို ရယူရာ၌ Cookie#getValue လုပ်ဆောင်ချက်ကို အသုံးပြုနိုင်ပါသည်။
Max-Age=DATE ကွတ်ကီး၏ သက်တမ်းကို သတ်မှတ်ရာတွင် အသုံးပြုနိုင်ပါသည်။ Max-Age=1000 ကဲ့သို့ရေးသားထားပါက ထိုကွတ်သီး၏ သက်တမ်းသည် 1000 s ရှိသည် ဟုသိရှိနိုင်ပါသည်။ စက္ကန့် 1000 အကြာတွင် သုံး၍ရမည် မဟုတ်တော့ပေ။ တဖန် အနုတ်ကိန်း သို့မဟုတ် သက်မှတ်ခြင်း မရှိပါက Browser မပိတ်မချင်း အသုံးပြုနိုင်ပါသည်။ တဖန် 0 ဟု ရေးသားလိုက်ပါက၊ Browser တွင်ရှိသော ထိုကွတ်ကီးကို ဖျက်ပစ်လိုက်မည် ဖြစ်သည်။ ကွတ်ကီးကို သတ်မှတ်ရာတွင် setMasAge ကို အသုံးပြု၍၊ တန်ဖိုးကို ရယူရာတွင် getMaxAge ကို အသုံးပြုနိုင်ပါသည်။
path=PATH ကွတ်ကီးအား ဆက်သွယ်နိုင်သည့် Path ကို သက်မှတ်ရာတွင် အသုံးပြုနိုင်ပါသည်။ setPath နှင့် getPath လုပ်ဆောင်ချက်များကို အသုံးပြုနိုင်ပါသည်။
domain=DOMAIN_NAME ကွတ်ကီးအား ပြန်လည်ပေးပို့ရမည့် ဆာဗာ ဒိုမိန်းအမည်ကို သက်မှတ်ရာတွင် အသုံးပြုနိုင်ပါသည်။ ရေးသားထားခြင်းမရှိပါက ကွတ်ကီးအား Browser ဆီသို့ပေးပို့လိုက်သော ဆာဗာဒိုမိန်းကို အသုံးပြုမည်ဖြစ်သည်။ setDomain နှင့် getDomain ကို အသုံးပြုနိုင်မည် ဖြစ်သည်။
version=VERSION ကွတ်ကီး ၏ ဗားရှင်းကို သတ်မှတ်ရာတွင် အသုံးပြုနိုင်ပေသည်။ 0 ဆိုပါက Netscape ၏ သတ်မှတ်ချက် ဗားရှင်းဖြစ်ပြီး၊ 1 ဆိုပါက RFC2109 သည်၊ Default တန်ဖိုးဖြစ်ပြီး၊ setVersion နှင့် getVersion ကို အသုံးပြုနိုင်ပါသည်။
comment=COMMENT ကွတ်ကီးတွင် မှတ်ချက်များရေးသားရာတွင် အသုံးပြုနိုင်ပါသည်။ သို့ရာတွင် Netscape သတ်မှတ်ချက် ဗားရှင်း ကွတ်ကီးများမှာမူ အသုံးပြုနိုင်မည် မဟုတ်ပါ။ setCommentနှင့် getCommentလုပ်ဆောင်ချက်များကို ပြင်ဆင်ထားပါသည်။
secure HTTPS ကို အသုံးပြုပြီး ဆက်သွယ်ရာတွင် သတ်မှတ်ရန်လိုအပ်ပါသည်။ setSecure နှင့်  getSecure လုပ်ဆောင်ချက် များကို အသုံးပြုနိုင်ပါသည်။


ကျွှန်တော်တို့ ကွတ်ကီး ကိုအသုံးပြုပြီး အသုံးပြုသူနှင့် ဆာဗာကြား၌ အချက်အလက်များကို အပြန်အလှန်အသုံးပြုကြည့်ပါမည်။ အသုံးပြုသူက ဘယ်နှစ်ကြိမ် ဆက်သွယ်ထားသလဲ ဆိုတာကို သိရှိနိုင်တဲ့ နမှုနာကို ရေးကြည့်ပါမည်။

Counter1.java
   @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        
        Cookie ck = null;
        // request မှ ကွတ်ကီးများကို ရယူခြင်း
        Cookie [] cks =  req.getCookies();

        // အသုံးပြုနေသော ကွတ်ကီးကို ရှာဖွေခြင်း
        if (null != cks) {
            for (int i=0; i<cks.length ; i++) {
                if(CK_NAME.equals(cks[i].getName())) {
                    ck = cks[i];
                }
            }
        }
        
        // အသုံးပြုနေသော ကွတ်ကီးကို ရှာမရခဲ့ပါက
        if(null == ck) {
            ck = new Cookie(CK_NAME, "0");
        }
        
        // ကွတ်ကီးမှ တန်ဖိုးကို ရယူ၍ တစ်ပေါင်းခြင်း
        int count = Integer.parseInt(ck.getValue()) + 1;
        // တစ်ပေါင်းထားသော ကွတ်ကီး၏တန်ဖိုးကို ပြန်လည် သတ်မှတ်ခြင်း
        ck.setValue(String.valueOf(count));
        resp.addCookie( ck);
        

        // response တွင် ယူနီကုဒ် Encoding အဖြစ် သတ်မှတ်ခြင်း
        resp.setCharacterEncoding("UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.println(getHeaderHtml(EPISODE, "ကွတ်ကီးကို အသုံးပြုခြင်း"));
        writer.print("သင်သည် ဤစာမျက်နှာသို့ " + String.valueOf(count) + "ကြိမ် လာရောက်ခဲ့ပါသည်");        
        writer.println(getFooterHtml());
    }

အထက်ပါ ကုဒ်များဖြင့် ကွတ်ကီးကို အသုံးပြု၍ Access Counter တစ်ခုကို တည်ဆောက်ထားခြင်းဖြစ်ပါသည်။ လက်ရှိ Application မှ အသုံးပြု နိုင်သော ကွတ်ကီးများကို HttpServletRequest#getCookies လုပ်ဆောင်ချက်ဖြင့် ရယူနိုင်ပါသည်။ ရရှိထားသော ကွတ်ကီးများထဲမှ၊ ဤပရိုဂရမ်ဖြင့် ပြုလုပ်ထားသော ကွတ်ကီး၏အမည်နှင့်တူသော ကွတ်ကီးကို for ဝါကျကို အသုံးပြု၍ တစ်ခုချင်း ရှာဖွေပါသည်။ အမည်တူ ကွတ်ကီးကို ရှာဖွေတွေ့ရှိပါက ဤပရိုဂရမ်အတွင်းတွင် အသုံးပြုရန်ကွတ်ကီး နေရာတွင် အစားထိုးလိုက်ပါသည်။

ကွတ်ကီးကိုရှာဖွေမတွေ့ရှိခဲ့ပါက new Cookie(CK_NAME, "0") ဟု တန်ဖိုး "0" ရှိသော ကွတ်ကီးတစ်ခုကို ပြုလုပ်၍ အသုံးပြုမည့်ကွတ်ကီး နေရာတွင် အစားထိုးပါသည်။ ကျန်သော သတ်မှတ်ချက်များကို မရေးသားထားပါသောကြောင့် Default အတိုင်း သတ်မှတ်သွားမည်ဖြစ်သည်။ သက်တမ်းသည်လည်း Web Browser မပိတ်မချင်း အသုံးပြုနိုင်မည် ဖြစ်သည်။

Access Count အဖြစ်အသုံးပြုရန် ကွတ်ကီး၏ တန်ဖိုးကို Cookie#getValue ဖြင့် ရယူပြီး၊ Integer အဖြစ်ပုံစံပြောင်းပြီး တစ်ပေါင်းပါသည်။ အကယ်၍ အသစ်ပြုလုပ်ထားသော ကွတ်ကီးဖြစ်ပါက တန်ဖိုးမှာ "0" ဖြစ်မည်ဖြစ်၍၊ တစ်ပေါင်းပြီးသောအခါ တစ်ကြိမ်မြောက် ဆက်သွယ်ခြင်းဖြစ်ပါမည်။ ပြီးပါက တစ်ပေါင်းထားသောတန်ဖိုးကို Cookie#setValue ကို အသုံးပြု၍ ကွတ်ကီး၏တန်ဖိုးအဖြစ် ပြောင်းလည်းသတ်မှတ်လိုက်ပါသည်။ နောက်ဆုံးတွင် ပြုပြင်ပြီးကွတ်ကီးအား HttpServletResponse#addCookie ဖြင့်၊ Web Browser ထံပြန်ပို့မည့် Response တွင် ဖြည့်စွက် လိုက်ပါသည်။

ကျန်သောအပိုင်းများသည် HTML စာသားများကို ရေးသားနေခြင်းသာဖြစ်ပါသည်။ ဤနေရာတွင် getHeaerHtml နှင့် getFooterHtml သည် အခြားသော Class များတွင်လည်း အလားတူ အသုံးပြုလိုပါသောကြောင့် ၏ Static လုပ်ဆောင်ချက်များအဖြစ် ခွဲထုတ်ထားခြင်း ဖြစ်ပါသည်။ Java SE 5 မှစ၍ static တန်ဖိုးနှင့် လုပ်ဆောင်ချက်များကို import လုပ်နိုင်ပါသောကြောင့် class အမည်ကို ရေးသားရန်မလိုပဲ ကိုယ်ပိုင် လုပ်ဆောင်ချက်ကဲ့သို့ ရေးသားနိုင်ခြင်းဖြစ်ပါသည်။ အနည်းဆုံးလိုအပ်သော လုပ်ဆောင်ချက်များမှအပ လုပ်ဆောင်ချက်များကို မရေးသားခြင်းသည် class ၏ ရည်ရွယ်ချက်ကို နားလည်လွယ်စေသောကြောင့် ဤနည်းသည် လုပ်ငန်းခွင်တွင် အသုံးများသော နည်းတစ်မျိုးဖြစ်ပါသည်။

ရေးသားထားသော နမှုနာများကို Easy Servlet တွင် စမ်းသပ်ကြည့်ရှုနိုင်ပါသည်။ ထို့အပြင် Easy Servlet ၏ Source Code များကိုလည်း Easy Servlet Source Site မှ ရယူနိုင်ပါသည်။



ကွတ်ကီးကို အသုံးပြုသော နမှုနာ လင့်ခ်ကို နှိပ်ကြည့်ပါက အောက်ပါအတိုင်း Access လုပ်လာသည့် အကြိမ်ကို ဖော်ပြပေးမည် ဖြစ်သည်။




Session
အထက်ပါ ကွတ်ကီးခေါင်းစဉ်တွင် တွေ့မြင်ခဲ့သည့်အတိုင်း ကွတ်ကီးသည်၊ တစ်ခုနှင့်တစ်ခု ပတ်သက်မှု့မရှိပဲရှိနေခဲ့သော HTTP ပရိုတိုကောတွင် ဆက်သွယ်မှု့ရှိနေကြောင်း အတွေးအခေါ်(Session) ကို အသုံးပြုနိုင်ရန် အထောက်အကူပြုခဲ့သည်။ သို့ရာတွင် Security ပိုင်းဆိုင်ရာ အမြင်မှ၊ အချို့သည် ကွတ်ကီးကို လက်မခံနိုင်အောင် သတ်မှတ်ထားခြင်း၊ ထို့အပြင် အချို့သော Mobile သုံး Browser များတွင် ကွတ်ကီးကို အသုံးပြုနိုင်ခြင်းများရှိကြသည်။ ထိုအခါမျိုးအတွက် အသုံးပြုနိုင်ရန် Servlet သည် Server ဘက်တွင်လည်း Session ကို အသုံးပြုနိုင်ရန် စီမံချက်များကို ပြင်ဆင်ထားပါသည်။



Servlet ဘက်တွင် HttpServletRequest#getSession လုပ်ဆောင်ချက်ကို အသုံးပြုခြင်းအားဖြင့်၊ Session ကို အသုံးပြုနိုင်ပါသည်။ ထိုလုပ်ဆောင်ချက်သည်၊ ပါရာမီတာကို true ဟု ရေးထားပါက လက်ရှိ အသုံးပြုသူနှင့် ဆာဗာ၏ကြားတွင် သုံးလက်စ Session မရှိသေးပါက၊ HttpSession Object ကို အသစ်ပြုလုပ်ပြီး၊ သုံးလက်စ Session ရှိခဲ့ပါက ရှိပြီးသားSession Object ကို ပြန်ပေးမည်ဖြစ်သည်။
HttpSession Object သည် တစ်ခုတည်းသော Session ID ကိုပိုင်ဆိုင်ပြီး၊ Servlet Container မှ Session ID ကို စမ်းစစ်ခြင်းအားဖြင့် HttpSession Object ကို Control လုပ်နိုင်ခြင်းဖြစ်သည်။ Servlet အပေါ်တွင် HttpServlet Object ကို ပြုလုပ်ပြီးသည့်နောက်၊ Client ဆီသို့ Response ပြန်သောအခါတွင် Session ID အားသိမ်းဆည်း ထားစေပါသည်။ Default အားဖြင့်၊ Session ID သည် ကွတ်ကီးနေရာတွင် သိမ်းဆည်းထားလေ့ရှိပြီး၊ ကွတ်ကီးအပြင် အခြားသောနေရာတွင်လည်း သိမ်းဆည်းထားနိုင်ပါသည်။ ကွတ်ကီးအပြင်အခြားသော နေရာတွင် Session ID ကို သိမ်းဆည်းထားနိုင်ပုံကို နောက်ပိုင်းတွင် ဆက်လက်ဖော်ပြထားပါသည်။
Client သည်လည်း Session ကို ပိုင်ဆိုင်ပြီးသည့်နောက်ပိုင်း Request များတွင် Session ID ကို ထည့်သွင်း Request လုပ်ခြင်းအားဖြင့်၊ Client နှင့် Server ကြားတွင် ဆက်သွယ်မှု့ကို ဖြစ်ပွားစေပြီး၊ ယခင် Request များဖြင့် Session အပေါ်တွင် သိမ်းဆည်းထားသော အချက်အလက်များကို ပြန်လည်အသုံးချနိုင်မည် ဖြစ်ပါသည်။ ထို့အတွက် အသုံးချသူက Log in ပြီးသည့်အချိန်မှ စ၍ လုပ်ငန်းဆောင်တာများ ပြီးဆုံးသည့် တိုင်အောင် အဆင့်ဆင့်မှအချက်အလက်များကို ဆက်သွယ်အသုံးပြုနိုင်မည် ဖြစ်ပါသည်။
ဖော်ပြပါအတိုင်း Session သည် Server Memory ပေါ်တွင် တည်ရှိသောကြောင့်၊ တစ်ချိန်ချိန်တွင် အဆုံးသတ်သင့်ပါသည်။ အသုံးမပြုသော Session ကို Server Memory ပေါ်မှ ဖျက်ပစ်နိုင်ရန် HttpSession#invalidate လုပ်ဆောင်ချက်ကို အသုံးပြုနိုင်ပါသည်။ ဤလုပ်ဆောင်ချက်ကို ခေါ်ယူလိုက်ပါက Session ကို ဖျက်ပစ်နိုင်မည်ဖြစ်သည်။ သို့ရာတွင် လူတိုင်းသည် Session ကို ဖျက်ပစ်သော Servlet ကို ခေါ်မည်ဟု မဆိုနိုင်ပေ။ ထို့ကြောင့် အသုံးပြုခြင်းမရှိပဲ အချိန်အတန်ကြာလျှင်သူ့အလိုလို ဖျက်စီးပစ်နိုင်သော စီမံချက်ကို Servlet တွင် ပြင်ဆင်ထားပါသည်။ web.xml တွင် session-timeout ကို သတ်မှတ်ထားပါက ထိုအချိန်အတွင်း အသုံးပြုခြင်းမရှိပါက သူ့အလိုလို ပျက်စီးသွားမည် ဖြစ်သည်။
    <session-config>
        <session-timeout>5</session-timeout>
    </session-config>
Session နှင့် Cookie ၏ ကွာခြားချက်များကို သိရှိနှင်စေရန် အထက်ပါ ကွတ်ကီးတွင်အသုံးပြုခဲ့သော နမှုနာကိုပဲ Session ကို အသုံးပြု၍ ရေးသားကြည့်ပါဦးမည်။

Counter2.java
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        
        // Session ကို ခေါ်ယူခြင်း
        HttpSession session = req.getSession(true);
        // Session မှ တန်ဖိုးကို ရယူခြင်း
        String _count = (String) session.getAttribute(COUNR);
        int count = 0;

        if(null !=  _count)
            count = Integer.parseInt(_count);

        // တစ်ပေါင်းခြင်း
        ++count;
        
        // တစ်ပေါင်းထားသော တန်ဖိုးကို Session တွင် သိမ်းဆည်းထားခြင်း
        session.setAttribute(COUNR, String.valueOf(count));
        
        // response တွင် ယူနီကုဒ် Encoding အဖြစ် သတ်မှတ်ခြင်း
        resp.setCharacterEncoding("UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.println(getHeaderHtml(EPISODE, "Session ကို အသုံးပြုခြင်း"));
        writer.print("သင်သည် ဤစာမျက်နှာသို့ " + String.valueOf(count) + "ကြိမ် လာရောက်ခဲ့ပါသည်");        
        writer.println(getFooterHtml());
    }
အထက်ပါ doGet လုပ်ဆောင်ချက်သည် အချက်အလက်ကို Session အတွင်းမှ ထုတ်ယူ၊ သိမ်းဆည်းနေခြင်း မှလွဲ၍ အခြားသော နေရာများသည် Counter1.java နှင့် အတူတူပင်ဖြစ်သည်။ ဦးစွာ HttpServletRequest#getSession ဖြင့် HttpSession ၏ Object ကို ရယူပါသည်။ တဖန် COUNTER အမည်ဖြင့် သိမ်းဆည်းထားသော အချက်အလက်ကို HttpSession#getAttribute ကို အသုံးပြု၍ရယူနေသည်။ အကယ်၍ ပဋ္ဌမအကြိမ်ဖြစ်ပါက တန်ဖိုးကို ရရှိမည်မဟုတ်သောကြောင့် count ကို 0 ဟုသတ်မှတ်ထား ခြင်းဖြစ်သည်။ တဖန် ရရှိလာသော တန်ဖိုးသည် null မဟုတ်ပါက Integer တန်ဖိုးအဖြစ်ပြောင်းပြီး count တွင် အစားထိုးပါသည်။
လက်ရှိအကြိမ်ကို သတ်မှတ်ရန်အတွက် count ကို တစ်ပေါင်း၍ HttpSession#setAttribute ကို သုံးပြီး Session တွင် သိမ်းဆည်းစေပါသည်။ ဤသို့ပြုလုပ်ခြင်းအားဖြင့် နောက်တစ်ကြိမ် Session Object မှ သိမ်းဆည်းထားသော အချက်အလက်များကို အသုံးပြုနိုင်ခြင်း
 ဖြစ်သည်။ HttpSession သည်လည်း လိုအပ်သောတန်ဖိုးများကို အမည်နှင့်တွဲ၍ သိမ်းဆည်းထားနိုင်ခြင်း၊ အမည်ဖြင့် သိမ်းဆည်းထားသည့် အချက်အလက်၏တန်ဖိုးများကို ရယူနိုင်ခြင်း တို့ကြောင့်၊ Java ၏ Map သို့မဟုတ် Table အစရှိသည့် Interface များနှင့် ခပ်ဆင်ဆင်ဖြစ်ပါသည်။ ကျန်သော အပိုင်းသည် HTML စာသားများကို ရေးသားနေခြင်းသာဖြစ်ပါသည်။
HttpServlet ၏ အသုံးများသော လုပ်ဆောင်ချက်များမှာ အောက်ပါအတိုင်းဖြစ်ကြသည်။

လုပ်ဆောင်ချက် ရှင်းလင်းချက်
getAttribute ပါရာမီတာ ၏အမည်ဖြင့် Session တွင် သိမ်းဆည်းထားသော တန်ဖိုးကို ရယူနိုင်ပါသည်။
getAttributeNames Session တွင် သိမ်းဆည်းထားသော ပါရာမီတာ ၏အမည်များကို ရယူနိုင်ပါသည်။
getCreationTime Session ကို ပြုလုပ်ထားသည့် အချိန်ကိုရယူနိုင်ပါသည်။
getId Session ID ကို ရယူနိုင်ပါသည်။
getLasAccessedTime Session အား နောက်ဆုံးဆက်သွယ်ထားသော အချိန်ကို ရယူနိုင်ပါသည်။
getInactiveInterval Session ကို ဆက်သွယ်ခြင်းမပြုပဲ သိမ်းဆည်းနိုင်သော ကာလ၏ တန်ဖိုးကို ရယူနိုင်ပါသည်။
getServletContext ထို Session နှင့် ပတ်သက်မှု့ရှိသော ServletContext ကို ရယူနိုင်ပါသည်။
invalidate Session အား ပြီးဆုံးစေနိုင်သော လုပ်ဆောင်ချက် ဖြစ်သည်။ Invalidate လုပ်ထားသော Session Object အားဆက်သွယ် အသုံးချ နိုင်မည် မဟုတ်ပေ။
isNew Session သည် အသစ်ပြုလုပ်ထားသော Session ဖြစ်သလား ဟု သိရှိနိုင်သော လုပ်ဆောင်ချက်ဖြစ်ပါသည်။ Session အသစ်ဖြစ်ပါက true ဟုပြန်ရပြီး၊ အသစ်မဟုတ်ပါက false ဟု ပြန်ရမည်ဖြစ်သည်။
removeAttribute Session တွင် သိမ်းဆည်းထားသော ပါရာမီတာကို ဖျက်ပစ်နိုင်သော လုပ်ဆောင်ချက်ဖြစ်ပါသည်။
setAttribute ပါရာမီတာ၏ အမည်နှင့် တန်ဖိုးကို Session Object တွင် သိမ်းဆည်းရာတွင် အသုံးပြုသော လုပ်ဆောင်ချက် ဖြစ်ပါသည်။

 Tomcat နဲ့ Glassfish ကို အသုံးပြုပြီးရေးပါက Session ကို အသုံးပြုရန် အထွေအထူး ဘာမှမလိုပေမဲ့၊ Google App Engine (jetty) ကို အသုံးပြုသည့်အခါ appengine-web.xml တွင် အောက်ပါအတိုင်း ဖြည့်စွက် ရေးသားရန်လိုအပ်ပါသည်။
<sessions-enabled>true</sessions-enabled>

အထက်ပါ နမှုနာကိုလည်း Easy Servlet တွင် စမ်းသပ်ကြည့်ရှုနိုင်ပါသည်။



ကွတ်ကီးကို လုံးဝအသုံးမပြုသော Session
အထက်ပါတွင် ဖော်ပြခဲ့သည့်အတိုင်း Session ID သည် Default အတိုင်းဆိုပါက၊ Cookie တွင် ရေးသား၍ Client နှင့် Server အကြားတွင် အဆက်အသွယ်ပြုလုပ်နေပါသည်။ သို့ရာတွင် Security အနေအထားအရ Browser တွင် Cookie ကို လက်မခံပါဟု သတ်မှတ်ထားသည့်အခါမျိုး၊ ဒါမှမဟုတ် Mobile များ၏ အချို့သော Browser များတွင် Cookie ကို Support မလုပ်သည့် အခါများတွင်Cookie ကို သုံးမရသည့် အခါမျိုးလည်း ရှိတတ်ပါသည်။
ထိုကဲ့သို့အခါများအတွက် Servlet တွင် အခြားသောနည်းလမ်းမျိုးကို ပြင်ဆင်ထားပါသည်။ နောက်ပိုင်း Session အဖြစ်အသုံးပြုလိုသည့် URL များတွင် Session ID ကို ဖြည့်စွက်ရေးသားသော နည်းဖြစ်ပါသည်။ HttpServletResponse#encodeURL လုပ်ဆောင်ချက်ကို အသုံးပြုပါက URL ၏အနောက်တွင် အလိုအလျှောက် Session ID ကို ဖြည့်စွက်ရေးသားပြီး ဖြစ်ပေမည်။

Counter3.java
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        
        // Session ကို ခေါ်ယူခြင်း
        HttpSession session = req.getSession(true);
        // Session မှ တန်ဖိုးကို ရယူခြင်း
        String _count = (String) session.getAttribute(COUNR);
        int count = 0;

        if(null !=  _count)
            count = Integer.parseInt(_count);

        // တစ်ပေါင်းခြင်း
        ++count;
        
        // တစ်ပေါင်းထားသော တန်ဖိုးကို Session တွင် သိမ်းဆည်းထားခြင်း
        session.setAttribute(COUNR, String.valueOf(count));
        
        // response တွင် ယူနီကုဒ် Encoding အဖြစ် သတ်မှတ်ခြင်း
        resp.setCharacterEncoding("UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.println(getHeaderHtml(EPISODE, "Session ကို အသုံးပြုခြင်း"));
        writer.print("သင်သည် ဤစာမျက်နှာသို့ " + String.valueOf(count) + "ကြိမ် လာရောက်ခဲ့ပါသည်။<br /><br />");
        writer.print("<a href='" + resp.encodeURL("/SessionCtr2") + "' >ကွတ်ကီးမသုံးပဲ ဆက်သွယ်ကြည့်ခြင်း</a>");
        writer.println(getFooterHtml());
    }
အတော်များများမှာ Counter2.java နှင့် အတူတူပင်ဖြစ်သည်။ ကွယ်ပြားခြားနားသည်မှာ အောက်ဆုံးမှစ၍ တတိယမြောက်စာကြောင်းဖြစ်ပါသည်။ Counter3.java ကို ခေါ်သော လင့်ခ်တစ်ခုကို ရေးသားထားပြီး URL အား HttpServletResponse#encodeURL ဖြင့် Session ID ကို ဖြည့်စွက် ရေးသားနေခြင်းဖြစ်ပါသည်။ ဤကဲ့သို့ရေးသားခြင်း အားဖြင့် ကွတ်ကီးကိုလက်မခံသည့် အခါမျိုးတွင်လည်း Session ကို အသုံးပြုနိုင်မည်ဖြစ်ပါသည်။
မှတ်ချက်။  ။ နမှုနာများကို Easy Servlet တွင် လက်တွေ့စမ်းသပ်နိုင်ပါသည်။
ဦးစွာ Browser တွင် ကွတ်ကီးကို အသုံးမပြုပါဟု အောက်ပါအတိုင်း သတ်မှတ်ပါမည်။


အောက်ပါအတိုင်း Tutorials > Episode 3 မှ ကွတ်ကီးနှင့်ဆက်ရှင် နမှုနာကို သွားပါမည်။


ပြီးပါက 3. Session ကို သာအသုံးပြုထားသော နမှုနာလင့်ခ်ကို နှိပ်ကြည့်ပါမည်။ အောက်ပါအတိုင်း တစ်ကြိမ် ဟုဖော်ပြနိုင်မည်ဖြစ်သည်။


တဖန် ကွတ်ကီးမသုံးပဲ ဆက်သွယ်ကြည့်ခြင်း လင့်ခ်ကိုနှိပ်ကြည့်ပါမည်။


အထက်ပါအတိုင်း နှစ်ကြိမ်ဖြစ်ကြောင်းဖော်ပြနိုင်သည်ကို တွေ့ရမည်ဖြစ်ပါသည်။ ကွတ်ကီးကိုလက်မခံဟု သတ်မှတ်ထားသော်လည်း Session ကို ကောင်းစွာ အသုံးပြုနိုင်ကြောင်းကို သိရှိနိုင်ပါသည်။
Session နှင့် Cookie သည် Server Side Programming အတွက် မသိမဖြစ်လိုအပ်သော နည်းပညာဖြစ်ပါသည်။ Servlet Container ဘက်တွင် ဤကဲ့သို့ Session နှင့် Cookie ကို Control လုပ်နိုင်ကြောင်း၊ Session နှင့် Cookie ၏Life Cycle နှင့် သဘောသဘာဝကို သိရှိထားသင့်ပါသည်။ လက်တွေ့ လုပ်ငန်းခွင်တွင် Java EE ကို အသုံးပြု၍ Server Side Application ကို ရေးပါက ဤ Session နှင့် Cookie ကို မဖြစ်မနေ အသုံးပြုရတတ်သောကြောင့်ဖြစ်ပါသည်။

ဤအခန်းတွင် ရေးသားထားသော Source Code များကို Easy Servlet Source တွင် ရယူနိုင်ပါသည်။

ကိုးကား
http://legacy.techscore.com/tech/J2EE/Servlet/4.html
http://www.servlets.com/jservlet2/
http://www.oracle.com/technetwork/java/javaee/servlet/index.html

လေးစားစွာဖြင့်
မင်းလွင်

November 2, 2011

Servlet အတွင်းမှ ပါရာမီတာများကိုရယူခြင်း

ပြီးခဲ့သော Java EE အခန်းဆက် Java EE ၏နည်းပညာတစ်ခုဖြစ်သော Servlet  အကြောင်းကိုစတင်ခဲ့ပြီး၊ ပဋ္ဌမဦးဆုံး Servlet ပရိုဂရမ်တစ်ခုကို လက်တွေ့ရေးကြည့်ခဲ့ကြပါသည်။ HelloServlet ကိုရေးသားရာတွင် HttpServlet ဆိုသည့် Super Class ကို extends လုပ်၍ ရေးသားခဲ့သည် ကို သတိထားမိမည်ဖြစ်သည်။ HelloServlet သည် HttpServlet ၏အမွေဆက်ခံသော sub class ဖြစ်ပါသောကြောင့် HttpServlet တွင်ပိုင်ဆိုင်သော လုပ်ဆောင်ချက်များကို ခေါ်ယူအသုံးချနိုင်ခြင်း ဖြစ်သည်။

ယခုဘလောဂ်တွင်လည်း Servlet တစ်ခု၏ အခြေခံသဘောတရားဖြစ်သော Life Cycle ကိုအစပြု၍၊ အခြေခံ လုပ်ဆောင်ချက်များကို နမှုနာပရိုဂရမ်များကို ရေးရင်း ဆက်လက် လေ့လာ သွားပါဦးမည်။


Life Cycle Of A Servlet

ကျွှန်တော်တို့ Servlet ၏ အခြေခံ အသုံးပြုပုံတွေကို မသွားသေးခင် Servlet တစ်ခု၏ Life Cycle ကို ဖော်ပြချင်ပါသေးသည်။ Servlet အားလုံး၏ အခြေခံမှာ javax.servlet.GenericServlet ဖြစ်ပါသည်။  Servlet ၏ အခြေခံလုပ်ဆောင်ချက်များအားလုံးသည် ဤ GenericServlet တွင် စုစည်းထားပါသည်။ ထို့ကြောင့် Servlet တစ်ခု၏ Life Cycle ကို GenericServlet အားအခြေခံ၍ လေ့လာသွားပါမည်။

Client ထံမှ စတင်၍ Request လာသောအခါမှစပြီး Servlet အား Servlet Container အပေါ် Load လုပ်မည်ဖြစ်သည်။ Web Module ၏ သတ်မှတ်ချက်ဖြစ်သော web.xml တွင်၊ Module အား Deploy ချိန်တွင် Servlet အား Load လုပ်ရန် ရေးသားနိုင်ပါသော်လည်း၊ Default မှာ ဦးဆုံး Request လာတဲ့အခါတွင် Load လုပ်ခြင်း ဖြစ်ပါသည်။

Class File ကို Load လုပ်သောအခါ Servlet Container က Servlet ၏ GenericServlet#init လုပ်ဆောင်ချက်ကို ခေါ်ယူမည်ဖြစ်သည်။ init လုပ်ဆောင်ချက်အား Servlet ၏ သက်တမ်း တစ်လျှောက်တွင် တစ်ကြိမ်တည်းသာ ခေါ်ယူအသုံးပြုမည်ဖြစ်ပြီး၊ Servlet အတွက်လိုအပ်သော initialize လုပ်ဆောင်ချက်များကို ရေးသားလေ့ရှိပါသည်။

Client မှ Servlet ဆီသို့ Request လုပ်လာသောအခါ၊ ထိုRequest တစ်ခုချင်းစီအပေါ်တွင် Thread တစ်ခုစီ တည်ဆောက်၍ တာဝန်ထမ်းဆောင်စေပါသည်။ ထို Thread ထဲတွင် Servlet#service လုပ်ဆောင်ချက်ကို ခေါ်ယူမည်ဖြစ်သည်။ ထိုလုပ်ဆောင်ချက်ထဲတွင် Client ၏ တောင်းဆိုချက်ကို ခွဲချမ်းစိတ်ဖြာ၍၊ သင့်တော်သော တုံ့ပြန်ချက်ကို Response အဖြစ်ပြန် ပေးမည်ဖြစ်သည်။ ဤကဲ့သို့ Servlet တစ်ခုအား Thread ပေါင်းများစွာမှ ခေါ်ယူအသုံးပြုမည် ဖြစ်ပါသဖြင့်၊ တစ်ကြိမ်တည်းတွင် အခေါ်ခံရပါက အမှားအယွင်းများမရှိနိုင်အောင်၊ အထူးသတိပြု၍ ပရိုဂရမ်ကို ရေးသားသင့်ပါသည်။

Servlet Container သည် နောက်ဆုံးတွင် GenericServlet#destroy ကို ခေါ်ယူမည်ဖြစ်ပါသည်။ အတိုင်းအတာတစ်ခုအတွင်း Servlet အပေါ်တွင် Request မရှိသောအခါ၊ ဒါမှမဟုတ် Container ကို Stop လုပ်သောအခါတွင် destroy ကို ခေါ်ယူမည် ဖြစ်ပါသည်။ destroy လုပ်ဆောင်ချက်သည်လည်း init လုပ်ဆောင်ချက်ကဲ့သို့ပင် Servlet ၏ ဘဝတစ်လျှောက်တွင် တစ်ကြိမ်တည်းသာ အသုံးပြုမည့် လုပ်ဆောင်ချက် ဖြစ်ပါသည်။

Client ၏ HTTP Request အပေါ်တွင် လုပ်ဆောင်စေလိုသောအခါ၊ javax.servlet.HttpServlet Class ကို အသုံးပြုရန်လိုအပ်ပါသည်။ HttpServlet သည်လည်း GenericServlet ၏ Sub Class တစ်ခုဖြစ်ပါသောကြောင့်၊ အတွင်းပိုင်းရှိ လုပ်ဆောင်ချက်များမှာ GenericServlet နှင့် အတူတူပင်ဖြစ်သည်။ သို့ရာတွင် Http Request ၏Header အပေါ်တွင်မှုတည်၍ doGet၊ doPost ကဲ့သို့ လုပ်ဆောင်ချက်များကို ပြင်ဆင်ထားပါသည်။ အထက်ပါ လုပ်ဆောင်ချက်များသည်လည်း service လုပ်ဆောင်ချက်အတွင်းမှ header ၏ method အပေါ်မှုတည်၍ ခွဲခြားခေါ်ယူ အသုံးချနေခြင်းသာဖြစ်ပါသည်။


Sample Application

Servlet အတွင်းမှ ပြင်ပမှ Parameter များကိုရယူနိုင်ရန် နည်းများကိုပြင်ဆင်ထားလေသည်။ အသုံးများသော နည်းများမှာ အောက်ပါအတိုင်း ဖြစ်ကြ၏။
  1. Servlet ကို စတင် Load လုပ်ရာတွင် အသုံးပြုနိုင်ရန် Init Parameter အဖြစ်ရယူခြင်း။
  2. Http Request တွင် ပါဝင်သော Request Parameter များကို ရယူခြင်း။
  3. Http Header တွင် ရှိသော အချက်အလက်များကို ရယူခြင်း။
အထက်ပါ နည်းများအားလုံးကိုအသုံးပြု၍ ကျွှန်တော်တို့ နမှုနာ Servlet တစ်ခုရေးကြည့်ပါမည်။ ကျွှန်တော်တို့ရေးမည့် Servlet တွင် Client မှ get အနေရဲ့ရော post အနေနဲ့ပါ အသုံးပြုနိုင်ရန် ရေးသားပါမည်။ အဲ့ဒီအတွက် Servlet အထဲတွင် HttpServlet#doGet ရော HttpServlet#doPost ကိုပါ ရေးသားရန်လိုအပ်ပါသည်။

Client ဆီမှ အောက်ပါအတိုင်း ၃မျိုး Request လုပ်ရန်ရည်ရွယ်ထားပါသည်။
  1. Request Parameter မပါပဲ get ပုံစံဖြင့် Request လုပ်သည့်အခါ Init Parameter ကို ရယူ၍ ဖော်ပြပေးပါမည်။
  2. Request Parameter များဖြင့် post ပုံစံဖြင့် Request လုပ်သည့်အခါ header ၏တန်ဖိုးကိုစစ်ဆေး၍၊ တန်ဖိုးသည် 1 မဟုတ်ပါက Request Parameter များကို ရယူ၍ ပြန်လည်ဖော်ပြပေးပါမည်။
  3. အကယ်၍ headerအမည်ရှိ ပါရာမီတာ၏ တန်ဖိုးမှာ 1 ဖြစ်ခဲ့ပါက HTML ၏ Header ထဲမှ အချက်အလက်များကို ရယူ၍ ဖော်ပြပေးပါမည်။
အထက်ပါ အတိုင်း Servlet တစ်ခုကို ယခင်တစ်ခေါက်ရေးသားခဲ့သော MyFirstServlet ပရိုဂျက်အတွင်းတွင် ရေးသားသွားပါမည်။ Request လုပ်ရန် စာမျက်နှာကိုလည်း ယခင်တစ်ခေါက် အသုံးပြုခဲ့သော index.html ကိုပဲ ပြန်လည်ပြုပြင်၍ အသုံးပြုသွားပါမည်။ အသုံးပြုသွားမည့် ဖိုင်များမှာ အောက်ပါအတိုင်းဖြစ်ပါသည်။
  1. BasicServlet.java
  2. web.xml
  3. episode2.html

ဤတစ်ခေါက် Servlet တွင် get ပုံစံသာမက၊ post ပုံစံပါ အတူတူအလုပ်လုပ်စေရန်လိုအပ်ပါသဖြင့်၊ doPost ကို ပါရေးသားရန်လိုအပ်ပါသည်။

doPost
   /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }

ဤနမှုနာ၌ post ရော get ပါ အတူတူအလုပ်လုပ်စေလိုပါသဖြင့်၊ အတွင်းပိုင်းတွင် ရရှိလာသော Arguments များဖြင့် doGet ကို ပြန်ခေါ်နေခြင်း ဖြစ်ပါသည်။ လက်တွေ့ရေးသားရာတွင် System ၏ လိုအပ်ချက်အတိုင်း post နှင့် get ကို ခွဲခြား၍ ရေးသားလေ့ရှိပါသည်။

doGet
    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // response တွင် ယူနီကုဒ် Encoding အဖြစ် သတ်မှတ်ခြင်း
        response.setCharacterEncoding("UTF-8");
        
        // request တွင်ပါဝင်သော parameter အမည်များကို ရယူခြင်း
        Enumeration<String> params = request.getParameterNames();
        
        // request မှ header အမည်ရှိသော parameter ၏ တန်ဖိုးကို ထုတ်ယူခြင်း
        String isHeader = request.getParameter("header");
        
        // parameter အမည်များမရရှိခဲ့ပါက
        if(null == params || !params.hasMoreElements()) {
            // အခန်း ၁ လုပ်ဆောင်ချက်ကို ခေါ်ယူခြင်း
            this.doInitParamProcess(request, response);
        } else {
            // header ၏တန်ဖိုးသည် 1 ဖြစ်ပါက
            if("1".equals(isHeader) ) {
                // အခန်း ၃ လုပ်ဆောင်ချက်ကိုခေါ်ယူခြင်း
                this.doHeaderInfoProcess(request, response);
            } else {
                // အခန်း ၂ လုပ်ဆောင်ချက်ကို ခေါ်ယူခြင်း
                this.doRequestParamProcess(request, response);
            }
        }
    }

doGet အတွင်းတွင် Program အတွင်း၌ အသုံးပြုနိုင်ရန် Request ပါရာမီတာများနှင့် header အမည်ရှိ ပါရာမီတာ၏ တန်ဖိုးကိုဦးစွာရယူပါသည်။ Request ပါရာမီတာများအား HttpRequest#getParameterNames ဖြင့် ရယူနိုင်ပြီး၊ header အမည်ရှိ ပါရာမီတာ၏ တန်ဖိုးအား HttpRequest#getParameter ဖြင့် ရယူနိုင်ပါသည်။

ရရှိလာသော အချက်အလက်များဖြင့် အတွင်းပိုင်းတွင်ခွဲခြားလုပ်ဆောင်စေခြင်းဖြစ်ပါသည်။ ပါရာမီတာများ params သည် null သို့မဟုတ် မပါခဲ့ပါက၊ အခန်း၁ လုပ်ဆောင်ချက်ဖြစ်သော BasicServlet#doInitParamProcess ကို ခေါ်ယူလုပ်ဆောင်စေပါသည်။ တဖန် ပါရာမီတာများသည် တစ်ခုမကပါဝင်၍၊ header ၏ တန်ဖိုးသည် 1 ဖြစ်ခဲ့ပါက၊ အခန်း ၃ လုပ်ဆောင်ချက်ဖြစ်သော၊ BasicServlet#doHeaderInfoProcess ကို လုပ်ဆောင်စေပါသည်။ နောက်ဆုံးတွင် ပါရာမီတာများသည် ၁ခုထက်မကပါဝင်ပြီး၊ header ၏တန်ဖိုးသည် 1 မဟုတ်ခဲ့ပါက BasicServlet#doRequestParamProcess ကို လုပ်ဆောင်စေပါသည်။


Init Param များကိုရယူခြင်း

Servlet တစ်ခုသည် Client ထံမှ ပဋ္ဌမအကြိမ် Request လုပ်သောအခါတွင် တစ်ကြိမ်တည်းသာ Servlet Container အပေါ်ကိုယ် ခေါ်ယူခြင်းခံရပါသည်။ ပြီးလျှင် Servlet Container အပေါ်တွင် Object အဖြစ်တည်ရှိနေပြီး၊ Client ထံမှ Request လာတိုင်း သင့်တော်သည့် လုပ်ဆောင်ချက်များကို Thread များအဖြစ် ခေါ်ယူလေ့ရှိ၏။

ပဋ္ဌမဦးဆုံးအချိန်ခေါ်ဆိုသည့် လုပ်ဆောင်ချက်မှာ GenericServlet#init ဖြစ်ပြီး Servlet ၏ လိုအပ်သောလုပ်ဆောင်ချက်များကို init အတွင်းတွင် လုပ်ဆောင်လေ့ရှိပါသည်။ တဖန် init-param များကို web.xml ၏ သတ်မှတ်ချက်တွင် ထည့်သွင်းရေးသားချင်းအားဖြင့်၊ servlet ကို Load လုပ်သောအခါ memory အပေါ်ကိုယ်ခေါ်ယူထားလေ့ရှိပါသည်။

init-param
    <servlet>
        <description></description>
        <display-name>BasicServlet</display-name>
        <servlet-name>BasicServlet</servlet-name>
        <servlet-class>com.episode2.BasicServlet</servlet-class>
        <init-param>
            <param-name>param1</param-name>
            <param-value>မြန်မာစာပါရာမီတာ။</param-value>
        </init-param>
        <init-param>
            <param-name>param2</param-name>
            <param-value>web.xmlတွင် မြန်မာစာကို အသုံးပြုနိုင်ပါသည်။</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>BasicServlet</servlet-name>
        <url-pattern>/Basic</url-pattern>
        <url-pattern>/Episode2</url-pattern>
    </servlet-mapping>

အထက်ပါအတိုင်း web.xml တွင် BasicServlet တွင်အသုံးပြုမည့် init-param များကို ဖြည့်စွက်ရေးသားနိုင်ပါသည်။ ဤသို့ရေးသားခြင်းအားဖြင့်၊ BasicServlet ကို Load လုပ်သောအခါ၊ init-param များအဖြစ် param1 နှင့် param2 ကို Memory အပေါ်ကို ခေါ်ယူထားနိုင်မည်ဖြစ်သည်။ Memory အပေါ်ရှိ init-param များကို Servlet အတွင်းမှခေါ်ယူပုံမှာ doInitParamProcess ထဲတွင်ဖော်ပြထားပါသည်။

doInitParamProcess
    /**
     * အခန်း ၁ လုပ်ဆောင်ချက်
     * @param request
     * @param response
     * @throws IOException 
     */
    private void doInitParamProcess(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        
        PrintWriter writer = response.getWriter();
        writer.println(getHeaderHtml("လေ့ကျင့်ခန်း ၁ ။   ။ Init Parameters များကို အသုံးပြုခြင်း။"));
        writer.println("<p>web.xml ၏ init-param ၏ တန်ဖိုးများဖြစ်ကြပါသည်။</p>");
        writer.println("<ul>");

        // init-param အမည်များကို ရယူခြင်း
        Enumeration<String> names = getInitParameterNames();
        // init-param အမည်များ မကုန်မချင်း
            while(names.hasMoreElements()) {
                String name = names.nextElement();
                writer.println("<li>");
                // init-param အမည်နှင့် တန်ဖိုးကိုရေးသားခြင်း
                writer.println(name + " : " + getInitParameter(name));
                writer.println("</li>");
            }
        writer.println("</ul>");
        writer.println(getFooterHtml());
    }

အထက်ပါ လုပ်ဆောင်ချက်ထဲတွင် ဦးစွာ private လုပ်ဆောင်ချက်ဖြစ်သော getHeaderHtml ဖြင့် HTML စာသားများကို ရယူပြီးResponse ၏ printWriter ဖြင့် ရေးသားစေပါသည်။ အလားတူနောက်ဆုံးတွင်လည်း getFooterHtml ဖြင့် HTML ၏ အောက်ပိုင်းစာသားများကို ရေးသားစေပါသည်။ getHeaderHtml ရော getFooterHtmlပါ ရိုးရိုးစာလုံးများကို ပြန်ပေးသောလုပ်ဆောင်ချက်များဖြစ်ပြီး၊ Servlet နှင့် မပတ်သက်ပါသဖြင့် အသေးစိတ်ဖော်ပြတော့မည် မဟုတ်ပါ။

တဖန် init-param များ၏အမည်အားလုံးအား GenericServlet#getInitParameterNames ဖြင့်ရယူနေပါသည်။ GenericServlet သည် Servlet အားလုံး၏ Super Class ဖြစ်ပါသဖြင့် Servlet များအားလုံးမှ သူ၏လုပ်ဆောင်ချက်များကို ခေါ်ယူအသုံးပြုနိုင်ခြင်းဖြစ်သည်။ ရရှိလာသော ပါရာမီတာများကို while ဝါကျကိုအသုံးပြု၍ မကုန်မချင်း အောက်ပါ လုပ်ဆောင်ချက်များကို လုပ်ဆောင်စေပါသည်။ ရရှိလာသော init-param အမည်ဖြင့် GenericServlet#getInitParameter ကို အသုံးပြု၍ တန်ဖိုးကိုရယူပါသည်။ ရယူထားသော ပါရာမီတာအမည်နှင့် တန်ဖိုးကို Response ဆီကို တစ်ကြောင်းခြင်း ရေးသားနေစေခြင်းဖြစ်ပါသည်။


Request Parameter များကို ရယူခြင်း
doRequestParamProcess
    /**
     * အခန်း၂ လုပ်ဆောင်ချက်
     * @param request
     * @param response
     * @throws IOException
     */
    private void doRequestParamProcess(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        PrintWriter writer = response.getWriter();
        writer.println(getHeaderHtml("လေ့ကျင့်ခန်း ၂ ။   ။ Request Parameters များကို အသုံးပြုခြင်း။"));
        writer.println("<p>Request Parameter ၏ တန်ဖိုးများဖြစ်ကြပါသည်။</p>");
        writer.println("<ul>");

        // request တွင်ပါဝင်သော parameter အမည်များကို ရယူခြင်း
        Enumeration<String> params = request.getParameterNames();
        while(params.hasMoreElements()) {
            String name = params.nextElement();
            writer.println("<li>");
            String value =  request.getParameter(name);
            value = new String(value.getBytes(DEF_ENC), "UTF-8");
            // param အမည်နှင့် တန်ဖိုးကိုရေးသားခြင်း
            writer.println(name + " : " + value);
            writer.println("</li>");
        }
        writer.println("</ul>");
        writer.println(getFooterHtml());
    }

Request အဖြစ်ပေးပို့လိုက်သော Parameter များအား HttpServletRequest#getParameterNames ကို အသုံးပြု၍ ရယူပါသည်။ တဖန် ပါရာမီတာ၏ တန်ဖိုးကိုလည်း HttpServletRequest#getParameter ကိုအသုံးပြု၍ ရယူပါသည်။ တစ်ခုသတိထားရမည်မှာ URL မှတဆင့် RequestParameter ကို ရယူရာတွင် မြန်မာယူနီကုဒ်ကို အသုံးပြုပါက Encoding လုပ်ရန်လိုအပ်သည်ဆိုသည့်အချက်ပင်ဖြစ်သည်။

Servlet မှ RequestParameter ကို ရယူရတွင် Default ISO-8859-1 ဖြင့် ရယူနေပါသဖြင့်၊ မြန်မာယူနီကုဒ်ကို အသုံးပြုလိုသောအခါ UTF-8 ပုံစံသို့ ပြောင်းရန်လိုအပ်သည် ဆိုသောအချက်ဖြစ်ပါသည်။ request.getParameter(name) ဖြင့်ရရှိလာသော String value မှတဆင့်၊ value.getBytes("ISO-8859-1")ဖြင့်၊ byte [] ပုံစံကိုပြန်ပြောင်းပါသည်။ တဖန် new String (byte [], "UTF-8") ဖြင့် ယူနီကုဒ်ပုံစံကို ပြန်ပြောင်းယူရန်လိုအပ်ပါသည်။

ကျန်သောနေရာမှာမှာ doInitParamProcess အတိုင်း၊ ပါရာမီတာများကို တစ်ကြောင်းခြင်းပြန် ရေးနေခြင်းသာဖြစ်ပါသည်။


မှတ်ချက် ။  ။ နမှုနာ Application ကို လက်တွေ့စမ်းသပ်ကြည့်ရှုနိုင်စေရန်၊ ပရိုဂျက်ကို Google Project Hosting တွင် တင်ထားပြီး၊ Application ကို လည်း Google App Engine အပေါ်တွင် တင်ထားပါသည်။ GAE Project တစ်ခုကို အသစ်တည်ဆောက်ပြီး၊ လိုအပ်သော web.xml, index.html နှင့် servlet src ဖိုင်များကို ယခင်ပရိုဂျက်မှ import လုပ်ယူလိုက်ရုံဖြင့် အလုပ်လုပ်ပါသည်။ သို့ရာတွင် မြန်မာစာ ယူနီကုဒ်အတွက် ပြောင်းလည်းရန်မလိုအပ်ပါသဖြင့်၊ byte ကို ပြောင်းနေတဲ့နေရာနဲ့ အစားထိုးနေတဲ့နေရာကို ပြုပြင်ရေးသားလိုက်ပါသည်။
Tomcat နှင့် Glassfish တွင် request parameter ၏ default character set မှာ ISO-8859-1 ဖြစ်သော်လည်း၊ Google App Engine တွင်အသုံးပြုနေသော jetty မှာ utf-8 ကို encode လုပ်နိုင်ပုံရပါသည်။
ထို့ကြောင့် Google App Engine တွင် မြန်မာယူနီကုဒ်ကို Request Parameter အဖြစ်အသုံးပြုလိုပါကစာလုံးပုံစံပြောင်းရန် မလိုအပ်ပဲ၊ HttpRequest#getParameter လုပ်ဆောင်ချက်ဖြင့် ရရှိသောတန်ဖိုး အတိုင်းအသုံးပြုနိုင်မည် ဖြစ်သည်။

Sample Application
http://mmitp-servlet-tutorial.appspot.com/

Source Code
http://code.google.com/p/mmitpro-tutorials/source/browse/servlet-tu...

Google App Engine (Start Guide)
http://code.google.com/intl/my/appengine/docs/java/gettingstarted/

HTML Header အချက်အလက်များကို ရယူခြင်း
doHeaderInfoProcess
   /**
     * အခန်း၃ လုပ်ဆောင်ချက်
     * @param request
     * @param response
     * @throws IOException
     */
    private void doHeaderInfoProcess(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        PrintWriter writer = response.getWriter();
        writer.println(getHeaderHtml("လေ့ကျင့်ခန်း ၃ ။   ။ Header ၏တန်ဖိုးများကို အသုံးပြုခြင်း။"));
        writer.println("<p>Request Headerတွင်ပါဝင်သော တန်ဖိုးများဖြစ်ကြပါသည်။</p>");
        writer.println("<ul>");
        // request တွင်ပါဝင်သော Header အမည်များကို ရယူခြင်း
        @SuppressWarnings("unchecked")
        Enumeration<String> names = request.getHeaderNames();
        while(names.hasMoreElements()) {
            String name = names.nextElement();
            writer.println("<li>");
            // အမည်နှင့် တန်ဖိုးကိုရေးသားခြင်း
            writer.println(name + " : " + request.getHeader(name));
            writer.println("</li>");
        }
        writer.println("</ul>");
        writer.println(getFooterHtml());
    }

အထက်ပါ လုပ်ဆောင်ချက်သည်လည်း အခြားသောလုပ်ဆောင်ချက်များကဲ့သို့ပင်၊ Header တွင်ပါဝင်သော အချက်အလက်များကိုရယူ၍၊ ရရှိလာသော Header  အချက်အလက်များကို တစ်ခုချင်း Response တွင်ပြန် ရေးနေခြင်းသာဖြစ်သည်။ Header တွင်ပါဝင်သော အချက်အလက်များကိုရယူရာ၌ HttpServletRequest#getHeaderNames ကို အသုံးပြုပြီး၊ header ၏တန်ဖိုးများကိုရယူရာတွင် HttpServletRequest#getHeader ကို အသုံးပြုနေပါသည်။

Sample Application ကို စမ်းသပ်ခြင်း

ရေးသားထားသော နမှုနာများကို Sample Application Site http://mmitp-servlet-tutorial.appspot.com/  တွင် လက်တွေ့ စမ်းသပ်ကြည့်နိုင်ပါသည်။



ပြီးလျှင် Episode2 ၏ Init Parameters လင့်ခ်ကို နှိပ်ကြည့်ပါမည်။ လင့်ခ်၏ href attribute ၏ တန်ဖိုးတွင် "/Basic" ဟုရေးသားထားပါသဖြင့် ပါရာမီတာမပါပဲ get method ဖြင့် BasicServlet ကို ခေါ်မည်ဖြစ်သည်။ ထိုအခါ BasicServlet#doGet လုပ်ဆောင်ချက်ကို အခေါ်ခံရမည်ဖြစ်ပြီး၊ Request Parameter မပါပါသဖြင့် BasicServlet#doInitParamProcess လုပ်ဆောင်ချက်ကို ခေါ်ယူမည်ဖြစ်သည်။



တဖန် မာတိကာသို့လင့်ခ်ဖြင့် ပင်မစာမျက်နှာသို့ပြန်သွား၍ ဒုတိယလင့်ခ်ဖြစ်သော Request Parameters ကို နှိပ်ကြည့်ပါမည်။ Request Parameters လင့်ခ်၏ onclick event ဖြင့် javascript လုပ်ဆောင်ချက် getEp2Ls2 ကို ခေါ်ယူနေပါသည်။ တဖန် ထိုလုပ်ဆောင်ချက်အတွင်းတွင် ep2ls2 Form ကို submit လုပ်နေပါသည်။ ep2ls2 သည် post ပုံစံဖြစ်ပါသဖြင့်၊ BasicServlet#doPost က အလုပ်လုပ်ပါလိမ့်မည်။ doPost အတွင်းမှ doGet ကို ပြန်ခေါ်နေပါသည်။ ထို့အပြင် request parameter မှာ null မဟုတ်ပဲ၊ header အမည်ရှိ parameter မှာမပါဝင်ပါသဖြင့် BasicServlet#doRequestParamProcess ကို အလုပ်လုပ်စေမည်ဖြစ်ပါသည်။



နောက်ဆုံးတွင် မာတိကာစာမျက်နှာမှ Header အချက်အလက်များ လင့်ခ်ကိုစမ်းကြည့်ပါမည်။ လင့်ခ်၏ href attribute ၏ တန်ဖိုးတွင် "/Basic?header=1"ဟု တိုက်ရိုက်ရေးသားထားပါသောကြောင့် get method တွင် header ပါရာမီတာကို ပေး၍ request လုပ်နေခြင်းဖြစ်ပါသည်။ Servlet Container မှ BasicServlet#doGet လုပ်ဆောင်ချက်ကို အခေါ်ခံရပြီး၊ request Parameter အထဲတွင် header၏တန်ဖိုးသည် 1 ဖြစ်သောကြောင့်၊ doGet အတွင်းတွင်ရေးသားထားသော လော့ဂျစ်အတိုင်း BasicServlet#doHeaderInfoProcess ကို  အလုပ်လုပ်စေမည် ဖြစ်ပါသည်။



ကျွှန်တော်တို့ ဤဘလောဂ်ဖြင့် Servlet တစ်ခုအတွင်းမှ Init Parameter များကို ရယူပုံ၊ Request Parameter များကိုရယူပုံ နှင့် Header တွင်ပါဝင်သော အချက်အလက်များကို ရယူပုံတို့ကို လေ့လာခဲ့ပါသည်။ တဖန်မြန်မာယူနီကုဒ်ကို Request Parameter အဖြစ်အသုံးပြုလိုပါက စာလုံးပုံစံကို ပြန်ပြောင်းရန်လိုအပ်ကြောင်းကိုလည်းလေ့လာခဲ့ပါသည်။

ဤဘလောဂ်တွင် HTML Tag များကိုလည်း Servlet မှရေးသားနေစေသည်ကို တွေ့ရပေမည်။ ဤကဲ့သို့ရေး၍ရပါသော်လည်း လက်တွေ့တွင်မှု ဤကဲ့သို့ရေးသားလေ့မရှိပါ။ HTML များကို ဒီဇိုင်းရေးရလွယ်ကူသော JSP အဖြစ်ရေးသားပြီး Servlet မှ လိုအပ်သောအချက်အလက်များကိုသာ ပြန်ပို့ပေးစေပါသည်။ လက်တွေ့တွင်မှု JSP နှင့် Servlet ကို တွဲ၍ ရေးသားသည်က များသော်လည်း၊ JSP အကြောင်းကို နောက်အခန်းများတွင် ဖော်ပြမည်ဖြစ်ပါသောကြောင့် လက်ရှိအနေအထားအရ Servlet တစ်ခုတည်း ဖြင့်သာ ရေးသားနေခြင်းဖြစ်ပါသည်။

နောက်အခန်းများဖြင့် Session နှင့် ကွတ်ကီးအကြောင်းကို ဆက်လက်၍ ဖော်ပြသွားပါဦးမည်။

လေးစားစွာဖြင့်
မင်းလွင်