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

ws part 11 The programming models

>> ก่อนจะเข้ารายละเอียดเรื่อง WebSocket ใคร่แนะนำ model หรือรูปแบบเทคโนโลยีของการสื่อสารหรือการพูดคุยกันระหว่าง client กับ server แบบ real-time (ความจริงคือเกือบจะ real-time, เพราะ real-time จริงๆนั้นยังมีค่าเวลาของระยะทางที่ใช้ในการสื่อสาร) ที่มีมาหรือยังคงใช้อยู่ในปัจจุบัน ดังต่อไปนี้
- Polling
- Long polling
- Server-sent Events
- และ WebSockets

>> Polling เพื่อนๆทราบอยู่แล้วว่าการสื่อสารกันระหว่าง client กับ server จะเกิดขึ้นก็ต่อเมื่อ client เป็นผู้เปิดประเด็น (connect) การเจรจานี้กับทาง server, หากเราต้องการให้ข้อความหรือข้อมูลนี้เป็นปัจจุบันที่สุดโดยวิธีการของ polling เราจะต้องสั่งให้ client ถามหรือตรวจสอบ new updates กับ server อยู่เกือบจะตลอดเวลา หรือตามเวลาที่เรากำหนดไว้ก็ได้ เช่นกี่วินาทีก็ว่าไป วิธีการนี้ในแง่ของการประกันการมีชีวิตหรือ server จะสามารถทำงานตอบสนองต่อการร้องขอของทุก client โดยที่ไม่ล่มหรือเป็นลมนั้นน้อยมาก กล่าวได้ว่าเป็นวิธีการที่ด้อยประสิทธิภาพโดยสิ้นเชิงน่ะหรือ ตอบว่าใช่ ยกเว้น server สามารถควบคุมจำนวนการติดต่อเข้ามาของ client ได้ก็อีกเรื่องหนึ่ง

>> Long polling ขยับจาก Polling ดีขึ้นมาหน่อยตรงที่ลดจำนวนของ request หรือคำร้องขอลงได้หากว่าคำร้องขอก่อนหน้านี้ยังไม่ได้รับการตอบสนองจาก server, อธิบายในอีกแง่มุมหนึ่งคือ client เมื่อร้องขอข้อความหรือข้อมูลที่เป็นปัจจุบันไปยัง server คำร้องนี้จะมีอายุกำกับทันที (ค่าอายุโดย default เท่าไรนั้นก็ไม่รู้เหมือนกัน) หาก server ตอบกลับมาก็แล้วไป แต่หาก server ไม่ยอมตอบกลับมาและคำร้องนี้หมดอายุ, client จึงสามารถรังสรรค์วิธีการจัดการ (handler) กับ error หรือสิ่งที่เกิดขึ้นได้โดยสวัสดิกะ ต่างจากแบบ polling ที่ไม่สนใจอะไรทั้งนั้น (มันสนไม่ได้ เพราะมันไม่มีความคิดในการจัดการเรื่องทำนองนี้) ตัวอย่างที่เห็นชัดเจนเช่นการใช้ Ajax ดึงข้อมูลบางส่วนหรือเนื้อหาเฉพาะในส่วนที่ต้องการ update แบบ real-time มาแสดง โดยไม่ต้อง refresh ทั้งหน้า

long polling สามารถปรับใช้ให้ดีขึ้นได้อีก โดยใช้สิ่งที่เรียกว่า message body streaming เพื่อส่งข้อความหรือข้อมูลที่ต้องการจาก server สู่ client กล่าวคือ client ติดต่อไปหา server ก่อน และกำหนดให้ช่องทางการติดต่อนี้คงอยู่ตลอดไป (server ต้องไม่ close connection) ระหว่างนี้ server ก็จะทยอย update ข้อความหรือข้อมูลที่ต้องการนี้ให้ (ขอเรียกว่า chunk), ตัวอย่างการประยุกต์ทำได้โดยการใช้ javascript tags ควบคู่กับ iframe ของ html เป็นต้น

>> Server-sent Events เล่าไปแล้ว ณ part 9

>> WebSockets คือความคิดหรือเทคโนโลยีเพื่อใช้ติดต่อสื่อสารระหว่าง client กับ server แบบ real-time จริงจัง โดยอาศัยการเปิด port หรือ connection แช่ไว้ เป็นการติดต่อแบบสองทาง (bidirectional) กล่าวคือ client ส่งข้อความหรือข้อมูลสู่ server ด้วยเส้นทางเดียวกันนี้ server ก็ส่งข้อความหรือข้อมูลถึง client ได้ และยังดีกว่าเทคโนโลยี Ajax เพราะ client ไม่ต้องส่ง request ใหม่ไปหา server ทุกรอบไป (ก็บอกแล้วไงว่ามันเปิดช่องทางสื่อสารทิ้งไว้เลยตั้งแต่แรกเริ่มเจรจา) ตัวอย่างการใช้งานที่พบได้ทั่วไปเช่น
- โซเชียลฟีด: ทำให้เรารู้ว่าเพื่อนของเรากำลังทำอะไรอยู่
- เกมส์มัลติเพลย์เยอร์: ทำเกมส์บนเว็บได้แค่เพียงเขียน javascript ติดต่อกับ DOM โดยไม่ต้องพึ่ง Flash อีกต่อไป
- การทำงานร่วมกัน อย่างการแก้ไขโค้ด เป็นต้น
- ข้อมูลการคลิก: การเก็บข้อมูลการคลิก 1 ครั้งโดยการใช้ 1 HTTP request นั้นเป็นเรื่องที่สิ้นเปลือง การใช้ WebSocket ซึ่งต่อ 1 connection ค้างเอาไว้แล้วส่งข้อมูลทุกการคลิกของผู้ใช้ดูจะเป็นเรื่องที่ประหยัดและเหมาะสมกว่า
- การแสดงผลการเปลี่ยนแปลงเกี่ยวกับการเงิน (financial ticker): เช่นการอัพเดตราคาหุ้น หรือสิ่งที่มีการเปลี่ยนแปลงราคาตลอดเวลา
- อัพเดตข่าวกีฬา
- มัลติมีเดียแชท
- การเรียนออนไลน์
- อื่นๆมากมาย

*** หมายเหตุ ตัวอย่างการใช้งานที่พบนี้นำมาจาก
https://www.blognone.com/node/58036

>> part ต่อไปเราจะมาเขียนโปรแกรม WebSocket โดยภาษาจาวาครับ คืนนี้ดึกแล้วสวัสดี

วันอาทิตย์ที่ 20 ธันวาคม พ.ศ. 2558

Progress Report with PHP OOP part 4

ใกล้ถึงเวลาได้แยกโครงสร้างหน้าบ้านออกจากหลังบ้านแล้วครับ

>> ขณะนี้กำลังทำ CRUD กึ่งๆศึกษางานของหน้าบ้านไปด้วย ล่าสุดก็จับระบบ Grid system ของ Bootstrap เวอร์ชันสามใส่เข้ามาได้สองสามหน้าแล้ว ชอบที่มันใช้งานได้ง่าย คือแบ่งส่วนเอกสารออกเป็นสิบสองส่วนในแนวคอลัมน์ เราอยากให้ทางซ้ายกว้างเป็นกีคอลัมน์ เหลือเท่าไรก็ให้ทางขวากว้างเท่านั้นคอลัมน์ จะใช้อัตราครึ่งๆ 6:6 ก็ได้ หรือจะแบ่งสามเป็น 4:4:4 ก็สะดวกโยธินครับ

>> ระบบ Grid system ของเวอร์ชันสองต่างจากสามนะครับ สองนั้นใช้คลาสชื่อ span ตามด้วยจำนวนคอลัมน์ แต่เวอร์ชันสามนี่ใช้คลาสชื่อ col-xs- หรือ col-sm- หรือ col-md- หรือ col-lg- ซึ่งก็จะได้มิติใหม่ถึง 4 x 12 = 48 แบบ (เวอร์ชันสองมีแค่ 12 แบบ) ครับ ประมาณนั้นแหละมั้ง

>> ส่วนเรื่องอื่นก็เช่น Hover rows, Horizontal form, Buttons และ Image shapes

>> พูดถึงเรื่องรับส่งค่าผ่านตัว $_POST บ้าง ก็ดูโปรแกรมเมอร์รุ่นพี่ (ผมเรียกว่าอาจารย์) ฝรั่งสอนให้นำมาเขียนเป็นคลาสครับ (ชอบม๊าก) จับยัดไว้ในคลาสชื่อ Input ซึ่งผมจัดให้มันเป็น Util เวลาเรียกใช้ก็แสนจะง่ายดาย หุหุ เช่น

- ถ้าเขียนอย่างนี้ Input::exists() เป็นการถามว่า มีการใช้ post หรือไม่ ถ้ามีคืนค่าจริง ถ้าไม่มีก็ได้ค่าเท็จ

- ถ้าเขียนอย่างนี้ Input::get('productName') เป็นการขอชื่อผลิตภัณฑ์นั่นเอง

>> สุดท้ายนี้เอาภาพมาโชว์อยู่สามหน้า ได้แก่
1) หน้าที่จะหยิบสินค้าหรือผลิตภัณฑ์ใส่ตะกร้า (บนซ้าย)
2) หน้าสร้างสินค้า (บนขวา หน้านี้จะเอาไว้หลังบ้านให้ Admin เป็นคนจัดการ)
3) และหน้าแก้ไขหรือ update สินค้า (ล่างขวา เช่นกัน Admin เป็นคนเติมสต๊อก)

>> PHP OOP นี้มันมันส์จริงๆ ไว้จะมาป่าวประกาศให้รับทราบกันอีกนะ (อย่าเพิ่งเบื่อล่ะ) คืนนี้ฝันหวานครับ

Progress Report with PHP OOP part 3

ก็มาเล่าเรื่องงาน หรือสิ่งที่ทำอยู่ ณ ขณะนี้ให้ฟังนะครับ

>> ช่วงนี้อย่างที่บอกว่าเขียน PHP โดยพยายามศึกษาหลักการ OOP ไปด้วย ตอนนี้ก็ได้เพิ่มหลักการ Data Access Objects หรือย่อเป็น DAO เข้าไปด้วย ก็เพื่อจัดการกับ CRUD ให้เป็นระบบมากขึ้น

>> โปรเจ็กต์ฝึกหัดนี้มีโครงสร้างเป็นอย่างนี้ครับ วางโครงสร้างด้วย MVC โดยให้
- M แบ่งหมวด Model ทั้งหลาย อย่างเช่นคลาส Product หรือใดๆที่มีลักษณะเป็น Plain Old Java Object (อ่าว Java มาไง) หรือย่อเป็น POJO ในที่นี้คือ PHP คลาสธรรมดาที่ประกอบไปด้วยสิ่งที่มันพึงมีเท่านั้น ได้แก่ id, name, detail และ price
- V แบ่งหมวด view ทั้งหลาย ไม่แน่ในภายหน้าจะแยกเป็นฝั่ง front end และ back end เจ้า V นี้จะรับข้อมูลใดๆจาก client หรือผู้ใช้งาน รวมไปถึง M ด้วย ซึ่งมันถูกควบคุมโดยเจ้า C
- C แบ่งหมวด Controller ทั้งหลาย ควบคุม URL เป็นหลัก ทั้งยังสั่ง control เจ้า M ให้ใส่ลงไปในเจ้า V เขาคนนี้สำคัญมากเลยนะ

>> ขอบคุณ PHP เวอร์ชัน 5 ที่มีเรื่อง OOP มาให้เล่นครับ เป็นการฝึกฝนที่มันส์ม๊วก เราสามารถสร้าง interface เพื่อพูดคุยกับ DAO ยังไม่รวม PHP Design Patterns ที่ทำให้ระบบของเรามีแบบแผน (อย่างน้อยผมก็รู้สึกอย่างนั้น) อยากบอกว่า interface นี่สำคัญมากนะครับ มันทำให้ออบเจ็กต์เกิดการ polymorphism กลางอากาศได้ (ภาษาผมเอง ผมหมายถึงขณะ runtime) จะสวดยอดไปไหนหือ ใครคิดสร้างกันเนี่ย

>> ความคืบหน้าตอนนี้คือ ทดสอบระบบหลังบ้านสำเร็จแล้ว มันสามารถเรียกดูผลิตภัณฑ์ทั้งหมด (find all products) หรือเฉพาะบางผลิตภัณฑ์ได้ (find product by id) โดยควบคุมได้จาก URL สามารถเพิ่ม แก้ไข ลบได้โดยสวัสดิกะ

>> วันต่อไปผมจะไปลุยที่หน้าบ้านบ้าง กะว่าจะฝึกใช้ JQuery กับ Bootstrap v.3 เพื่อนๆคนไหนพอจะแนะนำแนวทางแจ่มๆเชิญได้เสมอเลยนะครับ สุดท้ายนี้เอาภาพความเพียรค่อนคืนมาฝาก ขอบพระคุณที่ติดตามและเป็นกำลังใจให้ตลอดมา ฝันดีครับผม

Progress Report with PHP OOP part 2

ตอนนี้กำลัง implement งานครับ

สืบเนื่องจากอ่าน PHP OOP และมีระบบซื้อขายเล็กๆอยู่ในมือระบบหนึ่ง เห็นโค้ดที่เขาและเธอเขียนแล้วมันไม่มันส์ 555 ก็ต้องอ่านเว็บ + ดู Youtube ในส่วนที่อยากได้ครับ (ไม่นับหนังสือภาษาไทยที่เรามีนะ) ก็เพื่อความมันส์สะใจ (วันนี้ 9 เช้าโมงถึงตี 3 ของอีกวัน) สะพอไหมหือ? (นอนเถอะ)

ในที่สุดก็ได้ระบบ create query update delete (หลักการ CRUD) หลังบ้านมาเรียบร้อย วางโครงสร้างเป็น MVC เรียกทุกอย่างเป็น OOP ทั้งสิ้น (มึนจ้า 555) เอาเป็นว่าขอขอบคุณท่านทั้งหลายเหล่านี้ครับ (งานยังไม่เสร็จหรอกนะ)

- หนังสือ PHP เล่มดำของอาจารย์ บัญชา ปะสีละเตสัง (เป็นพระปะเนี่ย)
- Java Design Pattern โดยอาจารย์ กิตติพงษ์ กลมกล่อม (ตำนาน)
- Object-Oriented Programming with PHP5 สำหรับ php name conversation และ php design pattern ตั้งแต่บทที่ 2 เป็นต้นไป
- ระบบซื้อขายออนไลน์ จาก คัมภีร์ การโปรแกรมเชิงวัตถุด้วย PHP ของ KTP
- เว็บ codeofaninja.com หัวข้อ CRUD with JQuery and PHP สำหรับเรียกน้ำย่อย
- เว็บ php.net หัวข้อ PDOStatement::fetchAll สำหรับติดต่อฐานข้อมูลด้วย PDO
- เว็บ dev.mysql.com สำหรับการเซตอักษรภาษาไทยขณะที่เรียกใช้ SQL Create Statement
- สุดท้ายคือกลุ่มอาจารย์ phpacademy จาก www.youtube.com/channel/UCpOIUW62tnJTtpWFABxWZ8g ที่ช่วยชี้แนะแนวทางโค้ด OOP สำหรับงานนี้โดยเฉพาะ

ขอบพระคุณยิ่งครับ เหล่าท่านเป็นผู้มีคุณที่แบ่งปันความรู้ ทั้งในยามปกติและยามทุกข์เข็น ขอปิดท้ายด้วยภาพความคืบหน้าของงาน (โชว์นิด) ไม่มีอะไรได้มาง่ายดาย นี่คือสิ่งที่ผมเรียนรู้ครับ ฝันดีครับผม

Progress Report with PHP OOP part 1

พักนี้ช่วยน้องเขียนงานส่งอาจารย์ครับ

เป็น project ที่เขียนด้วยภาษา PHP ทว่าเราต่างก็เริ่มต้นเฉกกัน และแม้ผมจะมี project สำเร็จถืออยู่ในมือแล้ว ไม่รู้ว่าทำไม? เหมือนผมต้องการสิ่งอื่นนอกจากการเขียน PHP แบบโครงสร้างธรรมดาอย่างนั้นหรือ? คำตอบคือ ไม่แน่ใจครับ แต่คิดว่าใช่

ตอนนี้ก็ยังหลงเสน่ห์หลัการ OOP ไม่ส่างซา สืบเนื่องจากผมใช้ภาษาจาวาเป็นปกติ จึงคิดหาวิธีการออกแบบ OOP ที่มีหลักวิชามาสนองกับจาวา ความต้องการนี้ส่งผลถึง PHP ด้วย กลายเป็นว่าอยู่ในช่วงเสาะแสวงและเรียนรู้ PHP OOP Design ครับ

ดังนั้นเพื่อนคนไหนที่สนใจอย่างเดียวกันนี้ ผมชวนให้อ่านเล่มนี้ Object-Oriented Programming with PHP5 และหากมีโอกาสผมก็จะเอาเนื้อหาที่อ่านมาสรุปเป็นสิ่งที่เรียนรู้ได้ ก่อนที่จะเขียนมันไว้ ณ blog โปรแกรมเมอร์ฝึกหัดอย่างเคยๆ

>> ค้นหาที่ Google ด้วยคีย์ว่า Object-Oriented Programming with PHP5 pdf ครับ โหลดไปอ่านกันได้เลยจ้า (ระวังถูกจับนะ อิอิ)

Sub vs Function vs Method

Sub vs Function vs Method

>> สามศัพท์ข้างต้นเกิดขึ้นด้วยความหมายที่แตกต่างกัน นักพัฒนาโปรแกรมฝึกหัดเช่นผมควรทราบว่าการเขียนโปรแกรมหรืออ่านภาษาโปรแกรมแล้วพบศัพท์เหล่านี้นั้น ผู้ประดิษฐ์คิดค้นตัวภาษาเขาไม่ต้องการให้มันมีความหมายเหมือนกันนะครับ (เพราะถ้าอย่างนั้นคงเรียกชื่อเดียวไปแล้ว)

>> Sub มาจากคำว่า Subroutine คือ โปรแกรมย่อย ที่เมื่อทำงานเสร็จสิ้นแล้ว จะไม่ส่งผลการทำงาน (value) กลับไปยังโปรแกรมที่เรียก (ย้ำว่าไม่ส่งผลลัพธ์กลับ)

>> Function คือ โปรแกรมย่อย ที่เมื่อทำงานเสร็จสิ้นแล้ว จะส่งผลการทำงาน (value) กลับไปยังโปรแกรมที่เรียก

>> Method คือ พฤติกรรมของคลาส เขียนเพื่ออธิบายว่าคลาสใดๆนั้นมีพฤติกรรมอะไรบ้าง จะส่งผลการทำงานกลับไปยังคลาสที่เรียกใช้งานหรือไม่ ขึ้นอยู่กับพฤติกรรมนั้นๆเป็นหลัก

>> นักพัฒนาโปรแกรมมือใหม่มักจะสับสน แล้วนำเอาทั้งสามความคิดมารวบยอดเป็นวิธีการเขียนโปรแกรมของตัวเอง ซึ่งผิด มักเขียนเมธอดเป็นฟังก์ชัน เข้าใจการสร้างคลาสเป็นการแบ่งไฟล์ ทำโปรแกรมใหญ่ให้เป็นโปรแกรมย่อย ทั้งๆที่ใช้หลักการ OOP ส่งผลให้ Design Pattern ล่มสลายหรือไม่เคยเกิดขึ้นเลย

>> ถ้าถามว่าทำไมต้องแคร์เรื่องนี้ คำตอบส่วนตัวของผมก็คือ เพราะมันคือความรู้ของอาชีพ ยิ่งต้องทำงานกับคนหมู่มาก เราต้องทำความเข้าใจให้ตรงกันว่าแต่ละศัพท์ที่ใช้มีความหมายต่อการพัฒนางานอย่างไร การอ่านโค้ดจะง่ายขึ้น การประยุกต์ก็จะง่ายขึ้น รอยยิ้มปรากฏมากขึ้น รักและเชื่อมั่นในการงานอาชีพที่ทำครับ

ความรู้สึกของ Developer ฝึกหัด

>> ผมยังจำตอนที่อาสอนปั่นจักรยานสองล้อครั้งแรกได้ดี เดิมทีผมกลัวมากเพราะคิดว่าอย่างไรคงต้องล้ม ล้มแล้วต้องเจ็บตัว เด็กๆไม่อยากเจ็บตัว (พอพอกับไม่อยากโดนเฆี่ยน) อาบอกกับผมว่าไม่เป็นไร อาจะจับท้ายจักรยานไว้ไม่ต้องกลัวล้ม ผมก็มั่นใจว่าอาจะทำตามที่พูด แต่แล้วพอรู้ตัวว่าปั่นมาตั้งไกลโดยที่ไม่มีอาคอยจับท้ายไว้เลย (หันไปมองไง) ก็ตกใจล้มลงเสียอย่างนั้น เจ็บตัวด้วย

>> สองสามวันมานี้ผมวางโค้ดลง แล้วหันมาอ่านหนังสือและบทความจำพวกการวิเคราะห์และออกแบบระบบ (Systems analysis) ก็เข้าใจบ้างไม่เข้าใจบ้าง จะให้ไปถามใครต่อใครเขาคงไม่มาอธิบาย (ก็ไม่ตั้งใจเรียนเอง ช่วยไม่ได้) เรื่องแบบนี้เราต้องศึกษาเองไปสักระยะ รู้สึกยากยิ่งกว่าหัดปั่นจักรยานสมัยเด็กเสียอีก (ปวดหัวตุ๊บๆ) สำคัญคืออาคงสอนไม่ได้ (หัวเราะ)

>> เมื่อเราเริ่มศึกษาอะไรก็ตาม ยิ่งเป็นสิ่งที่เราไม่ถนัดหรือไม่เคยมีใจให้มันล่ะก็ มันยิ่งกว่าทากคลานเสียอีก ผมยังแอบอิจฉาเพื่อนๆหลายคนที่เขาอ่านออกแปลภาษาอังกฤษคล่อง ผมมานึกๆดู ขนาดเว็บภาษาไทยเราอ่านได้ความรู้เท่านี้ พวกที่อ่าน Text เหมือนอ่านหนังสือการ์ตูน จะได้รับความรู้ขนาดไหน โอ้บระเจ้า! คิดแล้วก็ปวดหัว ยิ่งอ่านก็ยิ่งมันส์ยิ่งปวด ตอนนี้ผมถึงต้องมาระบายว่าผมน่ะ ปวดหัวนะ แต่ผมก็สนุกกับมัน ผมน่ะกลัวเจ็บตัวนะแต่ก็อยากปั่นจักรยานให้ได้

>> ไม่มีสิ่งไหนได้มาง่ายดาย ตราบเท่าที่เราไม่ไขว่คว้าจนสุดกำลังก่อน คำว่าสุดกำลังนี้ไม่ได้หมายถึงหักดิบนะครับ แต่หมายถึงค่อยเป็นค่อยไป แต่มีจังหวะ มีเวลา มีทิศทางที่ถูกต้อง ก็เป็นกำลังใจให้สำหรับ Dev ฝึกหัดเช่นเดียวกับผมหรือกับเพื่อนๆที่กำลังพยายามทำบางสิ่งบางอย่างแต่รู้สึกท้อ วันนี้เราเป็นเต่า เป็นหอยทาก ก็คลานไปก่อนครับ ใครว่า ใครด่าช่างเขาไว้ก่อน ไว้เราได้รับสิ่งที่ต้องการแล้วค่อยกล่าวขอบคุณเขาภายหลังยังไม่สายเกินไป พอถึงเวลานั้นก็อย่าลืมขอบคุณตัวเองด้วยล่ะครับ สวัสดี

>> หนึ่งในเว็บที่ผมอ่านวันนี้ (สุดปวด)
http://www.somkiat.cc/

ไม่ใช้ DAO แต่ควรใช้ Repository?

วันก่อนอ่านบทความเรื่อง Don’t use DAO, use Repository มันเป็นยังไงนะ?

>> DAO ย่อมาจาก Data Access Object คือหลักการเพื่อต้องการให้จาวาคลาสธรรมดาสามารถสร้างความสัมพันธ์กับข้อมูลในฐานข้อมูลในรูปแบบ Relational Database ง่ายๆว่าหนึ่งคลาสคือหนึ่งตารางในฐานข้อมูล (จริงๆทำได้มากกว่านี้) เวลาจะอ้างถึงฟิลด์ (หรือคอลัมน์) ใดๆในตารางก็ให้เรียกไปที่เมธอด getters หรือ setters ของคลาสนั้นๆแทน

>> DAO ตามหลักการนั้นเราจะต้องออกแบบ Relational Database ให้ดีเสียก่อน เป็นไปได้เพื่อความง่ายต่อการนำไปใช้งาน ก็ควรให้มีหนึ่งตารางคู่กับหนึ่งคลาสเท่านั้น และคลาสดังกล่าวก็ควรเป็น POJO (Plain Old Java Object) ด้วยนะครับ

>> ทีนี้มาเข้าประเด็นไม่เอา DAO แต่ให้ใช้ Repository แทน? เรื่องเป็นมาอย่างนี้ครับ
- เดิมทีเขา (ผู้เขียนบทความไง) มีคลาส Account ที่ implement หลักการ DAO และตั้งชื่อว่า AccountDAO
- AccountDAO ประกอบด้วยบริการพื้นฐานสำหรับทำงานกับข้อมูลในตาราง โดยใช้หลักการ CRUD อีกที ได้แก่ การสร้าง (Create), การค้นหา (Retrieve), การแก้ไข (Update) และการลบ (Delete)
- หากเขาประสงค์ร้องขอ Account โดยกำหนดแค่นามสกุลของลูกค้า (last name) เพื่อไปค้นหาล่ะ? การค้นหาที่มีอยู่ตอบสนองเขาได้หรือไม่? มีสองคำตอบคือ 1) ไม่ได้ เพราะไม่มีบริการนี้อยู่ หรือ 2) ได้สิ ถ้าสร้างบริการที่ค้นด้วยนามสกุล (เพิ่มเมธอด)
- หากเขาประสงค์ร้องขอ Account โดยกำหนดเป็นหมายเลขบัญชีแทนนามสกุลล่ะ? คำตอบนั้นก็ยังเป็นเช่นเดิมใช่ไหมครับ
- จะเห็นได้ว่า AccountDAO ของเขากำลังอ้วนเมื่อเวลาผ่านไป (ว้ายตายแล้ว!) เนื่องจากเงื่อนไขที่กระทำต่อ CRUD เพิ่มมากขึ้น (และเพี้ยน) นั่นเท่ากับว่าความเป็น DAO นั้นลดน้อยลงไป (ถ้าคุณยอมรับได้ก็อีกเรื่อง จะผืนเรียกมันว่า DAO ต่อไปก็ตามใจ)
- แต่สำหรับเขาแล้ว เรื่องนี้มันสุดทานทน

>> ใช้หลักการ Repository ดีกว่า ซึ่งงานจริงๆก็ควรเป็นอย่างนี้ เอาล่ะ Repository ก็คือเซตของออบเจ็กต์ที่มีความสัมพันธ์ต่อกันในแง่ใดแง่หนึ่ง มันยังเป็นเหมือนกับ Collection หรือก็คือที่สะสมของปานนั้น
- เปลี่ยนคลาส Account ที่ implement หลักการ DAO เป็นหลัการ Repository โดยให้ชื่อว่า AccountRepository
- AccountRepository ประกอบด้วยบริการ addAccount, removeAccount และ updateAccount นอกจากนี้ให้เพิ่มบริการ query เข้าไป หัวใจของมันก็คือพารามิเตอร์ที่ชื่อ AccountSpecification ซึ่งเป็น interface ครับ ฮั่นแน่! ใครอ่านถึงตอนนี้คงเริ่มงงว่าเจ้า AccountSpecification นี้มันทำหน้าที่อะไร
- AccountSpecification จะเป็นตัวบริการคอยบอกว่า ข้อมูล จากฐานข้อมูล อยู่ใน หน่วยความจำหลัก แล้วหรือยัง? เรื่องนี้สำคัญมากนะครับ เพราะข้อมูลที่ต้องดึงจากฐานข้อมูลอยู่บ่อยๆนั้น ควรได้รับการบริหารจัดการที่ดี หนึ่งในวิธีการดังกล่าวคือ เมื่อไปดึงมาครั้งหนึ่งแล้ว (อยู่ในหน่วยความจำหลักเรียบร้อย) ครั้งต่อไปก็ไม่ต้องไปดึงมาอีก ให้หาเอาจากหน่วยความจำหลักได้เลย (ของที่อยู่ใน RAM อย่างไรก็ค้นไวกว่าของที่ต้องดึงจากดิสก์หลายสิบหลายร้อยเท่า) ฉะนั้น AccountSpecification จึงมีบริการ specified เตรียมไว้ให้ใช้ไงล่ะครับ (^^)
- AccountRepository ไม่เพียงแต่ต้องประกอบด้วย interface ที่ชื่อ AccountSpecification เพราะว่างาน query นั้นมักมากไปด้วยเงื่อนไข เขาได้เพิ่ม interface ที่ชื่อ SqlSpecification เข้าไปด้วย
- SqlSpecification ให้บริการ toSqlClauses เพื่อสร้างเงื่อนไข WHERE ต่อกันเป็นทอดๆนั่นเอง

>> ทั้งหมดนี้ผมอ่านจาก http://thinkinginobjects.com/2012/08/26/dont-use-dao-use-repository/

>> เขียนโปรแกรม 'ได้' นั้นไม่ยากครับ ดูไวยากรณ์ + business logic ให้เข้าใจก็เขียนได้ แต่เขียนโปรแกรม 'เป็น' นั้นยากกว่าหลายเท่านัก ไม่เพียงแต่ต้องเข้าใจ business logic ยังต้องเข้าใจวิธีการ implement งานด้วยหลักการต่างๆได้ด้วย อาชีพนักเขียนโปรแกรมนี้จึงจะเรียกได้ว่าถูกเติมเต็มอย่างภาคภูมิครับ

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

ws part 10 Server-Sent Event example




>> ทำความเข้าใจความคิดของ sse ไปแล้ว จึงได้ลองทำเป็นโปรเจ็กค์เล็กๆครับ ติดปัญหาการ config บ้าง, จาร์บ้าง หรือเวอร์ชันไม่รองรับบ้าง ส่วนตัวได้ผลสรุปว่า

- รันบน tomcat server ไม่ได้ผลตามที่ต้องการ, ไม่สามารถควบคุม uri ได้ กล่าวคือ เมื่อเรียก uri ไปหา resource ได้ กลับมองหา index page ไม่เจอ หรือในทางตรงข้าม หากเรียกไปหา index page ได้ก็จะหา resource ไม่เจอ, คาดว่าเป็นเรื่องของ context กับ url-pattern ครับ (แก้ไขได้ แต่ไว้ก่อน)

- รันบน glass fish server ได้ผลตามต้องการ สามารถควบคุม uri ได้ จึงได้นำมาโพสต์เป็นตัวอย่างในคราวนี้

>> ภาพทั้งหมดนี้อธิบายได้ว่า เมื่อ client ร้องขอไฟล์ index.html จาก server, ในไฟล์ index.html นั้นมีจาวาสคริปต์เรียกไปยังบริการลงทะเบียนของ sse อยู่ (services/events) เท่ากับบอกตัวตนของตัวเองแก่ server


>> ที่ server คำรองขอจาก index.html ข้างต้นจะเรียกไปยังเมธอดชื่อ getConnection สร้าง EventOutput และลงทะเบียนกับออบเจ็กค์ SseBroadcaster รอการถูก broadcast ต่อไป ความจริงแล้ว server รู้จัก client ก็ตรงนี้แหละครับ


>> สถานการณ์ต่อมา (วิ่งลงน้ำ อะไม่ใช่) client ร้องขอไฟล์ chat.html จาก server ก็จะได้ฟอร์มสำหรับ post ชื่อตัวเองกับข้อความ, เมื่อไรก็ตามที่ post ไปยัง server, บริการที่ชื่อ post จะทำงาน เกิดการสร้าง OutboundEvent (หรือก็คือหัวใจของการสร้างและรับ event ของ part ที่แล้ว) ส่งให้กับประดา client ที่ได้เคยมาลงทะเบียนไว้ ด้วยการ broadcast (ป่าวประกาศออกไปให้กับทุกคนนั่นเอง)


>> เป็นว่าทุก client ที่เรียกมายัง index.html ก็จะได้รับข้อความหรือ event เดียวกันทั้งหมดครับ เช่นห้องสนทนาของสามคนนี้ หมี, น้องเฟิร์น และโหน่งเต่ง ครับ



>> หลักความคิดของ sse ตัวอย่างนี้ใช้การ push events จาก server สู่ client เป็นการสื่อสารแบบทางเดียว โอกาสต่อไปเราจะเข้าสู่วิถีของ WebSocket ซึ่งเป็นการสื่อสารสองทาง และจาวาบรรจุความคิดนี้แล้วใน JEE เวอร์ชัน 7 เอาล่ะไว้เจอกันใหม่ สวัสดีครับ

>> ไฟล์อื่นๆ
- SseApplication.java


- มาดูที่ System.out.println ของ server กันบ้าง ว่า client คุยอะไรเข้ามา (นี่มันภาษาอะไร? มันคือ HTML entity)


- จึงต้องเอาไป decode (ผมเลือกใช้เว็บนี้ https://mothereff.in/html-entities)


>> สุดท้ายเป็นภาพที่ได้จาก http://caniuse.com/#feat=eventsource บอกได้ว่าในขณะนี้ browser รุ่นไหนของเจ้าไหนบ้างที่รองรับเทคโนโลยี sse ครับ

วันจันทร์ที่ 14 ธันวาคม พ.ศ. 2558

ws part 9 Jersey กับ Server-Sent Event

>> ทำอย่างไรจึงจะสร้างการเชื่อมต่อระหว่าง client กับ server ได้ โดย server มีหน้าที่ดูแลการเชื่อมต่อนั้น กลไกนี้จะ push ข้อมูลจาก server สู่ client ซึ่งปราศจากการสร้าง request ใหม่ เราสามารถประยุกต์ใช้กลไกดังกล่าวกับแอพพลิเคชันหลากหลายลักษณะ เป็นต้นว่า แอพ chatting, แอพ stock market หรือแอพที่ต้องการข้อมูลแบบ real-time เฉกตลาดหุ้น

>> โดยปกติแล้วเมื่อ client สมมติว่าเป็น browser ติดต่อกับ server มันจะส่ง request มาก่อน เมื่อ server รับทราบความต้องการนั้นๆก็จะส่ง response กลับไปให้ แล้วสิ้นสุดการติดต่อระหว่างกัน เป็นเช่นนี้ทุกครั้งตลอดระยะเวลาการสื่อสาร หากความต้องการข้อมูลมีความถี่มากขึ้น เป็นไปได้ที่ server ไม่อาจตอบสนองกับทุก request เหล่านั้นได้ทัน นี่จึงกลายเป็นเหตุผลที่ว่าสำหรับข้อมูลที่ต้องการ real-time อย่างจริงจังจะทำให้เกิดขึ้นได้อย่างไร? (ปกติร้องข้อมากเข้าเซิร์ฟก็จะล่ม)

>> วิธีการก็คือให้ server เป็นผู้ส่งข้อมูลให้ client เสียเอง (กล่าวคือไม่อนุญาตให้ client ส่ง request เข้ามาอีก) นั่นยังหมายความว่า client กับ server จะต้องเชื่อมต่อระหว่างกันอยู่เสมอ กลไกลนี้ถูกเรียกว่า Server-Sent Events (SSE) โดยการเชื่อมต่อใดๆจะถูกสร้างก็ต่อเมื่อ client ติดต่อเข้ามาหา server ก่อน และเป็นการเชื่อมต่อเพียงครั้งแรกและครั้งเดียวเท่านั้น จนกว่าการเชื่อมต่อดังกล่าวจะถูกปฏิเสธโดย client หรือ server จึงจะถือว่าการสื่อสารสิ้นสุดลงครับ

>> เขาบอกว่า (หนังสือบอกมา) เมื่อ client ส่งสัญญาณการติดต่อมาหา server ผ่าน EventSource ซึ่ง server พร้อมที่จะพยักหน้ารับหรือรับฟัง EventSource นี้อยู่ตลอดช่วงชีวิตของมัน เมื่อมันได้รับ EventSource มันจะสร้างการเชื่อมต่อใหม่ขึ้น และบริหารจัดการหน่วยความจำนี้ภายในคิว (queue) หลังจากนั้นจึงเริ่มส่ง event ให้ client ตาม business logic ที่กำหนดไว้ เรียกการสื่อสารลักษณะนี้ว่า การเชื่อมต่อทิศทางเดียว (unidirectional connection) คือจาก server สู่ client ครับ

>> ทีนี้มาดูทาง client บ้าง หลังจากที่มันถูกตอบรับการเชื่อมต่อ ตัวมันเองก็พร้อมที่จะรับฟัง event (ในนี้มีข้อมูลที่มันต้องการอยู่) และเมื่อไรก็ตามที่เกิด event ใหม่ ณ ฝั่ง server เจ้า event ก็จะถูก broadcast เพื่อส่งมาให้ client เป็นเช่นนี้ตลอดระยะการสื่อสารระหว่างกันครับ

>> แน่นอนว่าต้องเป็น browser ที่เข้าใจ HTML5 นะจ๊ะ ถึงจะสร้าง EventSource ได้

>> โดยทั่วไปแล้ว HTML5 จะใช้ภาษาจาวาสคริปต์เป็นตัวรับฟัง event ใหม่ๆที่ถูกส่งเข้ามาผ่านฟังก์ชัน onmessage แต่สำหรับ Jersey client ที่เราสนใจอยู่นี้ (เขียนด้วยภาษาจาวาเน๊อะ) จะมีเมธอด onEvent ที่ทำงานลักษณะเดียวกัน

>> เมื่อให้ client ติดต่อกับ server เป็นที่เรียบร้อยแล้วและเตรียมพร้อมจะส่ง event, server จะสร้างสิ่งที่เรียกว่า OutboundEvent ขึ้นมารับผิดชอบ event เจ้ากรรมที่กำลังจะส่งออกไป โดยจะต้องนำมา serialized (ถูกทำให้เป็น stream) ผ่าน OutboundEventWriter เสียก่อน ตอนนี้นี่เองที่เราสามารถกำหนดรูปแบบหรือ media type ให้กับข้อมูลที่อยู่ภายใน OutboundEvent ได้

>> เมื่อ event ที่อยู่ในรูปแบบ stream เดินทางมาถึง client, InboundEvent มีหน้าที่รับผิดชอบโดยตรงเพื่อรับ event ดังกล่าว ก่อนจะนำไป deserialized ผ่าน InboundEventReader, ข้อมูลจึงจะสามารถแสดงบน browser ได้

- รูปจาก https://www.safaribooksonline.com/library/view/developing-restful-web/9781783288298/ch05s02.html

วันอาทิตย์ที่ 13 ธันวาคม พ.ศ. 2558

ws part 8 Web Application Description Language

>> เมื่อเว็บเซอร์วิสถูกนำไปใช้ (deployed) บนเครื่อง server, บริการต่างๆที่เราได้เขียนขึ้นหรือ resource ใดๆที่เราต้องการประกาศออกไปเพื่อให้ฝั่งผู้ใช้บริการรับทราบ (client หรือในที่นี้ก็คือเว็บแอพพลิเคชันหรือโปรแกรมแอพพลิเคชันฝั่ง back-end ต่างๆ เช่น java, jsp, asp, php เป็นต้น) ย่อมต้องใช้เอกสารที่เรียกว่า Web Application Description Language (WADL) เป็น api หรือลายแทงตามหา resource เพื่อตระเตรียม input ที่ต้องส่งและ output ที่ต้องรับ ทั้งนี้จะได้คุยกันรู้เรื่องนั่นเอง

>> เพื่อนๆทราบอยู่แล้วว่าการร้องขอ resource หรือบริการต่างๆของ restful จำต้องใช้ uri, แล้วจะหามันได้จากไหนถ้าเกิดว่าโปรแกรมเมอร์ไม่บอกหรือแปะส่งอีเมล์มาให้? ไม่ยากครับ ให้เรียกไปที่ uri ที่เปิดให้บริการและหลังจากประเภทของเว็บไซต์ (.com, .co.th, .org) ให้เพิ่มสิ่งนี้เข้าไป
/application.wadl

ดังตัวอย่างที่ผ่านมา เราได้ deployed เว็บเซอร์วิสสู่ server ในชื่อ
http://localhost:8080/product_rs_services

จึงได้ว่า
http://localhost:8080/product_rs_services/application.wadl

>> กล่าวคือ เอกสารการใช้งานหรือ WADL นี้จะถูกสร้างขึ้นโดยอัตโนมัติเมื่อเว็บเซอร์วิสถูก deployed ไว้บนเครื่อง server, มีรูปแบบเป็น xml, ภายใต้ชื่อแท็ก resources ก็คือ path ของ resource ใดๆหรือบริการต่างๆที่เราสามารถนำมา / ต่อจาก http://localhost:8080/product_rs_services ได้นั่นเอง ถัดเข้าไปอีกคือแท็กของ method มันจะบอกว่าต้องการ input เช่นไรถ้าเกิดว่ามันต้องการ (request) และบอกว่าจะส่ง output อะไรออกมา (response)

>> ภาพด้านล่างนี้แสดงให้เห็นว่าเว็บเซอร์วิสตามตัวอย่างข้างต้นมี WADL เพื่อใช้อธิบายวิธีการใช้งานตัวมันอย่างไร


>> ส่วน part ต่อไปเราจะมาคุยกันเรื่องการส่งข้อมูลทางเดียวจาก server สู่ client ซึ่งเป็นการส่งข้อมูลแบบ automatic updates หรือโดยอัตโนมัติครับ

>> คืนนี้ฝันดีพรุ่งนี้ยังต้องทำงาน บายครับ

วันเสาร์ที่ 12 ธันวาคม พ.ศ. 2558

ws part 7 Server API example

>> HTTP methods ที่เพื่อนๆรู้จักกันแล้ว อาทิเช่น @GET เพื่อขอ resource, @PUT เพื่อ update resource, @POST เพื่อสร้าง resource และ @DELETE เพื่อลบ resource, เหล่านี้จะยกตัวอย่างการใช้งานตามโอกาสครับ

>> @Produces ง่ายๆเลย มันมีหน้าที่กำหนด output ของเมธอดบริการนั้นว่าจะให้ผลลัพธ์เป็นรูปแบบ (MIME types) อะไร มีได้มากกว่าหนึ่งรูปแบบ เช่น application/xml หรือ application/json หรือทั้งสองอย่างหรือมากกว่า, มันสามารถวางไว้ได้สองตำแหน่งครับ

1) ตำแหน่ง class-level หมายถึง นำ @Produces นี้ไปเขียนไว้ก่อนประกาศคลาสหรือ resource จะส่งผลให้เมธอดบริการภายใต้ resource นี้ทั้งหมดมี MIME types ตามที่กำหนดนี้เป็น default

2) ตำแหน่ง method-level หมายถึง นำ @Produces นี้ไปเขียนไว้ก่อนประกาศเมธอดหรือบริการ จะส่งผลให้เฉพาะเมธอดบริการนี้มี MIME types ตามที่กำหนด หรือกล่าวว่ามันจะไป override @Produces ของตำแหน่ง class-level ครับ

>> เมื่อมีการผลิตก็ต้องมีการบริโภค @Consumes มีไว้เพื่อบอกว่าเมธอดบริการนั้นๆสามารถรับ input จากฝั่งผู้ใช้บริการรูปแบบใดบ้าง เช่น post เข้ามาในรูปแบบ form หรือแบบ json หรือแบบ xml หรืออื่นๆ (ได้มากกว่าหนึ่งรูปแบบ โดยเลือกตามลำดับที่เรากำหนด), สามารถวางไว้ได้สองตำแหน่ง

1) ตำแหน่ง class-level
2) ตำแหน่ง method-level

ให้ผลลักษณะเดียวกับของ @Produces เพียงเปลี่ยนจากให้ output เป็นรับ input

>> จากตัวอย่างที่ผ่านมาเมื่อผมต้องการที่อยู่ของผู้ใช้ id เท่ากับ 001 หรือ 002 จะระบุเข้าไปที่ address url ดังนี้

../users/addresses/001 หรือ
../users/addresses/002

เจ้า 001 และ 002 คือ id แสดงว่าเราต้องมีวิธีการที่จะดึง id ข้างต้นออกจาก address url, ฮีโร่หรือผู้ช่วยเหลือในเรื่องนี้ก็คือ Parameter annotations ครับ ได้แก่
- @PathParam
- @QueryParam
- @MatrixParam
- @HeaderParam
- @CookieParam
- @FormParam
- @BeanParam

ครั้นจะมาอธิบายหน้าที่ของพวกมันในคราวเดียวก็ยังไงอยู่ เป็นว่าเราได้ใช้ตัวไหนจากตัวอย่างเช่นไร เราค่อยมาคุยรายละเอียดของพวกมันตัวนั้นๆกันนะครับ (แอบขี้เกียจ)

@Path("/{userId}")
@Produces(MediaType.APPLICATION_XML)
@GET
public Response getAddressesByUserId(@PathParam("userId") String userId) { ... }

โด้ดนี้เราได้เขียนกันไปแล้ว อธิบายได้ว่า
- เมธอด getAddressesByUserId นี้คือ subresource method นั่นเพราะมันมี @Path และ @GET HTTP method ระบุเอาไว้ (หากไม่มี HTTP methods ใดๆระบุไว้แต่มี @Path เมธอดลักษณะนี้มักคืนค่าเป็น resource หรือคลาสอื่น ซึ่งเราจะเรียกว่า subresource locator)
- หลังจาก ../users/addresses ก็คือ /{userId} หรือก็คือค่า input ที่ถูกส่งเข้ามาโดยใช้ @PathParam เป็นตัวรับ โดยชื่อจะต้องเหมือนกันกับใน @Path และรับเข้ามาให้ตัวแปร (พารามิเตอร์) อะไรนั้นก็ตามสะดวก ในที่นี้ก็ตั้งชื่อเดียวกัน นั่นคือ userId
- เมธอดนี้ผลิต (produce) ผลลัพธ์รูปแบบ MIME type เป็น xml ครับ

>> ตามประสามือใหม่ก็ต้องหัดจากตัวอย่างที่ง่ายและเป็นพื้นฐาน หลังจากนั้นจึงค่อยเพิ่มระดับความซับซ้อนเข้าไปทีละน้อย โอกาสนี้ผมขอใช้ตัวอย่างจากเว็บ

- http://javapapers.com/java/java-restful-web-services-with-json-and-jersey/

มาดัดแปลงแก้ไขพร้อมเพิ่ม layer เข้าไปอีกชั้นสองชั้น
- ให้เว็บเซอร์วิสมีความสามารถในการบริการดังนี้ แสดงรายการสินค้าทั้งหมด, เพิ่มสินค้าได้, ปรับปรุงรายละเอียดของสินค้าทีละชิ้นได้ และลบสินค้าทีละชิ้น ความคิดนี้คือ CRUD นั่นเอง
- ให้เว็บเซอร์วิสมีชั้นของ service และ dao เพื่อที่ root-resource จะเรียกชั้นของ service และชั้นของ service ไปเรียกชั้นของ dao โดยชั้นของ dao สมมติว่าติดต่อกับฐานข้อมูล
- โดยทั่วไปแล้วชั้นของ service และ dao มักถูก inject โดยกลไกอื่นแค่ครั้งเดียวและออบเจ็กค์เดียว ในที่นี้จึงขอใช้ static มาจัดการเพียงชั้นของ service ครับ
- โครงสร้างของ Server API

- จาร์ทั้งหมดที่ใช้

- root-resource


- ชั้นของ service ส่วนของ interface

- ชั้นของ service ส่วนของ implementation

- ชั้นของ dao ส่วนของ interface

- ชั้นของ dao ส่วนของ implementation



>> ทีนี้ก็มาทดสอบกัน โดยการใช้จาวาโปรเจ็กค์ธรรมดายิงเข้าไปขอใช้บริการ (เว็บเซอร์วิส: หลังบ้านคุยหลังบ้าน)
- จาร์ทั้งหมดและที่เพิ่มเข้ามาครับ

- pojo หรือจาวาคลาสธรรมดา โครงสร้างของมันนี้ฝั่งผู้ใช้บริการและฝั่งผู้ให้บริการต้องมีเหมือนกัน

- โค้ดทดสอบ ขอดูรายการสินค้าทั้งหมด, เพิ่มสินค้ารหัส 1002 หนึ่งชิ้น, ปรับปรุงชื่อสินค้ารหัส 1001 จาก iPhone 6s เป็น iPhone 6s plus!, ลบสินค้ารหัส 3003 และขอดูรายการสินค้าทั้งหมดอีกครั้ง


- สุดท้ายมาดู console ฝั่งผู้ให้บริการ (เว็บเซอร์วิส) ว่าเป็นอย่างไรเมื่อฝั่งผู้ใช้บริการขอใช้บริการครับ


>> อ่านข้อมูลเพิ่มเติม
- https://jersey.java.net/documentation/latest/message-body-workers.html
- https://jersey.java.net/documentation/latest/media.html#json.jackson

วันศุกร์ที่ 11 ธันวาคม พ.ศ. 2558

ws part 6 Client API

>> ก็มาถึงส่วนแบ่งที่หกกันแล้วนะครับ อันที่จริงควรจะเล่าเรื่อง server api ของ restful ให้ฟังก่อน เอาเป็นว่าเล่าไปพร้อมๆกันดีกว่า เท่าที่ผมเข้าใจเป็นดังนี้ครับเพื่อนๆ

>> ตอนนี้เรากำลังเขียนเว็บเซอร์วิสหรือเว็บให้บริการ resources ด้วย http protocol ซึ่งเป็นความคิดหรือหลักความคิดที่ถูกทำให้เกิดขึ้นจริงผ่าน library jar ชุดหนึ่งที่มีชื่อว่า Jersey ซึ่งมันได้แสดงให้เราเห็นแล้วว่า โค้ดฝั่ง server หรือฝั่ง services นั้นหน้าตาหรือแนวทางการเขียนเป็นอย่างไร ฉะนั้นต่อไปนี้เราจะมาเขียนโค้ดฝั่ง client หรือฝั่งผู้ขอใช้บริการกันบ้าง ว่าจะสามารถติดต่อผู้ให้บริการดังกล่าวนี้ได้อย่างไร

>> ควรทราบก่อนว่าฝั่งผู้ขอใช้บริการหรือเรียกว่า jax-rs client api ที่เรากำลังจะเขียนขึ้นกันนี้ มันถูกสร้างให้สามารถควบคุมหรือบังคับกลไกการขนส่งหรือการขอ connection บน http protocol, มันติดต่อได้กับทุกบริการที่สร้างอยู่บนรากฐานของ http protocol นั่นเองครับ

>> มี client api ตัวอื่นอีกไหมที่ทำอย่างเดียวกันนี้ได้ คำตอบคือ มีครับ อาทิเช่น HTTPURLConnection, Apache HTTP Library เป็นต้น แต่พวกเขาซับซ้อนเกินไป ใช้เวลาทำความเข้าใจมากกว่า (เทียบกับเวลาของมือใหม่) จะดีกว่าไหมในเมื่อ jax-rs client api ได้ห่อหุ้มและจัดการซ่อนความซับซ้อนนี้ให้ ทั้งยังมี api หรือวิธีการใช้งานที่ง่ายดายเพื่อนักพัฒนาจะได้ใช้เวลา ณ จุดๆนี้ลดน้อยลงไป แน่นอนมือใหม่อย่างผมยิ้มรับครับ

>> javax.ws.rs.client.ClientBuilder ขอตัวติดต่อเว็บเซอร์วิสจากเขาก่อน เราจะได้ออบเจ็กค์ client มาซึ่งจะต้องใช้ระบุไปยัง uri ที่เว็บเซอร์วิสนั้นให้บริการ แท้จริงแล้วเราสามารถเขียน uri ตรงๆไปยัง resource ใดๆได้เลย แต่ที่ตัดมาเขียนในเมธอด path ก็เพื่อเป็นการบ่งชี้ว่าส่วนที่ตัดมานี้คือ resources ที่ต้องการนะ ทั้งยังสามารถเขียนต่อๆกันได้ ดังรูปตัวอย่าง


>> โปรเจ็กค์ทดสอบนี้ ใช้จาวาโปรเจ็กค์ธรรมดา (รายงานผลลัพธ์ออกทาง console) จะสังเกตได้ว่าผลลัพธ์แรกคือ json ของข้อมูลที่อยู่ของ user ทุกคน ส่วนผลลัพธ์ที่สองคือ xml ของข้อมูลที่อยู่ของ user ที่มี id เท่ากับ 002 เพียงคนเดียว


>> ผลลัพธ์ที่เว็บเซอร์วิสส่งกลับมาเรากำหนดให้อ่านเป็นสตริง รับโดยออบเจ็กค์ Response และแสดงผลออกทาง console ดังที่ได้กล่าวไปครับ

>> โอกาสหน้าจะสลับไปเตรียม resources ฝั่งผู้ให้บริการมากกว่านี้หน่อย แล้วค่อยกลับมาคุยกันฝั่งผู้ใช้บริการอีกที

>> และนี่คือเหล่าจาร์ที่ใช้ในโปรเจ็กค์ครับ สวัสดี

วันพุธที่ 9 ธันวาคม พ.ศ. 2558

ws part 5, 10 Best Practices for Better RESTful API

>> ขอยกประโยชน์ที่ได้ทั้งหมดนี้ รวมถึงเครดิตแก่เว็บ
- http://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/
ที่ช่วยอธิบายวิธีการออกแบบเว็บเซอร์วิสให้เป็นไปในทางที่เกิดประโยชน์แก่ความคิดและการใช้งานครับ

>> ผมอ่านแล้วจึงนำมาเล่าในแบบที่ผมเข้าใจนะ (แต่ถ้าเกิดว่าแปลความผิดไปก็ขออภัยอย่างยิ่ง) เขาบอกว่านี้เป็น 10 วิธีการประดิษฐ์หน้าตาของ uri ที่จะใช้เข้าถึง resource ครับ มาเริ่มกันเลย

1) จงเขียนมันด้วยคำนาม (หากคำนั้นเติม s คือนามพหูพจน์) ไม่เขียนมันด้วยกริยา เพื่อให้ง่ายต่อการสื่อความหมาย ดังตัวอย่างต่อไปนี้

/cars
- สำหรับ GET คืออ่าน list หรือรายการรถทั้งหมดออกมา
- สำหรับ POST คือสร้างรถหนึ่งคัน (ไว้บน server หรือเครื่องที่ให้บริการไงล่ะ)
- สำหรับ PUT คือปรับปรุงเปลี่ยนแปลงกลุ่มของรถทั้งกลุ่ม
- DELETE คือลบรถทุกคันออกจาก server หรือระบบที่กำลังใช้งานอยู่

/cars/711
- สำหรับ GET คือขอรถคันที่มี id เท่ากับ 711 มาให้ฉันหน่อย มันต้องมีเพียงคันเดียว!
- POST เรียกไม่ได้ครับ จะต้องได้ error 405 "ไม่อนุญาต" กลับมา เพราะเราจะไม่สร้างรถโดยระบุ id เข้าไปด้วย, ส่วนใหญ่แล้วรถหนึ่งคันเมื่อถูกสั่งให้สร้างจะเรียกไปที่ POST ของ /cars เฉยๆ และ id ของรถคันนี้ก็จะถูกรังสรรค์มาพร้อมกับตัวรถ
- PUT คือปรับปรุงรถหนึ่งคันที่มี id เท่ากับ 711
- DELETE คือลบรถหนึ่งคันที่มี id เท่ากับ 711

เหล่านี้จัดว่าเป็น api หรือหน้าตาของ uri ที่ดี แต่ตัวอย่างด้านล่างนี้ไม่ดีและไม่ควรทำ เช่น

/getAllCars คือขอรถทุกคัน
/createNewCar คือสร้างรถหนึ่งคัน
/deleteAllRedCars คือลบรถสีแดงทุกคัน

2) ไม่ใช่ GET เพื่อเปลี่ยนแปลงสถานะ นี่คือตัวอย่างที่ไม่ดี

GET /users/711?activate คือผู้ใช้ id เท่ากับ 711 ตอนนี้ถูก activate แล้ว
หรือ
GET /users/711/activate คือความหมายเดียวกัน

ควรใช้ PUT, POST และ DELETE ในการเปลี่ยนแปลงสถานะของ resource นั้นๆตามหน้าที่ของมัน จากตัวอย่างข้างต้นควรใช้ PUT

3) ไม่ใช้คำนามเอกพนจน์ผสมกับคำนามพหูพจน์ เพื่อให้ง่ายควรใช้เพียงคำนามพหูพจน์เท่านั้นสำหรับทุกๆ resources ตัวอย่างเช่น

จงใช้ /cars แทน /car
จงใช้ /users แทน /user
จงใช้ /products แทน /product
จงใช้ /settings แทน /setting

5) ใช้ HTTP headers ในการกำหนดรูปแบบ (formats) ที่ต้องการ (ว่าจะเอา text/plain หรือ application/xml หรือ application/json) ไม่ว่าจะเป็นฝั่งผู้ขอใช้บริการ (client) หรือฝั่งผู้ให้บริการ (server) ควรกำหนดรูปแบบที่ชัดเจนในการพูดคุยกันไปเลย โดยกำหนดผ่าน HTTP-Header ดังนี้
- Content-Type ใช้กำหนดรูปแบบของ request
- Accept ใช้กำหนดรายการรูปแบบที่สามารถยอมรับได้ของ response เช่นปกติไม่ระบุอะไรเลยได้รูปแบบ xml กลับมา พอระบุรูปแบบ json ณ Accept ก็จะได้ json กลับมา (รูปแบบเหล่านี้ขึ้นอยู่กับฝั่งผู้ให้บริการว่าจัดเตรียมให้มากน้อยกี่รูปแบบ)

6) จงใช้ HATEOAS มันคืออะไร?
ย่อมาจาก Hypermedia as the Engine of Application State หรือวิธีการเข้าถึง resource ผ่าน hypertext links หรือลิงค์นั่นแหละ เพื่อการ navigate ที่ยอดเยี่ยม เช่น เราให้บริการค้นหาเครื่อง notebook โดยให้ผู้รับบริการใส่โค้ดเนมของมันเข้ามา ,ทีนี้ผู้รับบริการก็ใส่โค้ดเนมว่า 711 (สมมติไง) เพราะเขาต้องการหา drivers ต่างๆของมัน (พอดีว่าลงวินโดว์ใหม่) ผลลัพธ์ที่เราในฐานะผู้ให้บริการควรจัดเตรียมไว้ควรเป็นดังตัวอย่างนี้ (ดัดแปลงและตัดเอาบางส่วนมา)

{
"id": 711, "model": "X5", "drivers": [ { "id": "23", "name": "Dynaudio sound", "links": [ { "rel": "self", "href": "/api/v1/drivers/23" } ] } ]
}

เห็นไหมว่ามีลิงค์ให้สามารถตามต่อไปยัง drivers ได้

7) มันควรสามารถอำนวยความสะดวกการ filtering (เลือกเอาข้อมูลบางกลุ่ม), sorting (เรียงลำดับ), field selection (เลือกเอาเฉพาะฟิลด์ที่ต้องใช้) และทำ paging ได้ มาดูตัวอย่างกันเลย

- Filtering เช่น
GET /cars?color=red เลือกรถทุกคันที่ทาสีแดง
GET /cars?seats<=2 เลือกรถทุกคันที่มีสองที่นั่ง หนึ่งที่นั่ง หรือไม่มีให้นั่ง?

- Sorting เช่น
GET /cars?sort=-manufacturer,+model
คือเรียงลำดับ manufacturer หรือรายการผู้ผลิตแบบ descending (ใช้เครื่องหมายลบ เพื่อสื่อว่าเรียงแบบมากไปหาน้อย) และ model แบบ ascending (ใช้เครื่องหมายบวก เพื่อสื่อว่าเรียงแบบน้อยไปหามาก)

- Field selection อาทิเช่น
การแสดงผล resource บนโทรศัพท์มือถือที่มีพื้นที่จำกัด เราอาจเลือกได้ว่าจะให้ field ไหนปรากฏบ้าง ทั้งนี้ก็เป็นการลดการจารจรบนเครือข่าย (reduce the network traffic) ทั้งยังเพิ่มความเร็วให้กับ api ของเรา ตัวอย่าง

GET /cars?fields=manufacturer,model,id,color คือขอแค่สี่ฟิลด์มาแสดงผล (ความจริงมีมากกว่าสิบฟิลด์ก็เป็นได้)

- Paging
api ควรสามารถกำหนด limit และ offset ได้ เพื่อความยืดหยุ่นในการดึงข้อมูลจากฐานข้อมูล ค่าเริ่มต้นทั่วไปคือ limit=20 และ offset=0 ตัวอย่าง

GET /cars?offset=10&limit=5 คือขอรถทั้งหมดตั้งแต่คันที่ 10 เป็นต้นไปเป็นจำนวน 5 คัน

และสำหรับค่า total entries หรือจำนวนรายการทั้งหมดตั้งแต่ offset เท่านี้ไปเท่านั้น limit, ก็สามารถหาได้จากการ custom HTTP header เช่น X-Total-Count

ลองคิดถึงหน้ารายงานหรือตาราง เราสามารถสร้าง previous page, next page ตลอดจน first or last page ได้นะครับ โดยความคิดของเจ้า paging นี่แหละ

8) สามารถสร้างลักษณะที่บอกเวอร์ชันของ api ร่วมด้วยจะเริดมาก ด้วยการใช้อักษร v ตัวอย่างเช่น
/blog/api/v1

9) บอก error ด้วย status code เท่าที่จำเป็นและเพียงพอ (รายงานได้เพียง error 500 นี่ไม่เอานะ เลวร้ายเกินไป) status code ที่ควรรายงานได้อาทิเช่น

200 – OK – Eyerything is working
201 – OK – New resource has been created
204 – OK – The resource was successfully deleted

304 – Not Modified – The client can use cached data

400 – Bad Request – The request was invalid or cannot be served. The exact error should be explained in the error payload. E.g. "The JSON is not valid"
401 – Unauthorized – The request requires an user authentication
403 – Forbidden – The server understood the request, but is refusing it or the access is not allowed
404 – Not found
422 – Unprocessable Entity

500 – Internal Server Error

หรือรายงานเป็น error payloads แทน status code ข้างต้นก็ได้ คือแทนที่จะเป็น 200, 400 หรือ 500 ก็เป็นอะไรที่ผู้ขอบริการอ่านแล้วเข้าใจทันที ใช้อีกบริบทหนึ่งมาอธิบายสิ่งที่เกิดขึ้นกับสิ่งที่เขาต้องการจึงจัดว่าแจ่มกว่า

10) ยอมให้สามารถ overrideing HTTP method
เขาว่ามีเครื่องตัวแทน (proxy หรือก็คือเครื่องที่มีสภาวะแวดล้อมดุจเดียวกับเครื่อง server แต่ไม่จัดเก็บข้อมูลจริงๆไว้กับตัว) บางเครื่องรองรับแค่ POST กับ GET เท่านั้น ควรแล้วที่จะต้อง custom HTTP Header ได้ ด้วยการกำหนดค่า X-HTTP-Method-Override, อันนี้ไม่ค่อยเข้าใจ ตัวอย่างไม่มี ในตอนนี้จึงขอผ่านไปก่อนนะครับ

>> และก็ครบสิบข้อ เพื่อที่ part ต่อไปเราจะทดลองนำหลายข้อมาใช้กับเว็บเซอร์วิสตัวอย่างของเรา อิอิ ฝันดีจ้า

วันอาทิตย์ที่ 6 ธันวาคม พ.ศ. 2558

ws part 4 RESTful WS how to create subresource locators

>> ก็เพิ่งเริ่มต้นทำความรู้จักกับเว็บเซอร์วิส จับผิดจับถูกก็ถูไถไปตามตำราว่าไว้ บ้างก็ตำรายังไม่ได้กล่าวถึงก็ใจร้อนไปขอวิชาจากที่อื่นๆเสียก่อน ความเดิมจาก part ที่ผ่านมากะว่าจะสร้าง client เพื่อขอใช้บริการเว็บเซอร์วิส อื่ม ขออนุญาตถัดไปก่อนนะครับ ติดใจเรื่อง subresource locators

>> เกี่ยวกับ @Path เราทราบกันแล้วว่ามันวางได้สองตำแหน่ง
1) Class-level
2) API-level
class-level คือวาง @Path ไว้ ณ root-resource หรือก็คือ resource แรกที่เราต้องการเข้าถึงมันผ่าน uri หรือดังตัวอย่างที่ผ่านมานั่นแหละครับ ส่วน api-level ก็คือการวาง @Path ไว้ ณ subresource methods ที่อยู่ภายใต้ root-resource อีกที อื่ม ผมไม่งงนะ แต่ไม่แน่ใจว่าเข้าใจถูกไหม เข้าประเด็นคราวนี้กันเลยดีกว่า แล้ว subresource locators คืออะไร?

>> subresource locators ภาษาบ้านๆเลยก็คือเมธอดที่ไม่มี HTTP header resource methods ใดๆระบุไว้ คือไม่มีพวก @GET, @PUT, @POST, @DELETE อะไรอย่างนี้เขียนไว้น่ะ จะมีเพียง @Path ในระดับ api-level เท่านั้นที่กำกับเอาไว้ เพราะอะไรน่ะหรือ? เพราะว่า return type ของเมธอดดังกล่าวนี้จะ return คลาสที่เป็น resource ออกไปน่ะสิ

>> สมมติให้ user หนึ่งคนสามารถมีที่อยู่ได้ เราจะเรียก user ว่าเป็น root-resource จากนั้นจะไปตามหาที่อยู่ของเขาผ่าน subresource methods ก่อน โดยให้เจ้า subresource methods นี้คืนค่าออกมาเป็น resource อีกตัวหนึ่ง เราเรียกการ redirect จาก subresource methods ของ root-resource ใดๆไปยัง resource ตัวใหม่นี้ว่า subresource locators ครับ

>> จะสังเกตได้ว่าตอนนี้เราพอจะหยิบใช้ประดา annotation ได้หลายตัวแล้ว ไม่ว่าจะเป็น @Path, @Produces, @GET รวมถึง @XmlRootElement และเพื่อไม่ให้มือใหม่ (โดยเฉพาะผมเองนี่แหละ) ไม่สับสนจนเกินไป เราจะมาสร้างตัวอย่างง่ายๆเพื่อเรียกใช้ subresource locators กันครับ

>> สมมติโจทย์ว่า 1 user มีได้หลาย address เช่น ผมคนเดียวมีบ้านอยู่ที่กรุงเทพถึงสองหลัง เราจะให้ user เป็น root-resource และให้ address เป็น resource ที่ถูกเรียกโดยวิธีการ subresource locators
- จะมีสองคลาสที่เกี่ยวข้องกันดังนี้ user ถูกจัดการโดยคลาส UserResource และ address ถูกจัดการโดยคลาส AddressResource
- หากเรียกไปว่า ../users จะได้ข้อความ We are users!

- หากเรียกไปว่า ../users/addresses จะได้เป็น json ของรายการ user ทั้งหมดที่สมมติว่าเราได้เก็บไว้ในฐานข้อมูล

- และหากว่าเรียกไปยัง user id ใดๆ เช่น ../users/addresses/001 จะได้เป็น xml ของรายการที่อยู่ทั้งหมดของ user ที่มี id เป็น 001 ครับ



>> วิธีการออบแบบเว็บเซอร์วิส uri นี้ยังไม่ดีนักนะครับ โอกาสต่อๆไปเราควรได้พูดคุยกันบ้างถึงวิธีการ design ที่น่าจะดูดีมากกว่านี้ เอาล่ะ ลงมือได้

>> แรกเลย เรากำหนดให้เมธอด getAddresses ของคลาส UserResource กระทำ redirect ไปยัง resource เป้าหมาย นั่นคือคลาส AddressResource


>> คลาส AddressResource หรือ resource นี้มีบริการสองอย่าง หนึ่งคือ getAddresses จะให้ที่อยู่ทั้งหมดออกไป ส่วน getAddressesByUserId จะถามหา userId ใดๆที่เราจะต้องระบุมากับ uri ในที่นี้คือ ../users/addresses/001



>> เมื่อ subresource locators ข้างต้นถูกเรียก เราจะต้องเพิ่มจาร์ตัวนี้เข้ามาครับ มันคือ jersey-entity-filtering ไม่งั้นติด error

>> เมื่อพยายามส่งผลลัพธ์ (Response) กลับออกไปใสรูปของ List หากว่าเป็น json จะต้องเพิ่มจาร์ตัวนี้เข้าไป มันคือ jackson-module-jaxb-annotations


>> และเมื่อพยายามเปลี่ยนหรือต้องการส่งผลลัพธ์จาก List เป็น xml จะต้องห่อหุ้มหรือบอกว่าไส้ในของ List เป็นคลาสอะไร ให้ใช้คลาส javax.ws.rs.core.GenericEntity มาจัดการครับ ไม่งั้นติด page error 500

>> เย้ๆทำได้แล้ว แม้จะดูรกไปบ้างก็เถอะ เป็นว่า part ต่อไปเรามาพูดเรื่อง design กันหน่อยดีกว่านะ บายจ้า

>> ขอบคุณสำหรับความช่วยเหลือครับ
- http://stackoverflow.com/questions/11040995/a-message-body-writer-for-java-class-java-util-arraylist

ws part 3 RESTful WS how to generate XML or JSON

>> วันนี้ได้พูดคุยเรื่องนี้กับสองท่านผู้สนใจ เริ่มจากการเรียนรู้พื้นฐานของ part ที่ผ่านมา จุดหมายของเราคือสร้างเว็บเซอร์วิสที่สามารถให้ผลลัพธ์เป็น xml หรือ json ก็ได้ผ่าน pojo สักตัวหนึ่ง

>> เราใช้ jar ทั้งหมดของ part 1 มายำกันต่อ

>> ตามหนังสือเล่มเดิมที่บอกเล่าไปแล้ว ณ part 2 ลองผิดลองถูกกันได้ความว่า
- คลาสใดๆที่ถูกประกาศด้วย @Path คือ resource
- เมธอดใดๆของคลาสที่ถูกประกาศด้วย @Path คือ subresource method
- ตัวแปรใดๆที่ต้องการส่งค่าให้ resource สามารถเขียนได้ภายใต้เครื่องหมาย { และ }
- หากการส่งค่านี้อยู่ในระดับคลาส ทุกเมธอดของคลาสก็เรียกใช้ได้ ในที่นี้เรียกด้วย @PathParam
- หากการส่งค่านี้อยู่ ณ ระดับของเมธอด เมธอดนั้นก็เป็นผู้ใช้ด้วย @PathParam เช่นเดียวกัน



>> มาดูตัวอย่างง่ายๆกันก่อน เริ่มด้วยเมธอด getBook ที่เมื่อเราร้องขอ resource ผ่าน uri ที่ว่า
localhost:8080/ch01_provider_sample_2/books
มันจึงทำงานทันทีและคืนค่ามาว่า I am a book ไม่สงสัยอะไรใช่ไหม โอเค



>> ถัดไปเราเพิ่มเมธอด getBook เข้าไปอีก คราวนี้ต้องการพารามิเตอร์หนึ่งตัวชื่อ isbn (หมายเลขระเบียนหนังสือหรือ id ของหนังสือนั่นแหละ) ก็รับเข้ามา ณ ตัวแปร bookId สุดท้ายก็ส่งค่ากลับออกไป สมมติเลข isbn คือ 1122 ผลลัพธ์จึงได้
I am a book. My Id is 1122

>> ทั้งสองเมธอดที่กล่าวใช้ชื่อเดียวกันแต่รับพารามิเตอร์ไม่เท่ากัน กล่าวคือตัวแรกไม่รับพารามิเตอร์ใดๆ เรียกเฉยๆก็ให้ค่า ส่วนตัวที่สองรับหนึ่งตัว เรียกพร้อมกับส่งพารามิเตอร์ให้ดังนี้
localhost:8080/ch01_provider_sample_2/books/1122


>> เอาล่ะ ทดสอบส่งค่าออกมาเป็น xml กันบ้าง ก็แก้ไขเมธอด getBook เดิมนี่แหละครับ ให้มันส่งออบเจ็กค์ Response ออกไป สำคัญเลย ต้องระบุไทป์หรือชนิดของผลลัพธ์ที่ต้องการด้วย @Produces ซึ่งมีความหมายว่าต้องการให้มันผลิตอะไรส่งออกไปนั่นเอง เมื่อเรียก uri เดิมเหมือนข้างต้นจึงได้ผลลัพธ์เป็น xml ทันที แหม! เขียน xml ด้วย string นี่มันหมูจริงๆ


>> เอาล่ะขยับเข้าไปใกล้ POJO กันหน่อย จาวาคลาสธรรมดาที่เราอาจใช้มันเพื่อเป็นส่วนหนึ่งของงานหลังบ้าน หรืออาจใช้มัน persist ตัวของมันกับฐานข้อมูล (ความคิด JPA โดย Hibernate) เราจึงมาจำลองกันว่าหากเรามี pojo สักตัวชื่อ Book เราจะส่งมันออกไปเป็น xml หรือ json ได้อย่างไร



>> แรกเลยเราพยายามยัดเยียดมันให้ออบเจ็กค์ Response ดังนี้ Response.ok(book).build() มัน error เลยครับ ได้ความว่ามันไม่รู้จัก application/xml ล่ะเออ (ทีสตริงธรรมดานี่เข้าใจเลยนะ)



>> แก้ไขด้วยการเพิ่ม jar เข้าไปอีก นั่นคือ jersey-media-jaxb ผลคือสำเร็จ!


>> คราวนี้เปลี่ยนเป็น json กันบ้าง มัน error อีกแล้วครับ บอกว่าไม่รู้จัก application/json (ที xml นี้รู้จักดีเลยนะเออ)

>> แก้ไขด้วยการเพิ่ม jar เข้าไปอีก นั่นคือ jersey-media-json-jackson ผลคือร้องลั่น มัน error อีก บอกว่าอยากได้ ../json/JacksonJaxbJsonProvider จึงแก้ไขด้วยการเพิ่ม jar เข้าไปให้ นั่นคือ jackson-jaxrs-json-provider

>> เหมือนจะเสร็จ ยัง ยังร้องอีก อยากได้ ../base/ProviderBase โอเคจัด jackson-jaxrs-base ให้

>> ร้องอยากได้ ../core/Versioned โอเคจัด jackson-core

>> ร้องอยากได้ ../databind/ObjectReader งั้นเอา jackson-databind

>> สุดท้ายก็ jackson-annotations เงียบเลยเธอ และออกมาแล้ววว!
{"isbn":"1122","name":"I am a book"}



>> เป็นอันว่าบรรลุเป้าหมายที่วางไว้ครับ ส่วนแผนอันใกล้เราจะสร้างฝั่ง client ที่ขอใช้บริการเหล่านี้กันหน่อยเป็นไง เพื่อที่จะนำ xml หรือ json ที่ได้จากฝั่ง service นี้มาแปลงให้เป็นคลาสออบเจ็กค์ทำงานอื่นใดต่อไป คืนนี้ฝันดี อย่าลืมอ่านหนังสือที่ให้ไว้นะ บ้ายบาย