Archive for October, 2009

SpringSource tc Server Developer Edition

October 29th, 2009

แพลทฟอร์มที่ดีที่สุดสำหรับการสร้าง Spring แอพพลิเคชั่น
อาปาชี่ทอมแคทเป็นจาวาแอพพลิเคชั่นเซิร์ฟเวอร์ที่เป็นที่นิยมมากที่สุดในโลกและสปริงก็เป็นผู้นำในเรื่องของจาวาโปรแกรมมิ่งโมเดลระดับเอ็นเทอร์ไพรซ์ สองเทคโนโลยีนี้เมื่อนำมาใช้ร่วมกันแล้วทำให้เราได้เพลทฟอร์มที่ทรงพลังที่ทำให้โค้ดของแอพพลิเคชั่นของเรามีความซับซ้อนน้อยและไม่ต้องเข้าไปติดอยู่กับความยุ่งยากของ Java EE
SpringSource tc Server Developer Edition จริงๆแล้วคืออาปาชี่ทอมแคทที่ถูกนำมาปรับปรุงและพัฒนาให้สามารถทำงานเข้ากับสปริงได้อย่างดีที่สุดและสำหรับรุ่นนี้เป็นรุ่นที่เปิดให้ใช้ฟรี นอกจากนี้เรายังสามารถใช้ SpringSource Tool Suite ที่ถูกใส่เข้ามาในรุ่นนี้อีกด้วย
SpringSource tc Server Developer Edition introduces significant new efficiencies for developers including:
ส่วนที่เป็นหน้าตาให้กับ SpringSource tc Server Developer Edition ก็คือ Spring Insigh Console ซึ่งทำหน้าที่เป็น dashboard ที่มีไว้สำหรับการ monitor แอพพลิเคชั่นแบบ realtime ดังนั้นเราจึงสามารถใช้เครื่องมือตัวนี้เข้ามาช่วยในการทำ ตรวจจับ, วิเคราะห์ และ วินิจฉัยสาเหตุ ในเรื่องประสิทธิภาพของได้ การนิวิเกทไปสู่ต้นตอของปัญหาโดยเราสามารถดริวลงไปเป็นขั้นๆจนถึงระดับ requests และ controller actions นั่นหมายความว่าเราสามารถเข้าถึงจุดที่เกิดปัญหาได้อย่างรวดเร็วมากขึ้น เสริมความสามารถในเรื่องของการวินิจฉัยปัญหาเช่น deadlock detection, การจับ log ของ thread และ heap แบบ automatic และ on-demand และการแปลง thread ไปเป็น request URL
spring tc server

AtomFeedView ใน SpringMVC 3 (RESTFul)

October 22nd, 2009

ตัวอย่างการใช้งาน RestFul ใน SpringMVC3.0 คือเราจะสร้างแอพพลิเคชั่นที่สร้าง Content เหมือน Blog ขึ้นมาและเนื้อหาสามารถถูกแปลงจากการแสดงผลแบบ HTML ปกติให้ไปเป็น Atom Feed ได้ด้วยก่อนอื่นเรามาดูการสร้าง
Content ก่อนเพราะเราจะใช้ package de.svenjacobs.loremipsum.LoremIpsum เพื่อสุ่มสร้าง content มาดังนี้

...
import de.svenjacobs.loremipsum.LoremIpsum;

public class SampleContent {

	private static final LoremIpsum textGenerator = new LoremIpsum();
	private static final Random random = new Random();
	private static int idCounter = 0;
	private String author;
	private Date publicationDate;
	private String text;
	private int id;

	public static SampleContent generateContent(String author, Date date) {
		SampleContent content = new SampleContent();
		content.author = author;
		content.publicationDate = date;
		content.id = idCounter++;
		content.text = textGenerator.getParagraphs(random.nextInt(9) + 1);

		return content;
	}
	.
	.
	// get set
	.
}

จากนั้นเราจะสร้าง Controller ที่ทำหน้าที่สร้าง Content ที่สร้างวิวแบบ HTML ปกติก่อน

@Controller
public class ContentController {

	private List<SampleContent> contentList = new ArrayList<SampleContent>();

	@RequestMapping(value="/content", method=RequestMethod.GET)
	public ModelAndView getContent() {
		ModelAndView mav = new ModelAndView();
		mav.setViewName("content");
		mav.addObject("sampleContentList", contentList);
		return mav;
	}

	@RequestMapping(value="/content.html", method=RequestMethod.POST)
	public String addContent() {
		contentList.add(SampleContent.generateContent("Alef Arendsen", new Date()));
		return "redirect:content.html";
	}
}

การทำงานของ handler ตัวแรกจะทำหน้าที่ลิสท์ไอเท็ม SampleContent ออกมาโดยมันจะรับ request ประเภท GET เข้ามา ส่วน handler ตัวที่สองจะทำหน้าที่เพิ่มคอนเทนท์ SampleContent เข้าไปด้วยการใช้
SampleContent.generateContent() ส่วน handler ตัวนี้จะรับ request ประเภท POST นอกจากนี้เราใช้ @RequestMapping เพื่อระบุ URI ที่เราระบุให้ handler ซึ่งการใช้งาน @RequestMapping นั้นเราต้องไปเพิ่ม Configuration
ชื่อ Component Scanner เข้าไปที่ rest-servlet.xml นอกจากนี้ส่วนสำคัญที่เราต้องใช้ในการทำงาน REST สำหรับ Spring คือ ViewResolver ซึ่งในกรณีนี้เราจะใช้ InternalResourceViewResolver ซึ่งมันจะทำหน้าที่เลือกชนิดของวิวให้ถูกต้อง
(ในกรณีนี้คือ content เพราะเราใช้ getContent() ใน handler)

Implementing the AtomView
สำหรับการสร้าง AtomView เราจะใช้ Rome โปรเจคโดยส่วนของ AtomView นั้นเราจำเป็นต้องสร้างวิวของเราเองเพื่อใช้ในการแสดงผล Atom โดยเราจะสร้าง SampleContentAtomView ที่มีรายละเอียดการทำงานดังนี้

public class SampleContentAtomView extends AbstractAtomFeedView {

    @Override
    protected void buildFeedMetadata(Map model, Feed feed, HttpServletRequest request) {
        System.out.println("SampleContentAtomView.buildFeedMetadata");
        feed.setId("tag:springsource.com");
        feed.setTitle("Sample Content");
        @SuppressWarnings("unchecked")
        List<SampleContent> contentList = (List) model.get("sampleContentList");
        for (SampleContent content : contentList) {
            Date date = content.getPublicationDate();
            if (feed.getUpdated() == null || date.compareTo(feed.getUpdated()) > 0) {
                feed.setUpdated(date);
            }
        }
    }

   @Override
    protected List buildFeedEntries(Map model,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        @SuppressWarnings("unchecked")
        List<SampleContent> contentList = (List) model.get("sampleContentList");
        List entries = new ArrayList(contentList.size());

        for (SampleContent content : contentList) {
            Entry entry = new Entry();
            String date = String.format("%1$tY-%1$tm-%1$td", content.getPublicationDate());
// see http://diveintomark.org/archives/2004/05/28/howto-atom-id#other
            entry.setId(String.format("tag:springsource.com,%s:%d", date, content.getId()));
            entry.setTitle(String.format("On %s, %s wrote", date, content.getAuthor()));
            entry.setUpdated(content.getPublicationDate());

            Content summary = new Content();
            summary.setValue(content.getText());
            entry.setSummary(summary);

            entries.add(entry);
        }

        return entries;

    }
}

ส่วนสุดท้ายคือการการกำหนด media type เพิ่มสำหรับ Atom เนื่องจาก Atom จะใช้ ‘application/atom+xml’ สำหรับ Html เราจะใช้ ‘text/html’ หรือ ‘text/xhtml’ ซึ่งโดยปกติเบราส์เซอร์จะกำหนดค่ากลุ่มของ media type ที่มันส่งออไปไปพร้อม
กับ HTTP header และไม่มีทางที่เราจะเปลี่ยนประเภทของ media type ได้เลยโดยการใช้ browser ดังนั้นการใช้ประเภทของไฟล์ (*.atom, *.json …) เป็นทางออกที่ดีในการบอกเซิร์ฟเวอร์ว่าประเภทของ representation ที่เราต้องการคืออะไร

Spring 3.0 มี ContentNegotiatingViewResolver นั้นสามารถทำงานได้ทั้ง extension และ accept header และหลังจากที่เรากำหนด media type ที่เราต้องการใช้มันจะทำหน้าที่ส่งงานให้กับ view resolver ตัวที่เหมาะสามให้กับเราโดยในตัวอย่าง
นี้มันจะทำการโยนไปให้ BeanNameViewResolver( resolve ตัว SampleContentAtomView ในกรณีที่ต้องการ ) หรืออาจจะเป็น InternalResourceViewResolver ซึ่งเราจะต้องเปลี่ยน Configuration ให้เป็นแบบด้านล่าง

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="mediaTypes">
            <map>
                <entry key="atom" value="application/atom+xml"/>
                <entry key="html" value="text/html"/>
            </map>
        </property>

        <property name="viewResolvers">
            <list>
                <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
                <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                    <property name="prefix" value="/WEB-INF/jsp/"/>
                    <property name="suffix" value=".jsp"/>
                </bean>
            </list>
        </property>
    </bean>

ตัว ContentNegotiatingViewResolver จะทำการหา extension ก่อนว่ามีหรือไม่หลังจากที่ได้รับ Accept Header มาแล้วดังนั้นเราจะต้องแมป extension ให้เหมาะกับ media types ที่เราต้องการได้เช่น .html เราต้องกำหนดให้ใช้ text/html ส่วน
.atom เราจะให้ใช้ application/atom+xml ดังนั้นเมื่อ .atom ถูกส่งเข้ามาตัว ContentNegotiatingViewResolver จะทำการหาวิวที่เหมาะสมกับ application/atom+xml ส่วนในกรณีที่เป็น .html มันจะทำการเปลี่ยนไปหาวิวที่ใช้แสดง text/html
จากนี้เราจะสามารถเรียก

http://localhost:8888/SpringMVC3RestFul-1.0-SNAPSHOT/rest/content.html

เพื่อแสดงและเพิ่ม content ได้

http://localhost:8888/SpringMVC3RestFul-1.0-SNAPSHOT/rest/content.atom

ใช้สำหรับแสดงผลเป็น Atom Feed ครับ

******* source code สามารถ check out ได้ที่ http://code.google.com/p/spring-atomfeed-view/

เกริ่นนำเรื่อง RESTful ฟังก์ชั่นใน Spring-MVC 3.0 xx

October 20th, 2009

A Bit of Background
Spring RESTful ถูกเพิ่มเข้าไปในกลไกการทำงานของ Spring-MVC ตรงๆอันเนื่องมาจากเหตุผลทางเทคนิคบางประการนั่นหมายความว่าจะมีบางเรื่องใน Spring RESTful ไปซ้ำเข้ากับ JAX-RS นั่นเป็นเรื่องที่ทางทีมพัฒนาได้พยายามหลีกเลี่ยงอย่างมากแล้วแต่ก็หลีกไม่พ้น แต่สิ่งที่ได้กลับมาคือเราสามารถหรับตัวเข้ากับโมเดลการทำงานแบบนี้ได้อย่างง่ายดายไม่ว่าจะเป็นคนที่เคยทำงานกับ SpringMVC มาแล้วหรือแม้กระทั่งกับนักพัฒนาหน้าใหม่ที่ต้องการลองใช้ SpringMVC เองก็ตาม นอกจากนี้ปัจจุบันเองมีเฟรมเวิร์คหลายตัวที่รองรับการทำงานของ JAX-RS ยกตัวอย่างเช่น Jersey, RESTasy และ Restlet (มันเยอะอยู่แล้วจะเพิ่มไปอีกตัวก็ไม่เกิดประโยชน์อะไร)
ต่อไปเราจะมาดูองค์ประกอบหลักๆที่ทำให้เราสามารถทำ RESTful บน SpringMVC ได้แบบรายตัว
URI Templates
URI เทมเพลทก็คือ URI ที่เป็นสตริงที่เกิดขึ้นจากการรวมเอาชื่อตัวแปรและค่าของมันเข้าไป ซึ่งใน Spring 3.0 M1 มีเริ่มให้มรการใช้งาน @PathVariable annotation ตามตัวอย่างดังนี้

@RequestMapping("/hotels/{hotelId}")
public String getHotel(@PathVariable String hotelId, Model model) {
    Hotel hotel = hotelService.getHotel(hotelId);
    model.addAttribute("hotels", hotel);
    return "hotel";
}

จากโค้ดด้านบนเราจะเห็นได้ว่าเมื่อมีการเรียก URI ที่มีค่า /hotels/1 ค่า 1 จะถูกบายด์เข้ากับ hotelId โอวง่ายมากต่อไปเรามาดูกันว่าถ้าต้องการมีตัวแปรมากกว่าหนึ่งตัวเราจะทำอย่างไร

@RequestMapping(value="/hotels/{hotel}/bookings/{booking}", method=RequestMethod.GET)
public String getBooking(@PathVariable("hotel") long hotelId, @PathVariable("booking") long bookingId, Model model) {
    Hotel hotel = hotelService.getHotel(hotelId);
    Booking booking = hotel.getBooking(bookingId);
    model.addAttribute("booking", booking);
    return "booking";
}

จากโค้ดด้านบนเราจะได้ URI ที่มีลักษณะดังนี้ /hotels/1/bookings/2
นอกจากนี้เรายังสามารถใช้วิธีการเขียน URI แบบของ Ant ได้ด้วยเช่น

@RequestMapping(value="/hotels/*/bookings/{booking}", method=RequestMethod.GET)
public String getBooking(@PathVariable("booking") long bookingId, Model model) {
    ...
}

หรือเราจะไปไกลกว่านั้นด้วยการทำ Data Type Binding ได้ด้วยก็ได้เช่นเราต้องการสร้าง URI แบบนี้ /hotels/1/dates/2009-02-02

@InitBinder
public void initBinder(WebDataBinder binder) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}

@RequestMapping("/hotels/{hotel}/dates/{date}")
public void date(@PathVariable("hotel") String hotel, @PathVariable Date date) {
    ...
}

Content Negotiation
ในเวอร์ชั่น 2.5 นั้น Spring ให้ @Controller ทำการเลือกวิวที่จะใช้แสดงผลเองตามประเภทวิวของมันเอง( view name, ViewResolver) แต่สำหรับ RESTfule แล้วคนที่เลือกการแสดงผลนั้นกลับกลายเป็นฝั่งไคลแอนท์เองโดยทำผ่าน HTTP เฮดเดอร์ดังนั้นทางฝั่งเซิร์ฟเวอร์เองจะทำการส่งคอนเทนท์ที่ถูกต้องผ่าน Content-Type เฮดเดอร์ซึ่งกระบวนการนี้เรียกว่า Content Navigation
แต่ประเด็นหลักของการทำการสร้างคอนเทนท์นั้นเราไม่สามารถเปลี่ยนประเภทคอนเทนท์ได้ที่เวบเบราส์เซอร์ (HTML) ยกตัวอย่างเช่นใน Firefox นั้นมันจะทำการกำหนดค่าคงที่ให้เป็น
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
ดังนั้นเกิดอะไรขึ้นถ้าเราต้องการอ้างถึง PDF โดยการอ้าง URI แบบนี้ http://example.com/hotels.pdf นั่นคือการแสดงรายชื่อโรงแรมทั้งหมดเป็น PDF เหมือนกับที่ทำใน http://example.com/hotels แต่ให้เปลี่ยนเฮดเดอร์เป็น application/pdf. (นรกตกใส่) ดังนั้นเราจำเป็นต้องมีผู้ช่วยพระเอกที่ชื่อ ContentNegotiatingViewResolver โดยตัวมันจะทำการแรป ViewResolver หลายๆตัวเข้าไว้ด้วยกันและมองดูความเป็นไปได้ของประเภทของคอนเทนท์ที่จะเกิดขึ้นและเลือกใช้ ViewResolver ที่ถุกต้อง

Views
มรการเพิ่มส่วนของการแสดงผลเข้ามาใหม่อีกหลายตัวเช่น
-AbstractAtomFeedView และ AbstractRssFeedView, เพื่อใช้ส่งคอนเทนท์ประเภท Atom และ RSS feed,
-MarshallingView, เพื่อใช้สร้างการแสดงผลที่เป็น XML ซึ่งตัววิวตัวนี้ถูกสร้างบนพื้นฐานของ Object/XML แมปปิ้งโมดูล, ซึ่งได้รับอิทธิพลมาจาก Spring Web Services โดยที่โมดูลนี้ใช้กระบวนการแปลงของเอพีไอชื่อดังเช่น JAXB, Castor, JiBX
-JacksonJsonView, เพื่อใหช้สร้างคอนเทนท์ตสมมาตรฐาน JSON ซึ่งวิวนี้เป็นส่วนหนึ่งของ Spring JavaScript project,

HTTP methos conversion
หัวใจหลักของการทำงานด้วย REST คืดมันจะทำงานบนกลไกของการเชื่อมต่อที่ไม่ซับซ้อนดังนั้นหมายความว่าทุกรีเควสจะต้องสามารถถูกเรียกผ่าน HTTP เมธอดทั้งสี่ได้คือ GET, PUT, POST และ DELETE ซึ่งถ้าเราตามไปดูตามมาตรฐานของ HTTP โปรโตคอลแล้วความหมายของการทำงานของทั้งสี่เมธอดนี้เรียบง่ายมากๆเช่น GET มีไว้ทำงานที่ปลอดภัยที่ไม่มีผลข้างเคียงอะไรกับระบบ, PUT กับ DELETE เป็นเมธอดที่ถูกเรียกวนไปวนมาหลายครั้งหลายหนได้แต่ผลที่ได้ทุกครั้งจะต้องเหมือนเดิม
แต่อย่างไรก็ตามเรามีเมธอดตามมาตรฐานทั้งหมดสี่แต่สำหรับ HTML เราสามารถใช้งานมันได้แค่สองเท่านั้นคือ GET กับ POST แต่เพียงแค่สองมันก็พอให้เราใช้งานได้แล้วเพราะเราสามารถเขียน JavaScript สำหรับการทำ PUT, DELETE หรือ POST ก็ได้โดยทำผ่านพารามิเตอร์พิเศษที่ชื่อ “real” และสำหรับ Spring แล้วการกำหนดค่าให้กับตัวแปร “real” จะถูกทำงานผ่าน HiddenHttpMethodFilter โดยที่ฟิวเตอร์ตัวนี้ถูกสร้างขึ้นมาสำหรับ Spring 3.0 M1 และตัวมันเองก็เป็นแค่ฟิวเตอร์ปกติดังนั้นมันจึงสามารถนำไปใช้กับเวบเฟรมเวิร์คอื่นๆได้อีกหลายตัวไม่จำหัดแค่ SpringMVC โดยการใช้งานนั้นเราก็เพียงแค่เพิ่มฟิวเตอร์เข้าไปใน web.xml, และทำการ POST ค่า hidden _method จากนั้นพารามิเตอร์จะถูกแปลงไปเป็น HTTP เมธอดรีเควสพารามิเตอร์ตามที่ต้องการ
และแน่นอนในเมื่อเราต้องส่งพารามิเตอร์พิเศษนี้ทำให้เราต้องใส่ tag นี่ลงไปในฟอร์มของเราด้วยซึ่ง SpringMVC เองก็เตรียมไว้ให้เรียบร้อยแล้วเช่นกัน

<form:form method="delete">
    <p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>

สิ่งที่ได้จากการส่งรีเควสนี้คือมันจะทำการส่ง HTTP POST ที่เป็นประเภท delete เพื่อไปเรียกให้เมธอด deletePet ใน @Controller ทำงาน

@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
    this.clinic.deletePet(petId);
    return "redirect:/owners/" + ownerId;
}

ETag support
ตัว ETag( entity tag ) ถูกสร้างขึ้นมาเพื่อใช้สำหรับตรวจสอบการเปลี่ยนแปลงของคอนเทนท์บนเซิร์ฟเวอร์โดยที่ Etag เฮดเดอร์ที่เซิร์ฟเวอร์ส่งกลับมานั้นสามารถถุกตรวจสอบได้ ผ่าน If-None-Match เฮดเดอร์ถ้าคอนเทนท์ไม่มีการเปลี่ยนแปลงเซิร์ฟเวอร์จะทำการส่งค่า 304:Not Modified กลับมา
เช่นกันสำหรับ Spring 3.0 M1 นั้นได้มีการสร้าง ShollowETagHeadeFilter เหมือนเดิมมันเป็น Servlet Filter ตามมาตรฐานดังนั้นใครจะเอามันไปใช้งานก็ได้ อย่างไรก็ตามหลักการทำงานของฟิวเตอร์ตัวนี้นั้นไม่มีอะไรซับซ้อนโดยมันจะทำการแคชคอนเทนท์เอาไว้และทำการสร้างแฮช(MD5) ไว้และทำการส่งค่านั้นกลับไปในเรสพอนท์ เมื่อใดก็ตามที่มีการร้องขอคอนเทนท์เดิมกลับมา มันใช้ค่าแฮชที่ได้เก็บไว้เป็น If-None-Match และระบบจะทำการเรนเดอร์คอนเทนท์อีกครั้งและทำการเปรียบเทียบแฮชที่ได้ว่ามีค่าเท่าเดิมหรือไม่ถ้าเท่ากันก็จะส่ง “304″ กลับไป และจากกระบวนการทำงานจะเห็นว่า Filter ตัวนี้จะไปช่วยในเรื่องของการลดการประมวลผลแต่สิ่งที่มันช่วยลดคือแบนด์วิธต่างหาก เพราะคอนเทนท์ที่ซ้ำจะไม่ถูกส่งกลับมาที่ฝั่งไคล์แอนท์
จริงๆแล้วกระบวนการทำงานเชิงลึกของ ETags จะเป็นเรื่องที่ซับซ้อนมาก ในกรณีที่คอนเทนท์ของเราสร้างจากข้อมูลที่ถูกเก็บในระบบฐานข้อมูล และคอนเทนท์จะถูกสร้างใหม่ก็ต่อเมื่อข้อมูลในฐานข้อมูลมีการเปลียนแปลงเท่านั้น ซึ่งกระบวนการนี้ไม่สามารถทำได้ด้วยการใช่ ShollowETagHeadrFilter ธรรมดา เราต้องรอความสามารถใหม่ๆใน Spirng เวอร์ชั่นถัดไปที่จะต้องทำงานร่วมกับ JPA และ AspectJ ในเรื่องนี้

ดึงภาพจาก Flickr ด้วย Spring RestTemplate

October 16th, 2009

ต้นฉบับ http://blog.springsource.com/2009/03/27/rest-in-spring-3-resttemplate/
source code สามารถ checkout ได้ที่ https://spring-rest-template.googlecode.com/svn/trunk/
RESTFul ครองโลก ดังนั้นเพื่อไม่ให้เป็นการตกเทรนด์สปริงสามจึงเตรียม RestTemplate เป็นคลาสหลักสำหรับการเรียกใช้งาน REST เซอร์วิสเหล่านั้น โดยเราสามารถมอง RESTTemplate ให้เหมือนกับ JdbcTemplate หรือ JmsTemplate หรือ tempate อื่นๆที่สปริงเตรียมไว้ให้ ดังนั้น Template นี้จึงเป็น thread-safe เช่นกันเมื่อมันถูกสร้างขึ้นมาแล้วเราสามารถใช้ Callback เมธอดเพื่อกำหนดการทำงานโดยละเอียดตามใจเราภายในของมันได้อีกเช่นกัน
RestTemplate เมธอดมีให้เรียกใช้งานได้ 6 เมธอดตาม HTTP เมธอด:

DELETE delete(String, String…)
GET getForObject(String, Class, String…)
HEAD headForHeaders(String, String…)
OPTIONS optionsForAllow(String, String…)
POST postForLocation(String, Object, String…)
PUT put(String, Object, String…)

ชื่อเมธอดจะขึ้นต้นด้วยมาตรฐาน HTTP เมธอดตามด้วยผลที่เราจะได้รับจากการเรียกใช้ ( getForObject = get (ชื่อ HTTP เมธอด)+ Object(ผลที่ได้รับกลับ) )
URI Templates
อยากเอกสารอ้างอิงเราจะเห็นได้ว่าทุกๆเมธอดจะรับพารามิเตอร์ตัวแรกเป็น URI เสมอซึ่งเราสามารถเตรียม URI เป็น Template ได้เพื่อเตรียมตัวแปรที่ต้องการใช้ไว้ก่อนหลังจากนั้นเราสามารถกำหนดค่าของตัวแปรที่ต้องการใช้ผ่านกระบวนการได้สองแบบคือ กำหนดค่าเป็นอาร์เรย์ของ String หรือ ใช้ Map ดังตัวอย่างด้านล่าง

String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");

ตัวอย่างนี้เราจะทำการเรียก GET และส่ง URI ที่มีรูปแบบดังนี้ http://example.com/hotels/42/bookings/21 วิธีการนี้เป็นการกำหนดตัวแปรในรูปแบบของอาร์เรย์ของ String ส่วนต่อไปเป็นการกำหนดตัวแปรด้วย Map

Map<String, String> vars = new HashMap<String, String>();
vars.put("hotel", "42");
vars.put("booking", "21");
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class, vars);

HttpMessageConverters
Objects ที่ถูกส่งผ่านกลับมาจากการเรียกใช้เซอร์วิสเช่น getForObject(), postForLocation(), และ put() จะถูกแปลงเป็น HTTP requests และผลที่ได้จาก HTTP response จะถูกแปลงต่อจาก MIME types ไปเป็น Java Types ได้อยู่แล้วแต่ในกาณีที่เราต้องการเขียนวิธีการแปลงเองก็สามารถทำได้ดังนี้
ตัวอย่างการใช้งาน Spring RESTTemplate และการเขียน HttpMessageConverter เอง
ต่อไปจะเป็นตัวอย่างการใช้งาน RestTemplate ด้วยการดึงภาพมาจาก Flickr ด้วยคำค้นที่เราต้องการแต่ตัวอย่างนี้เราจะแสดงภาพที่ได้จาก Flickr ลงไปที่ Swing UI เพื่อจำลองการทำงานของ HTTP client แต่กก่อนอื่นเราต้องมี API key สำหรับ Flickr ก่อน

การค้นหาภาพ
Flickr เตรียม เพื่อให้เราทำงานกับภาพไว้มากมายยกตัวอย่างเช่น flickr.photos.search เมธอดนั้นเราสามารถค้นหาภาพได้จากการใช้ GET Request http://www.flickr.com/services/rest?method=flickr.photos.search&api+key=xxx&tags=penguins, โดยเราจำเป็นต้องป้อน API key ลงไปและหลังจากส่ง request ไปเราจะได้รับผลที่เป็น XML กลับมา

<photos page="2" pages="89" perpage="10" total="881">
	<photo id="2636" owner="47058503995@N01"
		secret="a123456" server="2" title="test_04"
		ispublic="1" isfriend="0" isfamily="0" />
	<photo id="2635" owner="47058503995@N01"
		secret="b123456" server="2" title="test_03"
		ispublic="0" isfriend="1" isfamily="1" />
	<photo id="2633" owner="47058503995@N01"
		secret="c123456" server="2" title="test_01"
		ispublic="1" isfriend="0" isfamily="0" />
	<photo id="2610" owner="12037949754@N01"
		secret="d123456" server="2" title="00_tall"
		ispublic="1" isfriend="0" isfamily="0" />
</photos>

ซึ่งเราสามารถส่ง request ไปด้วย restTemplate ได้ดังนี้

final String photoSearchUrl =  "http://www.flickr.com/services/rest?method=flickr.photos.search&amp;amp;amp;amp;amp;amp;api+key={api-key}&amp;amp;amp;amp;amp;amp;tags={tag}&amp;amp;amp;amp;amp;amp;per_page=10";
Source photos = restTemplate.getForObject(photoSearchUrl, Source.class, apiKey, searchTerm);

โดยที่ apiKey กับ searchTerm เป็นพารามิเตอร์ที่ต้องรับมาจากภายนอก ส่วน Source.class ทำหน้าที่เป็น SourceHttpMessageConverter เพื่อแปลงผลที่ได้จาก HTTP XML ไปเป็น javax.xml.transform.Source

การดึงภาพ
หลังจากเราได้ XML ที่ถูกแปลงออกมาเป็น javax.xml.transform.Source แล้วเราจะต้องทำการดึงภาพจริงมาที่ฝั่ง client ด้วย XPatn expression โดยเราจะใช้ XPathTemplate ในโปรเจค Spring Web Services เราจะเริ่มจากการดึงอิลิเมนท์ //photo ทุกๆตัวออกมา ต่อไปเราจะอิมพลีเมนท์รายละเอียดเมธอด mapNode() ของ NodeMapper อินเทอร์เฟสเพื่อทำการดึงรายละเอียดต่างๆเช่น server, id และ secret และนำไปใส่ใน Map และเราจะใช้ RestTemplate อีกครั้งเพื่อดึงเอาภาพตัวจริงมาใส่ใน java.awt.image.BufferedImage

List<BufferedImage> imageList = xpathTemplate.evaluate("//photo", photos, new NodeMapper() {
    public Object mapNode(Node node, int i) throws DOMException {
        Element photo = (Element) node;

        Map<String, String> variables = new HashMap<String, String>(3);
        variables.put("server", photo.getAttribute("server"));
        variables.put("id", photo.getAttribute("id"));
        variables.put("secret", photo.getAttribute("secret"));

        String photoUrl = "http://static.flickr.com/{server}/{id}_{secret}_m.jpg";
        return restTemplate.getForObject(photoUrl, BufferedImage.class, variables);
    }
});

ดังนั้นผลที่ได้ใน imageList จะได้ภาพสี่ภาพและ URL ที่เจาจะไปดึงภาพมาคือ http://static.flickr.com/2/2636_ a123456_m.jpg, และ http://static.flickr.com/2/2635_ b123456_m.jpg, …

การแปลงภาพ
ส่วนต่อไปคือเราต้องเขียนรายละเอียดของ HttpMessageConverter ที่จะสามารถอ่านได้จาก HTTP response และสร้าง BufferedImage

public class BufferedImageHttpMessageConverter implements HttpMessageConverter<BufferedImage> {

    public List<MediaType> getSupportedMediaTypes() {
        return Collections.singletonList(new MediaType("image", "jpeg"));
    }

    public boolean supports(Class<? extends BufferedImage> clazz) {
        return BufferedImage.class.equals(clazz);
    }

    public BufferedImage read(Class<BufferedImage> clazz, HttpInputMessage inputMessage) throws IOException {
        return ImageIO.read(inputMessage.getBody());
    }

    public void write(BufferedImage image, HttpOutputMessage message) throws IOException {
        throw new UnsupportedOperationException("Not implemented");
    }

}

หลังจากนั้นเราต้องไปใส่รายละเอียดให้กับ applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="flickrClient" class="com.springsource.samples.resttemplate.FlickrClient">
        <constructor-arg ref="restTemplate"/>
        <constructor-arg ref="xpathTemplate"/>
    </bean>

    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
                <bean class="com.springsource.samples.resttemplate.BufferedImageHttpMessageConverter"/>
            </list>
        </property>
    </bean>

    <bean id="xpathTemplate" class="org.springframework.xml.xpath.Jaxp13XPathTemplate"/>

</beans>

ส่วนของการแสดงผล
เราจะนำผลที่ได้ไปแสดงใน Swing UI

JFrame frame = new JFrame(searchTerm + " photos");
frame.setLayout(new GridLayout(2, imageList.size() / 2));
for (BufferedImage image : imageList) {
    frame.add(new JLabel(new ImageIcon(image)));
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);