Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Monday, March 08, 2010

OSS/J

OSS/J awalnya merupakan grup perusahaan-perusahaan yang berusaha membuat implementasi dari OSS/BSS dengan menggunakan Java sebagai teknologinya dan arsiteturnya mengacu pada NGOSS (Next Generation Operations Support System).

OSS/J dibentuk tahun 2003 dengan menggunakan Java Community Process (JCP) untuk membuat standar API yang dispesifikasikan pada dokumen JSR (Java Specification Request). Tahun 2006, OSS/J mulai bergabung dengan organisasi TMForum dan tahun 2008 mulai masuk dalam satu payung dalam TMForum Interface Program.

OSS/J API ini dibuat dengan beberapa teknologi untuk integrasi yang disebut integration profiles, yaitu Java RMI/IIOP, XML/JMS, dan Web Services.

Integration Profiles
[Gambar diambil dari dokumen OSSJ Design Guidelines]


Core API dari OOS/J ini didesain sesuai Core Business Entities (CBE) yang diadopsi dari Shared Information Data (SID) model yang dibuat oleh TMForum.

Awalnya API yang dibuat adalah sebagai berikut:

  1. Common API v1.0
  2. Inventory v1.0
  3. Trouble Ticket v1.0
Kemudian berkembang menjadi beberapa API sebagai berikut:
  1. Common API (JSR 144)
  2. Service Activation v1.1(JSR 89)
  3. Quality of Service v1.0 (JSR 90)
  4. Trouble Ticket (JSR 91)
  5. IP Billing (JSR 130)
  6. Inventory API (JSR 142)
Hingga saat ini beberapa perubahan terlah terjadi yaitu
- Service Activation API diganti menjadi Order Management API.
- Billing Mediation atau IP Billing API dan Quality of Service API sudah tidak dikelola lagi.
- Bagian dari QoS API yaitu fault management dibuat terpisah menjadi Fault Management API.

Saat ini OSS/J API dikembangkan dengan prinsip SOA (Service Oriented Architecture) dengan spesifikasi sebagai berikut:

  1. Common API v1.5 (JSR 144)
  2. Order Management v1.0 (JSR 246)
  3. Trouble Ticket v1.2 (JSR 91)
  4. Inventory v1.2 (JSR 142)
  5. Fault Management v1.0 (JSR 263)
  6. Discovery v1.0 (JSR 254)
Dokumen spesifikasi tersebut bisa di-download disini (website TMForum) atau disini (website JSR).



Monday, September 15, 2008

Memonitor JVM menggunakan SNMP

Jika anda membuat aplikasi Java dan ingin memonitornya menggunakan SNMP, ada cara yang sederhana yaitu memonitor JVM. Tentu saja object yang dimonitor adalah yang berkaitan dengan JVM misalnya memory, thread, class loading, uptime, dan lain-lain. Lebih detail mengenai MIB object, anda bisa download standard MIB untuk JVM yang distandarisasi pada JSR 163 JavaTM Platform Profiling Architecture.

Untuk mengaktifkan fitur agent SNMP pada JVM dapat dibaca di petunjuk praktis disini. Jika dalam satu komputer anda menjalankan banyak aplikasi Java maka monitoring juga masih dapat dilakukan dengan menspesifikasikan port SNMP yang berbeda untuk masing-masing JVM.

Fitur management/monitoring dengan SNMP ini ada pada JVM versi 1.5 keatas dan perlu diperhatikan bahwa SNMP yang digunakan adalah versi 2.

Posting di blog lain tentang monitoring JVM dengan SNMP ada disini dan disini

Monday, January 29, 2007

Too many open files

Pernah menjalankan program java dan mendapatkan error "Too many open files" seperti dibawah ini?


java.io.IOException: Too many open files
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:145)
...

atau

java.net.SocketException: Too many open files
at com.sun.xml.rpc.client.http.HttpClientTransport.invoke(HttpClientTransport.java:140)
at com.sun.xml.rpc.client.StreamingSender._send(StreamingSender.java:96)


Mudahnya, hal tersebut terjadi karena program java ataupun sistem operasi mencapai batas jumlah soket atau file yang dapat dibukan pada saat bersamaan (concurrent).

Yang perlu dilakukan ketika mendapatkan error tersebut adalah, pastikan program yang anda buat selalu menutup file atau socket setelah tidak digunakan. Yang kedua, tambah maximum jumlah socket atau file yang bisa dibuka bersamaan pada sistem operasi yang anda gunakan.

Untuk Linux anda bisa check jumlah socket atau file yang dibuka bersamaan dengan program atau command lsof, misalnya untuk mengecek jumlah socket yang sedang dibuka berikan perintah 'lsof -i. Maximum jumlah socket atau file yang bisa dibuka secara bersamaan dapat dilihat dengan perintah 'ulimit -a'

Untuk mengubah setting maximum jumlah file tersebut, anda bisa mengedit file /etc/security/limits.conf atau dengan perintah berikut:

ulimit -n 2048

echo 2048 > /proc/sys/fs/file-max
echo 16384 > /proc/sys/fs/inode-max

Lebih jelas mengenai masalah ini, bisa dibaca di artikel "Resolve to many open files."

Thursday, January 25, 2007

MM7 schema binding dengan JAXB

MM7 schema binding dengan JAXB

Ini adalah pengalaman saya menggunakan JAXB untuk men-generate class source code untuk MM7.
MM7 merupakan spesifikasi untuk mengirimkan MMS ke MMSC menggunakan XML SOAP. MM7 biasanya digunakan untuk menghubungkan content provider dengan operator telco.
Contoh ini mungkin berguna bagi yang mulai mencoba menggunakan JAXB dan menemui kesulitas dengan error yang sama.

Untuk membuat source code dari schema file (xsd) dengan meggunakan JAXB, gunakan perintah xjc.bat atau xjc.sh
Pertama kali yang saya lakukan untuk melakukan binding XML schema ke java code adalah dengan perintah seperti berikut:


C:\USERS\ejlp>%JWSDP_HOME%\jaxb\bin\xjc.bat -nv -extension REL-5-MM7-1-2.xsd

REL-5-MM7-1-2.xsd adalah file XML schema MM7 dan %JWSDP_HOME% adalah environtment varibel dari Java Web Service Developer Pack terinstal.
Yang saya dapat dari perintah diatas adalah pesan error seperti dibawah ini:


Warning: JAVA_HOME environment variable is not set.
If build fails because sun.* classes could not be found
you will need to set the JAVA_HOME environment variable
to the installation directory of java.

parsing a schema...
[WARNING] warning: <anyAttribute> ignored
line 48 of
[WARNING] warning: <anyAttribute> ignored
line 56 of
[WARNING] warning: <anyAttribute> ignored
line 64 of
[WARNING] warning: "final" attribute of <complexType> is not supported
line 99 of
[WARNING] warning: <anyAttribute> ignored
line 117 of
[WARNING] warning: <anyAttribute> ignored
line 324 of REL-5-MM7-1-2.xsd
[ERROR] A class/interface with the same name "org.xmlsoap.schemas.soap.envelope.Envelope" is already in use.
Use a class customization to resolve this conflict.
line 42 of
[ERROR] (Relevant to above error) another "Envelope" is generated from here.
line 41 of
[ERROR] A class/interface with the same name "org.xmlsoap.schemas.soap.envelope.Body" is already in use.
Use a class customization to resolve this conflict.
line 60 of
[ERROR] (Relevant to above error) another "Body" is generated from here.
line 59 of
[ERROR] A class/interface with the same name "org.xmlsoap.schemas.soap.envelope.Fault" is already in use.
Use a class customization to resolve this conflict.
line 99 of
[ERROR] (Relevant to above error) another "Fault" is generated from here.
line 98 of
[ERROR] A class/interface with the same name "org.xmlsoap.schemas.soap.envelope.Header" is already in use.
Use a class customization to resolve this conflict.
line 52 of
[ERROR] (Relevant to above error) another "Header" is generated from here.
line 51 of
Failed to parse a schema.


Saya abaikan pesan warning, saya cuma concern pada pesan error yang saya kira membuat file source java tidak terbuat. Error tersebut terjadi karena ada konflik penamaan. JAXB akan membuat class dengan nama yang sama, dalam hal ini adalah Envelope, Body, Fault dan Header.

Setelah mencari informasi penyebab error tersebut maka saya mendapatkan solusi yaitu dengan membuat file binding.xjb yang merupakan konfigurasi binding yang harus dilakukan JAXB. Dalam file yang dibuat tersebut, saya buat agar setiap class yang akan di-generate dengan package org.xmlsoap.schemas.soap.envelope dari elemen bernama Fault akan diberinama SOAPFault bukan Fault, dan yang dari elemen Header akan diberinama SOAPHeader bukan Header, begitu juga dengan elemen Body dan Envelope.

Dibawah ini adalah isi dari file binding.xjb

   
<jxb:bindings version="1.0"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:bindings schemaLocation="http://schemas.xmlsoap.org/soap/envelope/" node="/xs:schema">

<jxb:schemaBindings>
<jxb:package name="org.xmlsoap.schemas.soap.envelope"/>
<jxb:nameXmlTransform>
<jxb:elementName suffix="Element"/>
</jxb:nameXmlTransform>
</jxb:schemaBindings>

<jxb:bindings node="//xs:element[@name='Fault']">
<jxb:class name="SOAPFault"/>
</jxb:bindings>

<jxb:bindings node="//xs:element[@name='Header']">
<jxb:class name="SOAPHeader"/>
</jxb:bindings>

<jxb:bindings node="//xs:element[@name='Body']">
<jxb:class name="SOAPBody"/>
</jxb:bindings>

<jxb:bindings node="//xs:element[@name='Envelope']">
<jxb:class name="SOAPEnvelope"/>
</jxb:bindings>

</jxb:bindings>

</jxb:bindings>

Setelah itu saya jalankan lagi perintah ditas tadi dengan menggunakan file konfigurasi binging bernama binding.xjb.
Dengan konfigurasi diatas, tidak terjadi lagi konfilik penamaan class.

C:\USERS\ejlp>%JWSDP_HOME%\jaxb\bin\xjc.bat -nv -extension REL-5-MM7-1-2.xsd -b binding.xjb

Thursday, November 30, 2006

Daily log menggunakan Log4J

Jika anda menggunakan Log4J andan menginginkan log file selalu berganti setiap hari, maka ada beberapa solusi yang bisa digunakan.

Pertama, menggunakan class org.apache.log4j.DailyRollingFileAppender sebagai appender. Class tersebut telah ada dalam library Log4J, jadi kita bisa langsung menggunakannya.

Atau menggunakan solusi dari http://minaret.biz/tips/datedFileAppender.html

Kedua solusi tersebut berbeda, jadi anda harus memilih mana yang paling sesuai dengan kebutuhan.

Wednesday, November 01, 2006

Over-the-air Provisioning Aplikasi MIDP

Provisioning dapat diartikan sebagai membuat sesuatu tesedia. Over-the-air (OTA) provisioning memberikan pengertian proses tersedianya aplikasi MIDP (MIDlet) pada device hingga siap digunakan yang ditransfer lewat sebuah jaringan nirkabel (wireless network). Proses OTA provisioning untuk MIDlet mencakup proses mendapatkan aplikasi (discovery), download, install/update maupun penghapusan (removal).

Pada sisi pengguna, OTA provisioning mempermudah pengguna untuk melakukan verifikasi dan installasi MIDlet. Pada sisi penyedia layanan OTA provisioning memberikan kemampuan untuk memberikan informasi content, melakukan proses pembiayaan (charging) dan lain-lain.

OTA provisioning untuk aplikasi MIDP awalnya merupakan rekomendasi praktis bukan suatu spesifikasi. Rekomendasi tersebut dibuat setelah spesifikasi MIDP 1.0 namun kemudian rekomendasi ini masuk dalam spesifikasi MIDP 2.0 dengan beberapa pembaruan atau tambahan.

Arsitektur sebuah OTA provisioning aplikasi MIDlet terdiri dari:

- Provisioning server

Provisioning server adalah HTTP/Web server yang menyimpan file dan pemroses status hasil provisioning. File yang disimpan adalah file metadata atau java application descriptor (JAD) dan aplikasi MIDP (content) yang berupa file JAR. Content dapat juga disimpan di tempat lain misalnya pada suatu repository server atau content management system (CMS). Provisioning server berinteraksi dengan network elemen misalnya WAP gateway yang menghubungkannya dengan device pengguna. Server ini juga dapat berinteraksi dengan sistem lain misalnya charging/billing server, Authentication Authorization server dan lain-lain.

- Client atau AMS

Application Management Software (AMS) biasa digunakan sebagai istilah untuk software pada sebuah device yang bertanggung jawab untuk mengatur suatu aplikasi misalnya mendownload, menyimpan (install), menjalankan (execution) atau bahkan membuang aplikasi. AMS biasa juga disebut Java Application Manager (JAM) untuk menunjukan software yang bertanggung jawab mengatur aplikasi MIDP pada device.


Proses OTA provisioning dimulai dengan content discovery yaitu proses mendapatkan content dari web server. Pengguna biasanya mendapatkan content dari suatu website atau halaman WAP yang menggandung link ke file descriptor (JAD). Contoh link pada halaman WML:

<a href="http://www.javanensis.net/sample.jad">MIDlet sample</a>


Jika pengguna mengklik link tersebut, AMS (browser) akan mendownload file JAD menggunakan HTTP GET request ke web server.

Web server akan meresponse request dengan memberikan file JAD. File tersebut harus diberikan dengan MIME type "text/vnd.sun.j2me.app-descriptor" pada HTTP response header.

Dibawah ini contoh suatu file JAD yang didapat dari web server:

Manifest-Version: 1.0
Created-By: 1.3.1 (Sun Microsystems Inc.)
MIDlet-Version: 1.0.3
MIDlet-Name: Sample MIDlet
MIDlet-Vendor: Javanensis
MIDlet-1: System Viewer, /icons/system.gif, net.javanensis.sample,SystemViewer
MIDlet-2: Bluethoot Test, , net.javanensis.sample.BluethootTest
MIDlet-Description: Contoh aplikasi MIDP
MIDlet-Jar-Size: 86445
MIDlet-Jar-URL: http://www.javanensis.net/contents/sample.jar
MIDlet-Install-Notify: http://www.javanensis.net/MidpOtaProvisioing.do?file=sample.jar
MIDlet-Delete-Notify: http://www.javanensis.net/MidpOtaProvisioing.do?file=sample.jar
MIDlet-Delete-Confirm: Anda yakin akan menghapus aplikasi 'Sample MIDlet'?


Setelah mendapatkan file descriptor, AMS biasanya melakukan verifikasi dan konfirmasi. Dari informasi yang ada pada file descriptot AMS bisa melakukan verifikasi apakah aplikasi yang akan diinstall sesuai dengan device, atau apakah ukuran file mencukupi untuk diinstall. Sebelum proses dilanjutkan, AMS dapat pula memberikan informasi yang tertera pada file descriptor kepada user dan meberikan konfirmasi kepada user untuk melanjutkan proses download dan install atau menghapus aplikasi jika aplikasi telah ada.

Jika user memilih untuk melanjutkan proses download maka AMS akan mendownload MIDlet (file JAR) dari alamat URL yang tertera pada file descriptor bagian "MIDlet-Jar-URL". Proses download file JAR adalah proses HTTP download. AMS akan melakukan HTTP request ke web server, dan web server akan mengirimkan response yang berisi file dengan MIME type pada HTTP header adalah "application/java-archive"

Setelah proses pengambilan file selesai, AMS akan melakukan installasi di tempat (directory) yang telah ditentukan.

Bila proses installasi selesai, AMS akan memberitahukan ke provisioning server apakah proses tersebut berhasil atau tidak. Pemberitahuan hasil proses provisioning dilakukan AMS dengan mengirimkan HTTP POST request yang bersisi kode status dan pesan ke URL yang tertera pada "MIDlet-Install-Notify" dalam file descriptor.
Contoh pengiriman status:

POST /MidpOtaProvisioing.do?file=sample.jar HTTP/1.1
Host: www.javanensis.net
Content-Length: 13

900 Success


Bila user memilih hapus aplikasi karena aplikasi telah ada. Status proses penghapusan dapat juga dikirimkan oleh AMS ke URL yang tertera pada "MIDlet-Delete-Notify"

Code Description (Status Message)
------ ---------------------------------------------------------
900 Success
901 Insufficient Memory
902 User Cancelled
903 Loss of Service
904 JAR size mismatch
905 Attribute Mismatch
906 Invalid Descriptor
907 Invalid JAR (MIDP 2.0)
908 Incompatible Configuration or Profile (MIDP 2.0)
909 Application authentication failure (MIDP 2.0)
910 Application authorization failure (MIDP 2.0)
911 Push registration failure (MIDP 2.0)
912 Deletion Notification (MIDP 2.0)
913 Required package not supported by the device (MIDP 2.0)

Contoh proses diatas adalah proses yang biasa. Dibelakang layar sebuah provisioning server, penyedia layanan dapat menambahakan proses-proses yang diperlukan misalnya:

- Klasifikasi berdasarkan device pada saat user melakukan discovery
- Autentikasi user menggunakan Basic HTTP Authentication atau menggunakan cookies
- Charging (pembiayaan) pada user
- Tracking

Proses OTA provisioning MIDP ini diadopsi pada cakupan yang lebih luas yaitu Spesifikasi provisioning untuk aplikasi klien J2EE (JSR 124) dan OMA download.

== Spesifikasi provisioning untuk aplikasi klien J2EE

JSR 124 didesain tidak hanya untuk aplikasi MIDP, tapi masih mirip dengan OTA provisioning aplikasi MIDP. JSR 124 lebih kompleks karena menspesifikasikan cara untuk mengirimkan content termasuk packaging, publishing, authentication, policies, adapter, plug-in, konfigurasi device serta API untuk mengakses content repository.
Descriptor yang digunakan pada JSR 124 berupa XML.

== OMA Download OTA

Proses OMA download juga mirip dengan OTA provisioning aplikasi MIDP. OMA download tidak dikhususkan untuk men-download MIDlet atau tipe media spesifik lainnya, tetapi merupakan framework untuk mendownload media object apapun. OMA download juga menambahakan kemampuan untuk menghindari dari pengkopian ilegal suatu content dengan menambahkan spesidikasi Digital Rigth Management (DRM). Content descriptor yang digunakan OMA download berupa XML kecuali untuk aplikasi MIDP descriptor tetap menggunakan file JAD.

Contoh descriptor pada OMA download OTA:

<media xmlns="http://www.openmobilealliance.org/xmlns/dd">
<type>image/gif</type>
<objecturi>http:/foo.bar.com/pic-dir/picture.gif
</objecturi>
<size>1234</size>
<installnotify-uri>http:/foo.bar.com/status</installnotify-uri>
</media>


== Links

MIDP 1.0 OTA recommendation http://java.sun.com/products/midp/OTAProvisioning-1.0.pdf
MIDP 2.0 specification http://www.jcp.org/jsr/detail/118.jsp
J2EE Client Provisioning http://www.jcp.org/jsr/detail/124.jsp, http://java.sun.com/j2ee/provisioning
Deploying Wireless Java Applications http://wireless.java.sun.com/midp/articles/deploy
OMA Download OTA v.1.0 http://www.openmobilealliance.org/release_program/download_v10.html

20061031

Thursday, July 13, 2006

SAX Tutorial

-== Pengenalan SAX ==-

SAX (Simple API for XML) adalah API (common interface) untuk memparsing XML dokumen secara event-based.
SAX bukanlah sebuah XML parser. SAX didesain untuk dapat digunakan dengan berbagai macam event-based parser.

SAX tidak sulit dipelajari, karena sifatnya yang simpel.
SAX API cukup kecil, hanya terdiri 35 class/interface yang terdapat dalam 3 package:

org.xml.sax
org.xml.sax.ext
org.xml.sax.helpers

-== Event-based API ==-

Pada event-based API,suatu even akan dilemparkan lewat callback jika parser membaca karakter data atau awal/akhir
dari dokumen maupun element/tag.Kita harus membuat (mengimplementasikan) handler yang akan menangani event. Dengan
event-based API, akses ke XML dokumen menjadi lebih cepat dan sederhana. Juga dimungkinakan untuk memparsing
dokumen yang besar dari memori sistem yang tersedia karena struktur dokumen tidak disimpan dalam memori.

Catatan
Untuk yang belum tahu, selain parser yang event based ada juga parser XML yang DOM based dan pull streaming based.
Ketiga macam parser tersebut dibedakan dengan caranya memproses dokumen XML dan masing masing memiliki kelebihan dan
kekurangan.


-== Beberapa Java SAX2 parser ==-

Xerces http://xml.apache.org/xerces-j/index.html
Sun's JAXP http://java.sun.com/xml/
SAXON XSLT processor http://users.iclway.co.uk/mhkay/saxon/
Oracle XML Developer's Kit for Java http://technet.oracle.com/tech/xml/xdk_java.html
ParserAdapter http://www.megginson.com/SAX/Java/index.html

-== Menggunakan SAX ==-

Untuk dapat melakukan parsing pada XML dokumen kita membutuhkan class yang mengimplentasikan interface XMLReader.
XMLReader bertanggung jawab untuk mem-parsing dokumen XML.
Implemantasi interface org.xml.sax.XMLReader dapat di-instantiate dengan cara:

XMLReader xr = (XMLReader) Class.forName("org.apache.xerces.parsers.SAXParser").newInstance();

atau

XMLReader xr = new org.apache.xerces.parsers.SAXParser();

atau

XMLReader xr = org.xml.sax.helpers.XMLReaderFactory.createXMLReader();

Dianjurkan kita menggunakan cara yang terakhir, karena tidak tergantung pada suatu implementasi parser tertentu.
Dengan cara yang terakhir, XMLReader didapatkan dari class pada saat runtime dengan membaca setting pada:

- System property "org.xml.sax.driver" yang dispesifikasikan pada command line seperti

java -Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser JavaProgram

atau pada kode program seperti

System.setProperty("org.xml.sax.driver", "org.apache.xerces.parsers.SAXParser");

- Atau pada file META-INF/services/org.xml.sax.driver didalam JAR file


XMLReader membutuhkan class handler yang harus mengimplementasikan interface berikut:

- ContentHandler yang akan meng-handle jika terjadi event pada saat proses parsing
- ErrorHandler yang akan meng-handle jika terjadi warning/error
- DTDHandler yang akan meng-handle event pada dkumen DTD
- EntityResolver yang akan men jika ada external entities pada dokument XML (misalnya external DTD atau external
parameter entity)


Untuk mengimplementasikan handler diatas sebaiknya gunakan DefaultHandler sebagai base class.
DefaultHandler telah mengimplementasikan keempat interface diatas.
Method signature dari ContentHandler (atau Defaulthandler) yang penting untuk memproses dokumen XML diantaranya:

* void characters(char[] ch, int start, int length)
* void endDocument()
* void endElement(String uri, String localName, String rawName)
* void startDocument()
* void startElement(String uri, String localName, String rawName, Attributes attributes)

Dibawah ini contoh code penggunaan DefaultHandler sebagai base class yang hanya mengimplementasikan method-method
ContentHandler:

public class MyDefaultHandler extends DefaultHandler {
public void startElement(java.lang.String uri, String localName, String qName, Attributes attributes)
throws SAXException {
System.out.print("Start element: URI = " + uri +
", localname = " + localName +
", qname = " + qName +
", attributes = {" );
for (int i = 0; i < attributes.getLength(); i++) {
System.out.print( attributes.getQName(i) + "(" + attributes.getType(i) + ") = " +
attributes.getValue(i) + ", ");
}
System.out.println("}");
}


public void characters(char[] ch, int start, int length) throws SAXException {
System.out.println("characters: " + ch);
}

public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.println("End element: URI = " + uri + ", localname = " + localName + ", qname = " + qName);
}

}


XML memerlukan class InputSource sebagai input dalam melakukan parsing.
InputSource adalah representasi dari XML dokumen yang kemudian di-parsing oleh XMLReader.
XML dokumen dapat berada di lokal maupun remote dan URI digunakan sebagai parameter constructor dari class InputSource.

FileInputStream is = new FileInputStream("C:/Users.xml");
xmlReader.setContentHandler(myDefaultHandler);
xmlReader.parse(new InputSource(is));

-== Perubahan class pada SAX1 ke SAX2 ==-

Saat ini SAX terbaru adalah versi 2, biasa ditulis SAX2 dan SAX versi sebelumnya biasa ditulis SAX1.
Ada beberapa perbedaan antara SAX1 dan SAX2 walaupun demikian parser yang mengimplementasikan SAX1 yaitu class Parser
masih dapat digunakan oleh SAX2, demikian juga sebaliknya.
Dibawah ini adalah table class yang berubah pada SAX1 ke SAX2

-------------------------------------
Class pada SAX1 Class pada SAX2
================= ===================
ParserFactory XMLReaderFactory
Parser XMLReader
DocumentHandler ContentHandler
HandlerBase DefaultHandler
AttibuteList Attributes
-------------------------------------

Parser dari SAX1 dapat digunakan pada sebagai SAX2 XMLReader dengan menggunakan class ParserAdapter

SAX2 XMLReader juga dapat digunakan sebagai XA1 Parser dengan menggunakan class XMLReaderAdapter

-== Contoh kode yang lengkap ==-

public class MyDefaultHandler extends DefaultHandler {

public static void main(String[] args) {
try
FileInputStream is = new FileInputStream("C:/Users.xml");
XMLReaderFactory fact = XMLReaderFactory.newInstance();
XMLReader xmlReader = parser.createXMLReader();
xmlReader.setContentHandler(this);
xmlReaderreader.setErrorHandler(this);
xmlReader.setFeature("http://xml.org/sax/features/validation" ,true);
xmlReader.setEntityResolver(this);
xmlReader.parse(new InputSource(is));
} catch (Exception e) {
e.printStackTrace();
}
}


public void startElement(java.lang.String uri, String localName, String qName, Attributes attributes)
throws SAXException {
System.out.print("Start element: URI = " + uri +
", localname = " + localName +
", qname = " + qName +
", attributes = {" );
for (int i = 0; i < attributes.getLength(); i++) {
System.out.print( attributes.getQName(i) + "(" + attributes.getType(i) + ") = " +
attributes.getValue(i) + ", ");
}
System.out.println("}");
}


public void characters(char[] ch, int start, int length) throws SAXException {
System.out.println("characters: " + ch);
}

public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.println("End element: URI = " + uri + ", localname = " + localName + ", qname = " + qName);
}



}



-== Menggunakan JAXP (versi 1.1) ==-

Pada JAXP, XMLReader dibungkus (wrap) oleh class lain yaitu javax.xml.parsers.SAXParser yang dapat diperoleh dari
factory class yaitu javax.xml.parsers.SAXParserFactory. Dibawah ini contoh kode untuk mendapatkan instance dari class
SAXParser:

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();

Class SAXParser sudah cukup untuk dapat melakukan parsing pada XML dokumen, yaitu dengan menggunakan method
parse(InputSource, DefaultHandler). Jika menginginkan mendapatkan class XMLReader, kita dapat memanggil method
getXMLReader() pada class SAXParser.

SAXParserFactory adalah class abstract, implementasinya didapatkan dari metode lookup yaitu:

- Menggunakan system property "javax.xml.parsers.SAXParserFactory"
- Menggunakan standar java properties file yang ada di "lib/jaxp.properties" pada direktori JRE
- Menggunakan service API yaitu pada file META-INF/services/javax.xml.parsers.SAXParserFactory di dalam file JAR
- Default instance yang ada pada platform

Thursday, May 11, 2006

JAX-RPC berubah menjadi JAX-WS

JAX-RPC (Java API for XML Based RPC) yang dispesifikasikan pada JSR-101 berubah nama menjadi JAX-WS (Java API for XML Web Services) dengan spesifikasi JSR-224.

Perubahan ini terjadi pada versi JAX-RPC 2.0, setelah JAX-RPC 1.1

Hal ini karena kesalahaan penamaan awal sebab JAX-RPC memberi kesan spesifikasi/library hanya untuk RPC padahal sebenarnya termasuk juga untuk Web Services.

Perubahan mendasar pada versi ini adalah digunakannya JAXB sebagai library untuk databinding.

Katanya dengan JAX-WS dibanding JAX-RPC maka generated code dari WSDL akan lebih sedikit (dalam hal line of code dan tentu saja size/byte) dan coding menjadi lebih mudah.

Tetapi target platform JAX-WS adalah Java SE 5, jadi jangan harap yang masih menggunakan JDK/JRE 1.4 bisa menggunakannya. Pada library ini code yang kita buat dimudahkan dengan adanya annotation yang mulai ada pada JDK/JRE versi 1.5

Seperti versi sebelumnya JAX-WS ini memenuhi standar:
  • WS-I Basic Profile 1.1
  • WS-I Attachments Profile 1.0
  • WS-I Simple SOAP Binding Profile 1.0

Yang saya tidak suka adalah, JAX-WS ini akan dimasukan dalam standar library JRE/JDK yaitu pada versi Mustang (Java SE 6). Buat saya seharusnya library ini cukup dimasukan ke spesifikasi Java Enterprise (JEE) sehingga JRE tidak semakin besar dan dipenuhi dengan framework-framework yang bukan inti (core libray).

Wednesday, May 10, 2006

Component/dependecy analyst

Setelah selesai membuat suatu program/software, ada baiknya Anda lakukan analisis komponen yaitu analisis ketergantungan antar modul atau komponen yang ada pada program/software Anda untuk kemudian anda dapat me-refactor kode program sehingga hubungan antar komponen menjadi lebih teratur (nggak jelimet).

Beberapa aplikasi untuk component/dependecy analyst untuk program Java diantaranya:

http://clarkware.com/software/JDepend.html
http://www.kirkk.com/Main/JarAnalyzer

Saturday, March 18, 2006

Service provider dalam file JAR

Sejak Java 1.3, suatu file JAR dapat berisi file metadata yang menspesifikasikan service provider class. File tersebut berada di direktori META-INF/services dan disebut sebagai file konfigurasi-profider (provider-configuration file)

Service disini adalah suatu set dari beberapa interface atau beberapa class, dan provider (service provider) adalah implementasi spesifik dari suatu service.

Sebuah provider biasanya sebuah class berfungsi sebagai proxy dari semua operasi yang ada pada sebuah service. Provider haruslah tidak memiliki argumen pada constructor-nya, sehingga dapat diinisialisasi saat pencarian (lookup).

Sebuah provider-configuration file harus memiliki nama sesuai dengan nama (fully-qualified name) dari abstract service class. File tersebut berisi list dari provider-class konkrit yang ada dalam file JAR. Komentar dapat ditambahakan pada file dengan karakter '#'

Sebagai contoh, mari kita membuat sebuah class service pada suatu aplikasi dengan nama com.ejlp.spi.EncryptorProvider yang

memiliki method abstract sebagai berikut:

public abstract Encryptor getEncryptor(String s);


Kita dapat membuat suatu provider dalam file JAR yang terpisah dengan aplikasi dan mengimplementasikan abstract class com.ejlp.spi.EncryptorProvider. Misalnya kita membuat class com.ic.DESEncryptor sebagi provider:

public class DESEncryptor extends com.ejlp.spi.EncryptorProvider {

   public DESEncryptor() {}

   public abstract Encryptor getEncryptor(String s) {
      //...
   }

}


Kemudian pada JAR file kita buat teks file META-INF/services/com.ejlp.spi.EncryptorProvider yang berisi line berikut

#Provider for DES encryptor
com.ic.DESEncryptor


Pada aplikasi yang akan menggunakan provider, kita bisa membuat mekanisme seperti ini:

 static {
     for (Iterator i = Service.providers(EncryptorProvider.class); i.hasNext(); ) {
         EncryptorProvider ep = (EncryptorProvider) i.next();
         registerProvider(ep.class.getName, ep);
     }
 }


 registerProvider(String name, EncryptorProvider ep) {
   //... Add provider to a Map
 }


 Encryptor getEncryptor(String providerName) {
   //... get provider by its name, otherwise get default provider
 }



Links:
JAR File Specification
Bacaan bagus tentang service framework: Introduction to Services Framework

Wednesday, March 15, 2006

JSR 160 (JMX Remoting)

JSR-160 (JMX Remoting)

JMX (JSR-3) mendefinisikan API untuk management aplikasi Java yang bersifat local.
Jika dibutuhkan client yang terkoneksi ke aplikasi Java yang mendukung JMX, maka koneksi dilakukan dengan cara yang tidak standar, biasanaya menggunakan RMI atau HTTP.
JMX remote API merupakan ekstensi dari spesifikasi JMX versi 1.2

Untuk itu dibuatlah JSR-160 yang memperluas JSR-3 yaitu memberikan sebuah standar API untuk koneksi ke suatu aplikasi JMX secara remote.
JMX remoting API terdiri dari package yang harus (mandatory) diimplementasikan dan ada bagian yang tidak mandatory.

Package yang ada pada JSR-160 adalah:

javax.management.remote
javax.management.remote.generic (optional package)
javax.management.remote.jmxmp (optional package)
javax.management.remote.message (digunakan untuk wrapping object yang dikirim dari client ke server)
javax.management.remote.rmi (mandatory package)

*** Connector
Connector (JMX API connector) adalah bagian yang bertanggung jawab untuk mentransmisikan permintaan (request) client ke sebuah remote MBean server.
Connector yang mandatory pada JSR-160 adalah yang berbasis RMI (RMI/JRMP atau RMI/IIOP)
Connector yang tidak mandatory adalah yang berbasis pada:
  • Java serialization (JMX Messaging Protocol/JMXMP) yang disebut Generic JMX API Connector
  • TCP socket protokol lainnya (disebut User-Defined Protocols Connector)
Connector merupakan class yang mengimplementasikan interface javax.management.remote.JMXConnector


*** Connector server
Connector server (JMX API connector server) disisipkan (attach) pada MBean Server (JMX agent) agar connector dapat berinteraksi dengannya.
Direpresentasikan dengan suatu super class: javax.management.remote.JMXConnectorServer
Connector server berasosiasi dengan MBean server dengan cara meregisterkannya pada MBean server atau memberikan MBean server pada constructornya.
Connector server perlu di-start() untuk mulai mendengarkan (listening) koneksi dari client.
Connector server berhenti ketika di-stop() atau akan di-unregister dari MBean Server

*** Pengalamatan
Alamat JMXConnectorServer menggunakan format URL.
URL tersebut digunakan JMXConnector untuk melakukan koneksi ke JMXConnectorServer
Bentuk pengalamannya seperti ini:

service:jmx:://[host[:port]][url-path]

protocol adalah jenis protokol yang digunakan misalnya rmi", "iiop", "jmxmp" atau yang lainnya
Representasi alamat connector adalah class javax.management.remote.JMXServiceURL

*** Contoh

Membuat connector server:

MBeanServer mbs = MBeanServerFactory.createMBeanServer();
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/server");
Map environment = null;

JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, environment, mbs);
cs.start();

Membuat connector client:

JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/server");
Map environment = null;
JMXConnector connector = JMXConnectorFactory.newJMXConnector(url, environment);
connector.connect(connectionEnvironment);

// Obtain a "stub" for the remote MBeanServer
MBeanServerConnection mbsc = connector.getMBeanServerConnection();

// Call the remote MBeanServer
String domain = mbsc.getDefaultDomain()

*** Discovery dan Lookup Service

Digunakan untuk 'mengiklankan' (advertise) dan mencari JMX agent
Infrasuktur untuk discovery dan lookup Service ini bisa menggunakan yang sudah ada seperti:
  • Service Location Protocol (SLP)
  • Java Naming and Directory Interface (JNDI)
  • Jini Network Technology


*** Referensi JMX Remote API
JavaTM Management Extensions (JMX) Remote API Overview
JavaTM Management Extensions (JMX) Remote API Tutorial
API Documentation

Semuanya bisa didownload dari pake reference implementation (RI)

Wednesday, March 08, 2006

Membuat scheduler atau proses periodik

Kita bisa menggunakan Timer seperti ini:


int initialDelay = 30000; // start after 30 seconds
int period = 5000; // repeat every 5 seconds
Timer timer = new Timer();
TimerTask task = new TimerTask() {
public void run() {
// job code here
}
};
timer.scheduleAtFixedRate(task, initialDelay, period);


Atau menggunakan EDU.oswego.cs.dl.util.concurrent.ClockDaemon seperti ini:


static protected ClockDaemon clockDaemon = new ClockDaemon();

static
{
log.debug("Setting the clockDaemon's thread factory");
clockDaemon.setThreadFactory(new ThreadFactory()
{
public Thread newThread(Runnable r)
{
Thread t = new Thread(getThreadGroup(), r, "ThreadDaemon");
t.setDaemon(true);
return t;
}
});
}

public static ThreadGroup getThreadGroup()
{
if (threadGroup.isDestroyed())
threadGroup = new ThreadGroup("Our Threads");
return threadGroup;
}


public static void main(String[] args)
{
int period = 500; //millis
clockDaemon.executePeriodically(
period,
new Runnable() {
public void run() {
System.out.println("Running...");
}
},
true);

}


Atau menggunakan Quartz, job scheduling system.

Tuesday, March 07, 2006

Alternatif untuk Logging framework

Untuk keperluan logging kita bisa menggunakan bermacam-macam framework seperti

Jakarta Commons Logging (JCL)
Log4J
simple-log, A logging anti-framework


Untuk logging yang sederhana, kita bisa gunakan Simple Logging Facade for Java (SLF4J)
yang dibuat oleh pendiri project Log4J, Ceki Gülcü.

Sesuai namanya SLF4J merupakan facade API yang sederhana, inti dari API-nya adalah service provider interface (SPI). Dengan SLF4J, kita bisa berpindah-pindah dari logging framework satu ke logging framework yang lain hanya dengan mengganti file JAR saja.

SLF4J mirip dengan JCL atau juga LogBridge yang dapat diintegrasikan dengan logging framework lainny. Tapi yang saya suka dari SLF4J adalah caranya mengkonstruksi pesan (message) log, yaitu dengan mengubah tanda {}. Seperti kode contoh dibawah ini:

logger.debug("The new entry is {}. It replaces {}.", object, oldObject);
logger.debug("Value {} was inserted between {} and {}.",
new Object[] {newVal, below, above});

Alternatif framework lain untuk logging bisa dilihat di sini

Saturday, February 11, 2006

Ganti java.util.Properties dengan Preferences API

Tujuan utama dibuatnya Preferences API:

Kebutuhan untuk mengatur (menyimpan, mengambilnya kembali dan memodifikasi) preference pengguna (user) dan data konfigurasi dari suatu aplikasi. Data seperti itu biasanya memiliki sifat:
  • Data yang kecil (Spesifikasi API menyaratkan maximum 8192 bytes per value)
  • Data yang jarang diakses
  • Bukan data penting (critical)
  • Data yang penting untuk fungsi dari suatu aplikasi
Untuk memenuhi kebutuhan tersebut biasanya programmer menggunakan file properties (file text) yang diakses lewat API java.util.Properties. Dengan adanya API ini, java.util.Properties menjadi tergantikan.

Tipe data yang dapat disimpan menggunakan Preferences API adalah:
  • String
  • boolean
  • double
  • float
  • int
  • long
  • byte array
API ini terdiri dari:

  • 3 Interfaces
    • NodeChangeListener
    • PreferenceChangeListener
    • PreferencesFactory
  • 4 Classes
    • AbstractPreferences
    • NodeChangeEvent
    • PreferenceChangeEvent
    • Preferences
  • 2 Exceptions
    • BackingStoreException
    • InvalidPreferencesFormatException
- API ini ada mulai pada Java versi 1.4 pada package java.util.prefs
- API yang didesain sederhana
- Spesifikasi tidak mendefinisikan di tempat mana data akan disimpan (backing store)
- Memiliki dua kategori preference yaitu User dan System
- Data merupakan pasangan key-value
- Struktur pohon (tree): Tiap node dapat menyimpan data dan atau node lain
- Support callback pada saat ada perubahan pada node (penambahan/penghapusan) atau pada data
- Tidak support transaksi pada saat mengubah data (update bersifat atomic)


- Class terpenting yang sering digunakan adalah class Preferences (Sempatkan untuk membaca dokumentasinya)
- Untuk menyimpan data digunakan method putXXX(key, value) pada class Preferences
- Untuk menggambil data digunakan method getXXX(key, default-value) pada class Preferences
- Jika backing store tidak ada, mengakses data atau mengimport preferences akan menghasilkan BackingStoreException
- Gunakan Preferences.systemNodeForPackage(Class) untuk menyimpan data per-system
- Gunakan Preferences.userNodeForPackage(Class) untuk menyimpan data per-user
- Dibalik layar, class Preferences yang merupakan impleentasi dari AbstractPreferences akan dihasilkan dari class PreferencesFactory
- Class Preferences bertanggung jawab terhadap penyimpanan data
- Pada Windows biasanya default Preference akan menggunakan java.util.prefs.WindowsPreferences yang menggunakan native mothod untuk menyimpan data pada registry
- Pada sistem operasi UNIX-like digunakan java.util.prefs.FileSystemProperties yang menyimpan data pada file
- JVM akan menggunakan PreferencesFactory sesuai dengan property java.util.prefs.PreferenceFactory yang ada pada JVM
- PreferencesFactory dapat diubah dengan cara
  • Memberikan command line argument pada saat menjalankan JVM, contoh

    java -Djava.util.prefs.PreferencesFactory=com.ejlp.perf.TestPreferencesFactory
  • Menambahakan kode sebelum Preferences digunakan:

    System.setProperty("java.util.prefs.PreferencesFactory", "DisabledPreferencesFactory");

- Support export-import ke format XML dengan menggunakan method exportNode() atau exportSubtree() dan importPreferences()


- Media penyimpanan:

Pada sistem operasi windows biasaya preferences data disimpan pada Windows Registry yaitu pada:

HKEY_CURRENT_USER\Software\JavaSoft\Prefs (untuk system preferences)
HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs (untuk user preferences)

Pada sistem operasi lain dapat berada pada file di direktori home user atau ditempat lain.



Contoh penggunaan:

package net.ejlp.test.prefs;
import java.io.IOException;
import java.util.prefs.*;

public class TestPreferences {
// Preference key
private static final String FONT = "font";

// Membuat Preferences instance menggunakan User Preferences
private static Preferences prefs =
Preferences.userNodeForPackage(TestPreferences.class);

public static void main(String[] args) {
TestPreferences.ambil();

// Simpan data pada Preferences
prefs.put(FONT, "Courier New");

TestPreferences.ambil();

// Tambahkan listerner untuk mengangani perubahan yang terjadi
prefs.addPreferenceChangeListener(new PreferenceChangeListener() {
public void preferenceChange(PreferenceChangeEvent evt) {
System.out.println(
"Perubahan preference terdeteksi pada key = " + evt.getKey() +
", new value = " + evt.getNewValue());
}
});

// Ubah value pada key font sekali lagi
prefs.put(FONT, "Courier");

// Export ke format XML dan tampilkan di layar
try {
prefs.exportNode(System.out);
} catch (IOException e) {
} catch (BackingStoreException e) {
}

}

public static void ambil() {
String font = prefs.get(FONT, "Arial");
System.out.println("Data: Key = " + FONT + ", value = " + font);
}
}


- Bahan bacaan:
JSR-10
Dokumentasi Java API > 1.4
How to Completely Disable Sun Java's 1.4.x Preferences Subsystem

Thursday, February 09, 2006

Menggunakan Shutdown Hook: Bersihkan dosa sebelum mati :)

Pada saat aplikasi mati, baik itu diinginkan ataupun tidak (misalnya karena ada error), kadang perlu dilakukan proses akhir sehingga tidak menimbulkan sesuatu hal yang tidak diingankan, misalnya hilangnya data, tidak ter-commit-nya suatu transaction dan lain-lain. Beberapa contoh proses pada saat aplikasi mati diantaranya:
  • Mematikan koneksi jaringan
  • Menyimpan session state
  • Menghapus file sementara (temporary)
  • Menutup file handle

Untuk melakukan proses akhir tersebut kita bisa menggunakan solusi dengan cara:
  • Buat suatu fungsi yang akan dipanggil disetiap baris yang membuat aplikasi mati atau disetiap baris sebelum memanggil System.exit()

Tapi solusi tersebut tentu saja tidak efektif karena kita harus menuliskan kode pemanggil fungsi tersebut disetiap bagian dimana memungkinkan aplikasi mati.

Solusi yang baik adalah menggunakan shutdown hook yang ada pada standar Java API.

Shutdown hook:
  • Meknisme shutdown hook sudah ada sejak Java 1.3.
  • Sebelumnya kita dapat menggunakan Runtime.runFinalizersOnExit(), yang sekarang ini sudah deprecated.
  • Dengan shutdown hook pemberhentian suatu aplikasi dengan sengaja (voluntary) tau tidak sengaja dapat ditangani dengan baik.
  • Sebuah shutdown hook adalah sebuah class Thread dengan method run() yang kita isi dengan kode yang akan dipanggil sebelum JVM mati.
  • Kita dapat meregistrasi beberapa shoutdown hook dalam suatu aplikasi.
  • 3 Method pada class java.lang.Runtime yang berhubungan dengan mekanisme 'exit' suatu aplikasi yang perlu dipelajari (baca dokumentasi Java API)
    • addShutdownHook
    • removeShutdownHook
    • halt
  • Tips:
    • Gunakan kode yang thread-safe pada shutdown hook
    • Buat kode yang sederhana dan cepat dieksekusi sehingga mempercepat waktu shutdown
  • Contoh dibawah ini memperlihatkan penggunaan shutdown hook:
package com.ejlp.test.shutdownhook;

import java.io.*;

public class TestShutdownHook {
private BufferedWriter bw_log;

public TestShutdownHook() throws IOException {
// Buat file log.txt
bw_log = new BufferedWriter(new FileWriter("log.txt"));

// Register sebuah shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
// Tutup log file
bw_log.close();
}
catch (IOException e) {
System.err.println(e);
}
}
});;
}

// Tulis sesuatu ke file log.txt
public void processApp1() throws IOException {
bw_log.write("Jika tidak ada shutdown hook yang menutup bw_log, tulisan ini tidak adakan ada di log.txt");
bw_log.newLine();
}

public static void main(String args[]) {
try {
TestShutdownHook demo = new TestShutdownHook();
demo.processApp1();
} catch (IOException e) {
e.printStackTrace();
}

// Simulasikan program error
throw new RuntimeException();
}
}

Wednesday, December 14, 2005

Java decompiler


Dari kesimpulan hasil test yang dilakukan pada 9 java decompiler,
JODE merupakan Java decompiler terbaik.

Lihat detail testnya disini

http://www.program-transformation.org/Transform/JavaDecompilerTests

Java decompiler wiki page
http://www.program-transformation.org/Transform/JavaDecompilers


http://blog.technoetic.com/jode-eclipse-plugin/

JAD http://www.kpdus.com/jad.html

Object serialization


Java Serialization

- Object disimpan di memory dan dapat digunakan lagi selama JVM masih running
- Bagaimana menyimpan object agar masih bisa digunakan dilain waktu

- Object serialization adalah proses penyimpanan suatu object pada kondisi
tertentu dalam bentuk deretan byte (sequence of bytes) atau proses
kebalikannya yaitu membuatnya kembali dari deretan byte menjadi object real
dalam JVM

- Object serialization hanya menyimpan object's state. File class dari object
serta method-method-nya tidak disimpan.

- Deretan byte tersebut dapat kemudian kita simpan menjadi suatu file atau
dalam database atau ditransfer lewat jaringan.

- Suatu object yang dapat disimpan dan dibuat lagi (persist) harus dibuat
menjadi persistance object.

- Object yang dapat disimpan dengan mekanisme object serialization disebut
serializable object atau object tersebut bersifat serializable

- Untuk membuat object menjadi serializable, maka object tersebut ditandai
dengan cara mengimplementasikan interface java.io.Serializable baik secara
langsung ataupun tidak langsung (maksudnya secara hirarki object tersebut


- Contoh :

import java.io.Serializable;
import java.util.Date;
import java.util.Calendar;
public class PersistentTime implements Serializable {
private Date time;
public PersistentTime() {
time = Calendar.getInstance().getTime();
}

public Date getTime() {
return time;
}
}

- Object diatas, karena telah mengimplementasikan interface Serializable,
dapat disimpan sebagai object persistence

- Interface Serializable merupakan interface kosong yang hanya sebuah
interface penanda (marker)

- Untuk menyimpan atau mentransfer serializable object kita dapat
menggunakan mentod ObjectOutputStream.writeObject(Object obj).

- ObjectOutputStream merupakan sebuah filter stream class yang dapat kita
gunakan untuk menyimpan byte stream yang disimpan di memori atau disimpan
menjadi file di disk atau ditransfer lewat network

- Contoh menyimpan object menjadi suatu file:

import java.io.ObjectOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FlattenTime {
public static void main(String [] args) {
PersistentTime time = new PersistentTime();
FileOutputStream fos = null;
ObjectOutputStream out = null;
try {
fos = new FileOutputStream("time.obj");
out = new ObjectOutputStream(fos);
out.writeObject(time);
out.close();
} catch(IOException ex) {
ex.printStackTrace();
}
}
}


contoh diatas akan menyimpan object menjadi suatu file bernama time.obj

- Contoh untuk membuat object dari persistance object

import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Calendar;

public class InflateTime {
public static void main(String [] args) {
PersistentTime time = null;
FileInputStream fis = null;
ObjectInputStream in = null;
try {
fis = new FileInputStream("time.obj");
in = new ObjectInputStream(fis);

// Recreate object
time = (PersistentTime)in.readObject();
in.close();
} catch(IOException ex) {
ex.printStackTrace();
} catch(ClassNotFoundException ex) {
ex.printStackTrace();
}
}


contoh diatas membuat object dari file bernama time.obj

- Proses untuk mendapatkan object dari sequence of bytes disebut proses
deserialization

- Untuk mendapatkan real object kita harus melakukan casting menggunakan
class aslinya dari object tersebut. Dalam contoh diatas adalah
PersistentTime.

Karena sebenarnya persistance object hanya menyimpan state/property
dari object itu saja

- Tidak semua object serializable, contohnya Thread, Socket, OutputStream
dan subclass-nya.

- Bagaimana jika object yang serializable memiliki object yang tidak
serializable?

Object tersebut harus ditandai dengan deklarasi "transient"

- Ketika object di-deserialize (dilakukan casting setelah byte stream
dibaca) constructor dari object tersebut tidak dipanggil.
Bagaimana jika ada proses yang penting yang ada di constructor object
tersebut?

- Kita dapat membuat method tersendiri dan constructor memanggil method
tersebut. Jika object di-deserialize maka kita harus secara manual
memanggil method tersebut.

- Proses tersebut dapat diotomatisasi dengan mengimplementasikan method
spesial berikut :

private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;

- Contoh :

public class MyObject implements Serializable {

public MyObject() {
initialization();
}

private void initialization() {
// code constructor disini
}

private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
//
in.defaultReadObject();

initialization();
}
}


- Kita dapat melakukan hal yang sama untuk proses serialization
menggunakan method spesial berikut:

private void writeObject(java.io.ObjectOutputStream out)
throws IOException

- Method spesial readObject() dan writeObject() juga dapat digunakan untuk
memastikan suatu proses dilakukan pada saat serialize atau deserialize.

Misalnya untuk keperluan security pada object yang disimpan maka setiap
proses serialization object akan dienkrip terlebih dahulu dan didekrip
saat proses deserialization

Contoh lain misalnya pada kasus suatu class serializable merupakan
subclass dari class yang non-serializable. Untuk memastikan field dari
parent class tersimpan dalam byte stream kita bisa menambahan code untuk
menyimpan field dari parent class pasa method writeObject().
Lihat contoh code di:

http://java.sun.com/j2se/1.4.2/docs/guide/serialization/examples/nonserialsuper/sources.html


- Bagaimana jika suatu object yang merupakan subclass dari object
yang Serializable tidak ingin bisa di-serialize atau di-deserialize?
Gunakan method spesial tadi untuk menglemparkan Exception throw misalnya :

throw new NotSerializableException("This object cannot be serialize");

- Saat object di-serialize, maka dibuat sebuah "fingerprint" yang disebut
serialVersionUID berupa 64-bit data.

- Kita dapat membuat serialVersionUID sendiri dari awal pada serializable
object dengan menambahkan field long bernilai sembarang seperti ini:

static final long serialVersionUID = 4070409649129120458L;

- Jika suatu file dari persistance object disimpan kemudian object tersebut
mengalami perubahan kecil, misalnya penambahan field (java code berubah),
dan kita dapat mengatakan bahwa file dari persistance object yang
disimpan masih kompatibel dengan object yang baru maka object baru
tersebut harus diberikan serialVersionUID yang sama object yang lama.

Jika hal ini tidak dilakukan proses deserialization akan menghasilkan
exception java.io.InvalidClassException.

- Untuk mengetahui serialVersionUID dari byte stream file, dapat digunakan
tool "serialver" yang ada di JDK.

- Serialization menggunakan algoritma rekursif, semua object yang dimiliki object
serializable akan di serialize.
Jika class serializable A tersebut memiliki class serializable B yang memiliki
class serializable C, maka proses serialization pada object A akan juga men-
serialize object B dan C

- Proses serialization menggunakan reflection

[http://java.sun.com/docs/books/tutorial/reflect/]


- The Externalizable Interface

package java.io;

public interface Externalizable extends Serializable {
public void writeExternal(ObjectOutput out) throws IOException;
public void readExternal(ObjectInput in) throws IOException,
java.lang.ClassNotFoundException;
}


- Interface Externalizable memiliki 2 method yang memberikan kontrol kepada
implementor saat melakukan serialization, misalnya kita dapat menuliskan
kode yang menspesifikasikan atribut mana saja yang dimiliki object tersebut
yang akan disimpan dalam persistent object.

- Contoh:

import java.io.*;

public class User implements Externalizable {
long userID;
String firstName;
String lastName;
java.util.Date createdDate;
String password;

public void writeExternal(ObjectOutput stream) throws java.io.IOException {
stream.writeLong(userID);
stream.writeUTF(firstName);
}

public void readExternal(ObjectInput stream) throws java.io.IOException {
userID = stream.readLong();
firstName = stream.readUTF();
}

}

- Dengan interface Externalizable kita dapat membuat proses serialization
menjadi lebih efisien.


- Links :
http://java.sun.com/developer/technicalArticles/Programming/serialization/
http://www.jguru.com/faq/subtopic.jsp?topicID=3341

Wednesday, July 27, 2005

commons-dbutils little hacks

commons-dbutils-1.0

1. Mapping nama kolom ke bean properties
Untuk merubah mapping nama kolom ke bean properties, dari standar penulisan field tabel di database yaitu dengan separator underscore "_" menjadi standar penulisan object field di java class yaitu tanpa separator dengan huruf kapital di awal kata. Misalnya: CUSTOMER_NAME menjadi customerName.

Ubah method mapColumnsToProperties pada class BeanProcessor pada line 365 yaitu:

String columnName = rsmd.getColumnName(col);
menjadi
columnName = StringUtils.replace(columnName, "_", "");
Referensi ini ternyata lebih baik dari apa yang saya buat disini.

2. QueryRunner yang bersifat scrollable.
Kita biasanya perlu berpindah-pindah cursor (scrollable) dari suatu result set dengan menggunakan method afterLast(), beforeFirst() dan lain-lain.

Default class QueryRunner dalam commons-dbutils adalah menghasilkan ResultSet yang bersifat TYPE_FORWARD_ONLY dan CONCUR_READ_ONLY. Untuk mengubah agar ResultSet dapat kita perlu membuat subclass dari class QueryRunner yang implementasinya seperti ini:

/*
* Created on Sep 23, 2004
*
*/

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.sql.DataSource;
import org.apache.commons.dbutils.QueryRunner;

public class QueryRunnerScrollable extends QueryRunner {

public QueryRunnerScrollable() {
super();
}

public QueryRunnerScrollable(DataSource ds) {
super(ds);
}

protected PreparedStatement prepareStatement(Connection conn, String sql)
throws SQLException {
return conn.prepareStatement(
sql,
ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_READ_ONLY);
}
}

Monday, June 06, 2005

Java basic: Protected modifier and default modifier

Protected modifier dari suatu field dalam suatu class berarti selain dirinya sendiri yang dapat mengakses field tersebut, class lain yang bisa mengakses field tersebut adalah:
  • class yang satu package dengan class tersebut
  • class yang merupakan subclass dari class tersebut walaupun berbeda package

Default modifier agak berbeda dengan protected modifier, karena

  • subclass di package yang berbeda tidak dapat mengakses field tersebut

Java Network Launching application

Ini beberapa alternatif aplikasi untuk lauching java program lewat network:

Followers