Memahami Cara Kerja Builder Di Groovy

Saya akan mengawali tulisan pertama di tahun 2013 ini dengan topik yang belum pernah saya bahas sebelumnya yaitu bahasa pemograman Groovy.  Bahasa pemograman Groovy adalah sebuah bahasa pemograman yang berjalan di atas platform Java.  Dengan demikian, Groovy dapat memakai semua class yang sudah ada di Java.   Hal ini akan sangat membantu, karena Java memiliki sangat banyak API yang beragam; sebuah bahasa yang baru lahir mustahil memiliki daftar class selengkap Java yang teruji selama bertahun-tahun (baik yang resmi maupun oleh komunitas open-source).  Sebenarnya tidak hanya Groovy yang dapat memanggil class Java, JRuby (implementasi bahasa pemograman Ruby di Java) dan Jython (implementasi bahasa pemograman Python di Java) juga dapat melakukannya.

Kelebihan Groovy dibanding bahasa pemograman Java adalah syntax-nya yang lebih sederhana sehingga coding bisa lebih ringkas.  Tapi bukan hanya itu saja, Groovy juga memiliki fitur yang tidak ada di Java seperti GString (seperti String di PHP), operator untuk regex seperti di Perl,  mendukung Invoke Dynamic Java 7 (memanggil method tanpa perlu mengetahui apa method tersebut), dan sebagainya.

Salah fitur Groovy adalah ia memiliki Builder yang menerapkan Builder pattern.  Sebagai contoh, saya bisa menghasilkan XML secara singkat dengan memakai class MarkupBuilder, seperti yang terlihat pada kode program Groovy berikut ini:

def writer = new StringWriter()
def builder = new groovy.xml.MarkupBuilder(writer)
builder.daftar {
    mahasiswa(nim: "99887766", nama: "Sandu Kosaso") {
        matakuliah(nama: "data structure", nilai: "D")
        matakuliah(nama: "character building", nilai: "E")
        matakuliah(nama: "business plan", nilai: "A")
    }
    mahasiswa(nim: "99887755", nama: "Ide O") {
        matakuliah(nama: "java programming", nilai: "A")
        matakuliah(nama: "business plan", nilai: "B")
        matakuliah(nama: "system architecture", nilai: "C")
    }
}
print writer

Hasilnya adalah sebuah XML yang terlihat seperti berikut ini:

<daftar>
  <mahasiswa nim='99887766' nama='Sandu Kosaso'>
    <matakuliah nama='data structure' nilai='D' />
    <matakuliah nama='character building' nilai='E' />
    <matakuliah nama='business plan' nilai='A' />
  </mahasiswa>
  <mahasiswa nim='99887755' nama='Ide O'>
    <matakuliah nama='java programming' nilai='A' />
    <matakuliah nama='business plan' nilai='B' />
    <matakuliah nama='system architecture' nilai='C' />
  </mahasiswa>
</daftar>

Kode program ini benar-benar sangat singkat bila dibandingkan dengan cara yang harus ditempuh melalui Java.

Tapi pertanyaannya adalah kenapa bisa demikian?  Tidak ada method daftar di MarkupBuilder, bukan? Juga tidak ada definisi method mahasiswa dan matakuliah, bukan?  Lalu kenapa bisa jadi XML??

Hal ini berkaitan dengan apa yang disebut dengan Meta Object Protocol (MOP) yang memungkinkan programmer untuk mengubah atau menambah method pada class secara dinamis.   Setiap class milik Groovy selalu mengimplementasikan interface groovy.lang.GroovyObject.   Terdapat juga sebuah class GroovyObjectSupport sebagai class utility yang mengimplementasikan interface GroovyObjectSupport.   Sebagai contoh, class MarkupBuilder diturunkan dari BuilderSupport yang diturunkan dari GroovyObjectSupport. UML Class Diagram berikut ini memperlihatkan struktur class yang ada:

Hierari Class MarkupBuilder

Hierari Class MarkupBuilder

Jika pada saat program dikerjakan, sebuah method yang dipanggil tidak ditemukan dalam definisi class tersebut, maka method getProperty(), setProperty(), atau invokeMethod() milik GroovyObject tersebut akan dikerjakan.

Dengan demikian, pada kode program saya, method daftar(), mahasiswa(), dan matakuliah() yang pada dasarnya tidak ada di MarkupBuilder, akan menyebabkan method invokeMethod() di class BuilderSupport dikerjakan,  yang dimana selanjutnya akan memanggil method createNode()untuk membentuk XML.

Dengan MOP, saya dapat mengubah perilaku sebuah object pada saat program dijalankan.  Proses membuat kode program di sebuah bahasa pemograman agar bahasa pemograman tersebut berperilaku sesuai dengan yang saya harapkan disebut sebagai metaprogramming.

Hal yang membedakan metaprogramming dan membuat bahasa pemograman baru adalah pada metaprogramming, saya memakai kemampuan MOP dari bahasa pemograman tersebut untuk mengubah perilaku bahasa pemograman itu sendiri dimana perubahan perilaku ini akan terjadi setelah program dijalankan.

Hasil akhir metaprogramming bisa saja membentuk sebuah bahasa pemograman mini yang spesifik sesuai kebutuhan dan nantinya dipakai oleh metaprogrammer.  Bahasa seperti ini disebut sebagai Domain-specific Language(DSL).

Memakai MessageConverter di Spring 3.1 Untuk Web Services REST

Salah satu fitur menarik yang diperkenalkan oleh Spring Framework 3.1 adalah message converter yang dalam bentuk tag <mvc:message-converters>.  Dengan message converter, saya bisa membuat representasi  object di aplikasi saya dengan mudah dalam bentuk JSON dan XML secara otomatis.   Untuk mendukung konversi objek ke/dari JSON, saya memakai Jackson JSON library (http://jackson.codehaus.org).  Sementara untuk konversi objek ke/dari XML, saya memakai Castor (http://castor.codehaus.org).

Saya mulai dengan membuat sebuah proyek baru di STS, dengan memakai template Spring MVC Project.   Sebelum mulai membuat kode program, saya menambahkan dependencies Maven ke proyek saya seperti yang terlihat pada gambar berikut ini:

Dependencies Maven

Dependencies Maven

Kemudian saya membuat sebuah class dengan nama Mahasiswa di package co.id.jocki.domain.  Isi dari class Mahasiswa adalah:

package co.id.jocki.domain;

import java.io.Serializable;

public class Mahasiswa implements Serializable {

	private static final long serialVersionUID = 225855015823197676L;

	private String nim;
	private String nama;
	private int usia;

	public String getNim() {
		return nim;
	}
	public void setNim(String nim) {
		this.nim = nim;
	}
	public String getNama() {
		return nama;
	}
	public void setNama(String nama) {
		this.nama = nama;
	}
	public int getUsia() {
		return usia;
	}
	public void setUsia(int usia) {
		this.usia = usia;
	}	

}

Lalu, pada package co.id.jocki, saya membuat sebuah class bernama LatihanController.  Isi dari class tersebut adalah:

package co.id.jocki;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import co.id.jocki.domain.Mahasiswa;

@Controller
@RequestMapping(value="/mahasiswa")
public class LatihanController {

	private Mahasiswa mahasiswa;

	@RequestMapping(value="/listdata", method=RequestMethod.GET)
	@ResponseBody
	public Mahasiswa listData() {
		if (mahasiswa==null) {
			mahasiswa = new Mahasiswa();
			mahasiswa.setNama("Makdalena Hendry");
			mahasiswa.setNim("99999999");
			mahasiswa.setUsia(21);
		}
		return mahasiswa;
	}

}

Pada kasus nyata, tentu saja isi controller tidak sesederhana ini (misalnya masih ada get, delete, update, dsb).  Object yang ada juga tidak dibuat disini, melainkan seharusnya diambil dari medium penyimpanan (misalnya database).

Yang menarik disini adalah saya  tidak melakukan proses transformasi ke JSON ataupun XML secara manual.  Saya juga tidak memanggil sebuah fungsi ajaib.  Saya hanya mengembalikan sebuah objek mahasiswa seperti biasanya layaknya kode program standard.

Lalu bagaimana konversi bisa dilakukan?  Karena saya memberitahukannya secara deklaratif (tanpa kode program) dengan mengedit file servlet-context.xml.  File ini dapat ditemukan di lokasi src/main/webapp/WEB-INF/spring/appServlet.  Saya mengubah file tersebut sehingga isinya menjadi:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

  <mvc:annotation-driven>
    <mvc:message-converters>
      <beans:bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
      <beans:bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
        <beans:property name="marshaller" ref="castorMarshaller"/>
        <beans:property name="unmarshaller" ref="castorMarshaller"/>
      </beans:bean>
    </mvc:message-converters>
  </mvc:annotation-driven>

  <beans:bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller">
    <beans:property name="mappingLocation" value="classpath:oxm-mapping.xml"/>
  </beans:bean>

  <context:component-scan base-package="co.id.jocki" />

</beans:beans>

Rahasianya terletak di <mvc:message-converters> dimana saya mendeklarasikan bean dari class MappingJacksonHttpMessageConverter dan class MarshallingHttpMessageConverter.  Khusus untuk yang XML, saya perlu membuat file oxm-mapping.xml (nama yang sama seperti di property mappingLocation di bean castorMarshaller.

Saya akan membuat file oxm-mapping.xml ini di folder src/main/resources.  Isi file tersebut menentukan bagaimana memetakan sebuah class Java ke XML (dan sebaliknya) seperti yang terlihat di berikut ini:

<?xml version="1.0" encoding="UTF-8"?>
<mapping xmlns="http://castor.exolab.org/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://castor.exolab.org/ http://castor.org/mapping.xsd">

  <class name="co.id.jocki.domain.Mahasiswa">
    <map-to xml="mahasiswa" ns-uri="http://jockihendry.com/mahasiswa/"
       ns-prefix="mahasiswa"/>

    <field name="nim" type="string" >
      <bind-xml auto-naming="deriveByField" node="element"/>
    </field>

    <field name="nama" type="string">
      <bind-xml auto-naming="deriveByField" node="element" />
    </field>

    <field name="usia" type="integer">
      <bind-xml auto-naming="deriveByField" node="element" />
    </field>
  </class>

</mapping>

Setelah ini, saya  menjalankan tc Server untuk menguji web service REST tersebut.

Untuk melakukan pengujian, saya akan menggunakan cURL (http://curl.haxx.se), sebuah tools command-line yang bisa dipakai untuk browsing berbasis teks.  Karena saya pernah meng-install Zend Studio, tools tersebut secara otomatis sudah ada dan siap dipakai.

Saya mulai dengan memanggil halaman http://localhost:8080/latihan-rest/mahasiswa/listdata.  Pada kode program, terlihat controller hanya mengembalikan sebuah objek mahasiswa.  Tetapi saya menginginkan kembalian berupa JSON.  Dan, server saya ternyata sudah mendukungnya seperti yang terlihat di tampilan berikut:

Komunikasi REST dengan JSON

Komunikasi REST dengan JSON

Lalu, kali ini saya menginginkan kembalian berupa XML.  Dan sekali lagi, server saya secara otomatis sudah mendukungnya seperti yang terlihat di gambar berikut:

Komunikasi REST dengan XML

Komunikasi REST dengan XML

Day 16: XSLT dengan Oracle XDK

Learning In HomeOriginal Date: 28 Januari 2009

Hari ini aku akan mencoba komponen XSLT milik Oracle XDK. Untuk melakukan transformasi melalui command line, aku dapat menggunakan tool oraxsl. Sebelum mulai menjalankan tool tersebut, aku mengatur CLASSPATH agar mengandung file xmlparserv2.jar terlebih dahulu.

Oracle menyediakan perluasan pada XSLT Processing-nya, dimana aku bisa menyertakan fungsi dan ekspresi Java di dalam dokumen XSL, seperti pada contoh berikut:


<xsl:stylesheet 
  xmlns:xsl=
"http://www.w3.org/1999/XSL/Transform" 
  version="1.0"
xmlns:math="http://www.oracle.com/XSL/
Transform/java/java.lang.Math">
<xsl:template match="/">
  <xsl:value-of 
     select="math:pow(10,5)" />
</xsl:template>

</xsl:stylesheet>

Contoh di atas akan selalu menghasilkan nilai 10^5 jika di-proses dengan oraxsl. Contoh lainnya, misalnya, untuk menampilkan tanggal hari ini:


<xsl:stylesheet 
  xmlns:xsl=
"http://www.w3.org/1999/XSL/Transform" 
  version="1.0"
  xmlns:date=
"http://www.oracle.com/XSL/Transform/
java/java.util.Date">
<xsl:variable name="tanggal" 
  select="date:new()" />
<xsl:template match="nama">

<xsl:value-of select=
 "date:toLocaleString($tanggal)" />
</xsl:template>
</xsl:stylesheet>

Pada contoh ini, aku mendeklarasikan sebuah object baru dari java.util.Date dengan nama tanggal. Lalu, aku memanggil method toLocaleString() dari object tanggal tersebut. Saat memanggil method apa saja dari sebuah class baru, aku menyertakan parameter pertama berupa nama variabel dari class tersebut.

Aku juga dapat melakukan transformasi di dalam program Java, seperti yang terlihat pada contoh berikut:


try {
			
  InputStream xslInput = new 
FileInputStream("c:\\test.xsl");
  XSLProcessor xslProcessor = new 
XSLProcessor();
  XSLStylesheet xsl = xslProcessor.
newXSLStylesheet(xslInput);
			
  DOMParser parser = new DOMParser();
  InputStream xmlInput = new 
FileInputStream("c:\\test.xml");
  parser.parse(xmlInput);
  XMLDocument doc = 
parser.getDocument();
			
  XMLDocumentFragment docResult = 
xslProcessor.processXSL(xsl, doc);
  docResult.print(System.out);
		
	
} catch (Exception e) {
  e.printStackTrace();
}

Day 15: Oracle XDK

Learning In HomeOriginal Date: 27 Januari 2009

Masih dalam suasana yang meriah ini, aku akan menyemarakkannya dengan mempelajari Oracle XML Developer Kit (XDK). XDK adalah kumpulan tools untuk mempermudah membangun aplikasi berbasis XML. Pada instalasi database Oracle, ia dapat ditemukan di folder %ORACLE_HOME%\XDK. XDK memiliki komponen yang dapat dipakai oleh aplikasi Java, C, dan C++, yang terdiri atas XML Parser, XSLT Processor, XSLT VM, XML Schema Processor, XML Class Generator, XML JAXB Class Generator, XML Java Beans, XML SQL Utility, XSQL Servlet, XML Pipeline processor. Pertama kali melihat nama-nama ini membuatku sedikit kewalahan, tapi karena nama-nama komponen tersebut terlihat cukup keren, aku tidak patah semangat.

Aku akan mulai dengan mencoba memakai XML Parser di Java. XDK menyediakan dua jenis XML Parser, yaitu DOM Parser dan SAX Parser. DOM Parser akan menghasilkan representasi dokumen XML dalam bentuk tree, sementara SAX Parser men-parse berurutan dan diprogram berdasarkan event. Secara garis besar, DOM Parser membutuhkan memori lebih besar dan sesuai digunakan jika struktur tree dokumen XML akan dimodifikasi. Sementara SAX Parser lebih hemat memori dan lebih tepat digunakan bila tidak ada perubahan pada dokumen XML.

Untuk menggunakan XML Parser dari XDK, aku harus memasukkan file %ORACLE_HOME%\lib\xmlparserv2.jar di CLASSPATH. Aku akan mencoba menvalidasi XML dengan DOM Parser berdasarkan DTD, dan men-parse DTD terlebih dahulu sebelum men-parse dokumen XML dengan contoh berikut:


DOMParser parser = new DOMParser();
parser.setErrorStream(System.out);
parser.showWarnings(true);
parser.setValidationMode(
  DOMParser.DTD_VALIDATION);

FileInputStream fileDTD = new 
  FileInputStream("C:\\test.dtd");
BufferedInputStream inputDTD = new 
  BufferedInputStream(fileDTD);
parser.parseDTD(inputDTD, 
  "root-element");
parser.setDoctype(parser.getDoctype());

Setelah itu, aku akan men-parse dokumen XML dan menampilkan seluruh element-nya:


FileInputStream fis = new 
  FileInputStream("C:\\test.xml");
BufferedInputStream input = new 
  BufferedInputStream(fis);			
parser.parse(input);
			
XMLDocument doc = 
  parser.getDocument();
NodeList nl = doc.
  getElementsByTagName("*");
for (int i=0; i<nl.getLength(); 
     i++) {
  Node n = nl.item(i);
  System.out.println(n.
    getNodeName());
}

Untuk menampilkan atribut untuk seluruh element bernama “property”, aku menggunakan kode yang seperti berikut ini:


XMLDocument doc = parser.
  getDocument();
NodeList nl = doc.
  getElementsByTagName(
   "property");
for (int i=0; i<nl.getLength(); 
  i++) {
 Node n = nl.item(i);
 NamedNodeMap nnm = 
   n.getAttributes();
 StringBuilder sb = new 
   StringBuilder("PROPERTY ");
 if (nnm != null) {
   for (int j=0; 
        j<nnm.getLength();
        j++) {
    Node n2 = nnm.item(j);
    sb.append(n2.getNodeName() + 
      "=" + 
      n2.getNodeValue() + ";");
   }
 }
 System.out.println(
   sb.toString());
}

Berikutnya aku akan latihan men-parse dokumen XML yang sama, tapi kali ini aku akan menggunakan SAX Parser. Aku membuat kode program seperti berikut ini:


import java.io.*;
import oracle.xml.parser.v2.SAXParser;
import org.xml.sax.*;

public class TestSAXParser implements 
  ContentHandler {

 public TestSAXParser()  {
   try {
		
    SAXParser parser = new SAXParser();
    parser.setContentHandler(this);
    parser.setValidationMode(
      SAXParser.NONVALIDATING);
    parser.setAttribute(
      SAXParser.STANDALONE, 
      Boolean.TRUE);
						
    FileInputStream fis = new 
      FileInputStream("C:\\test.xml");
    BufferedInputStream input = new 
      BufferedInputStream(fis);			
    parser.parse(input);
			
   } catch (Exception ex) {
       ex.printStackTrace();
   }
 }
	
 public static void main(String[] args) {
   new TestSAXParser();
 }


 public void characters(char[] ch, 
   int start, int length)
   throws SAXException {}


 public void endDocument() throws 
   SAXException {
   System.out.println("Selesai parsing!");
 }


 public void endElement(String uri, 
   String localName, String name)
   throws SAXException {}


 public void endPrefixMapping(
   String prefix) throws SAXException {}

 
 public void ignorableWhitespace(char[] ch, 
   int start, int length)
   throws SAXException {}


 public void processingInstruction(
   String target, String data)
   throws SAXException {}


 public void setDocumentLocator(
   Locator locator) {}


 public void skippedEntity(String name) 
   throws SAXException {}


 public void startDocument() throws 
   SAXException {
  System.out.println("Mulai Parsing...");
 }


 public void startElement(String uri, 
   String localName, String name,
   Attributes atts) throws SAXException {
  StringBuilder sb = new 
     StringBuilder(name + " = ");
  for (int i=0; i<atts.getLength(); i++) {
    sb.append(atts.getLocalName(i) + 
    "=" + atts.getValue(i) + "; ");
  }
  System.out.println(sb.toString());
	
 }


 public void startPrefixMapping(
   String prefix, String uri)
   throws SAXException {}

}


Sebagai latihan terakhir dalam hari ini, aku akan membuat dan memodifikasi struktur XML (melalui DOM Parser) di dalam program, kemudian menuliskannya ke dalam sebuah file:

		
String strXML = 
  "<karyawan><nama>snake</nama>" +
  "<nama>solid</nama></karyawan>"; 
						
DOMParser parser = new DOMParser();
	
try {
  parser.parse(
    new StringReader(strXML));
			
  XMLDocument doc = 
    parser.getDocument();
			
  Element e = doc.
    createElement("nama");
  e.setTextContent(
    "karyawan baru");
  doc.getDocumentElement().
    appendChild(e);
			
  FileOutputStream fos = new 
    FileOutputStream(
      "c:\\test.xml");
  doc.print(fos);
			
} catch (Exception e) {
  e.printStackTrace();
}