<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>J.a.i.m's Diary &#187; Programming</title>
	<atom:link href="http://jaim.log.web.id/category/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://jaim.log.web.id</link>
	<description>J.a.i.m bukan berarti Jaga-Image, isi weblog ini cerita pengalaman yang bahkan jauh deh dari arti itu</description>
	<lastBuildDate>Wed, 31 Mar 2010 01:48:48 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>Trik menyiasati kodifikasi nomor dokumen yang dinamis</title>
		<link>http://jaim.log.web.id/2010/03/trik-menyiasati-kodifikasi-nomor-dokumen-yang-dinamis/</link>
		<comments>http://jaim.log.web.id/2010/03/trik-menyiasati-kodifikasi-nomor-dokumen-yang-dinamis/#comments</comments>
		<pubDate>Tue, 30 Mar 2010 17:22:09 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Tips]]></category>

		<guid isPermaLink="false">http://jaim.log.web.id/?p=178</guid>
		<description><![CDATA[Ada kasus yang cukup menggelitik yang pernah saya temui dalam proses pengembangan aplikasi Invoicing: perusahaan memiliki cukup banyak pelanggan/customer dan dalam menerbitkan dokumen invoice, perusahaan menerapkan bentuk format penomoran dokumen invoice yang berbeda-beda untuk tiap-tiap pelanggan yang ada. Kasus ini aslinya sebenarnya terselesaikan dengan trik yang saya buat dengan menggunakan bahasa pemrograman delphi, namun kali [...]]]></description>
			<content:encoded><![CDATA[<p>Ada kasus yang cukup menggelitik yang pernah saya temui dalam proses pengembangan aplikasi Invoicing: perusahaan memiliki cukup banyak pelanggan/customer dan dalam menerbitkan dokumen invoice, perusahaan menerapkan bentuk format penomoran dokumen invoice yang berbeda-beda untuk tiap-tiap pelanggan yang ada. Kasus ini aslinya sebenarnya terselesaikan dengan trik yang saya buat dengan menggunakan bahasa pemrograman delphi, namun kali ini saya akan mencoba mengupasnya dengan memanfaatkan <a href="http://www.python.org/">python</a>. :)</p>
<h2>Studi Kasus</h2>
<p>Adalah satu hal yang lumrah dalam penerbitan dokumen invoice perusahaan menerapkan kodifikasi penomoran untuk setiap dokumen invoice yang diterbitkan, sebagai contoh: <strong>30912/INV/EXP/III/2010</strong>. Hal ini dilakukan untuk mempermudah manajemen dan pengaturan atas dokumen yang dibuat. Permasalahannya adalah bagaimana jika kodifikasi tersebut bersifat dinamis, dalam artian bergantung pada pelanggan-nya, dalam arti misalnya: untuk pelangan PT. DAUN BUAH menggunakan format dokumen 30912/EXP/MAR/2010, sementara untuk PT. SEGARA PERKASA berformat 30912/EXP/03/2010, dan untuk PT. ABADI NUSA menggunakan format 30912/EXP/INV/2010. Hal ini tentunya akan menjadi kasus yang cukup menggelitik untuk dipecahkan.<br />
<span id="more-178"></span><br />
Pada kasus tersebut, saya memecahkannya dengan membuat rutin khusus untuk menangani kodifikasi yang dinamis tersebut dengan cara melakukan parsing atas format dokumen yang digunakan. Sebagai contoh, dokumen bernomor 30912/EXP/MAR/2010 terdiri atas elemen-elemen berikut: nomor dokumen (<strong>30912</strong>), bulan (<strong>MAR</strong>) dan tahun (<strong>2010</strong>) adapun karakter lainnya dapat dianggap sebagai karakter penyerta (<strong>/EXP/</strong>). </p>
<p>Secara keseluruhan, format kodifikasi penomoran dokumen pada umumnya melibatkan dua variabel utama, yaitu tanggal, dan nomor dokumen itu sendiri. Atas dua jenis variabel tersebut kemudian kita bisa menyusun elemen-elemen pembentuk atas format nomor dokumen yang akan dibuat, dalam hal ini misalnya: <strong>X</strong> untuk sebagai nomor dokumen, <strong>D</strong> untuk tanggal, <strong>M</strong> untuk bulam, dan <strong>Y</strong> untuk tahun. Untuk lebih mempermudah pengimplementasian, saya menggunakan aturan yang diterapkan pada bahasa C/C++ untuk memformat tanggal dengan beberapa penambahan fleksibilitas.  Sebagai contoh, berikut ini adalah contoh nomor dokumen dan format yang saya implementasikan:</p>
<p><strong><em>30912/EXP/MAR/2010 = [XXXXX]/EXP/[MMM]/[YYYY]</em></strong><br />
Elemen bulan dalam hal ini menggunakan metode penamaan pendek (Jan, Feb, Mar, dst), untuk hal tersebut kita menggunakan M sebanyak 3 kali, kemudian karena format yang diinginkan adalah menggunakan kapitalisasi maka, ketiga elemen tersebut ditulis sebagai huruf kapital. Artinya, kalau menginginkan format 30912/EXP/mar/2010, maka tinggal menyesuaikannya sebagai [XXXXX]/EXP/[mmm]/[YYYY], atau kalau menginginkan model <em>proper-case</em> seperti ini: 30912/EXP/Mar/2010, maka tinggal menyesuaikannya sebagai: [XXXXX]/EXP/[Mmm]/[YYYY].</p>
<p><strong><em>00912/INV/III/10 = [XXXXX]/EXP/[M]/[YY]</em></strong><br />
Elemen bulan dalam hal ini menggunakan metode bilangan romawi, untuk kasus ini saya memanfaatkan elemen M (dikapitalisasi) satu perulangan. begitu pula dengan elemen tahun (Y), dalam hal ini penomoran yang diinginkan adalah menggunakan model dua digit tahun, karenanya kodifikasi disesuaikan dengan menggunakan dua elemen Y. </p>
<p><strong><em>30912/INV/30/March/2010 = [XXXXX]/INV/[DD]/[Mmmm]/[YYYY]</em></strong><br />
Dalam kasus ini, penomoran dokumen yang diinginkan adalah menggunakan elemen dua digit tanggal karenanya format atas dokumen ini menggunakan dua elemen D. Di sisi lain penomoran tersebut menggunakan nama bulan secara penuh dan menggunakan <em>proper-case</em>, karenanya format elemen bulan tersebut disesuaikan dengan menggunakan 4 elemen M secara proper-case [Mmmm]. Sebagai perbandingan lain, jika menginginkan penggunaan nama bulan dalam huruf kapital seperti ini: 30912/INV/EXP/MARCH/2010, maka tentunya tinggal menyesuaikan elemen bulan (M) dalam 4 elemen huruf kapital [MMMM].</p>
<h2>Implementasi</h2>
<p>Pemecahan yang paling fleksibel atas kasus di atas adalah dengan memanfaatkan metode parsing atas format dokumen yang ada, parameter input dalam hal ini adalah format dokumen, nomor dokumen, serta tanggal. Proses parsing dilakukan dengan cara memisahkan antara elemen penenomoran serta karakter pembentuk, dalam hal ini untuk membedakan antara elemen dan karakter pembentuk dilakukan dengan mengapit elemen dengan dengan menggunakan karakter yang dianggap paling jarang digunakan, dalam hal ini adalah karakter &#8220;[" sebagai pembuka elemen dan karakter "]&#8221; sebagai penutup. Atas karakter-karakter yang tidak dianggap sebagai elemen langsung kita salin sebagai output, kemudian saat menemukan elemen yang dikenal maka program akan menghitung berapa jumlah karakter elemen yang digunakan serta jumlah kapitalisasinya kemudian menyisipkan dengan nilai yang sesuai atas elemen tersebut. Berikut adalah implementasi kode serta cara penggunaannya:</p>
<pre><code class="python">
import datetime as dt

def formatNumber(strfmt, aval, dt):
  '''Format the document number'''
  prd_list = ('I','II','III','IV','V','VI',
      'VII','VIII','IX','X','XI','XII')
  shmonths = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
  lnmonths = ('January', 'February', 'March', 'April', 'May', 'June',
              'July', 'August', 'September', 'October', 'November', 'December')

  ncount = ntotal = ncapital = 0
  calc_total = False
  ret_str = ""
  last_token = None

  for ch in strfmt:
    token = ch.upper()
    if calc_total:
      if last_token == token:
        ntotal = ntotal+1
        if last_token == ch:
          ncapital = ncapital + 1
      else:
        if last_token == 'X':
          astr = "%.*d" % (ntotal, int(aval))
          ret_str = ret_str + astr[:ntotal]
        elif last_token == 'D':
          astr = "%.*d" % (2, int(dt.day))
          ret_str = ret_str + astr[:2]
        elif last_token == 'M':
          if ntotal in (1, 2, 3):
            if (ntotal == 1) and (ncapital == 1):
              ret_str = ret_str + prd_list[dt.month]
            elif ntotal in (1, 2):
              astr = "%.*d" % (ntotal, int(dt.month))
              ret_str = ret_str + astr[:ntotal]
            else:
              shmon = shmonths[dt.month]
              if ncapital == ntotal:
                smon = shmon.upper()
              elif ncapital == 0:
                smon = shmon.lower()
              ret_str = ret_str + smon
          else:
            shmon = lnmonths[dt.month]
            if ncapital == ntotal:
              smon = shmon.upper()
            elif ncapital == 0:
              smon = shmon.lower()
            ret_str = ret_str + smon
        elif last_token == 'Y':
          if ntotal <= 2:
            astr = "%.*d" % (2, int(dt.year % 100))
            ret_str = ret_str + astr[:2]
          else:
            astr = "%.*d" % (4, int(dt.year))
            ret_str = ret_str + astr[:4]
        ntotal = ncapital = 0
        calc_total = False
    if not calc_total:
      if token == '[':
          ncount = ncount+1
      elif token == ']':
          ncount = ncount-1
      elif token in ('X', 'D', 'M', 'Y'):
        if ncount > 0:
          calc_total = True
          last_token = token
          ntotal = 1
          if last_token == ch:
            ncapital = ncapital + 1
        else:
          ret_str = ret_str+ch
      else:
        ret_str = ret_str+ch
  return ret_str

def_format = "[xxxx]/INV/EXP/[MM]/[YYYY]"
print(formatNumber(def_format, 8540, dt.date.today()))

</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://jaim.log.web.id/2010/03/trik-menyiasati-kodifikasi-nomor-dokumen-yang-dinamis/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Dealing with Windows Vista weird behaviour</title>
		<link>http://jaim.log.web.id/2009/03/dealing-with-windows-vista-weird-behaviour/</link>
		<comments>http://jaim.log.web.id/2009/03/dealing-with-windows-vista-weird-behaviour/#comments</comments>
		<pubDate>Sat, 28 Mar 2009 08:03:18 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[MMF]]></category>
		<category><![CDATA[Vista]]></category>

		<guid isPermaLink="false">http://jaim.log.web.id/2009/03/dealing-with-windows-vista-weird-behaviour/</guid>
		<description><![CDATA[Yesterday, I was shocked receiving a phone call from my boss reporting the application I wrote were unable to run on Windows Vista. Actually I know there were sort of problems reported on existing applications failed to run on from online developers network, but none that I aware of since we do not have any [...]]]></description>
			<content:encoded><![CDATA[<p>Yesterday, I was shocked receiving a phone call from my boss reporting the application I wrote were unable to run on <a href="http://en.wikipedia.org/wiki/Windows_Vista">Windows Vista</a>. Actually I know there were sort of problems reported on existing applications failed to run on from online developers network, but none that I aware of since we do not have any windows vista ever deployed on our company&#8217;s infrastructure, and it continues for years until yesterday. Now, my boss&#8217;s notebook were installed with Vista and suppose the application could runs on it also.</p>
<p>It is quite hard to start of, mainly because I do not have any windows vista installed on my desktop, nor on my notebook. The only way is trying to analyze the problem from the exception log file he sent to me, then I can find of and start with the main source line where the problem occured:</p>
<pre class="sourcecode"><code class="Delphi">
  InitializeSecurityDescriptor(@Sd,SECURITY_DESCRIPTOR_REVISION);
  SetSecurityDescriptorDacl(@Sd,true,nil,false);
  Sa.nLength := SizeOf(Sa);
  Sa.lpSecurityDescriptor := @Sd;
  Sa.bInheritHandle := true;
  m_Handle := CreateFileMapping(INVALID_HANDLE_VALUE,
                @Sa, PAGE_READWRITE OR SEC_COMMIT,
                0, m_Size, PChar(FileName));

  if m_Handle = 0 then
    raise EMemoryMappedFileException.Create(SCreateError);
</code></pre>
<p>Not knowing what I should do with this line since I can not debug it to know it&#8217;s behaviour on Vista, I try to search on google and did find some workaround to deal with, but none of them proved to works. Does diabling the UAC (<em>User Access Control</em>) or run the application on Administration mode is the last solution? i hope not, I still believe there were another better way to deal with. </p>
<p>Any suggestion are welcome.</p>
]]></content:encoded>
			<wfw:commentRss>http://jaim.log.web.id/2009/03/dealing-with-windows-vista-weird-behaviour/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Delphi: Memanfaatkan LDAP sebagai single-sign-on</title>
		<link>http://jaim.log.web.id/2009/02/delphi-memanfaatkan-ldap-sebagai-single-sign-on/</link>
		<comments>http://jaim.log.web.id/2009/02/delphi-memanfaatkan-ldap-sebagai-single-sign-on/#comments</comments>
		<pubDate>Sat, 28 Feb 2009 04:52:09 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Delphi]]></category>

		<guid isPermaLink="false">http://jaim.log.web.id/2009/02/delphi-memanfaatkan-ldap-sebagai-single-sign-on/</guid>
		<description><![CDATA[Barangkali kita pernah pusing mengalami masalah dengan implementasi system yang terdiri dari beberapa aplikasi dimana tiap-tiap aplikasi memanajemen informasi user secara sendiri-sendiri, pada masa terdahulu dengan menggunakan model Client/Server, kasus yang sama itu sebenarnya bisa diselesaikan dengan memanfaatkan otentikasi RDBMS untuk memfasilitasi kebutuhan penyediaan otorisasi user yang tersentralisir, namun hal ini dapat dikatakan cukup riskan [...]]]></description>
			<content:encoded><![CDATA[<p>Barangkali kita pernah pusing mengalami masalah dengan implementasi system yang terdiri dari beberapa aplikasi dimana tiap-tiap aplikasi memanajemen informasi user secara sendiri-sendiri, pada masa terdahulu dengan menggunakan model Client/Server, kasus yang sama itu sebenarnya bisa diselesaikan dengan memanfaatkan otentikasi RDBMS untuk memfasilitasi kebutuhan penyediaan otorisasi user yang tersentralisir, namun hal ini dapat dikatakan cukup riskan karena membawa resiko security tambahan.</p>
<p>Salah satu solusi yang ditawarkan sebagai pemecahan atas kasus ini adalah dengan memanfaatkan a <a href="http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol">LDAP</a> sebagai penyedia layanan otentikasi pengguna secara tersentralisir. Dengan memanfaatkan LDAP, manajemen user, password, dan informasi-informasi yang terkait lainnya dapat dilakukan secara tersentralisir, aplikasi hanya menyediakan mapping atas role, dan group yang diijinkan untuk mengakses fitur-fitur tertentu dari aplikasi tersebut. Saat ini ada cukup banyak alternatif perangkat lunak server LDAP yang bisa digunakan:</p>
<ul>
<li>Microsoft Actice Directory Service perangkat lunak ini  terintegrasi dengan Windows mulai versi 2000 ke atas.</li>
<li><a href="http://www.openldap.org">OpenLDAP</a> Pemain lama, stabil dan terbukti berperforma baik, hanya sayang dukungan binary untuk OS Windows nampaknya dianaktirikan.</li>
<li><a href="http://www.microsoft.com/windowsserver2003/adam/default.mspx">Microsoft ADAM</a>, perangkat lunak Active Directory yang mandiri, ringan, dan memiliki fitur yang cukup untuk memenuhi kebutuhan secara umum.</li>
<li><a href="http://www.opends.org">OpenDS</a>, salah satu alternatif pilihan server berbasis Java, pada dasarnya saya sangat suka kemudahan yang ditawarkan OpenDS untuk melakukan manajemen pengguna, control panel bawaannya telah memiliki form-form yang sangat memudahkan pengguna tanpa harus dipusingkan terlebih dulu atribut-atribut yang terdapat pada LDAP.</li>
<li><a href="http://directory.apache.org/apacheds/1.5/">Apache Directory</a>, perangkat lunak ini sederhananya adalah yang paling saya sukai, karena fitur yang ditawarkanny, dan terlebih karena memiliki <a href="http://directory.apache.org/studio/">Apache Directory Studio</a>, tool di sisi client yang sangat ampuh dalam menangani kompleksitas manajemen LDAP secara mudah. Hanya saja versi yang terbaru dari ApacheDS (1.5.x) masih terbilang belum stabil, namun kendati demikian untuk operasional sehari-hari dalam lingkup internal network, sejauh ini saya tidak menemui masalah apapun.</li>
</ul>
<p><span id="more-120"></span></p>
<p>Di sisi aplikasi client sendiri, dengan memanfaatkan unit <strong>ldapsend.pas</strong> dari <a href="http://synapse.ararat.cz">synapse</a> sebagai pustaka pihak ke-tiga, kita bisa membuat rutin untuk melakukan verifikasi atas user dan password secara mudah:</p>
<pre class="sourcecode"><code class="Delphi">
function TfrmMain.VerifyUserAuth(const AServer, APort, AUserName, APassword,
  ABaseDN, AUserRDN, AFilter: string; AAtribs, AReturn: TStrings): boolean;
var
  ALDAPSender: TLDAPSend;
  AUserDN: string;
  AResult: boolean;
  I: integer;
begin
  Result := False;
  AReturn.Clear;
  ALDAPSender := TLDAPSend.Create;
  try
    ALDAPSender.TargetHost := AServer;
    ALDAPSender.TargetPort := APort;
    AUserDN := Format('%s=%s,%s',[AUserRDN, AUserName, ABaseDN]);
    ALDAPSender.UserName := AUserDN;
    ALDAPSender.Password := APassword;
    AResult := ALDAPSender.Login;
    if not AResult then
      raise Exception.Create('Could not establish connection to LDAP server.');
    AResult := ALDAPSender.Bind;
    if not AResult then
      raise Exception.Create('Credential information supplied is not valid.');
    AResult := ALDAPSender.Search(AUserDN, False, AFilter, AAtribs);
    if AResult then
    begin
      for I := 0 to ALDAPSender.SearchResult[0].Attributes.Count-1 do
      begin
        AReturn.Values[
          ALDAPSender.SearchResult[0].
            Attributes[i].AttributeName
          ] := ALDAPSender.SearchResult[0].Attributes[i][0];
      end;
    end;
    Result := AResult;
  finally
    ALDAPSender.Free;
  end;
end;
</code></pre>
<p>Contoh pemanfaatan dalam aplikasi:</p>
<pre class="sourcecode"><code class="Delphi">
procedure TfrmMain.btnTestLogin3Click(Sender: TObject);
var
  AAtribs, AReturn: TStrings;
begin
  AAtribs := TStringList.Create;
  AReturn := TStringList.Create;
  try
    AAtribs.Add('displayName');
    AAtribs.Add('mail');
    if VerifyUserAuth('127.0.0.1','389',
                      'jaimy',
                      'password',
                      'ou=users,dc=example,dc=com',
                      'uid',
                      '(objectClass=inetOrgPerson)',
                      AAtribs, AReturn) then
    begin
      ShowMessage(Format('Hallo %s&lt;%s&gt;, anda berhasil login',
        [
          AReturn.Values['displayName'],
          AReturn.Values['mail']
        ]));
    end;
  finally
    AReturn.Free;
    AAtribs.Free;
  end;
end;
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://jaim.log.web.id/2009/02/delphi-memanfaatkan-ldap-sebagai-single-sign-on/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Interfacing Delphi dan Java</title>
		<link>http://jaim.log.web.id/2008/08/interfacing-delphi-dan-java/</link>
		<comments>http://jaim.log.web.id/2008/08/interfacing-delphi-dan-java/#comments</comments>
		<pubDate>Wed, 13 Aug 2008 02:55:47 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://jaim.log.web.id/2008/08/interfacing-delphi-dan-java/</guid>
		<description><![CDATA[Java sebagai bahasa pemrograman aplikasi bisnis enterprise menawarkan begitu banyak ragam alternatif solusi yang beberapa di antaranya sudah mencapai taraf kematangan yang cukup baik terutama untuk kebutuhan aplikasi berbasis multi-tier, sebut saja TopLink yang saat ini menjadi standar persistent API di java, kemudian Spring, Atomikos, dan begitu banyak lainnya yang tidak mungkin disebutkan satu persatu [...]]]></description>
			<content:encoded><![CDATA[<p>Java sebagai bahasa pemrograman aplikasi bisnis enterprise menawarkan begitu banyak ragam alternatif solusi yang beberapa di antaranya sudah mencapai taraf kematangan yang cukup baik terutama untuk kebutuhan aplikasi berbasis multi-tier, sebut saja <a href="http://www.oracle.com/technology/products/ias/toplink/index.html">TopLink</a> yang saat ini menjadi standar persistent API di java, kemudian <a href="http://www.springframework.org/">Spring</a>, <a href="http://www.atomikos.com/">Atomikos</a>, dan begitu banyak lainnya yang tidak mungkin disebutkan satu persatu dalam blog ini. Java juga menarik digunakan sebagai sarana pemasaran karena plethoria-nya yang begitu luas. Sementara itu Delphi, khususnya untuk digunakan sebagai bahasa pemrograman untuk middleware belum memiliki ragam pilihan seperti yang terdapat di Java, namun demikian, karena sifatnya sebagai native code, Delphi memungkinkan kita membuat middleware server yang memiliki performa dan stabilitas yang mengungguli middleware sejenis yang dibuat dengan menggunakan Java. </p>
<p>Ragam solusi yang terdapat di java sangat menarik dan terlalu sayang untuk tidak dimanfaatkan, namun permasalahannya, bagaimana jika kita sudah memiliki sebuah middleware server yang dibangun dengan delphi? Memportingnya ke java jelas bukanlah sebuah solusi yang mudah karena itu bisa berarti menulis ulang keseluruhan sistem dari awal. Solusi yang paling memungkinkan tentunya adalah menginterfacekan middleware server yang dibuat dengan delphi tersebut dengn Java, dengan kata lain meng-embbed java virtual machine ke dalam middleware server Delphi sehingga memungkinkan kita memanfaatkan java dalam sistem yang kita buat dengan menggunakan Delphi.<br />
<span id="more-89"></span><br />
<h3>Bagaimana Caranya?</h3>
<p>Cukup sederhana sebenarnya, komunitas delphi, meskipun tidak sebesar komunitas java, namun tidak pula sekecil yang kita pikirkan. Solusi untuk menginterfacekan Delphi dengan Java dengan memanfaatkan <abbr title="Java Native Interface">JNI</abbr> telah tersedia dan bisa dimanfaatkan untuk kebutuhan kita. <a href="http://www.pacifier.com/~mmead/">Matthew Mead</a> telah membuat translasi <a href="http://www.pacifier.com/~mmead/jni/delphi/index.html">Java Native Interface Wrapper</a> yang memungkinkan Delphi bisa melakukan interfacing dengan kode-kode yang ditulis dengan java. Yang kita butuhkankan hanyalah mendownload wrapper tersebut.<br />
&nbsp;&nbsp;&nbsp;
<ul>
<li><a href="http://www.pacifier.com/~mmead/jni/delphi/files/jni_pas.zip">Download Java Native Interface Wrapper</a></li>
</ul>
<p>Kita perlu sedikit melakukan modifikasi pada file JNI.pas, mulai pada baris 89 dengan kode berikut:</p>
<pre class="sourcecode"><code class="Delphi">
  JNI_VERSION_1_5 = JInt($00010005);
  {$EXTERNALSYM JNI_VERSION_1_5}
  JNI_VERSION_1_6 = JInt($00010006);
  {$EXTERNALSYM JNI_VERSION_1_6}

{$DEFINE DYNAMIC_LINKING}
</code></pre>
<p>Strukturisasi class yang terdapat dalam wrapper tersebut bisa dikatakan cukup baik, kita hanya perlu memastikan hanya ada satu instance class TJavaVM dalam keseluruhan aplikasi kita. Barangkali akan timbul pertanyaan, middleware umumnya berbasis multithread, dan menggunakan single instance seperti itu tentunya akan beresiko tinggi, atau setidaknya menurunkan performa karena harus melakukan locking dan sinkronisasi setiap akan menggunakannya. Kita tidak perlu kuatir untuk hal ini karena kita tidak akan menggunakan object instance dari TJavaVM secara langsung, melainkan untuk tiap-tiap thread kita membuat satu instance dari class TJNIEnv dengan menggunakan TJavaVM sebagai referer. </p>
<h3>Inisialisasi JavaVM</h3>
<p>Mari kita lanjutkan dengan membuat inisialisasi TJavaVM pada class utama aplikasi, bisa main class, atau main server object class, bergantung pada bagaimana struktur class yang terdapat dalam middleware yang dibuat.</p>
<pre class="sourcecode"><code class="Delphi">
type
  TServerManager = class(TAbstractLifeCycle)
  ...
  private
    FJavaVM: TJavaVM;
    procedure InitializeVM(FEngine: TJavaVM);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    property JavaVM: TJavaVM read FJavaVM;
  end;

...
const
  CJVM_Path = 'C:\Java\jdk1.6.0_04\jre\bin\client\jvm.dll';

constructor TServerManager.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ...
  FJavaVM := TJavaVM.Create(JNI_VERSION_1_6, CJVM_Path);
end;

destructor TServerManager.Destroy;
begin
  FJavaVM.Free;
  ...
  inherited Destroy;
end;

procedure TServerManager.InitializeVM(FEngine: TJavaVM);
var
  Options: array [0..4] of JavaVMOption;
  VM_args: JavaVMInitArgs;
  Errcode: Integer;
begin
  Options[0].optionString := '-Djava.class.path=.'; // tambahkan library lain pada classpath jika diperlukan
  VM_args.version := JNI_VERSION_1_6;
  VM_args.options := @Options;
  VM_args.nOptions := 1;
  Errcode := FEngine.LoadVM(VM_args);
  if Errcode &lt; 0 then
    Raise Exception.Create(Format('Error loading JavaVM, error code = %d', [Errcode]));
end;
</code></pre>
<p>Pastikan method InitializeVM dipanggil hanya sekali setelah object class utama itu di-create, namun jangan menggabungkannya pada constructor TServerEngine karena bisa menyebabkan memory leakage jika penanganan atas itu keliru.</p>
<pre class="sourcecode"><code class="Delphi">
begin
  FServerManager := TServerManager.Create(Self)
  FServerManager.InitializeVM(FServerManager.JavaVM);
end;
</code></pre>
<p>Seperti yang telah disebutkan sebelumnya, sebelum kita mulai melakukan invoking method java dari delphi, kita harus membuat terlebih dulu satu buah instance dari class TJNIEnv untuk tiap-tiap process thread dari middleware tersebut dengan menggunakan object instance TJavaVM sebagai referer.</p>
<pre class="sourcecode"><code class="Delphi">
type
  TServerCommandHandler = class(TBaseCommandHandler)
  ...
  private
    FJNIEnv: TJNIEnv;
    FServerManager: TServerManager;

    procedure SetServerManager(AValue: TServerManager);
  public   

    property JNIEnv: TJNIEnv read FJNIEnv;
    property Servermanager: TServerManager read FServerManager write SetServerManager;
  end;

procedure TServerCommandHandler.SetServerManager(AValue: TServerManager);
begin
   if AValue = FServerManager then
      exit;
   if AValue = nil then
   begin
      if Assigned(FJNIEnv) then
          FreeAndNil(FJNIEnv);
   end else
   begin
      if Assigned(FJNIEnv) then
          FreeAndNil(FJNIEnv);
      FJNIEnv := TJNIEnv.Create(AValue.JavaVM.Env);
   end;
   FServerManager := AValue;
end;
</code></pre>
<h3>Invoking Java Method dari Delphi</h3>
<p>Untuk melakukan pemanggilan atas method Java melalui delphi dari process thread tersebut, kita tinggal menggunakan object instance TJNIEnv yang telah dibuat sebelumnya. Implementasi pada bagian ini sangat beragam, dan bisa menjadi sangat kompleks bergantung pada tipikal task yang dikerjakan di sisi middleware yang dibuat, namun contoh yang paling sederhananya adalah sebagai berikut:</p>
<pre class="sourcecode"><code class="Delphi">
var
  Cls: JClass;
  Mid: JMethodID;
  JStr: JString;
begin
  Cls := FJNIEnv.FindClass('FunctionTest');
  if Cls = nil then
    LogAndRaise('Can''t find class: FunctionTest');
  Mid := FJNIEnv.GetStaticMethodID(Cls, 'sayHello', '(Ljava/lang/String;)Ljava/lang/String;');
  if Mid = nil then
    LogAndRaise('Can''t find method: sayHello');

   // Call the static method
  JStr := FJNIEnv.CallStaticObjectMethod(Cls, Mid, ['Hello World']);

  // Convert the returned JString to a Delphi string
  DataMessage.Reply(FJNIEnv.JStringToString(JStr));
end;
</code></pre>
<p>Kode java yang akan dipanggil dalam hal ini adalah sebagai berikut:</p>
<pre class="sourcecode"><code class="Java">
public class FunctionTest{

  public static String sayHello(String inputHello)
  {
    String hello = "Hello World";
    return hello;
  }
}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://jaim.log.web.id/2008/08/interfacing-delphi-dan-java/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Membuat Aplikasi Modular secara Dinamis</title>
		<link>http://jaim.log.web.id/2008/08/membuat-aplikasi-modular-secara-dinamis/</link>
		<comments>http://jaim.log.web.id/2008/08/membuat-aplikasi-modular-secara-dinamis/#comments</comments>
		<pubDate>Fri, 08 Aug 2008 09:18:15 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Tips]]></category>

		<guid isPermaLink="false">http://jaim.log.web.id/2008/08/membuat-aplikasi-modular-secara-dinamis/</guid>
		<description><![CDATA[Aplikasi yang bersifat modular sebenarnya tidaklah asing saat ini, dengan memecah kompleksitas software ke dalam sejumlah modul akan memudahkan developer memaintain lifetime aplikasi yang dibuatnya secara lebih baik, hal ini karena developer dapat lebih memfokuskan permasalahan pada modul-modul yang ada. Delphi secara RAD sebenarnya sudah mempermudah developer untuk menyusun aplikasi ini sebagai modul-modul yang disusun [...]]]></description>
			<content:encoded><![CDATA[<p>Aplikasi yang bersifat modular sebenarnya tidaklah asing saat ini, dengan memecah kompleksitas software ke dalam sejumlah modul akan memudahkan developer memaintain lifetime aplikasi yang dibuatnya secara lebih baik, hal ini karena developer dapat lebih memfokuskan permasalahan pada modul-modul yang ada. Delphi secara <abbr title="Rapid Application Development">RAD</abbr> sebenarnya sudah mempermudah developer untuk menyusun aplikasi ini sebagai modul-modul yang disusun baik dengan memanfaatkan TDataModule ataupun TForm. Namun seringkali pula kita terjebak dalam proses pangil memanggil modul satu dengan modul yang lain yang biasanya dilakukan secara langsung seperti contoh berikut:</p>
<pre class="sourcecode"><code class="Delphi">
  with TForm2.Create(Self) do
  try
    ShowModal;
  finally
    Free;
  end;
</code></pre>
<p>Tidak salah sebenarnya, namun proses panggil memanggil seperti ini menjadikan aplikasi menjadi kompleks, karena modul pemanggil sebagai aplikasi utama harus menyertakan pula unit atas modul-modul yang akan dipanggil. Program berskala yang lebih besar semacam dan kompleks akan melibatkan puluhan, ratusan, atau bahkan ribuan modul di dalamnya, apakah untuk program semacam ini semua modul tersebut harus disertakan? tentunya akan lebih baik bila modul-modul itu dapat dipanggil secara dinamis dan otomatis oleh pemanggil dengan mudah. Kita cukup membuat modulnya, meregistrasikannya, dan recompile. Aplikasi utama akan secara otomatis menampilkan modul baru tersebut. :)</p>
<div align="center"><img style="margin-right: 10px;" title="screenshot" src="http://jaim.log.web.id/wp-content/uploads/2008/08/screenshot.jpg" border="0" alt="Dynamic modular application"  /></div>
<p><span id="more-64"></span><br />
<h3>Bagaimana Caranya?</h3>
<p>Delphi sebagai bahasa yang mendukung <abbr title="Pemrograman Berorientasi Object">OOP</abbr> menyediakan Class Abstraction yang bisa kita manfaatkan untuk membuat modular sistem secara dinamis. Dalam hal ini kita bisa memulainya dengan membuat class Abstract Module yang akan menjadi class dasar yang digunakan oleh aplikasi utama untuk memanggil modul-modul yang terdapat dalam aplikasi secara keseluruhan.</p>
<pre class="sourcecode"><code class="Delphi">
  TAbstractModule = class(TComponent)
  public
    procedure Execute; virtual; abstract;

    class function ModuleName: string; virtual;
    class function ModuleDesc: string; virtual;
  end;
  TAbstractModuleClass = class of TAbstractModule;

...
...

class function TAbstractModule.ModuleDesc: string;
begin
  Result := 'Undefined';
end;

class function TAbstractModule.ModuleName: string;
begin
  Result := 'Undefined';
end;
</code></pre>
<p>Kemudian langkah selanjutnya, setiap modul yang akan dibuat menjadi dinamis harus dibuat sebagai class dan diturunkan dari abstract module di atas.</p>
<pre class="sourcecode"><code class="Delphi">
  TTestModule1 = class(TAbstractModule)
  public
    procedure Execute; override;

    class function ModuleName: string; override;
    class function ModuleDesc: string; override;
  end;

...
...

class function TTestModule1.ModuleDesc: string;
begin
  Result := 'This is a Test Module #1 Description';
end;

class function TTestModule1.ModuleName: string;
begin
  Result := 'Test Module 1';
end;
</code></pre>
<p>Selanjutnya jangan lupa, ini bagian terpentingnya, dibagian paling bawah, registerkan setiap modul class yang dibuat pada class registration punyanya delphi.</p>
<pre class="sourcecode"><code class="Delphi">
initialization
  RegisterClass(TTestModule1);

finalization
  UnRegisterClass(TTestModule1);
</code></pre>
<p>Sampai di sini apa yang harus dilakukan di sisi modulnya telah selesai, kini tinggal di program utama (Main Form). Ada beberapa hal yang harus dilakukan di sini, yaitu meng-construct list module dan membuat generic event yang akan mencari class yang sesuai saat user memilih modul tersebut.</p>
<pre class="sourcecode"><code class="Delphi">
procedure TfrmMain.CollectModules;
var
  AClassFinder: TClassFinder;
begin
  ListBox1.Clear;
  AClassFinder := TClassFinder.Create(TAbstractModule);
  try
    AClassFinder.GetClasses(EvaluateClass);
  finally
    AClassFinder.Free;
  end;
end;

procedure TfrmMain.EvaluateClass(AClass: TPersistentClass);
begin
  if (AClass.InheritsFrom(TAbstractModule)) and (AClass <> TAbstractModule)  then
  begin
    ListBox1.AddItem(Format('%s - %s',
      [
        TAbstractModuleClass(AClass).ModuleName,
        TAbstractModuleClass(AClass).ModuleDesc
      ]),
      TObject(AClass)
    );
  end;
end;
</code></pre>
<p>Nah selesai, dengan trik sederhana ini, kita bisa memanfaatkan fitur-fitur standar yang ada pada delphi untuk membuat aplikasi modular secara dinamis. Jika tertarik dengan demo dan source dari artikel ini, anda bisa mendownloadnya <a href="http://jaim.log.web.id/wp-content/uploads/2008/08/modular-system.zip">di sini</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://jaim.log.web.id/2008/08/membuat-aplikasi-modular-secara-dinamis/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Kode yang lebih singkat dan lebih mudah dibaca</title>
		<link>http://jaim.log.web.id/2008/08/kode-yang-lebih-singkat-dan-lebih-mudah-dibaca/</link>
		<comments>http://jaim.log.web.id/2008/08/kode-yang-lebih-singkat-dan-lebih-mudah-dibaca/#comments</comments>
		<pubDate>Sat, 02 Aug 2008 02:41:46 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://jaim.log.web.id/2008/08/kode-yang-lebih-singkat-dan-lebih-mudah-dibaca/</guid>
		<description><![CDATA[Tertarik dengan cuplikan kode yang ditulis pak budi, saya mencoba membuat implementasi serupa dengan menggunakan python, sebelumnya sebenarnya Waskita Adijarto telah mencoba memportingnya ke dalam bahasa C, dan sepertinya masih cukup panjang, kendati kode yang ditulisnya relatif lebih mudah dibaca. Mari kita jajal, apakah menurut anda python lebih ringkas dengan tetap mempertahankan aspek readability? :)

#! [...]]]></description>
			<content:encoded><![CDATA[<p>Tertarik dengan <a href="http://rahard.wordpress.com/2008/08/01/hacking-perl-code/">cuplikan kode yang ditulis pak budi</a>, saya mencoba membuat implementasi serupa dengan menggunakan python, sebelumnya sebenarnya <a href="http://waskita.wordpress.com/">Waskita Adijarto</a> telah mencoba <a href="http://waskita.wordpress.com/2008/08/01/programming-lebih-santai-lagi/">memportingnya</a> ke dalam bahasa C, dan sepertinya masih cukup panjang, kendati kode yang ditulisnya relatif lebih mudah dibaca. Mari kita jajal, apakah menurut anda python lebih ringkas dengan tetap mempertahankan aspek readability? :)</p>
<pre class="sourcecode"><code class="Python">
#! /usr/bin/python
m = n = 4
printout = lambda x, y: ' %d ' % y if x else ''

for i in range(m*n):
  y = i / 4
  x = i % 4
  print "%d:" % i,
  print printout(y != 0, (y-1) * m + x),
  print printout((x!=(m-1)), y * m + (x + 1)),
  print printout((y!=(n-1)),(y+1) * m + x),
  print printout((x != 0),y * m + (x - 1))

</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://jaim.log.web.id/2008/08/kode-yang-lebih-singkat-dan-lebih-mudah-dibaca/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Tulis ulang DBExpress wrapper for Python</title>
		<link>http://jaim.log.web.id/2008/07/tulis-ulang-dbexpress-wrapper-for-python/</link>
		<comments>http://jaim.log.web.id/2008/07/tulis-ulang-dbexpress-wrapper-for-python/#comments</comments>
		<pubDate>Fri, 25 Jul 2008 19:46:00 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://jaim.log.web.id/2008/07/tulis-ulang-dbexpress-wrapper-for-python/</guid>
		<description><![CDATA[Hari ini saya mencoba revisit ulang driver DBExpress wrapper untuk python yang kompatibel dengan DB-API, driver ini sebenarnya telah saya buat sebelumnya, namun sepertinya saat itu karena masih banyak instability issue atas project middleware server yang saya kerja, saya coba lepasin untuk meminimalisir permasalahan yang mungkin disebabkan karena driver ini.
Namun setelah host midleware server-nya mencapai [...]]]></description>
			<content:encoded><![CDATA[<p>Hari ini saya mencoba revisit ulang driver DBExpress wrapper untuk python yang kompatibel dengan <a href="http://www.python.org/dev/peps/pep-0249/">DB-API</a>, driver ini sebenarnya telah saya buat sebelumnya, namun sepertinya saat itu karena masih banyak instability issue atas project middleware server yang saya kerja, saya coba lepasin untuk meminimalisir permasalahan yang mungkin disebabkan karena driver ini.</p>
<p>Namun setelah host midleware server-nya mencapai tahapan maturity yang cukup baik, sepertinya ini saat yang cukup tepat untuk melakukan revisit, dan sepertinya bisa dikatakan tulis ulang karena kode yang saya buat sebelumnya banyak yang keliru dalam pemanfaatan Python API functionya. Meskipun belum sempat saya test lebih lanjut, kode dasar ini saya postingkan di sini sebagai alternatif pengarsipan.<br />
<span id="more-18"></span></p>
<p>Berikut adalah source code DBExpress Wrapper dalam Delphi:</p>
<pre class="sourcecode"><code class="Delphi">
unit dbxapi;

interface

uses
  SysUtils, Classes, DBCommonTypes, SqlExpr, Variants, ECFPythonEngine,
  ecfpyinf, FMTBcd, DB, DBXCommon;

type
  TECFDbxAccess = class(TComponent)
  private
    FConnection: TSQLConnection;
    FQuery: TCustomSQLDataSet;
  public
    constructor Create( AOwner : TComponent ); override;
    destructor  Destroy; override;

    procedure Execute(const SQL: WideString);

    property Connection: TSQLConnection read FConnection;
    property Query: TCustomSQLDataSet read FQuery;
  end;

  TECFDbxConn = class(TPyObject)
  private
    FSQLConn: TECFDbxAccess;

    function getHeaders: PPyObject;
  public
    constructor Create( APythonType : TPythonType ); override;
    destructor  Destroy; override;

    function _SetDriverName( args : PPyObject ) : PPyObject; cdecl;
    function _SetGetDriverFunc( args : PPyObject ) : PPyObject; cdecl;
    function _SetLibraryName( args : PPyObject ) : PPyObject; cdecl;
    function _SetParams( args : PPyObject ) : PPyObject; cdecl;
    function _VendorLib( args : PPyObject ) : PPyObject; cdecl;
    function _beginTrans( args : PPyObject ) : PPyObject; cdecl;
    function _commitTrans( args : PPyObject ) : PPyObject; cdecl;
    function _rollbackTrans( args : PPyObject ) : PPyObject; cdecl;
    function _query( args : PPyObject ) : PPyObject; cdecl;
    function _fetch_array( args : PPyObject ) : PPyObject; cdecl;
    function _close( args : PPyObject ) : PPyObject; cdecl;

    class procedure RegisterMethods( PythonType : TPythonType ); override;
  end;

  TPythonDbxConn = class( TPythonType)
  public
    constructor Create( AOwner : TComponent ); override;
  end;

  TECFDbxAPI = class(TECFPythonModule)
  private
    FPythonDBXConn: TPythonDbxConn;
  public
    function PyGetConnection( pyself, args : PPyObject ) : PPyObject; cdecl;

    procedure RaiseDBXError(const Msg: string);
  public
    constructor Create( AOwner : TComponent ); override;
    destructor  Destroy; override;

    procedure Initialize; override;
    procedure SetupObjectType;
  end;

  procedure CreateComponents( AOwner : TComponent; APyEngine: TPythonEngine );

implementation
uses
  ActiveX;

type
  TProtCustomSQLDataSet = class(TCustomSQLDataSet);

const
  TYPE_STRING	= 1;
  TYPE_BINARY	= 2;
  TYPE_NUMBER	= 3;
  TYPE_DATETIME	= 4;
  TYPE_DECIMAL	= 5;

var
  DbxAPIModule: TECFDbxAPI;

procedure CreateComponents( AOwner : TComponent; APyEngine: TPythonEngine );
begin
  with TECFDbxAPI.Create(AOwner) do
  begin
    Engine := APyEngine;
    SetupObjectType;
  end;
end;

{ TECFDbxAPI }

constructor TECFDbxAPI.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ModuleName := '_dbxapi';
  Name := 'ECFDbxAPI';
  with DocString do
    begin
      Add( 'This module contains several Object Types that' );
      Add( 'will let you work with the DBExpress and access' );
      Add( 'a database in a python DB-API compatible implementation.' );
      Add( '' );
      Add( 'getdbxconn(drv,getdrvfnc,libnm,vlibnm,params) -&gt; Get dbx connection object' );
    end;
  with Errors.Add do
    Name := 'DBXError';
  DbxAPIModule := Self;
end;

destructor TECFDbxAPI.Destroy;
begin
  inherited Destroy;
end;

procedure TECFDbxAPI.Initialize;
begin
  AddDelphiMethod('getdbxconn',
    PyGetConnection,
    'getdbxconn(drv,getdrvfnc,libnm,vlibnm,params) -- get connection object with driver attached '+
    'on it.');
  inherited Initialize;
end;

function TECFDbxAPI.PyGetConnection(pyself, args: PPyObject): PPyObject;
var
  drv,getdrvfnc,libnm,vlibnm,params:PChar;
  AThreadState: PPyThreadState;
begin
  Result := nil;
  with GetPythonEngine do
  begin
    if Assigned(args) and (PyTuple_Size(args) &gt; 0) and
      (PyArg_ParseTuple( args, 'sssss:', [@drv,@getdrvfnc,@libnm,@vlibnm,@params]) &lt;&gt; 0) then
    begin
      Result := FPythonDBXConn.CreateInstance;
      if (Result &lt;&gt; nil) then
      begin
        with TECFDbxConn(PythonToDelphi(Result)).FSQLConn do
        begin
          if Connection.Connected then
            Connection.Close;
          Connection.DriverName := String(drv);
          Connection.GetDriverFunc := String(getdrvfnc);
          Connection.LibraryName := String(libnm);
          Connection.VendorLib := String(vlibnm);
          Connection.Params.Text := String(params);
          Connection.LoginPrompt := False;
          try
            AThreadState := PyEval_SaveThread;
            try
              Connection.Open;
            finally
              PyEval_RestoreThread(AThreadState);
            end;
          except
            on E:Exception do
              begin
                Py_XDECREF(Result);
                RaiseDBXError(E.Message);
                Result := nil;
              end;
          end;
        end;
      end;
    end else
      RaiseDBXError('Connection could not be established');
  end;
end;

procedure TECFDbxAPI.RaiseDBXError(const Msg: string);
begin
  RaiseError('DBXError', Msg);
end;

procedure TECFDbxAPI.SetupObjectType;
begin
  FPythonDBXConn := TPythonDbxConn.Create(Self);
end;

{ TECFDbxAccess }

constructor TECFDbxAccess.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  CoInitialize(nil);
  FConnection             := TSQLConnection.Create(Self);
  FConnection.LoginPrompt := False;
  FQuery                  := TCustomSQLDataSet.Create(Self);
  FQuery.SQLConnection    := FConnection;
end;

destructor TECFDbxAccess.Destroy;
begin
  FQuery.Free;
  FConnection.Free;
  inherited Destroy;
end;

procedure TECFDbxAccess.Execute(const SQL: WideString);
begin
  FConnection.ConnectionState := csStateExecuting;
  try
    CoInitialize(nil);
    FQuery.Active := False;
    TProtCustomSQLDataSet(FQuery).CommandText := SQL;
    FQuery.Active := True;
  finally
    FConnection.ConnectionState := csStateOpen;
  end;
end;

{ TPythonDbxConn }

constructor TPythonDbxConn.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  if AOwner is TECFPythonModule then
  begin
    Module := TECFPythonModule(AOwner);
    Engine := Module.Engine;
  end;
  Name     := 'typeDbxConn';
  TypeName := 'DBXConn';
  PyObjectClass := TECFDbxConn;
  GenerateCreateFunction := False;
end;

{ TECFDbxConn }

constructor TECFDbxConn.Create(APythonType: TPythonType);
begin
  inherited Create(APythonType);
  FSQLConn := TECFDbxAccess.Create(nil);
end;

destructor TECFDbxConn.Destroy;
begin
  FSQLConn.Free;
  inherited Destroy;
end;

function TECFDbxConn.getHeaders: PPyObject;
var
  APyHeader, APyColumn: PPyObject;
  AColType: integer;
  AColName: string;
  I: Integer;
begin
  with GetPythonEngine do
  begin
    APyHeader := PyTuple_New(FSQLConn.Query.FieldCount);
    if APyHeader = nil then
      CheckError(False);
    for I := 0 to FSQLConn.Query.FieldCount - 1 do
    begin
      AColType := 0;
      case FSQLConn.Query.Fields[i].DataType of
        ftString, ftFixedChar, ftWideString, // 19..24
        ftFixedWideChar, ftWideMemo
            : AColType := TYPE_STRING;
        ftBlob, ftMemo, ftGraphic, ftFmtMemo, // 12..18
        ftBytes, ftVarBytes, ftADT, ftArray, ftTypedBinary
            : AColType := TYPE_BINARY;
        ftSmallint, ftInteger, ftWord, // 0..4
        ftBoolean, ftAutoInc, ftLargeint
            : AColType := TYPE_NUMBER;
        ftDate, ftTime, ftDateTime, // 5..11
        ftTimeStamp
            : AColType := TYPE_DATETIME;
        ftFloat, ftCurrency, ftBCD, ftFMTBcd
            : AColType := TYPE_DECIMAL;
      end;
      AColName := FSQLConn.Query.Fields[i].FieldName;
      APyColumn := PyTuple_New(2);
      if APyColumn = nil then
      begin
        Py_XDECREF(APyHeader);
        CheckError(False);
      end;
      PyTuple_SetItem(APyColumn, 0, PyString_FromString(PChar(AColName)));
      PyTuple_SetItem(APyColumn, 1, PyLong_FromLong(AColType));
      PyTuple_SetItem(APyHeader, I, APyColumn);
    end;
    Result := APyHeader;
  end;
end;

class procedure TECFDbxConn.RegisterMethods(PythonType: TPythonType);
begin
  inherited RegisterMethods(PythonType);
  with PythonType do
  begin
    AddMethod( 'driverName',  @TECFDbxConn._SetDriverName,
      'DBXConn.driverName(drvname) -&gt; None' );
    AddMethod( 'getDriverFunc',  @TECFDbxConn._SetGetDriverFunc,
      'DBXConn.getDriverFunc(getdrvfunc) -&gt; None' );
    AddMethod( 'libraryName',  @TECFDbxConn._SetLibraryName,
      'DBXConn.libraryName(libname) -&gt; None' );
    AddMethod( 'setParam',  @TECFDbxConn._SetParams,
      'DBXConn.setParam(name, value) -&gt; None' );
    AddMethod( 'vendorLib',  @TECFDbxConn._VendorLib,
      'DBXConn.vendorLib(vdrlib) -&gt; None' );
    AddMethod( 'beginTrans',  @TECFDbxConn._beginTrans,
      'DBXConn.beginTrans() -&gt; TransactionID' );
    AddMethod( 'commit',  @TECFDbxConn._commitTrans,
      'DBXConn.commit(TransactionID) -&gt; None' );
    AddMethod( 'rollback',  @TECFDbxConn._rollbackTrans,
      'DBXConn.rollback(TransactionID) -&gt; None' );
    AddMethod( 'query',  @TECFDbxConn._query,
      'DBXConn.query(SQL) -&gt; affected rows' );
    AddMethod( 'fetch_array',  @TECFDbxConn._fetch_array,
      'DBXConn._fetch_array() -&gt; array of result' );
    AddMethod( 'close',  @TECFDbxConn._close,
      'DBXConn.close) -&gt; None' );
  end;
end;

function TECFDbxConn._beginTrans(args: PPyObject): PPyObject;
var
  AThreadState: PPyThreadState;
  ATransactionID: Integer;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    Result := nil;
    if not FSQLConn.Connection.Connected then
      DbxAPIModule.RaiseDBXError('Not connected to the database')
    else
    begin
      try
        AThreadState := PyEval_SaveThread;
        try
          ATransactionID := Integer(FSQLConn.Connection.BeginTransaction);
        finally
          PyEval_RestoreThread(AThreadState);
        end;
        Result := PyLong_FromLong(ATransactionID);
      except
        on E:Exception do
          begin
            DbxAPIModule.RaiseDBXError(E.Message);
          end;
      end;
    end;
  end;
end;

function TECFDbxConn._close(args: PPyObject): PPyObject;
var
  AThreadState: PPyThreadState;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    Result := nil;
    if not FSQLConn.Connection.Connected then
      DbxAPIModule.RaiseDBXError('Not connected to the database')
    else
    begin
      try
        AThreadState := PyEval_SaveThread;
        try
          if FSQLConn.Query.Active then
            FSQLConn.Query.Close;
          if FSQLConn.Connection.Connected then
            FSQLConn.Connection.Close;
        finally
          PyEval_RestoreThread(AThreadState);
        end;
        Result := ReturnNone;
      except
        on E:Exception do
          begin
            DbxAPIModule.RaiseDBXError(E.Message);
          end;
      end;
    end;
  end;
end;

function TECFDbxConn._commitTrans(args: PPyObject): PPyObject;
var
  AThreadState: PPyThreadState;
  ATransactionID: Integer;
  ATrans: TDBXTransaction;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    Result := nil;
    if not FSQLConn.Connection.Connected then
      DbxAPIModule.RaiseDBXError('Not connected to the database')
    else
    begin
      if (PyArg_ParseTuple( args, 'L:DBXConn.commit', [@ATransactionID] ) &lt;&gt; 0) then
      begin
        ATrans := TDBXTransaction(Ptr(ATransactionID));
        if ATrans = nil then
          DbxAPIModule.RaiseDBXError('Invalid Transaction ID')
        else
        begin
          try
            AThreadState := PyEval_SaveThread;
            try
              FSQLConn.Connection.CommitFreeAndNil(ATrans);
            finally
              PyEval_RestoreThread(AThreadState);
            end;
            Result := ReturnNone;
          except
            on E:Exception do
              begin
                DbxAPIModule.RaiseDBXError(E.Message);
              end;
          end;
        end;
      end else
      begin
        DbxAPIModule.RaiseDBXError('TransactionID should be specified');
        Result := nil;
      end;
    end;
  end;
end;

function TECFDbxConn._fetch_array(args: PPyObject): PPyObject;
var
  APyResult, APyHeader, APyRecord, APyRecordLst, APyRecordTuple: PPyObject;
  AThreadState: PPyThreadState;
  ACount: Int64;
  I: integer;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    Result := nil;
    if not FSQLConn.Connection.Connected then
      DbxAPIModule.RaiseDBXError('Not connected to the database')
    else
    begin
      try
        APyResult := PyTuple_New(3);
        if APyResult = nil then
          CheckError(False);
        try
          APyHeader := getHeaders;
          PyTuple_SetItem(APyResult, 0, APyHeader);
        except
          Py_XDECREF(APyResult);
          raise;
        end;
        ACount := 0;
        APyRecordLst := PyList_New(0);
        while not FSQLConn.Query.Eof do
        begin
          APyRecord := PyTuple_New(FSQLConn.Query.FieldCount);
          if APyRecord = nil then
          begin
            Py_XDECREF(APyRecordLst);
            Py_XDECREF(APyResult);
            CheckError(False);
          end;
          for i := 0 to FSQLConn.Query.FieldCount - 1 do
            PyTuple_SetItem(APyRecord, i, VariantAsPyObject(FSQLConn.Query.Fields[i].Value));
          PyList_Append(APyRecordLst, APyRecord);
          Inc(ACount);
          AThreadState := PyEval_SaveThread;
          FSQLConn.Query.Next;
          PyEval_RestoreThread(AThreadState);
        end;
        APyRecordTuple := PyList_AsTuple(APyRecordLst);
        Py_XDECREF(APyRecordLst);
        if APyRecordTuple = nil then
        begin
          Py_XDECREF(APyResult);
          CheckError(False);
        end;
        PyTuple_SetItem(APyResult, 1, PyLong_FromLong(ACount));
        PyTuple_SetItem(APyResult, 2, APyRecordTuple);
        Result := APyResult;
      except
        on E:Exception do
          begin
            DbxAPIModule.RaiseDBXError(E.Message);
          end;
      end;
    end;
  end;
end;

function TECFDbxConn._query(args: PPyObject): PPyObject;
var
  AThreadState: PPyThreadState;
  ASQL: PChar;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    Result := nil;
    if not FSQLConn.Connection.Connected then
      DbxAPIModule.RaiseDBXError('Not connected to the database')
    else
    begin
      if (PyArg_ParseTuple( args, 's:DBXConn.query', [@ASQL] ) &lt;&gt; 0) then
      begin
        try
          AThreadState := PyEval_SaveThread;
          try
            FSQLConn.Execute(String(ASQL));
          finally
            PyEval_RestoreThread(AThreadState);
          end;
          // Result := PyLong_FromLong(TProtCustomSQLDataSet(FSQLConn.Query).RowsAffected);
          Result := PyLong_FromLong(1);
        except
          on E:Exception do
            begin
              DbxAPIModule.RaiseDBXError(E.Message);
            end;
        end;
      end else
      begin
        DbxAPIModule.RaiseDBXError('TransactionID should be specified');
        Result := nil;
      end;
    end;
  end;
end;

function TECFDbxConn._rollbackTrans(args: PPyObject): PPyObject;
var
  AThreadState: PPyThreadState;
  ATransactionID: Integer;
  ATrans: TDBXTransaction;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    Result := nil;
    if not FSQLConn.Connection.Connected then
      DbxAPIModule.RaiseDBXError('Not connected to the database')
    else
    begin
      if (PyArg_ParseTuple( args, 'L:DBXConn.rollback', [@ATransactionID] ) &lt;&gt; 0) then
      begin
        ATrans := TDBXTransaction(Ptr(ATransactionID));
        if ATrans = nil then
          DbxAPIModule.RaiseDBXError('Invalid Transaction ID')
        else
        begin
          try
            AThreadState := PyEval_SaveThread;
            try
              FSQLConn.Connection.RollbackFreeAndNil(ATrans);
            finally
              PyEval_RestoreThread(AThreadState);
            end;
            Result := ReturnNone;
          except
            on E:Exception do
              begin
                DbxAPIModule.RaiseDBXError(E.Message);
              end;
          end;
        end;
      end else
      begin
        DbxAPIModule.RaiseDBXError('TransactionID should be specified');
        Result := nil;
      end;
    end;
  end;
end;

function TECFDbxConn._SetDriverName(args: PPyObject): PPyObject;
var
  AValue : PChar;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    if (PyArg_ParseTuple( args, 's:DBXConn.driverName', [@AValue] ) &lt;&gt; 0) then
    begin
      FSQLConn.Connection.DriverName := String(AValue);
      Result := ReturnNone;
    end else
    begin
      DbxAPIModule.RaiseDBXError('Driver name should be specified');
      Result := nil;
    end;
  end;
end;

function TECFDbxConn._SetGetDriverFunc(args: PPyObject): PPyObject;
var
  AValue : PChar;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    if (PyArg_ParseTuple( args, 's:DBXConn.getDriverFunc', [@AValue] ) &lt;&gt; 0) then
    begin
      FSQLConn.Connection.GetDriverFunc := String(AValue);
      Result := ReturnNone;
    end else
    begin
      DbxAPIModule.RaiseDBXError('GetDriverFunction name should be specified');
      Result := nil;
    end;
  end;
end;

function TECFDbxConn._SetLibraryName(args: PPyObject): PPyObject;
var
  AValue : PChar;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    if (PyArg_ParseTuple( args, 's:DBXConn.libraryName', [@AValue] ) &lt;&gt; 0) then
    begin
      FSQLConn.Connection.LibraryName := String(AValue);
      Result := ReturnNone;
    end else
    begin
      DbxAPIModule.RaiseDBXError('Library name should be specified');
      Result := nil;
    end;
  end;
end;

function TECFDbxConn._SetParams(args: PPyObject): PPyObject;
var
  AKey, AValue : PChar;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    if (PyArg_ParseTuple( args, 'ss:DBXConn.setParam', [@AKey, @AValue] ) &lt;&gt; 0) then
    begin
      FSQLConn.Connection.Params.Add(Format('%s=%s', [String(AKey), String(AValue)]));
      Result := ReturnNone;
    end else
    begin
      DbxAPIModule.RaiseDBXError('Parameter name, and value should be specified');
      Result := nil;
    end;
  end;
end;

function TECFDbxConn._VendorLib(args: PPyObject): PPyObject;
var
  AValue : PChar;
begin
  with GetPythonEngine do
  begin
    Adjust(@Self);
    if (PyArg_ParseTuple( args, 's:DBXConn.vendorLib', [@AValue] ) &lt;&gt; 0) then
    begin
      FSQLConn.Connection.VendorLib := String(AValue);
      Result := ReturnNone;
    end else
    begin
      DbxAPIModule.RaiseDBXError('Vendor Library name should be specified');
      Result := nil;
    end;
  end;
end;

end.
</code></pre>
<p>Sementara Python DBExpress API Wrappernya adalah sebagai berikut:</p>
<pre class="sourcecode"><code class="Python">
__author__ = "Jaimy Azle"
__version__ = '0.0.1'

import _dbxapi
import types
import string
import time
import datetime

# compliant with DB SIG 2.0
apilevel = '2.0'

# module may be shared, but not connections
threadsafety = 1

# this module use extended python format codes
paramstyle = 'pyformat'

class DBAPITypeObject:
  def __init__(self,*values):
    self.values = values
  def __cmp__(self,other):
    if other in self.values:
      return 0
    if other &lt; self.values:
      return 1
    else:
      return -1

STRING = DBAPITypeObject(1)
BINARY = DBAPITypeObject(2)
NUMBER = DBAPITypeObject(3)
DATETIME = DBAPITypeObject(4)
DECIMAL = DBAPITypeObject(5)

class Warning(StandardError):
  pass

class Error(StandardError):
  pass

class InterfaceError(Error):
  pass

class DatabaseError(Error):
  pass

class DataError(DatabaseError):
  pass

class OperationalError(DatabaseError):
  pass

class IntegrityError(DatabaseError):
  pass

class InternalError(DatabaseError):
  pass

class ProgrammingError(DatabaseError):
  pass

class NotSupportedError(DatabaseError):
  pass

### cursor object

class pydbxCursor:

  def __init__(self, src):
    self.__source = src
    self.description = None
    self.rowcount = -1
    self.arraysize = 1
    self._result = []
    self.__fetchpos = 0

  def close(self):
    self.__source = None
    self.description = None
    self.result = []
    self.rowcount = -1

  def execute(self, operation, params = None):
    # "The parameters may also be specified as list of
    # tuples to e.g. insert multiple rows in a single
    # operation, but this kind of usage is depreciated:
    if params and type(params) == types.ListType and \
          type(params[0]) == types.TupleType:
      self.executemany(operation, params)
    else:
      # not a list of tuples
      self.executemany(operation, (params,))

  def executemany(self, operation, param_seq):
    self.description = None
    self.rowcount = -1
    self.__fetchpos = 0

    # first try to execute all queries
    totrows = 0
    sql = ""
    try:
      for params in param_seq:
        if params != None:
          sql = _quoteparams(operation, params)
        else:
          sql = operation
        # print sql
        ret = self.__source.query(sql)
        if ret == 1:
          self._result = self.__source.fetch_array()
          totrows = totrows + self._result[1]
        else:
            self._result = None
            raise DatabaseError, "error: %s" % self.__source.errmsg()
    except:
      raise DatabaseError, "internal error: %s" % self.__source.errmsg()

    # then initialize result raw count and description
    self.description = None
    self.rowcount = self._result[1]

  def fetchone(self):
    ret = self.fetchmany(1)
    if ret: return ret[0]
    else: return None

  def fetchall(self):
    return self._result[2][self.__fetchpos:]

  def fetchmany(self, size = None, keep = 1):
    if size == None:
      size = self.arraysize
    if keep == 1:
      self.arraysize = size
    res = self._result
    if res[1]==self.__fetchpos:
        return []
    reslen = len(res[2][self.__fetchpos:])
    if reslen &lt; size:
        size = res[1]
    ret = res[2][self.__fetchpos:self.__fetchpos+size]
    self.__fetchpos = self.__fetchpos + size
    return ret

  def setinputsizes(self, sizes):
    pass

  def setoutputsize(self, size, col = 0):
    pass

def _quote(x):
  if type(x) == types.StringType:
    x = "'" + string.replace(str(x), "'", "''") + "'"
  elif type(x) in (types.IntType, types.LongType, types.FloatType):
    pass
  elif x is None:
    x = 'NULL'
  # datetime quoting
  # described under "Writing International Transact-SQL Statements" in BOL
  # beware the order: isinstance(x,datetime.date)=True if x is
  # datetime.datetime ! Also round x.microsecond to milliseconds,
  # otherwise we get Msg 241, Level 16, State 1: Syntax error
  elif isinstance(x, datetime.datetime):
    x = "{ts '%04d-%02d-%02d %02d:%02d:%02d.%s'}" % \
      (x.year,x.month, x.day,
      x.hour, x.minute, x.second, x.microsecond / 1000)
  elif isinstance(x, datetime.date):
    x = "{d '%04d-%02d-%02d'}" % (x.year, x.month, x.day)
  # alternative quoting by Luciano Pacheco
  #elif hasattr(x, 'timetuple'):
  # x = time.strftime('\'%Y%m%d %H:%M:%S\'', x.timetuple())
  else:
    #print "didn't like " + x + " " + str(type(x))
    raise InterfaceError, 'do not know how to handle type %s' % type(x)

  return x

def _quoteparams(s, params):
  if hasattr(params, 'has_key'):
    x = {}
    for k, v in params.items():
      x[k] = _quote(v)
    params = x
  else:
    params = tuple(map(_quote, params))
  return s % params

### connection object

class pydbxCnx:

  def __init__(self, cnx):
    self.__cnx = cnx
    self.transid = None
    try:
      transid = self.__cnx.beginTrans()
      self.__cnx.commit(transid)
    except:
      raise OperationalError, "invalid connection."

  def close(self):
    if self.__cnx == None:
      raise OperationalError, "invalid connection."
    self.__cnx.close()
    self.__cnx = None

  def commit(self):
    if self.__cnx == None:
      raise OperationalError, "invalid connection."
    try:
      if self.transid:
        self.__cnx.commit(self.transid)
      self.transid = self.__cnx.beginTrans()
    except:
      raise OperationalError, "can't commit."

  def rollback(self):
    if self.__cnx == None:
      raise OperationalError, "invalid connection."
    try:
      if self.transid:
        self.__cnx.rollback(self.transid)
      self.transid = self.__cnx.beginTrans()
    except:
      raise OperationalError, "can't rollback."

  def cursor(self):
    if self.__cnx == None:
      raise OperationalError, "invalid connection."
    try:
      return pydbxCursor(self.__cnx)
    except:
      raise OperationalError, "invalid connection."

# connects to a database
def connect(dsn = None, drv="", getdrvfnc="", libnm="", vlibnm=""):
  # open the connection
  dbdsn = dsn.strip()
  if dbdsn != '':
    dbdict = dict([x.split('=') for x in dsn.split(';')])
    dbdrv = dbdict.pop('DriverName') if dbdict.has_key('DriverName') else drv
    dbgetdrvfnc = dbdict.pop('GetDriverFunc') if dbdict.has_key('GetDriverFunc') else getdrvfnc
    dblibnm = dbdict.pop('LibraryName') if dbdict.has_key('LibraryName') else libnm
    dbvlibnm = dbdict.pop('VendorLibName') if dbdict.has_key('VendorLibName') else vlibnm
    dbdsn = '\n'.join(['%s=%s' % (key, value) for key, value in dbdict.iteritems()])
  else:
    dbdrv = drv
    dbgetdrvfnc = getdrvfnc
    dblibnm = libnm
    dbvlibnm = vlibnm
  con = _dbxapi.getdbxconn(dbdrv, dbgetdrvfnc, dblibnm, dbvlibnm, dbdsn)
  return pydbxCnx(con)
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://jaim.log.web.id/2008/07/tulis-ulang-dbexpress-wrapper-for-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Jython 2.5 alpha dirilis</title>
		<link>http://jaim.log.web.id/2008/07/jython-25-alpha-dirilis/</link>
		<comments>http://jaim.log.web.id/2008/07/jython-25-alpha-dirilis/#comments</comments>
		<pubDate>Fri, 18 Jul 2008 04:59:35 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://jaim.log.web.id/2008/07/jython-25-alpha-di-rilis/</guid>
		<description><![CDATA[Berita pagi ini juga cukup menarik, kabar terbaru yang saya dapatkan Jython merilis versi alpha atas python language yang kompatible dengan CPython 2.5. Meski masih dalam taraf alpha, namun rilis ini menarik untuk dieksplorasi sejauh mana kompatibilitas bahasa yang ditawarkan Jython yang tentunya pula akan berdampak pada besar kecilnya probabilitas Jython untuk dapat diintegrasikan dengan [...]]]></description>
			<content:encoded><![CDATA[<p>Berita pagi ini juga cukup menarik, kabar terbaru yang saya dapatkan <a href="http://www.jython.org/">Jython</a> merilis versi alpha atas python language yang kompatible dengan CPython 2.5. Meski masih dalam taraf alpha, namun rilis ini menarik untuk dieksplorasi sejauh mana kompatibilitas bahasa yang ditawarkan Jython yang tentunya pula akan berdampak pada besar kecilnya probabilitas Jython untuk dapat diintegrasikan dengan library-library yang menggunakan CPython sebagai basis seperti SQLAlchemy, dll.</p>
<p>Seberapa begitu menarikkah Jython ini dibanding CPython? Perlu diketahui, CPython saat ini memiliki kelemahan jika digunakan dalam mesin-mesin berbasis multi-core karena keberadaan <abbr title="Global Interpreter Lock">GIL</abbr> membuatnya secara internal hanya satu thread yang aktif dieksekusi (meski sebenarnya ada beberapa thread yang berjalan bersamaan), akibatnya tentu saja hanya 1 CPU yang bisa termanfaatkan. Sebenarnya hal ini bisa sedikit banyak diminimalisir dengan memporting kode-kode yang bersifat kritikal ke dalam extenstion module yang dibuat dengan native language, namun besar kecilnya manfaat sangat bergantung pada bagaimana model kerja kode dalam python yang dieksekusi, begitu juga dengan membuat process instance, dan beberapa solusi lainnya. Saya tidak ingin panjang lebar membahas lebih jauh tentang ini, <a href="http://www.artima.com/weblogs/index.jsp?blogger=guido">Guido</a> punya alasan yang kuat dan benar yang mendasari kenapa <abbr title="Global Interpreter Lock">GIL</abbr> tetap harus dipertahankan setidaknya di Python300 nanti.</p>
<p><span id="more-16"></span><br />
Jython dalam hal ini menawarkan fitur yang menjadi keterbatasan CPython saat ini, mendukung multithread dengan memanfaatkan fitur threading dalam <abbr title="Java Virtual Machine">JVM</abbr> dan mentransformasi python code menjadi java byte code sehingga secara logika tingkat performanya lebih kurang setara seperti halnya kode Java aslinya. Namun demikian, tidak berarti Jython berada di atas segala-galanya atas CPython, karena dikembangkan dengan menggunakan Java sebagai basis, otomatis memiliki kelemahan atas kompatibilitas dengan extension module yang banyak menggunakan native language sebagai pendukungnya.</p>
<p>Sejauh ini implementasi yang saya gunakan dengan memanfaatkan CPython sudah cukup memuaskan, skalabilitas implementasi masih bisa dieksplorasi secara lebih jauh seiring dengan masih adanya opsi-opsi yang bisa dipilih terkait dengan isue ini, namun demikian Jython tetap menarik untuk dieksplorasi, sebagai alternatif paling akhir apabila memang tidak ada lagi pilihan untuk itu guna meningkatkan skalabilitas platform system yang ada saat ini.</p>
]]></content:encoded>
			<wfw:commentRss>http://jaim.log.web.id/2008/07/jython-25-alpha-dirilis/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>DB2-Express di python yang menarik namun menyebalkan</title>
		<link>http://jaim.log.web.id/2008/07/db2-express-di-python-yang-menarik-namun-menyebalkan/</link>
		<comments>http://jaim.log.web.id/2008/07/db2-express-di-python-yang-menarik-namun-menyebalkan/#comments</comments>
		<pubDate>Wed, 16 Jul 2008 17:06:36 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[DB2]]></category>

		<guid isPermaLink="false">http://jaim.log.web.id/2008/07/db2-express-yang-menarik-namun-menyebalkan/</guid>
		<description><![CDATA[DB2-Express C merupakan RDBMS versi gratis yang ditawarkan oleh IBM sebenarnya sangat menarik untuk menjadi kandidat pilihan karena RDBMS ini tidak membatasi kapasitas penyimpanan (storage) seperti halnya Oracle-Express, atau SQLServer Express Edition, mendukung pemanfaatan multi-core processor hingga 2 core, dan mendukung pemanfaatan memory hingga 2 Gb membuat database ini lebih dari cukup untuk digunakan di [...]]]></description>
			<content:encoded><![CDATA[<p>DB2-Express C merupakan RDBMS versi gratis yang ditawarkan oleh IBM sebenarnya sangat menarik untuk menjadi kandidat pilihan karena RDBMS ini tidak membatasi kapasitas penyimpanan (<em>storage</em>) seperti halnya Oracle-Express, atau SQLServer Express Edition, mendukung pemanfaatan multi-core processor hingga 2 core, dan mendukung pemanfaatan memory hingga 2 Gb membuat database ini lebih dari cukup untuk digunakan di level produksi perusahaan skala medium enterprise.</p>
<p>Sudah cukup lama sebenarnya saya mengevaluasi RDBMS ini, tertarik dengan fitur-fitur yang ditawarkannya sebagai high-end database system untuk dijadikan alternatif pilihan RDBMS yang disupport dalam platform yang saya kembangkan. Permasalahannya tinggal satu sebenarnya, namun justru itulah yang paling menyebalkan, <a href="http://code.google.com/p/ibm-db/">python wrapper</a> untuk akses database secara remote masih terbilang <em>immature</em>, sangat buggy. Hanya dalam hitungan menit setelah application server utama dijalankan, ia membuat crash system secara keseluruhan, karena memory corrupt yang menyebabkan python garbage collection tidak bisa menjalankan fungsinya secara normal.</p>
<p><span id="more-13"></span></p>
<p>Tertarik dengan itu, saya mencoba menelaah source code yang ada, ada cukup banyak hal yang terasa aneh dalam source code tersebut yang membuat saya mengira-ngira, apakah developernya menggunakan python versi debug-mode saat menulis driver tersebut? source code tersebut banyak sekali menggunakan API function <a href="http://docs.python.org/api/dictObjects.html">PyDict_SetItem</a>, dan <a href="http://docs.python.org/api/dictObjects.html">PyDict_GetItem</a> dan cara menghandle reference count dengan cara berbeda dari yang umum dilakukan, bahkan pada internal CPython source-code-nya sendiri.</p>
<p>Saya mencoba untuk sedikit merubah internal source code driver tersebut, membenarkan bagian-bagian yang saya curigai keliru, recompile dan test, namun hasilnya masih nihil, sepertinya belum mencapai inti permasalahan yang sebenarnya, dan butuh waktu lebih untuk memeriksa alur kerja driver tersebut secara lebih detil. Sayangnya untuk meluangkan waktu secara khusus untuk itu, saat ini masih terbilang mahal, observasi yang saya coba lakukan kemudian beralih kepada kode-kode yang saya tulis, memastikannya bahwa letak permasalahan tersebut bukan di sisi saya, melakukan testing dengan RDBMS lain yang lebih dulu disupport, dan hasilnya server platform saya bisa jalan mulus berhari-hari dari awal hinga akhir ia dishutdown tanpa masalah, simpulan saya memang driver tersebut saat ini masih belum stabil. </p>
<p>Sayang sekali sebenarnya, RDBMS  yang terbilang bagus itu ternyata masih belum bisa masuk dalam alternatif pilihan, mungkin suatu saat nanti saya akan revisit kembali.</p>
]]></content:encoded>
			<wfw:commentRss>http://jaim.log.web.id/2008/07/db2-express-di-python-yang-menarik-namun-menyebalkan/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tiburon Sebentar Lagi</title>
		<link>http://jaim.log.web.id/2008/07/tiburon-sebentar-lagi/</link>
		<comments>http://jaim.log.web.id/2008/07/tiburon-sebentar-lagi/#comments</comments>
		<pubDate>Wed, 16 Jul 2008 08:32:56 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://jaim.log.web.id/2008/07/tiburon-sebentar-lagi/</guid>
		<description><![CDATA[Berita pagi ini cukup menarik, Delphi versi terbaru (tiburon) dikabarkan sebentar lagi akan segera dirilis. Versi baru nanti akan membawa beberapa hal baru di dalamnya. Dukungan atas internal VCL terhadap unicode? tentu saja meski sebenarnya bisa dikatakan terlambat (saat kebanyakan bahasa lain sudah mendukung pemanfaatan unicode secara penuh dalam basic library yang mereka sertakan), namun [...]]]></description>
			<content:encoded><![CDATA[<p>Berita pagi ini cukup menarik, Delphi versi terbaru (tiburon) dikabarkan sebentar lagi akan segera dirilis. Versi baru nanti akan membawa beberapa hal baru di dalamnya. Dukungan atas internal VCL terhadap unicode? tentu saja meski sebenarnya bisa dikatakan terlambat (saat kebanyakan bahasa lain sudah mendukung pemanfaatan unicode secara penuh dalam basic library yang mereka sertakan), namun setidaknya better late than never. :)</p>
<p>Namun tentu saja unicode bukan satu-satunya fitur utama, ada banyak fitur lainnya yang cukup menarik dan sangat bernilai, seperti paralel processing library untuk mensupport multicore CPU, DataSnap framework, arsitektur COM dan ActiveX yang diperbarui, dan banyak hal lainnya lagi. :)</p>
<p>sumber:<br />
    1. <a href="http://blogs.codegear.com/nickhodges/2008/07/15/39066">Here Comes Tiburon</a><br />
    2. <a href="http://blogs.codegear.com/davidi/2008/07/15/38895">Unicode database support in Tiburon for Delphi and C++</a></p>
]]></content:encoded>
			<wfw:commentRss>http://jaim.log.web.id/2008/07/tiburon-sebentar-lagi/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
