วันพุธที่ 17 พฤศจิกายน พ.ศ. 2553

Add header on config for log4j

วิธีการเพิ่ม header ให้กับ log file ของ log4j เจอตัวอย่างมา
ก็เลยไปไล่ดู สรุป มันมี get header อยู่แล้วให้ทำการ overwrite มัน
ก็จะได้ประมาณนี้

//-------------- ex -------------------
public class MyPatternLayout extends PatternLayout {
    public MyPatternLayout() {
        super();
    }

    public String getManualHeader(){
        StringBuffer buf = new StringBuffer();
        buf.append(" ===== HEADER FILE ===== " \r\n);
        return buf.toString();
    }

    public String getHeader() {

        StringBuffer buf = new StringBuffer();
        buf.append( this.getManualHeader);

        String superHeader = super.getHeader();
        if ( superHeader != null ) {
            buf.append( superHeader );
        }
        
        return new String(buf);
    }
}


เวลาทำการ config properites file ก็ทำการเปลี่ยนไปว่า

#-------------- ex -------------------
log4j.appender.ErrorLog=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ErrorLog.layout=com.mycompany.package.MyPatternLayout
log4j.appender.ErrorLog.layout.ConversionPattern=%m%n
log4j.appender.ErrorLog.File=/usr/local/muwaho/back/log/ErrorLog.log
log4j.appender.ErrorLog.DatePattern='.'yyyyMMdd

วันพุธที่ 6 ตุลาคม พ.ศ. 2553

special charactor for html

✎✟ஐ ≈ ✲ ❈ ➹ ҳ̸Ҳ̸ҳ ☀☂☁【】┱ ┲ ❣ ✚ ✪ ✄ ☪ ☣ ☢ ☠ ☭ ♈ ➸ ✓ ✔ ✕ ✖ ∷ ღ ☃ ❆ £∆ Š Õεїз Ő ő ∞ © ‡ † ☆ ★♀♂♫ ♬ ♪ ♩ ♭ ♪ o O # ♀♂ √ ╮╭ ╯╰ ァ ┱ ┲-─
-─═┳︻∝╬══→┠♥:: ======>> ┈━═☆ (*)·´`·.¸.»┣▇▇▇═─ .:* «-(¯`v´¯)«-- ..·.¸¸·´¯`·.¸¸.ஐ ..¤¸¸.·´¯`·.¸·.>>--» ‧ °º¤ø,¸¸,ø¤º°`°º¤ø•.★*... ...*★.•━══━

Letters
άåαÀẵ ĪäÅÄÀÁặ βвБ฿вß Ç℃© כċς δÐđ ḗέәЕỀỂ℮ēềёξè ғ₣₣ƒſfƒ Ĝġģ ђЂҢĤĥħ їίÎĮįι ĴĵјЈĴ ĵj ķĶҚķĸκ Ļ₤ℓł£łḺĽ м ηйиή ๐ØΘỢỔổǿΦõō●ớσ ρþÞρPpþÞ ¶ q ЃЯŖŘяѓ ṩ§ѕ$Ššی ş ţ†ŧ†Ŧŧŧτ μΰϋüµυЏùύ ν ω Ŵώώ χ×Х χХ× xҲҳ× ¥λчφҸỲỳỴЎўýỹ ẕź ŻżŽžZZz ㎜㎝¥۵Ħяŧ§۵Ħn۵°˜ ㏕㎡°ŧėł


▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▉ ▊ ▋ ▌ ▍ ▎─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏ ┐ ┑ ┒┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟ ┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯ ┰ ┱︻ ︼


✪✦✧✩ ☆ ✫¤ ∫√Ÿ¤S¥ 〢◇ ε‥★²ºº๑x④╳..* ≮┊≯◇┢┦≤ ≥ <├┤╰☆╮★【】〖〗@﹕﹗/ " _ < > `,·。≈{}~ ~() _ -『』√ $ @ * & # ※┓┗┛┳⊥﹃﹄┌ ┐└ ┘∟「」↑↓→←↘↙♀♂┇┅ ﹉﹊﹍﹎♡ з ◦¯ω¯◦ *.:。✿。.:* ...❤ 。❤... ☆۩ ۞ ۩ ۩ ۞ ۩☆卐 々∞Ψ ∪∩∈∏ ♣ ▣ ▤ ▥ ▦ ▩ ◘ ◙ ◈ ♫の ℡ ぁ §∮〝〞ミ灬ξ№∑⌒ξζω*ㄨ ≮≯ +╭
''''''<>▧ ▨》《 ↘.﹌ └◆灬♂┅•``*✿╱╲╱╲ ♥の © ® ℡ « » ™ ░ ◑ ╄━ 卍 ┛┗ ┓┏ ◇ ◆ ㄨ⌒ ※ × ◎ ☂ ☜ ☞ ☜ ☞ ❤ ♡ ♥『』♦ ◊ ◦ ◘ ◙ ♠ ∠ ⌒ ⊙ ≌ ∽ √ ≦ ≧ ≒ ≡ ﹢ ﹣ ﹤ ﹥ ﹦~ ∟ ⊿ ㏒ ㏑ 兀♧ ♣ ▣▤ ▥ ▦ ▩ ๑ ▪ ▫ □ 【】〖〗ψ▓ ► ◄ ▒ ░ Café №@ ㊣ ™ ℡ ▪ ▫ ☼ ■♀『』◆◣◥♂ ♀ ♥ ♡ ☜ ☞ ☎ ☏ ⊙ ◎ ☺ ☻► ◄ ▧ ▨ ♨ ◐ ◑ ↔ ↕ .


\("▔□▔)/\("▔□▔)/\("▔□▔)/<( ̄︶ ̄)/<( ̄︶ ̄)/<( ̄︶ ̄)/ ~( ̄▽ ̄)~( ̄▽ ̄)~!<( ‵□′)───C<─___-)|| 跌(┬_┬) ↘ (。・ω・)ノ゙ (ξ・з・)ξ (・Ω・)ノ (。Ф∀Ф。)o-┭┮__┭┮.▪ ▫ ミ●﹏☉ミ *∩-ℓ¦©ï°ü$ _∩* ≧0≦ o(╥﹏╥)o //(ㄒoㄒ)// {{{(>_<)}}}≧▽≦y (/≧▽≦/) (≧▽≦)y (/≧▽≦)/~┴┴ (≧▽≦)(@)\(≧3≦)/ \(≧ω≦)/ o(≧ω≦)o o(≧o≦)o ˋ( ° ▽、° ) (_ _。) ┴┴~\(≧▽≦)/~┴┴ ≧□≦○ ︽⊙_⊙︽ (⊙_⊙)︹ ︺ ︿ ﹀ ︽ ︾ ︻ ︼ ︷ ︸ ︵ ︶(;゚曲゚) ð å Å Â ª Ä Á À ã Ã ß Ç ç © ¢ d ð Ð ë é ê è € £ Ë É Ê È ƒ µ ï í î ì ¡ ¦ Ï Í Î Ì ¿ £ ¿ ñ Ñ ø Ø º ° ¤ Õ ò ๐ O þ Þ ¶ ® § ک ¯|¯ ‡ †ü ü ú û ù µ Ü Ú Ù Û × ÿ ¥ æ Æ Œ ™ ½ ¼ ¾ ¹ ² ³ ‡ † Ψ ╥ ± ÷ ※ ぷ ₪ ௫ ◎ べ ▽「」『』# の ๑ εїз ♂ ♀ ↔ ↕ あ ஓ ぃ ξ ζ ω ஐ № ╝╚╔ ╗╬ ╠ ╣ ═ ╓ ╩ ┌ .: ∴ ≈ ۩۩.๑۩۞۩๑ ۞ ۩ﺴ ▓ ▒ ҳ̸Ҳ̸ҳ☆ ★ ❤ ♥ ♡ ღ ♠ ♣ ♪♫ ♬ ♩ ♭ ♪ ▲ ▼ ► ◄ ◢ ◣ ◥ ◤ ▽ ¤ ◦ ◊ ♦ ◇ ◆ ▪ ▫ ◙ ▣ ☼ ▄ █ ▌ ⊙ ◎ ○ ☺ ☻ 。 • ● □ ■ ✲❉ ✿ ❀ ✪✣✤✥✦✧✩✫✬✭✮✯✰✱✲✳❃❂❁❀✿✾✽✼✻✺✹✸✷☣☢☠☭♈➸╬∴☀☆∷﹌★◎▶☺☻►◄♨◐◑↔↕↘☼♦◊◦♠♣✎✟➹❀✏ஓ☂✖®✲ღ☃❆∆ŠÕ╠╣Őő (;°○°)(>c<)(づ ̄³ ̄)づ»-(¯`v´¯)-»-─═┳︻∝╬══ ::====== >>┈━═☆ ┣▇▇▇═─ ⊹⊱⋛⋋⋌⋚⊰⊹ 彡➸❝❞°﹌﹎╱╲☁₪¡Þ✱௫μ※◤◥﹏﹋﹌∩∈∏╰☆╮≠→№←ミ●﹏☉ミ

*∩_∩*♂♀♥♡☜☞☎☏⊙◎☺☻►◄▧▨♨◐◑↔↕▪▫☼♦▀▄█▌▐░▒▬♦◊◦☼♠♣▣▤▥▦▩◘◙◈♫♬♪♩♭♪の☆→あぃ£❤。◕‿◕。 ღ❀◢◣◥◤▽▧▨▣▤▥▦▩◘◙▓▒░Café№@㊣™℡凸の๑۞๑๑۩ﺴﺴ۩๑๑۩۞۩๑o(‧'''‧)o❆べò⊹⊱⋛⋋⋌⋚⊰⊹ⓛⓞⓥⓔべ ☀☼☜☞⊙®◈♦◊◦◇◆εїз ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ

(◡‿◡✿) (◕‿◕✿) (◕〝◕) •.★*... ...*★.• ·*.:。✿✲-·(¯`°.•°•.★* *★ .•°•.°´¯)*¤°•★ •:*´¨`*:•.☆۩ ۞ ۩ ۩ ۞ ۩☆•:*´¨`*:•.
*.:。 ✿*゚‘゚・✿.。.:* *.:。✿*゚’゚・✿.。.:* *.:。✿*゚¨゚✎・ ✿.。.:* *.:。✿*?(•ิ_•ิ)? ( > c < ) \(•ิ_•ิ\) (/•ิ_•ิ)/ (◕‿◕✿) (◕〝◕) (◡‿◡✿) •.★* »-(¯`v´¯)-» ╬══→ ::======>> ┈━═ (づ ̄ ³ ̄)づ (;°○° )❤ 。◕‿◕。 (◡‿◡✿) ◕。◕ (◕〝◕) o(‧'''‧)o ✖︶︹︺✖ o(╥﹏╥)o {{{(>_<)}}} ☜❤☞❃ ºº ₪ ¤ 큐 « » ↘ 〒 ¢

(◕‿◕✿) (◕〝◕) (◡‿◡✿) (◡‿◡❀) (●*∩_∩*●) (◕〝◕) (。◕‿◕。) (◕‿-。) (•‘๑’•) ❤。◕‿◕。❤ (o'‿'o✿) .:。✿。◕ε◕✿。:.
⊙▂⊙  ⊙0⊙ ⊙︿⊙ ⊙ω⊙ ⊙﹏⊙ ⊙△⊙ ⊙▽⊙ ↔ ↕ ▪ ▫ → ❤ ︷╅╊✿ (¯`•._.• •._.•´¯)(¯`•¸•´¯) ❤`•.¸¸.•´´¯`•• .¸¸.•´¯`•.•●•۰• ••.•´¯`•.•• ••.•´¯`•.••*≡▂ ▃ ▄ ▅ ▆ ▇ █▐ ░ ▒▬ ☊ ☋☌☍✖︶︹︺✖ o(╥﹏╥)o ♡ ♢ ♣♤♥ ♦ ♧ ♨ ♩ ︶ㄣo★≮≯ ♫ ♬ ♪ ♩ ♭ ♪ ¤ ◎ ▶☺ ☻❤ 。◕‿◕。 (◡‿◡✿) ◕。◕ (◕〝◕)



เเระก๊อ อันเน้ไว้เเต่งเอ็มน๊ะ สำหรับที่ก๊อปปี้ไปวางมิดั้ย
☺ Alt+1
☻ Alt+2
♥ Alt+3
♦ Alt+4
♣ Alt+5
♠ Alt+6
• Alt+7
◘ Alt+8
○ Alt+9
↓ Alt+4889
↑ Alt+4888
€ Alt+7552
 Alt+255
๛ Alt+2555
♀ Alt+12
♂ Alt+11
♪ Alt+13
♫ Alt+14
☼ Alt+15
► Alt+16
◄ Alt+17
↕ Alt+18
¶ Alt+20
§ Alt+21
▬ Alt+22
→ Alt+26
← Alt+27
∟ Alt+28
↔ Alt+29
◙ Alt+522
▲ Alt+30
▼ Alt+31

วันพุธที่ 29 กันยายน พ.ศ. 2553

how to check "undefined" on javascript

undefined คือค่าที่ javascript ไม่สามารถกำหนดได้
คือไม่ได้มีค่ากำหนดมา แล้วอยู่ ๆ เราก็ดึกมาใช้งาน
มีวิธีเช็ค โดยใช้คำสั่งดังนี้

if(typeof(value) !== 'undefined') {
    // command...
}

//or 
if (typeof(value) === "undefined"){
    // command...
}

วันอาทิตย์ที่ 26 กันยายน พ.ศ. 2553

Get start liferay tomcat on eclipse

เอาหละ เรื่องง่าย ๆ ของการ develop web ก็มาถึงซะที เมื่อ user ขี้จุกจิก ต้องการเปลี่ยน content ไป ๆ มา ๆ
เราเลยได้พบกับอะไรที่มันง่ายขึ้น สามารถให้ user ปรับแต่งเองได้ (จริง ๆ แล้ว user ต้องการ เหอ ๆ เราเลยต้องเรียนรู้ซะหน่อย)

แล้วไอ้เจ้า portal คืออะไรหว่า ?
เป็น web site ที่ผู้ใช้สามารถที่เข้าถึงข้อมูลข่าวสารต่างๆได้อย่างรวดเร็ว โดยที่ portal จะคำนึงถึงผู้ใช้เป็นศูนย์กลาง กล่าวคือข้อมูลข่าวสารที่แสดงจะเป็นข้อมูลที่เกี่ยวข้องกับผู้ใช้หรือเป็นข้อมูลที่ผู้ใช้สนใจเท่านั้น (หรือจะต่อท้ายว่า เท่าที่มีให้เลือกละกัน)

แล้วเริ่มไงดีอะ ร้อน ๆ แต่ ของ่าย ๆ ก่อน


set IDE (เครื่องมือที่ทำให้ชีวิตง่ายขึ้น)

เอาเป็นว่าเริ่มแรกก็ download eclipse มาติดตั้ง เลือกที่เป็น J2EE นะ เพราะจะ support java web application เป็นอย่างดี (ดีและฟรี โอ้ววววว !!! i love eclipse) ที่ใช้ตอนนี้ version 3.5

ที่ http://www.eclipse.org/

แล้วตามด้วย liferay sdk กับ liferay tomcat (ชอบ tomcat เลย load tomcat มา จะทำหน้าที่เป็นเหมือน server tomcat web server)

- liferay-plugins-sdk-6.0.5
- liferay-portal-6.0.5

ที่ http://www.liferay.com/

ทำการ set eclipse ให้รู้จักคุ้นเคยกะ liferay โดย ไปที่ menu
Help >> Install New Software... >>
เลือก Add :

Liferay - http://releases.liferay.com/tools/ide/eclipse/galileo/stable/

หลังจากนั้น eclipse ต้องการอาบน้ำ เอ้ย restart ตัวเองทีนึงเพื่อจัดเรียงความเรียบร้อยใหม่
เสร็จแล้วเราจะเห็น menu liferay icon ดำ ๆ ขึ้นมา

set SDK โดยเข้าไปที่ menu
Window >> Preferences >> Liferay >> Installed SDKs
เลือก folder ของ sdk ตัวอย่าง
- E:/liferay-plugins-sdk-6.0.5

set web server โดยเข้าไปที่ menu
Window >> Preferences >> Liferay
คลิ๊กที่ Create a new Liferay runtime environment
แล้วทำการ add part web server ที่เรา load มา ตัวอย่าง
- E:\liferay-portal-6.0.5\tomcat-6.0.26


first portlet (เริ่มสร้างก้อนเล็ก ๆ กัน)

เราใช้ความสามารถของ eclipse ให้คุ้มค่า โดยเริ่มจาก เลือกที่
File >> New >> Liferay Plug-in project
หลักจากนั้นก็ set Project name และ Display name ตามความสบายใจ ในที่นี้ใช้ชื่อว่า test1
เลือก Plug-in Type เป็น Portlet
หลักจากสร้างเสร็จ ก็จะมี project ใหม่กิ๊ก ชื่อว่า
test1-portlet

สิ่งแรกที่เราต้องทำก่อนเลยคือ run มันซะ
คลิ๊กขวาที่ project folder แล้วเลือก
Run as >> Run on server
แล้วเลือก Liferay server tomcat
กด next เพื่อเลือก project test 1 เข้าไป และกด Finish

หลังจากนั้น web จะเริ่มทำงาน ให้เปิด browser ตัวอะไรก็ได้ที่เราว่าแหล่ม ๆ แล้วพิมพ์ url
http://localhost:8080/

จะเจอหน้า Welcome - Liferay ก็ช่างมัน ปล่อยมันไปก่อน
คลิ๊กที่ Sign in ที่หน้า web
Email Address : test@liferay.com
Password : test
กดปุ่ม Sign in

เราจะเห็นว่า portlet ในหน้า อันต่าง ๆ ขยับได้ด้วย(คือเราบังคับได้ว่าจะย้ายไปใหน ไม่ใช่มันดิ้นไปดิ้นมาได้เอง)

ให้เราเลือก Add ด้านบน กด More...
แล้วเราก็ พิมพ์ในช่อง Search ว่า test1 คือ ชื่อ portlet plugin แรกที่เราสร้างมาพร้อม project
ก็จะเห็นขึ้นมา 1 record แล้วเราก็ลากมาวางบน web ได้เลย หรือ กดที่คำว่า Add ในบรรทัดเดียวกับคำว่า test1
(ว่าแต่ คำว่า test1 ทำไมมันแปลง เป็น Test1 คือเอาตัวใหญ่ขึ้นหน้าให้ ยังไม่รู้คำตอบ)


detail file (ลงลึกในแต่ละ file ที่เราพอจะแก้เล่นได้)

อันแรกเลยเป็น portlet.xml คร่าว ๆ คือ เป็นเหมือนผูก view กับ controller และสร้างสิทธิขึ้น

 test1
 Test1
 com.liferay.util.bridges.mvc.MVCPortlet
 
  view-jsp
  /view.jsp
 
 0
 
  text/html
 
 
  <title>Test1</title>
  Test1
  Test1
 
 
  administrator
 
 
  guest
 
 
  power-user
 
 
  user
 


ถ้าเราต้องการเขียน controller ใหม่ก็แก้ไข tag xml portlet-class ข้างบนดังนี้
com.test.TestLiferayPorlet1

และทำการสร้าง java file ขึ้นมา
package com.test;

import com.liferay.util.bridges.mvc.MVCPortlet;

/**
 * Portlet implementation class TestLiferayPorlet1
 */
public class TestLiferayPorlet1 extends MVCPortlet {
 

}

ในที่นี้ขอว่าง ๆ ไปก่อนแล้วกัน เพื่อไม่ให้มันยาวเกินไปสำหรับเริ่มต้น


อีกส่วนคือ liferay-portlet.xml อันนี้ยังไม่รู้จะแก้อะไรดี
ก็จะถูกประกาศไว้ตามนี้


 test1
 /icon.png
 true
 /css/main.css
 /js/main.js
 test1-portlet


liferay-display.xml เอาไว้กำหนดให้โชว์บนหน้าจอ โดยผูก category หรือแบ่งประเภทได้

 
  
 


อะสุดท้ายแระ view.jsp
ในส่วนนี้ จะถูกอ้างจาก portlet ใน portlet.xml ดังนั้น สามารถเปลี่ยนชื่อได้
และเป็น template view เรานั่นเอง

จบแค่นี้ก่อนแระกัน เดี๋ยวจะยืดยาวไป สำหรับการเริ่มต้น

วันอังคารที่ 31 สิงหาคม พ.ศ. 2553

start css

CSS คืออะไรอ่า ???
ว่าแต่จริง ๆ เขียนบทความนี้ค่อนข้างเก่า
แต่จำเป็นอย่างมากสำหรับ web ต่าง ๆ นา ๆ
ก็เลยเขียนไว้เล่น ๆ ขึ้นดีฟ่า
ใหน ๆ ก็ไม่รู้จะเขียนอะไรมานานมากมายแล้ว
ให้ blog ขยับบ้าง เล็ก ๆ น่ารัก ๆ

CSS คือ ?
-Cascading Style Sheets << เฮ้ย นี่ไม่ใช่ "คือ" นี่มันชื่อเต็ม
-_-"
-เอาเป็นว่า มันใช้ในการกำหนด style ให้กับ web ของเรา
แล้วดีอย่างไงหรอ ก็ดีตรงที่ เรากำหนด รูปแบบ กับ web เรา
ทีเดียว เวลาเปลี่ยนก็เปลียนแค่ style (css file)
ส่วน html ไม่ต้องทำไรแล้ว

แล้วมันยากใหม ?
-ไม่ยากหรอก ถ้าเรารู้จักจัดการ และแบ่งหมวดหมู่
รวมถึง group ในส่วนต่าง ๆ เป็น ไม่ใช่กำหนดสะเปะสะปะ

แล้วต้องรู้อะไรบ้าง ?
-รู้ attribute และ style ในแต่ละ html tag ว่าสามารถกำหนดอะไรได้บ้าง เช่น
<table> </table> 
จะสามารถกำหนด
<table 
width="200px" height="300px" style="border: 1px solid blue;"> 
</table> 
อะไรแบบนี้ เป็นต้น รู้แค่นี้ก่อน ก็ไปได้เกินครึ่งทางแล้ว
แต่อย่าไปไกลมาก กลับมาดูกันต่อก่อน

จะเริ่มอย่างไรหละ ?
- ไม่ยาก เริ่มจาก เขียน html ขึ้นมาก่อน file นึ่ง
ตัวอย่างตามนี้แล้วกัน

<table border="1px">
 <tr>
  <td>TD1-1</td>
  <td>TD1-2</td>
  <td>TD1-3</td>
 </tr>
 <tr>
  <td>TD2-1</td>
  <td>TD2-2</td>
  <td>TD2-3</td>
 </tr>
 <tr>
  <td>TD3-1</td>
  <td>TD3-2</td>
  <td>TD3-3</td>
 </tr>
</table>

TD1-1 TD1-2 TD1-3
TD2-1 TD2-2 TD2-3
TD3-1 TD3-2 TD3-3


ที่นี้เราก็ลองใส่ class ให้แต่ละ tag ที่สำคัญ ๆ

<table border="1px" class="main_table">
 <tr class="col_head">
  <td>TD1-1</td>
  <td>TD1-2</td>
  <td>TD1-3</td>
 </tr>
 <tr class="col_detail">
  <td class="row_head">TD2-1</td>
  <td>TD2-2</td>
  <td>TD2-3</td>
 </tr>
 <tr class="col_detail">
  <td class="row_head">TD3-1</td>
  <td>TD3-2</td>
  <td  id="tag_test_id">TD3-3</td>
 </tr>
</table>

ผลทีได้คือ ไม่มีอะไรเปลี่ยนแปลง
เพราะ class ที่สร้างขึ้น ไม่ได้ก่อผลกระทบ อะไรเลย

TD1-1 TD1-2 TD1-3
TD2-1 TD2-2 TD2-3
TD3-1 TD3-2 TD3-3

เราก็มาเริ่ม แทรก css เข้าไปดังนี้
<style type="text/css">
.main_table {
 border: ridge;
 width: 300;
}
.col_head {
 text-align: center;
 background-color: orange;
}
.col_head td {
 border: 1px solid blue;
 height: 100px;
}
.col_detail {
 text-align: right;
}
.col_detail td {
 border: 1px solid red;
}
.row_head {
 background-color: yellow;
 width: 100px;
}
#tag_test_id {
        color: red;
}
</style>

ผมที่ได้ก็จะเป็นดังนี้


TD1-1 TD1-2 TD1-3
TD2-1 TD2-2 TD2-3
TD3-1 TD3-2 TD3-3

แล้วจะอธิบายหน่อยเปล่าว่าแต่ละอยางคืออะไร ???
-เอ่อ จริงด้วย เอาทีละอันแล้วกันนะ ตามตัวอย่างที่เราเห็น

.main_table : ในส่วนนี้เราจะแทน html tag ที่มี class ชื่อ main_table อยู่
โดย แต่ละ tag ก็สามารถมีได้หลาย class
วิธีการใส่ class หลาย ๆ คลาส ก็คือ class="class1 class2 class3"
ใช้การเว้นวรรค ในการแบ่งครับ

.col_detail td : ในส่วนนี้คือ td ที่อยู่ใต้ html tag ที่มี class ชื่อ col_detail
โดยจะสามารถไล่ลงไปเรื่อย ๆ ได้ ตัวอย่าง เช่น
.col_detail td table tr td table tr td div : ในตัวอย่างนี้คือลงไปอีก 2 table
จนถึง tag div นั่นเอง ลงแค่นี้พอ เดี๋ยวลงมากจะงง

#tag_test_id : ในส่วนนี้คือ html tag ที่มี id เป็น tag_test_id คือแค่เปลี่ยนจาก
การกำหนด class เป็น id แทน ไม่ต้องลงท้าย id แบบตัวอย่างก็ได้นะ เป็นชื่ออะไร
ก็ได้ตามสบาย ลักษณะการใช้งานก็คล้าย ๆ กับการกำหนดแบบ class ครับ

ในกรณีที่ใช้งานจริง จะแยก ในส่วน tag style ออกมา ใส่ file นามสกุล .css
ไว้ แล้วใช้ html เรียก ด้วย tag ตัวอย่าง ดังนี้
<link href="/css/style.css" type="text/css" rel="stylesheet" />
เอาเป็นว่าผู้เขียนไม่มีข้อสงสัยอะไรจะขอจบเพียงเท่านี้แล

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

design database for item product

เคยได้ทำการเขียนโปรแกรมที่ทำการซื้อขาย จัดการ หรือ ERP แล้วไม่เคย สะดุดใจเลยว่าราคาเป็นส่วนหนึ่งของ product
รูปร่างหน้าตาการ design เลยออกมาแบบนี้

---------------------------------
PRODUCT
---------------------------------
id
name
detail
color << optional
size << optional
width << optional
height << optional
barcode
type << type_id
buy_price
sale_price
create_datetime
create_by
update_datetime
update_by
---------------------------------

แต่พอคิดอีกที ราคามันแปรผันได้ตลอด ใครจะมานั่งเปลี่ยนให้เนี่ย
แล้วเกิดว่า ถ้าเปลี่ยนขึ้นมาจริง รายการเก่าจะเกิดปัญหาอะไรบ้างเนี่ย อีกมากมาย
ดั้งนั้นเราก็ต้องปล่อยไป เดี๋ยวมีใครโวยวายแล้วก็บอกว่า มีคนแก้ราคา จบข่าว
ไม่ใช่แระ -_-'

ดังนั้น เอาออกไปดีกว่า
---------------------------------
PRODUCT
---------------------------------
id
name
detail
color << optional
size << optional
width << optional
height << optional
barcode
type << type_id
create_datetime
create_by
update_datetime
update_by
---------------------------------

แล้วตอนขาย จะเอาราคาที่ใหนมาแปะ ในใบเสร็จหละ
เอ่อ น่านสิ ก็จำเอาแล้วกัน ^0^ นายนี่ความจำเป็นเลิศจริง ๆ

ก็ทำ table รองรับราคาสิ
---------------------------------
PRODUCT_PRICE
---------------------------------
id
product << product_id
price
currency << currency_id
create_datetime
create_by
expire_datetime
expire_by
---------------------------------

แค่นี้น่าจะพอ แล้วถ้าราคาเปลี่ยนก็เป็นการ insert record เพิ่ม ไม่ใช่ update
ตอนเรียกใช้ก็เรียกลำดับ ล่าสุดมาใช้งาน
- currency คือค่าสนุลเงิน
เพื่อใช้คำนวณเปรียบเทียบเมื่อมีการเปลี่ยนแปลงสกุลเงิน
- expire จะถูกเก็บข้อมูล กรณีมีข้อมูลใหม่เข้ามา หรือ ราคาใหม่เข้ามานั่งเอง


เพียงเท่านี้ก็สามารถเพิ่มเติม item เดิม ในใบรายการเดียวกัน
กรณีราคาเพิ่มในวันถัดไป และสามารถอ้างอิงย้อนหลังได้
ว่าราคาในแต่ละช่วงเป็นเท่าไร

วันอังคารที่ 27 กรกฎาคม พ.ศ. 2553

visual c# diff java

อ่าว ไหง ออกมา .net ได้เนี่ย แต่มิใช่ปัญหาเลยเมื่อมองลองดูเนื้อภาษากันจริง ๆ แล้วสำหรับคนที่เคยเขียน java จะบอกว่า
เขียนแทบจะเหมือนกัน และด้วย concept oop ที่แน่นมากทำให้ลักษณะ และคุณสมบัติ ของทั้ง สองภาษานี้ เหมือนกันเลย
แล้วที่ต่างกันอะ น่านสิ ลองดูเล่น ๆ กันดีกว่า

อย่างแรกเลยคงเป็น package หรือลำดับการเข้าถึง
java : เนื่องด้วยการเก็บ file ไว้ใน folder จริงทำให้มีการอ้างถึงตาม folder ที่อยู่
เช่น th >> co >> google >> code
package th.co.google.code

// how to use

import th.co.google.code.Clazz 

vc# : ในส่วนของ vc# นั้นจะต่างกันเนื่องจากว่า เป็นแค่การอ้างถึงการเข้าถึงจริง ๆ
ไม่ได้มีการแยกที่อยู่ไว้อย่างไร และจะเป็นการห่อหุ้ม Code ที่ต้องการประกาศการเข้าถึง
namespace th.co.google.code{
    // insert class hear 
}

// or

namespace th{
    namespace co{
        namespace google{
            namespace code{
                // insert class hear 
            }
        }
    }
}

// how to use

using th.co.google.code.Clazz 

concept get-set ในกรณี
java :
public class Clock{
    private int hour;
    public int getHour(){
        return hour;
    }
    public void setHour(int value){
        hour = value;
    }
}

// how to use

Clock c = new Clock();
c.setHour(8);
System.out.println(c.getHour());

vc#:
public class Clock{
    public int Hour {
        get {
            return hour;
        }
        set { 
            hour = value;
        }
    }
}

// how to use

Clock c = new Clock();
c.Hour = 8;
Console.WriteLine(c.Hour);

จะเห็นได้ว่า ภาษา java ค่อนข้างจะเก็บ concept เดิมไว้ได้ดี
แต่ภาษา c# นี่ยัดกันเป็นรูปแบบแปลก ๆ ขึ้นมาเชียว -_-" เหมือนทำ Inner class เลย...


การ สืบทอดคุณสมบัติจาก class แม่ อันนี้ต่างกันนิดหน่อย
java :
public class Parent{
    public void print(){
        System.out.println("== Parent ==");
    }
}
public class Child extends Parent{
    public void print(){
        System.out.println("== Child ==");
    }
    public void printAll(){
        this.print();
        super.print();
    }
}

// how to use

Child c = new Child();
c.printAll();

vc# :
public class Parent{
    public void print(){
        System.out.println("== Parent ==");
    }
}
public class Child : Parent{
    public void print(){
        System.out.println("== Child ==");
    }
    public void printAll(){
        this.print();
        base.print();
    }
}

// how to use

Child c = new Child();
c.printAll();

และ การรับ interface ที่แยกไม่ออกจริง ๆ พี่น้อง ^-^"
java :
public interface Interfazz{
    public void printTest();
}
public class Clazz implements Interfazz {
    public void printTest(){
        System.out.println("Clazz from Interfazz");
    }
}

vc# :
public interface Interfazz{
    public void printTest();
}
public class Clazz : Interfazz {
    public void printTest(){
        Console.WriteLine("Clazz from Interfazz");
    }
}

...

python solution for of for of for

อ่า หัวข้องง ๆ กันเลยทีเดียว
หลายคนคงเคยเจอกันแล้ว ที่ มีการวน loop ซ้อน loop ซ้อน loop
มันจะเป็นประมาณนี้ ให้นำแต่ละตัวมารวมกัน ใส่อีก list นึง

l1 = ['www', 'wap']
l2 = ['google', 'yahoo', 'gmail']
l3 = ['com', 'co.th']

list = []
for i in l1:
    for j in l2:
        for k in l3:
            list.append('http://%s.%s.%s' %(i, j, k))

print list

ผลลัพท์
['http://www.google.com',
'http://www.google.co.th',
'http://www.yahoo.com',
'http://www.yahoo.co.th',
'http://www.gmail.com',
'http://www.gmail.co.th',
'http://wap.google.com',
'http://wap.google.co.th',
'http://wap.yahoo.com',
'http://wap.yahoo.co.th',
'http://wap.gmail.com',
'http://wap.gmail.co.th']


แยก logic กับ business ออกจากกัน
l1 = ['www', 'wap']
l2 = ['google', 'yahoo', 'gmail']
l3 = ['com', 'co.th']

def for_list(lists):  
    for i in l1:
        for j in l2:
            for k in l3:
                yield l1, l2, l3

lists = [l1, l2, l3]
list = ['http://%s.%s.%s' %(i, j, k) for i, j, k in for_list(lists)]

SyntaxHighlighter include link for blog

  




วันพุธที่ 12 พฤษภาคม พ.ศ. 2553

rmi remote client on spring framework by annotation

หลังจากที่ได้ทำ service server rmi ไปแล้ว เราก็ไปทำที่ client บ้างดีกว่า concept
จะต่างกับ server นิดหน่อยคือ service มีกี่ service ก็จะประกาศเท่าจำนวน แต่ clinet
นั้นเนื่องจากเราเอา Annotation ไปแปะไว้ตอนเรียกที่ต่าง ๆ ทำให้เราต้องมีการ check
ก่อนว่า service ที่เราเรียกนั้น มันเคยถูกประกาศไว้หรือยัง ฟังแล้วอาจงง ไปเริ่มดีกว่า

บทความก่อนหน้า
rmi remote server on spring framework by annotation

file แรก เช่นเดิมเลยคือ
project\WebContent\WEB-INF\web.xml

    TestRmiAnnoClient
    
        index.html
        index.htm
        index.jsp
        default.html
        default.htm
        default.jsp
    
    
        contextConfigLocationclasspath:conf/applicationContext.xml
    
        org.springframework.web.context.ContextLoaderListener
    


ต่อไปก็ config file
project\src\conf\applicationContext.xml



    <context:annotation-config/>
    <context:component-scan base-package="com.company.project.service" />
    <import resource="/rmiContext.xml" />



แยกในส่วน rmi มาเป็นอีก file
project\src\conf\rmiContext.xml




    
        <property name="basePackage" value="com.company.project.service" />
        <property name="host" value="localhost" />
        <property name="port" value="1019" />
        <property name="refreshStubOnConnectFailure" value="true" />
        <property name="cacheStub" value="false" />
        <property name="lookupStubOnStartup" value="false" />
    



file Annotation ที่นำไปใช้ขอเรียก service
project\src\com\company\spring\remoting\RmiClient.java
package com.company.spring.remoting;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;

@Target( { ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RmiClient {

}

file class ที่ทำการเรียก service มาผูก ด้วยการ call rmi
project\src\com\company\spring\remoting\RmiClientAnnotationPostProcessor.java
package com.company.spring.remoting;

import java.lang.reflect.Field;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;

public class RmiClientAnnotationPostProcessor implements
  BeanFactoryPostProcessor, BeanClassLoaderAware {

 private List<Class<?>> remoteServiceInterfaces;

 private String host;

 private int port;

 private String basePackage;

 private ClassLoader classLoader;

 private boolean refreshStubOnConnectFailure;

 private boolean cacheStub;

 private boolean lookupStubOnStartup;

 @Override
 public void postProcessBeanFactory(
   ConfigurableListableBeanFactory beanFactory) throws BeansException {
  System.out
    .println(" RmiClientAnnotationPostProcessor >> postProcessBeanFactory ");

  for (String beanName : beanFactory.getBeanDefinitionNames()) {
   BeanDefinition beanDefinition = beanFactory
     .getBeanDefinition(beanName);

   System.out.println(" BeanDefinitionNames : " + beanName);

   // Ignore abstract bean!
   if (beanDefinition.isAbstract()
     || beanDefinition.getBeanClassName() == null
     || beanDefinition.getBeanClassName().indexOf(basePackage) == -1) {
    continue;
   }
   try {
    Class<?> clazz = Class.forName(beanDefinition
      .getBeanClassName());

    Field[] fields = clazz.getDeclaredFields();

    for (Field field : fields) {
     RmiClient client = field.getAnnotation(RmiClient.class);

     if (client == null) {
      continue;
     }
     Class<?> remoteServiceInterface = field.getType();

     RmiProxyFactoryBean clientBean = getRmiProxyFactoryBean(remoteServiceInterface);

     String clientBeanName = getClientName(field.getName());

     if (!beanFactory.containsSingleton(clientBeanName)) {
      beanFactory.registerSingleton(clientBeanName,
        clientBean);

      System.out.println("Register rmi client: " + clientBean
        + " " + "with name '" + clientBeanName
        + "' success.");
     }

    }
   } catch (ClassNotFoundException e) {
    throw new FatalBeanException("No class for name " + beanName, e);
   }

  }
 }

 private String getClientName(String remoteServiceInterfaceName) {
  return StringUtils.uncapitalize(remoteServiceInterfaceName) + "Client";
 }

 private RmiProxyFactoryBean getRmiProxyFactoryBean(
   Class<?> remoteServiceInterface) {
  RmiProxyFactoryBean client = new RmiProxyFactoryBean();
  System.out.println(" RmiClientAnnotationPostProcessor >> getRmiProxyFactoryBean");
  System.out.println(" >> RemoteServiceInterface name : " + remoteServiceInterface.getSimpleName());
  System.out.println(" >> RemoteServiceInterface host : " + host);
  System.out.println(" >> RemoteServiceInterface port : " + port);
  client.setServiceInterface(remoteServiceInterface);
  client.setServiceUrl("rmi://" + host + ":" + port + "/"
    + remoteServiceInterface.getSimpleName());

  client.setRefreshStubOnConnectFailure(refreshStubOnConnectFailure);
  client.setCacheStub(cacheStub);
  client.setLookupStubOnStartup(lookupStubOnStartup);
  client.setBeanClassLoader(classLoader);

  client.afterPropertiesSet();

  return client;
 }

 public void setRemoteServiceInterfaces(
   List<Class<?>> remoteServiceInterfaces) {
  this.remoteServiceInterfaces = remoteServiceInterfaces;
 }

 public List<Class<?>> getRemoteServiceInterfaces() {
  return remoteServiceInterfaces;
 }

 public String getBasePackage() {
  return basePackage;
 }

 public void setBasePackage(String basePackage) {
  this.basePackage = basePackage;
 }

 public String getHost() {
  return host;
 }

 public void setHost(String host) {
  this.host = host;
 }

 public int getPort() {
  return port;
 }

 public void setPort(int port) {
  this.port = port;
 }

 @Override
 public void setBeanClassLoader(ClassLoader classLoader) {
  this.classLoader = classLoader;
 }

 public boolean isRefreshStubOnConnectFailure() {
  return refreshStubOnConnectFailure;
 }

 public void setRefreshStubOnConnectFailure(
   boolean refreshStubOnConnectFailure) {
  this.refreshStubOnConnectFailure = refreshStubOnConnectFailure;
 }

 public boolean isCacheStub() {
  return cacheStub;
 }

 public void setCacheStub(boolean cacheStub) {
  this.cacheStub = cacheStub;
 }

 public boolean isLookupStubOnStartup() {
  return lookupStubOnStartup;
 }

 public void setLookupStubOnStartup(boolean lookupStubOnStartup) {
  this.lookupStubOnStartup = lookupStubOnStartup;
 }
}


========================================
ส่วนต่อไปคือเริ่มทำ service ที่ใช้งานจริงๆ กันเลยดีกว่าเริ่มด้วย
Interface rmi server service ตัวนี้คือของ interface server ตอนนำไปใช้งานจริง
คงถูก pack เป็น Jar file สำหรับใช้งาน แต่ตอนนี้ แค่ copy มาไว้ก่อนเพื่อความง่าย
project\src\com\company\project\service\ItestService.java
package com.company.project.service;

public interface ItestService {
 public String getPrefixHello(String str);
}

Interface service
project\src\com\company\project\service\IServiceNormal.java
package com.company.project.service;

public interface IServiceNormal {
 public String getTestRmiClientCall(String str);
}

Implements service
project\src\com\company\project\service\ServiceNormal.java
package com.company.project.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.company.spring.remoting.RmiClient;

@Service
public class ServiceNormal implements IServiceNormal {
 
 @Autowired
 @RmiClient
 private ItestService itestService;

 public void setItestService(ItestService itestService) {
  this.itestService = itestService;
 }

 @Override
 public String getTestRmiClientCall(String str) {
  System.out
  .println(" ServiceNormal >> getTestRmiClientCall ");
  String outStr = itestService.getPrefixHello(str);
  System.out
  .println(" result : "+outStr);
  return outStr;
 } 
}

หลังจากนั้นก็ลอง test service ดูโดยทำตัว test ขึ้นมา (ตาม step เมพขิง ๆ)
project\src\com\company\project\test\TestServiceNormal.java
package com.company.project.test;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.company.project.service.IServiceNormal;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/conf/applicationContext.xml"})
public class TestServiceNormal {
 
 private IServiceNormal iServiceNormal;
 
 @Autowired
 public void setiServiceNormal(IServiceNormal iServiceNormal) {
  this.iServiceNormal = iServiceNormal;
 }

 @Before
 public void setUp() throws Exception {
  System.out.println("setUp");
 }

 @After
 public void tearDown() throws Exception {
  System.out.println("tearDown");
 }
 
 @Test
 public void testServiceNormal(){
  iServiceNormal.getTestRmiClientCall(" test service client ...");
 }

}

log ที่ได้หลังจาก run test
12 พ.ค. 2553 11:34:08 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
INFO: @TestExecutionListeners is not present for class [class com.company.project.test.TestServiceNormal]: using defaults.
12 พ.ค. 2553 11:34:08 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/applicationContext.xml]
12 พ.ค. 2553 11:34:09 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/rmiContext.xml]
12 พ.ค. 2553 11:34:09 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericApplicationContext@118f375: startup date [Wed May 12 11:34:09 ICT 2010]; root of context hierarchy
RmiClientAnnotationPostProcessor >> postProcessBeanFactory
BeanDefinitionNames : org.springframework.context.annotation.internalConfigurationAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalAutowiredAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalRequiredAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalCommonAnnotationProcessor
BeanDefinitionNames : serviceNormal
RmiClientAnnotationPostProcessor >> getRmiProxyFactoryBean
>> RemoteServiceInterface name : ItestService
>> RemoteServiceInterface host : localhost
>> RemoteServiceInterface port : 1019
Register rmi client: org.springframework.remoting.rmi.RmiProxyFactoryBean@1d2fc36 with name 'itestServiceClient' success.
BeanDefinitionNames : com.company.spring.remoting.RmiClientAnnotationPostProcessor#0
12 พ.ค. 2553 11:34:09 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1016632: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,serviceNormal,com.company.spring.remoting.RmiClientAnnotationPostProcessor#0]; root of factory hierarchy
setUp
ServiceNormal >> getTestRmiClientCall
result : Hello 123 : test service client ...
tearDown

12 พ.ค. 2553 11:34:10 org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.GenericApplicationContext@118f375: startup date [Wed May 12 11:34:09 ICT 2010]; root of context hierarchy
12 พ.ค. 2553 11:34:10 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1016632: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,serviceNormal,com.company.spring.remoting.RmiClientAnnotationPostProcessor#0]; root of factory hierarchy

log ที่ได้หลังจาก run test บน server เพื่อให้เห็นว่ามีการ request มายัง server ได้
TestService >> getPrefixHello

rmi remote server on spring framework by annotation

rmi คือ การ remote service ข้ามเครื่องวิธีหนึ่ง ส่วนใหญ่จะใช้ในกรณีแยก service ออกจาก interface เพื่อ

1. ความปลอดภัย คือ ทำให้สามารถควบคุมการเข้าถึง การเรียกใช้ และการดึงข้อมูลที่สำคัญ หรือเป็นความลับ
รวมถึงการกำหนดข้อจำกัดต่าง ๆ

2. การทำเป็น service กลาง ซึ่งทำให้ไม่ต้องเขียน service ขึ้นมาใหม่บ่อย ๆ ในรูปแบบงานที่คล้าย ๆ กันเกิดขึ้นใหม่
และเป็นการแยก interface ทำให้สามารถ แก้ไข หรือ เปลีี่ยนแปลง interface ได้ตลอดเวลาโดยไม่กระทบ

ที่เหลือคิดไม่ออก แล้ว ไว้ว่าง ๆ มาเขียนข้อดีเพิ่มให้ครับ ^^'

ตัวอย่างโดยใช้ xml ทำการ config บน spring framework
http://static.springsource.org/spring/docs/2.0.4/reference/remoting.html

บทความถัดไป
rmi remote client on spring framework by annotation

เรามาเปลี่ยนจาก xml เป็น annotation กัน เริ่มจาก
project/WebContent/WEB-INF/web.xml


    MUWAHOBack
    
        contextConfigLocationclasspath:conf/applicationContext.xml
    
        org.springframework.web.context.ContextLoaderListener
    
    
        org.springframework.web.context.request.RequestContextListener
    
    
        30
    
    
        index.jsp
    


มาถึง config service file
project\src\conf\applicationContext.xml




    <context:annotation-config/>
    <context:component-scan base-package="com.company.project.service" />
    <import resource="/rmiContext.xml" />



อันนี้แยกในส่วน rmi ออกมาเป็น
project\src\conf\rmiContext.xml




    
        
        
    

    
    

    
        
    



มาถึงส่วนสำคัญในการประกาศ service rmi
ส่วนแรกจะเป็น Application Context ที่ประกาศเป็นตัวเริ่มต้น เพื่อให้ reuse ได้
project\src\com\company\spring\context\ApplicationContextProvider.java
package com.company.spring.context;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public abstract class ApplicationContextProvider implements
  ApplicationContextAware, BeanFactoryPostProcessor, BeanPostProcessor,
  DisposableBean {

 private ApplicationContext context;

 public ApplicationContext getContext() {
  return context;
 }

 @Override
 public void setApplicationContext(ApplicationContext context)
   throws BeansException {
  this.context = context;
 }
}

ต่อมาเป็นในส่วนที่ทำเพื่อประกาศ Annotation เราจะนำ Annotation ตัวนี้ไปแปะไว้
บน Class implement ที่เราต้องการประกาศให้เป็น service rmi
project\src\com\company\spring\remoting\RmiServer.java
package com.company.spring.remoting;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.stereotype.Component;

@Target( { ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RmiServer {
 
 String name() default "";
 
 Class<?> serviceInterface();
}

ส่วนต่อไป ทำการผูก service ที่ประกาศ Annotation @RmiServer เพื่อประกาศ
service ให้ใช้บริการ rmi ได้โดยผูกเข้ากับ Bean factory ของ Spring
project\src\com\company\spring\remoting\RemoteAnnotationBeanFactoryPostProcessor.java
package com.company.spring.remoting;

import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.List;
import org.springframework.aop.framework.Advised;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.remoting.rmi.RmiServiceExporter;
import org.springframework.remoting.support.RemoteExporter;
import com.company.spring.context.ApplicationContextProvider;

public class RemoteAnnotationBeanFactoryPostProcessor extends
  ApplicationContextProvider {

 private String basePackage;

 private List<String> remoteExporterNames = new ArrayList<String>();

 @Override
 public Object postProcessBeforeInitialization(Object obj, String beanName)
   throws BeansException {
  System.out
    .println(" RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeforeInitialization ");
  System.out.println(" >> Bean name : " + beanName);
  System.out.println(" >> Class name : " + obj);
  return obj;
 }

 @Override
 public void postProcessBeanFactory(
   ConfigurableListableBeanFactory beanFactory) throws BeansException {
  System.out
    .println(" RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeanFactory ");

  for (String beanName : beanFactory.getBeanDefinitionNames()) {

   System.out.println(" BeanDefinitionNames : " + beanName);

   BeanDefinition beanDefinition = beanFactory
     .getBeanDefinition(beanName);

   // Ignore abstract bean! and empty beans
   if (beanDefinition.isAbstract()
     || beanDefinition.getBeanClassName() == null
     || beanDefinition.getBeanClassName().indexOf(basePackage) == -1) {
    continue;
   }

   try {
    RmiServer remote = AnnotationUtils.findAnnotation(Class
      .forName(beanDefinition.getBeanClassName()),
      RmiServer.class);

    if (remote == null) {
     continue;
    }

    String remoteExporterName = getRmiServiceBeanName(remote);
    System.out.println(" Register Singleton RemoteExporterName : "
      + remoteExporterName);
    RemoteExporter remoteExporter = createRemoteExporter(remote);

    beanFactory.registerSingleton(remoteExporterName,
      remoteExporter);

    remoteExporterNames.add(remoteExporterName);
   } catch (ClassNotFoundException e) {
    throw new FatalBeanException("No class for name " + beanName, e);
   }
  }
 }

 private String getRmiServiceBeanName(RmiServer remote) {
  return "/" + remote.serviceInterface().getSimpleName();
 }

 private RemoteExporter createRemoteExporter(RmiServer remote) {
  RmiServiceExporter remoteExporter = new RmiServiceExporter();

  remoteExporter.setServiceInterface(remote.serviceInterface());
  remoteExporter
    .setServiceName(remote.serviceInterface().getSimpleName());

  return remoteExporter;
 }

 @Override
 public Object postProcessAfterInitialization(Object obj, String beanName)
   throws BeansException {
  System.out
    .println(" RemoteAnnotationBeanFactoryPostProcessor >> postProcessAfterInitialization ");
  System.out.println(" >> Bean name : " + beanName);
  System.out.println(" >> Class name : " + obj);
  Class<?> targetClass = null;
  Object _obj = obj;

  while (_obj instanceof Advised) {
   Advised advised = (Advised) _obj;
   try {
    _obj = advised.getTargetSource().getTarget();
   } catch (Exception e) {
    throw new FatalBeanException(
      "Exception initializing RmiServiceExporter", e);
   }
  }

  targetClass = _obj.getClass();

  RmiServer remote = AnnotationUtils.findAnnotation(targetClass,
    RmiServer.class);

  if (remote == null) {
   return obj;
  }
  try {

   RmiServiceExporter remoteExporter = (RmiServiceExporter) getContext()
     .getBean(getRmiServiceBeanName(remote));
   remoteExporter.setService(obj);

   Registry registry = getContext().getBean("rmiRegistry",
     Registry.class);
   remoteExporter.setRegistry(registry);
   remoteExporter.afterPropertiesSet();

   System.out.println("Create remote service success " + "with name "
     + remote.serviceInterface().getSimpleName()
     + " with RMI registry: " + registry + ".");
  } catch (Exception e) {
   throw new FatalBeanException(
     "Exception initializing RmiServiceExporter", e);
  }

  return obj;
 }

 @Override
 public void destroy() throws Exception {
  System.out
    .println(" RemoteAnnotationBeanFactoryPostProcessor >> destroy ");
  for (String remoteExporterName : remoteExporterNames) {
   getContext().getBean(remoteExporterName, RmiServiceExporter.class)
     .destroy();
  }
 }

 /*
  * auto-create get-set method.
  */
 public String getBasePackage() {
  return basePackage;
 }

 public void setBasePackage(String basePackage) {
  this.basePackage = basePackage;
 }

 public List<String> getRemoteExporterNames() {
  return remoteExporterNames;
 }

 public void setRemoteExporterNames(List<String> remoteExporterNames) {
  this.remoteExporterNames = remoteExporterNames;
 }
}


========================================
ส่วนต่อไปคือเริ่มทำ service ที่ใช้งานจริงๆ กันเลยดีกว่าเริ่มด้วย
Interface service
project\src\com\company\project\service\ItestService.java
package com.company.project.service;

public interface ItestService {
 public String getPrefixHello(String str);
}
project\src\com\company\project\service\TestService.java
Implements service
package com.company.project.service;

import org.springframework.stereotype.Service;
import com.company.spring.remoting.RmiServer;

@RmiServer(serviceInterface = ItestService.class)
@Service
public class TestService implements ItestService {

 @Override
 public String getPrefixHello(String str) {
  System.out.println(" TestService >> getPrefixHello ");
  return "Hello 123 : "+str;
 }

}

หลังจากนั้นก็ลอง test service ดูโดยทำตัว test ขึ้นมา
project\src\com\company\project\test\TestServiceTest.java
package com.company.project.test;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.company.project.service.ItestService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/conf/applicationContext.xml"})
public class TestServiceTest {
 
 @Autowired
 private ItestService itestService;
 
 public void setItestService(ItestService itestService) {
  this.itestService = itestService;
 }

 @Before
 public void setUp() throws Exception {
  System.out.println("setUp");
 }

 @After
 public void tearDown() throws Exception {
  System.out.println("tearDown");
 }
 
 @Test
 public void testGetData() {
  System.out.println("testGetData");
  String outStr = "";
  try{
   outStr = this.itestService.getPrefixHello("test 123 d^^");
   System.out.println("result : " + outStr);
  }catch(Exception e){
   System.out.println("ERROR : " + e);
   e.printStackTrace();
  }
 }
}

log ที่ได้หลังจาก run test
12 พ.ค. 2553 11:06:38 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
INFO: @TestExecutionListeners is not present for class [class com.company.project.test.TestServiceTest]: using defaults.
12 พ.ค. 2553 11:06:38 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/applicationContext.xml]
12 พ.ค. 2553 11:06:39 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/rmiContext.xml]
12 พ.ค. 2553 11:06:39 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericApplicationContext@7ffe01: startup date [Wed May 12 11:06:39 ICT 2010]; root of context hierarchy
12 พ.ค. 2553 11:06:39 org.springframework.remoting.rmi.RmiRegistryFactoryBean getRegistry
INFO: Looking for RMI registry at port '1019'
12 พ.ค. 2553 11:06:40 org.springframework.remoting.rmi.RmiRegistryFactoryBean getRegistry
INFO: Could not detect RMI registry - creating new one
RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeanFactory
BeanDefinitionNames : org.springframework.context.annotation.internalConfigurationAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalAutowiredAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalRequiredAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalCommonAnnotationProcessor
BeanDefinitionNames : testService
Register Singleton RemoteExporterName : /ItestService
BeanDefinitionNames : rmiRegistry
BeanDefinitionNames : appContextProvider
BeanDefinitionNames : com.company.spring.remoting.RemoteAnnotationBeanFactoryPostProcessor#0
12 พ.ค. 2553 11:06:40 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@ff057f: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,testService,rmiRegistry,appContextProvider,com.company.spring.remoting.RemoteAnnotationBeanFactoryPostProcessor#0]; root of factory hierarchy
RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeforeInitialization
>> Bean name : testService
>> Class name : com.company.project.service.TestService@15a6029
RemoteAnnotationBeanFactoryPostProcessor >> postProcessAfterInitialization
>> Bean name : testService
>> Class name : com.company.project.service.TestService@15a6029
12 พ.ค. 2553 11:06:40 org.springframework.remoting.rmi.RmiServiceExporter prepare
INFO: Binding service 'ItestService' to RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[10.239.20.141:1019](local),objID:[0:0:0, 0]]]]
Create remote service success with name ItestService with RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[10.239.20.141:1019](local),objID:[0:0:0, 0]]]].
RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeforeInitialization
>> Bean name : com.company.project.test.TestServiceTest
>> Class name : com.company.project.test.TestServiceTest@f9c40
RemoteAnnotationBeanFactoryPostProcessor >> postProcessAfterInitialization
>> Bean name : com.company.project.test.TestServiceTest
>> Class name : com.company.project.test.TestServiceTest@f9c40
setUp
testGetData
TestService >> getPrefixHello
result : Hello 123 : test 123 d^^
tearDown

12 พ.ค. 2553 11:06:40 org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.GenericApplicationContext@7ffe01: startup date [Wed May 12 11:06:39 ICT 2010]; root of context hierarchy
12 พ.ค. 2553 11:06:40 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@ff057f: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,testService,rmiRegistry,appContextProvider,com.company.spring.remoting.RemoteAnnotationBeanFactoryPostProcessor#0]; root of factory hierarchy
12 พ.ค. 2553 11:06:40 org.springframework.remoting.rmi.RmiServiceExporter destroy
INFO: Unbinding RMI service 'ItestService' from registry at port '1099'
12 พ.ค. 2553 11:06:40 org.springframework.remoting.rmi.RmiRegistryFactoryBean destroy
INFO: Unexporting RMI registry
RemoteAnnotationBeanFactoryPostProcessor >> destroy

ลอง run บน env server tomcat6
12 พ.ค. 2553 11:06:01 org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.jee.server:TestRmiAnnoServer' did not find a matching property.
12 พ.ค. 2553 11:06:01 org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jre6\bin;.;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:/Program Files/Java/jre6/bin/client;C:/Program Files/Java/jre6/bin;C:\Program Files\CA\SC\CAWIN\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\system32\wbem;.;C:\Program Files\Java\jdk1.6.0_11\bin;C:\Program Files\Common Files\NetSarang;C:\Program Files\Java\jdk1.6.0_11\BIN;C:\ant\bin;C:\Oracle9i\bin;C:\Program Files\Oracle\jre\1.3.1\bin;C:\Program Files\Oracle\jre\1.1.8\bin;C:\Ora6i\bin;C:\ORANT\bin;C:\TUXEDO\bin;C:\Program Files\SQLLIB\BIN;C:\Program Files\SQLLIB\HELP;C:\Ora6i\jdk\bin;C:\ORAWIN\Bin;C:\Program Files\cvsnt;;C:\Program Files\Common Files\Compuware;C:\PROGRA~1\CA\SC\CAM\bin;C:\Program Files\CA\DSM\bin;C:\ORAWIN\BIN;C:\ORANT\BIN;C:\CUBICS3T;C:\TUXEDO\BIN;
12 พ.ค. 2553 11:06:01 org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-8080
12 พ.ค. 2553 11:06:01 org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 801 ms
12 พ.ค. 2553 11:06:01 org.apache.catalina.core.StandardService start
INFO: Starting service Catalina
12 พ.ค. 2553 11:06:01 org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/6.0.18
12 พ.ค. 2553 11:06:02 org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring root WebApplicationContext
12 พ.ค. 2553 11:06:02 org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
12 พ.ค. 2553 11:06:02 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Wed May 12 11:06:02 ICT 2010]; root of context hierarchy
12 พ.ค. 2553 11:06:03 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/applicationContext.xml]
12 พ.ค. 2553 11:06:03 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/rmiContext.xml]
12 พ.ค. 2553 11:06:04 org.springframework.remoting.rmi.RmiRegistryFactoryBean getRegistry
INFO: Looking for RMI registry at port '1019'
12 พ.ค. 2553 11:06:05 org.springframework.remoting.rmi.RmiRegistryFactoryBean getRegistry
INFO: Could not detect RMI registry - creating new one
RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeanFactory
BeanDefinitionNames : org.springframework.context.annotation.internalConfigurationAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalAutowiredAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalRequiredAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalCommonAnnotationProcessor
BeanDefinitionNames : testService
Register Singleton RemoteExporterName : /ItestService
BeanDefinitionNames : rmiRegistry
BeanDefinitionNames : appContextProvider
BeanDefinitionNames : com.company.spring.remoting.RemoteAnnotationBeanFactoryPostProcessor#0
12 พ.ค. 2553 11:06:05 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1e1dadb: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,testService,rmiRegistry,appContextProvider,com.company.spring.remoting.RemoteAnnotationBeanFactoryPostProcessor#0]; root of factory hierarchy
RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeforeInitialization
>> Bean name : testService
>> Class name : com.company.project.service.TestService@1777b1
RemoteAnnotationBeanFactoryPostProcessor >> postProcessAfterInitialization
>> Bean name : testService
>> Class name : com.company.project.service.TestService@1777b1
12 พ.ค. 2553 11:06:05 org.springframework.remoting.rmi.RmiServiceExporter prepare
INFO: Binding service 'ItestService' to RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[10.239.20.141:1019](local),objID:[0:0:0, 0]]]]
Create remote service success with name ItestService with RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[10.239.20.141:1019](local),objID:[0:0:0, 0]]]].
12 พ.ค. 2553 11:06:05 org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 2516 ms
12 พ.ค. 2553 11:06:05 org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on http-8080
12 พ.ค. 2553 11:06:05 org.apache.jk.common.ChannelSocket init
INFO: JK: ajp13 listening on /0.0.0.0:8009
12 พ.ค. 2553 11:06:05 org.apache.jk.server.JkMain start
INFO: Jk running ID=0 time=0/47 config=null
12 พ.ค. 2553 11:06:05 org.apache.catalina.startup.Catalina start
INFO: Server startup in 4107 ms

วันอังคารที่ 11 พฤษภาคม พ.ศ. 2553

note_2010-05-11

11:25
เช้าวันนี้ก็มาทำงานตามปกติ แต่ปลั๊กกลับไม่มีไฟซะงั้น กว่าจะใช้ได้ก็ 9:30 น

ได้เห็นเกมส์ StarCraft 2 แล้วก็อยากกลับไปเล่นอีกที ^^'

วันนี้ก็มาทำ RMI เป็น Annotation บน Spring Framework กันต่อ ทำไปก็งงไป
แกะจากพี่ไวท์อีกที ในฝั่ง server แกะโครงมาได้และ ยังไม่ได้ test เพราะ
รอในส่วน client ที่กำลังทำเนีี่ยแหละ ว่าแต่ concept ต่างกันนิดหน่อย -_-'
แต่คงต้องรีบทำให้เสร็จภายในอาทิตย์นี้สินะ รู้สึกใยแมงมุม และสนิกขึ้น
ทำงานสบายเกินช่วงหลัง T_T' เลยเีสียนิสัยเลยว่างั้น

ถ้าเขียนเสร็จคงได้เอามาแปะหัวข้อเพิ่มอีก แต่ตอนนี้ งม ๆ ๆ ไปก่อน

ว่าแต่ช่วงนี้คงลด Facebook ลงมาแระ เล่นเยอะชักไม่ได้ทำอะไร
ต้องเริ่มเอาชนะใจตัวเองอีกครั้ง ^^y

วันพฤหัสบดีที่ 6 พฤษภาคม พ.ศ. 2553

java web-service with cxf(2.2.7) on spring framework

ตกลงก็มามอง cxf ในการทำ web-service ทั้ง client และ server เพราะความง่าย อะอย่างพล่ามมากเลย เริ่มดีกว่า
เราก็ต้อง create project เป็น web อยู่แล้ว ในที่นี้ก็คงต้องเดินไปหา
project/WebContent/WEB-INF/web.xml แล้วก็ละเลงตามนี้




    Auth Manager

    
        contextConfigLocationclasspath:com/company/auth/service/cxf.xml
    
        org.springframework.web.context.ContextLoaderListener
    
    
        CXFServlet
        org.apache.cxf.transport.servlet.CXFServlet
    
    
        CXFServlet
        /services/*
    



ต่อไป model ตัวที่จะลาก parameter ต่าง ๆ ไป ๆ มาก ๆ
project/src/com/company/auth/bean/Employee.java
package com.company.auth.bean;

import java.io.Serializable;
import java.util.Set;

public class Employee implements Serializable {
 
 private static final long serialVersionUID = 1L;
 private String gid;
 private String lastName;
 private String firstName;
 private Set privileges;
 
 public Employee() {}

 public String getGid() {
  return gid;
 }
 public void setGid(String gid) {
  this.gid = gid;
 }
 public String getLastName() {
  return lastName;
 }
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 public String getFirstName() {
  return firstName;
 }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 public Set getPrivileges() {
  return privileges;
 }
 public void setPrivileges(Set privileges) {
  this.privileges = privileges;
 }
 
 public boolean isUserInRole(String role) {
  if(privileges == null) { return false; }
  else { return privileges.contains(role); }
 }
}

เริ่มด้วยการประกาศ service บน xml
project/src/com/company/auth/service/cxf.xml


    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
    <jaxws:endpoint id="auth"
      implementor="com.company.auth.service.AuthServiceImpl"
      address="/cxfAuth"/>


ต่อมาก็เริ่มในส่วน service ที่จะเป็นตัวถูกเรียกใช้ง่าน
project/src/com/company/auth/service/AuthService.java
package com.company.auth.service;

import javax.jws.WebParam;
import javax.jws.WebService;
import com.company.auth.bean.Employee;

@WebService
public interface AuthService {
 Employee getEmployee(@WebParam(name="gid") String gid);
}

project/src/com/company/auth/service/AuthServiceImpl.java
package com.company.auth.service;

import javax.jws.WebService;
import com.company.auth.bean.Employee;

@WebService(endpointInterface = "com.company.auth.service.AuthService", serviceName = "corporateAuthService")
public class AuthServiceImpl implements AuthService{

 @Override
 public Employee getEmployee(String gid) {
  System.out.print("test log for apache cxf web-service... input gid : " + gid);
  return new Employee();
 }
}

หลังจากที่ทำการเขียน service ขึ้นมาเสร็จเรียบร้อยแล้วก็สั่ง run server รอไว้เลย
แล้วเราก็มาเขียน client แบบง่าย ๆ เพื่อลองเรียกใช้กัน(จริง ๆ แล้วเขียนไว้ที่ใหนก็ได้
แต่ต้องนำ model กับ interface มาด้วย แต่ในที่นี้เอาง่าย ๆ ไว้ก่อน)
project/src/com/company/auth/client/Client.java
package com.company.auth.client;

import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import com.company.auth.bean.Employee;
import com.company.auth.service.AuthService;

public class Client {

    private Client() {
    } 

    public static void main(String args[]) throws Exception {

        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

        factory.getInInterceptors().add(new LoggingInInterceptor());
        factory.getOutInterceptors().add(new LoggingOutInterceptor());
        factory.setServiceClass(AuthService.class);
        factory.setAddress("http://localhost:8080/Test1/services/cxfAuth");
        AuthService client = (AuthService) factory.create();

        Employee employee = client.getEmployee("0223938");
        System.out.println("Server said: " + employee.getLastName() + ", " + employee.getFirstName());
        System.exit(0);

    }
}

ในส่วน ของ wsdl จะเข้าไปที่
http://localhost:8080/project/services/cxfAuth?wsdl

กรณีที่เราต้องการเขียนเฉพาะ client ให้เราไปมอง server ที่ตัว file wsdl แล้วใช้
wsdl2java

โดยเมื่อ download cxf มา จะได้ file apache-cxf-2.2.7.zip คำสั่งจะอยู่ที่
apache-cxf-2.2.7\bin\wsdl2java.bat

ก็ให้เราใช้คำสั่ง ตามหลังด้วย url ของ wsdl ได้เลย (java file และ folder ก็จะ
ออกมายัง part ที่เราใช้คำสั่ง ก็ย้ายไปไว้ที่อื่นเอาเอง)
wsdl2java http://localhost:8080/project/services/cxfAuth?wsdl

ในที่นี้ได้เป็น part : com\company\auth\service\
AuthService.java
CorporateAuthService.java
Employee.java
GetEmployee.java
GetEmployeeResponse.java
ObjectFactory.java
package-info.java

java spring framework 3 quickstart

ในส่วนนี้จะเป็นการทบทวนเล็ก ๆ เนื่องจากห่างไปสักพัก กับ java spring framework
สำหรับผู้ที่ยังไม่มีประสพการณ์แนะนำอ่าน Servlet, JSP ก่อนเลยนะเพราะต่อยอดมาครับ

ขั้นแรกก็คงมิพ้น file
project/WebContent/WEB-INF/web.xml


    TestJsp
    
        contextConfigLocationclasspath:conf/applicationContext.xml
    
        org.springframework.web.context.ContextLoaderListener
    
    
        dispatcher
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocationclasspath:conf/dispatcher-servlet.xml
        2
    
    
        dispatcher
        *.do
    
    
        30
    
    
        redirect.jsp
    


ส่วนต่อมา ในการกำหนด การชี้ไปยัง page ต่าง ๆ ใช้ annotation เข้าไป scan หา
project/src/conf/dispatcher-servlet.xml


  
    

    
      
    

    
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    



ส่วนต่อมา ในการกำหนด service ในที่นี้เราใช้ annotation ทำให้ เขียน xml น้องลงไปเยอะ
project/src/conf/applicationContext.xml



    <context:annotation-config/>
    <context:component-scan base-package="spring.anno"/>



เริ่มแรกเรามาลอง service กันเลยดีกว่า เพื่อทดสอบ config
project/src/spring/anno/Iface.java
package spring.anno;

public interface Iface {
    public String getData(String str);
}

project/src/spring/anno/IfaceImpl.java
package spring.anno;

import org.springframework.stereotype.Service;

@Service
public class IfaceImpl implements Iface{

    @Override
    public String getData(String str) {
        System.out.println("have call IfaceImpl.getData() : " + str);
        return str;
    }
}

ต่อมาก็สร้างตัว test เลย กรณีนี้ใช้ eclipse เข้าช่วย ดังนั้น อาจต้องแก้ junit เป็น v4 ก่อน และ load ในส่วน library spring test เพิ่มมา
project/src/spring/anno/test/IfaceTest.java
package spring.anno.test;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import spring.anno.Iface;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/conf/applicationContext.xml"})
public class IfaceTest {
 
 private Iface iface;

 @Autowired
 public void setIface(Iface iface) {
  this.iface = iface;
 }

 @Before
 public void setUp() throws Exception {
  System.out.println("setUp");
 }

 @After
 public void tearDown() throws Exception {
  System.out.println("tearDown");
 }

 @Test
 public void testGetData() {
  System.out.println("testGetData");
  this.iface.getData("test 123 d^^");
 }
}

ถ้ามิมีสิ่งใดผิดพลาดจะเป็นประมาณนี้
6 พ.ค. 2553 9:18:56 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
INFO: @TestExecutionListeners is not present for class [class spring.anno.test.IfaceTest]: using defaults.
6 พ.ค. 2553 9:18:56 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/applicationContext.xml]
6 พ.ค. 2553 9:18:57 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericApplicationContext@fd13b5: startup date [Thu May 06 09:18:57 ICT 2010]; root of context hierarchy
6 พ.ค. 2553 9:18:57 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1b3f8f6: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,ifaceImpl,ifacePage]; root of factory hierarchy
setUp
testGetData
have call IfaceImpl.getData() : test 123 d^^
tearDown
6 พ.ค. 2553 9:18:57 org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.GenericApplicationContext@fd13b5: startup date [Thu May 06 09:18:57 ICT 2010]; root of context hierarchy
6 พ.ค. 2553 9:18:57 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1b3f8f6: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,ifaceImpl,ifacePage]; root of factory hierarchy

หลังจากนั้นเราก็มาเริ่มสร้าง controller กับเลยดีกว่า
project/src/spring/anno/web/IfacePage.java
package spring.anno.web;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import spring.anno.Iface;

@Controller
public class IfacePage {
    
    private Iface iface;
    
    @Autowired
    public void setIface(Iface iface) {
        this.iface = iface;
    }
    
    public Iface getIface() {
        return iface;
    }

    @RequestMapping("/helloWorld.do")
    public ModelMap helloWorld() {
        System.out.println("request get http for helloWorld...");
        return new ModelMap();
    }
    
    @RequestMapping("/helloCat.do")
    public ModelMap helloCat() {
        System.out.println("request get http for helloCat...");
        String data = this.getIface().getData("request get http for helloCat...");
        ModelMap mm = new ModelMap();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("data", data);
        List<Integer> l = new LinkedList<Integer>();
        for(int i=0; i<10; i++){
            l.add(i);
        }
        map.put("testloop", l);
        mm.addAllAttributes(map);
        return mm;
    }
}
และ template เพื่อแสดงผล ในหน้านี้ยังมิมีอะไร เอาง่าย ๆ ก่อน การเข้าถึง โดย
http://localhost:8080/proeject/helloWorld.do
project/WebContent/WEB-INF/jsp/helloWorld.jsp
<html>
    <head>
        <title>test helloWorld</title>
    </head>
    <body>
        data : helloWorld
    </body>
</html>
และ template เพื่อแสดงผล และทำการเีรียก service และ เริ่มผูกข้อมูลมายังหน้า page เอาง่าย ๆ อีกเช่นกัน การเข้าถึง โดย
http://localhost:8080/proeject/helloCat.do
project/WebContent/WEB-INF/jsp/helloCat.jsp
<html>
    <head>
        <title>test helloCat</title>
    </head>
    <body>
    data : helloCat
    <br />
    <% out.print(request.getAttribute("data")); %>
    <br />
    <table border="1">
    <%@page import="java.util.LinkedList"%>
    <%@page import="java.util.List"%>
    <%
    List<Integer> testloop = (List<Integer>)request.getAttribute("testloop");
    if( null == testloop){
        out.print("<tr><td>");
        out.print("data loop empty...");
        out.print("</td></tr>");
    }else{
        for(Integer i : testloop){
            out.print("<tr><td>");
            out.print(" test </td><td>" + i);
            out.print("</td></tr>");
        }
    }
%>
    </table>
    </body>
</html>
เพิ่มเติมในส่วน file redirect.jsp เพื่อเป็น default page site ซึ่งจะทำงานในกรณีที่เราใส่แค่
http://localhost:8080/proeject
(ซึ่งตอนนี้ยังคงไม่ได้ทำอะไรในตอนนี้ เอาไว้ต่อยอดในบทความถัด ๆ ไปครับ) project/WebContent/WEB-INF/redirect.jsp
<%--
Views should be stored under the WEB-INF folder so that
they are not accessible except through controller process.

This JSP is here to provide a redirect to the dispatcher
servlet but should be the only JSP outside of WEB-INF.
--%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<% response.sendRedirect("security/index.do"); %>

ข้อสำคัญอย่าลืม library ต่าง ๆ มาให้ครบ เพราะเราไม่ได้เขียนเองตั้งแต่ต้น เราพึ่งพา library ต่าง ๆ ของ spring และ อื่น ๆเป็นหลัก เขียนเองคงรอชาติเศษ ๆ หุหุ
ไม่แน่ใจว่าตกหล่นอะไรไปหรือเปล่า เพราะทำแล้วลองแล้ว ก็ลากมาแปะไว้ในนี้เลย ^^'
จับผิดได้แจ้งด้วยนะครับ
ขอบคุณครับ

วันพุธที่ 10 กุมภาพันธ์ พ.ศ. 2553

Turbogears2 How to receive Data on MIME

ในส่วนนี้ เกิดขึ้นจากปัญหาการรับ request data ที่ได้จากข้อมูลสองชุด ลักษณะข้อมูลที่ได้จาก body


#Example data 1
#-------------------------------------------------

--sendReport@sdgs:8I50nM01
Content-Type: text/xml; charset=utf-8
Content-Transfer-Encoding: binary
Content-ID:

...data xml format string...
--sendReport@sdgs:8I50nM01
Content-Type: application/x-gzip
Content-Transfer-Encoding: binary
Content-ID: 3884032_2010020817_sdgs.log.gz
Content-Location: 3884032_2010020817_sdgs.log.gz
Content-Description: "attachment"; filename="3884032_2010020817_sdgs.log.gz"; size=180;

...data binary string...
--sendReport@sdgs:8I50nM01--

#-------------------------------------------------
#โดยที่
#data ชุดแรกเป็น xml string ธรรมดา
#data ชุดที่สองเป็น zip file ที่ถูกส่งมาในรูปแบบ binary


#Example data 2
#-------------------------------------------------

------=_Part_0_201002091513
Content-type: text/xml;charset="UTF-8"
Content-Length: 1840

...data xml format string...
------=_Part_0_201002091513
Content-Type: text/plain; name=sms-text.txt; charset=UTF-8
Content-Transfer-Encoding: 7bit
Content-ID: sms-text.txt

...data string...
------=_Part_0_201002091513--

#-------------------------------------------------
#โดยที่
#data ชุดแรกเป็น xml string ธรรมดา
#data ชุดที่สองเป็น text string ธรรมดา

เราจัดการรแยกข้อมูลออกเป็นสองชุดโดยการ

def get_data(self, data):
    printlog.debug('get_data_mo_receive : %s' %data)
    if None == data or '' == data.strip():
        rreceivee Exception('invalid request from receive.')

    '''
        get --xxxyy:zzz 
    '''
    ds = data.split()[0]
    '''
        delete --xxxyy:zzz-- end of message
    '''
    data2 = data.replace('%s--' %ds, '')
    printlog.debug(' read line partition : %s ', ds)
    dats = data2.split(ds)
    xml = ''
    req_msg = ''
    content_type = 'text'

    dats = [d.strip() for d in dats if d != None and '' != d.strip()]
    printlog.debug(' read line all data : %s ', dats)

    for dat in dats:
        try:
            printlog.debug(' read line data : %s ', dat)
        except:
            pass

        d = email.message_from_string(dat)
        try:
            printlog.debug(' read line content_type : %s ', d.get_content_type())
        except:
            pass

        try:
            printlog.debug(' read line payload : %s ', d.get_payload())
        except:
            pass

        '''
            type of content
            text/xml : main data
            text/plain : request mo 
            application/x-gzip : offline process
        '''
        if 'text/xml' == d.get_content_type():
            xml = d.get_payload()

        elif 'application/x-gzip' == d.get_content_type():
            content_type = 'file'
            content_location = d.get('Content-ID').strip()
            f = open('/usr/local/appprod/upload/receive/offline/receive/%s' %content_location, 'wb')
            f.write(d.get_payload())
            f.close()

        elif 'text/plain'== d.get_content_type():
            content_type = 'text'
            req_msg = d.get_payload().strip()

    return xml.strip(), req_msg.strip(), content_type

#-------------------------------------------------

ในที่นี้ จะทำการ return xml ออกมา พร้อม request แต่
ในกรณี file จะทำการ save ลง path แล้วเพื่อเอาไปทำงานอย่างอื่นต่อไป
เนื่องจาก file ที่ได้รับอาจมีหลากหลายประเภท

Turbogears2 Basic receive Data

ในส่วน turbogears2 นั้นถ้าเราเขียน method รับ request จะมีหน้าตาแบบนี้

@expose()
def func(self, *arg, **kw):
    pass

นี่คือหน้าตาปกติ โดยที่

self เป็นตัวแปรที่ทำหน้าที่แทน class controller ปัจจุบัน

*arg จะเป็น data ต่อจาก url เช่น
url = http://localhost:8080/func/a/b/c
*arg = ['a', 'b', 'c']

**kw จะเป็น name, value เช่น
url = http://localhost:8080/func?a=12&b=13&c=14
**kw = {'a':12, 'b':13, 'c':14}

-------------------------------------------

และในกรณีที่เราต้องการ data stream เช่นการ post xml มา
เราก็จะใช้ pylons ในการขอดูข้อมูล รูปแบบจะได้ดังนี้

@expose()
def func(self, *arg, **kw):
    body = pylons.request.body
    headers = pylons.request.headers

ให้ทำการ import pylons เข้ามาก่อน

body จะมีข้อมูลเป็น text string

header จะมีข้อมูลเป็น dict เก็บข้อมูล MIME Type ต่างๆ

ซึ่งทั้งหมดนี้ น่าจะครอบคลุมการทำงานได้หลากหลายครับ

วันพุธที่ 13 มกราคม พ.ศ. 2553

Python convert Python syntax color to HTML

หัวข้อนี้น่าสนใจมากครับ สำหรับคนทำ Blog หรือ Web ที่นำเสนอ Code Python
อยากเสนออย่างรุนแรง อาจเป็นเรื่องเก่านะครับ แต่นำมาเล่าใหม่

เพราะเจ้าของ Blog ส่วนใหญ่ที่เรา ๆ เขียนกันนั้น ถ้าเป็น Code Python กันแล้ว
ก็จะมีการนำเสนอ โดยการเขียน code ลงไปใน Blog กับแบบนี้


<code>
<pre>
list = ['a', 'b', 'c', 'd']
r = ''
i = 1
for l in list:
    r = i == len(list) and '%s%s' %(r, l) or '%s%s, ' %(r, l)
    i = i+1
</pre>
</code>


ก็จะได้ผลลัพท์ ดังต่อไปนี้


list = ['a', 'b', 'c', 'd']
r = ''
i = 1
for l in list:
    r = i == len(list) and '%s%s' %(r, l) or '%s%s, ' %(r, l)
    i = i+1


OK รับได้ รับความงามพอสมควร แต่ไม่สวย ก็เคยเห็นคนต่างชาติทำกัน
สีสันดูดีมาก ๆ และก็ไม่ใช่รูปตัดจาก tool มาแปะ กระจอกอันนั้น
เลยพยายามไปหามา ไม่ยอม Blog เราจะกระจอกเกินไปแระ

และแล้ว ความพยายามก็ประสพผมสำเร็จ เจอแล้ว
Pygments
แรก ๆ ก็งงครับ แล้วไง ใช้ไงหรอ -_____-?
อารมณ์ หน้าตาดีแต่กินไม่ได้เลย

เลยไปอ่าน Document เอา ขอโทษนะน้องพี่ก็กร่อมแกร้มพออ่านได้เหมือนกัน หุหุ

เลยไปเจอท่านพี่บอกมากว่า
ให้ลง python ก่อน แล้วก็ตามด้วย easy_install ถ้าเคยลงแล้วก็ข้ามไป

python โหลดที่:
http://www.python.org/download/

ติดตั้ง:

ทดลองว่าเสร็จแล้วโดยลองพิมพ์:

$ python --version
Python 2.6.2


easy_install ทำการ download ez_setup.py โหลดที่:
http://peak.telecommunity.com/dist/ez_setup.py

พิมพ์ command:

$ python ez_setup.py

ทดลองว่าเสร็จแล้วโดยลองพิมพ์:

$ python
>>> import setuptools
>>> setuptools.__version__
'0.6c9'


-----------------------------------------

หลังจากนั้นก็มาถึง Pygments
สูตรสำเร็จ กรณี Windows พิมพ์ว่า

easy_install Pygments


จะขึ้นว่า

C:\Users\Kan>easy_install Pygments
Searching for Pygments
Reading http://pypi.python.org/simple/Pygments/
Reading http://pygments.org/
Reading http://pygments.pocoo.org/
Best match: Pygments 1.2.2
Downloading http://pypi.python.org/packages/2.6/P/Pygments/Pygments-1.2.2-py2.6.
egg#md5=4c78e542315e7ef3a60a265b7eba107e
Processing Pygments-1.2.2-py2.6.egg
creating c:\python26\lib\site-packages\Pygments-1.2.2-py2.6.egg
Extracting Pygments-1.2.2-py2.6.egg to c:\python26\lib\site-packages
Adding Pygments 1.2.2 to easy-install.pth file
Installing pygmentize script to C:\Python26\Scripts

Installed c:\python26\lib\site-packages\pygments-1.2.2-py2.6.egg
Processing dependencies for Pygments
Finished processing dependencies for Pygments

C:\Users\Kan>


ถ้าเป็น Linux จะมีเรื่องสิทธิประกอบ ก็พิมพ์ว่า

# sudo easy_install Pygments


ข้อความที่ขึ้นหลังจากนี้ก็ไม่ค่อยต่างกับ Windows เท่าไร

สามารถ download tar file แล้วมา unpack แล้ว install เองได้โดยใช้คำสั่ง

# sudo python setup.py install

แต่ไม่จำเป็นอย่าเปลืองพลังงาน โลกยิ่งร้อน ๆ อยู่

เสร็จแล้วก็จัดไป โดยขั้นแรก ให้พิมพ์คำว่า
python
ก็จะขึ้นหน้าจอแบบนี้

C:\Users\Kan>python
Python 2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>


หลังจากนั้นก็จัดการพิมพ์ต่อไปว่า

>>> from pygments import highlight
>>> from pygments.lexers import PythonLexer
>>> from pygments.formatters import HtmlFormatter


เพื่อทำการ import library ทั้ง 3 มา
หลังจากนั้นก็เริ่มกันเลย โดยเอา source ที่เราต้องการแปลงให้สวยหรูมา ใส่ตัวแปรชื่อ code

>>> code = '''
... list = ['a', 'b', 'c', 'd']
... r = ''
... i = 1
... for l in list:
... r = i == len(list) and '%s%s' %(r, l) or '%s%s, ' %(r, l)
... i = i+1
... '''


เสร็จแล้วก็ convert ซะโดยคำสั่งตามนี้

>>> print highlight(code, PythonLexer(), HtmlFormatter())


ก็จะมี output ออกมาเป็น

<div class="highlight">
<pre><span class="nb">list</span> <span class="o">=</span> <span class="p">[</span><span class="s">'a'</span><span class="p">,</span> <span class="s">'b'</span><span class="p">,</span> <span class="s">'c'</span><span class="p">,</span> <span class="s">'d'</span><span class="p">]</span>
<span class="n">r</span> <span class="o">=</span> <span class="s">''</span>
<span class="n">i</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">:</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">i</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span> <span class="ow">and</span> <span class="s">'</span><span class="si">%s%s</span><span class="s">'</span><span class="o">%</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">l</span><span class="p">)</span> <span class="ow">or</span> <span class="s">'</span><span class="si">%s%s</span><span class="s">, '</span> <span class="o">%</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">l</span><span class="p">)</span>
<span class="n">i</span> <span class="o">=</span> <span class="n">i</span><span class="o">+</span><span class="mi">1</span>
</pre>
</div>

เยอะแยะ มากมายอย่างที่คิดไว้ ก็แหงสิจะแยกสีมันต้องมีการเปลืองกันเล็กน้อย

เสร็จแล้วก็ Copy เอาไปวางใน Blog
สรุปยังไม่เสร็จ เพราะอะไรอะหรือครับ ถ้าสังเกตุดีดี code ที่เรา Generate ออกมา
ตัว Code มีการอ้างอิงถึง CSS เพื่อเอามาทำเรื่อง style ให้กับ View HTML
CSS อย่าถามว่าคืออะไรนะ โต ๆ กันแล้ว ไปดูบรรทัดต่อมาใน Document
จะมีคำสั่ง Generate CSS สำหรับเรื่องนี้อยู่ คือคำสั่ง

>>> print HtmlFormatter().get_style_defs('.highlight')



ก็จะมี output ออกมาเป็น ซึ่งยาวมิใช่น้อย ก็ก็ไม่มากเกินไป

.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #408080; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #FF0000 } /* Error */
.highlight .k { color: #008000; font-weight: bold } /* Keyword */
.highlight .o { color: #666666 } /* Operator */
.
.
.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #19177C } /* Name.Variable.Class */
.highlight .vg { color: #19177C } /* Name.Variable.Global */
.highlight .vi { color: #19177C } /* Name.Variable.Instance */
.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */

ประมาณนี้ ไม่โชว์ให้เห็นตอนนี้หมด เปลืองเนื้อที่ครับ
เราก็ copy css style ส่วนนี้ทั้งหมดไปเพิ่มไว้ใน แม่แบบของ Blog นั้น ๆ
แต่ละที่ไม่น่าจะเหมือนกัน ลองศึกษาดูนะครับ ในส่วน BlogSpot จะอยู่ที่
ปรับแต่ง : รูปแบบ : แก้ไข HTML
เผอิญตั้งภาษาไทยไว้ ก็เดา ๆ เอานะไม่น่ายากอะไรครับ

แล้วมาดูผลลัพท์ Code ที่อยู่ใน Blog กันเลย

list = ['a', 'b', 'c', 'd']
r = ''
i = 1
for l in list:
    r = i == len(list) and '%s%s'%(r, l) or '%s%s, ' %(r, l)
    i = i+1

โอ้มันยอดมากเลย ดูดีที่สุดอะ หุหุ

ไม่จำเป็นต้องใส่ tag code ครอบอีกแล้ว

<code>
...code...
</code>


จบแล้ว มีข้อสงสัยถามได้นะครับ ....