Logging sederhana di Java

Posted April 10, 2008 by nadamar
Categories: java

Tool-tools development untuk bahasa Java banyak yang menyediakan fitur debugging, yang mana sangat berguna untuk pengembangan. Namun kadang-kadang, fitur debugging itu tidak dapat digunakan seperti semestinya. Hal ini terjadi ketika misalnya kita hendak men-debug plugin. Nadamar tidak mengetahui contoh lainnya, mungkin pembaca bisa memberikan masukkan, hehe.

Namun proses debugging tetap perlu dilakukan. Setelah bingung2 harus bagaimana, akhirnya Nadamar diperkenalkan dengan teknik logging. Logging sebenarnya tidak jauh berbeda dengan misalnya ngeprint suatu variabel dengan nilainya ke layar, hanya saja ini dituliskan ke dalam file. Namun ternyata teknik logging ini bisa lebih dari sekedar itu apabila digunakan dengan benar. Bos Nadamar menunjukkan bagaimana teknik ini dapat digunakan untuk mengetahui letak bug dengan cepat.

Java sendiri telah menyediakan kelas-kelas untuk keperluan logging, namun karena Nadamar kurang paham penggunaannya, Nadamar memutuskan untuk membungkus java.util.logging.* sesuai kebutuhan Nadamar.

import java.io.IOException;
import java.text.*;
import java.util.Calendar;
import java.util.logging.*;

public class SimpleLog {
    private static SimpleLog mSingleton = new SimpleLog();
    private Logger mLogger;

    private SimpleLog() {
        try {
            mLogger = Logger.getLogger("logger");
            mLogger.setUseParentHandlers(false);
            FileHandler tHandler = new FileHandler("log.txt",true);
            tHandler.setFormatter(new SimpleLogFormatter());
            mLogger.addHandler(tHandler);
        } catch(IOException ex) {
            //no comment..
        }
    }

    public Logger getLogger() { return mLogger; }

    public static void write
       (
            Level pLevel, String pMessage,
            String pClassName, String pMethodName,
            Throwable pThrowable
        )
    {
        LogRecord tRecord = new LogRecord(pLevel, pMessage);
        tRecord.setSourceClassName(pClassName);
        tRecord.setSourceMethodName(pMethodName);
        tRecord.setThrown(pThrowable);
        mSingleton.getLogger().log(tRecord);
    }

    public static void main(String [] args) {
        String string = null;
        try{
            string.substring(3, 4);
        }catch(NullPointerException e) {
            SimpleLog.write(Level.SEVERE, "trying to substring variabel string", "SimpleLog", "main", e);
        }
    }
}

class SimpleLogFormatter extends Formatter {

    private void appendDate(StringBuffer sb) {
        SimpleDateFormat t_SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        FieldPosition t_FieldPosition = new FieldPosition(NumberFormat.INTEGER_FIELD);
        t_SimpleDateFormat.format(Calendar.getInstance().getTime(), sb, t_FieldPosition);
    }

    @Override
    public String format(LogRecord record) {
        StringBuffer sb = new StringBuffer();
    	sb.append("[");
    	appendDate(sb);
    	sb.append("]");
    	if (record.getSourceClassName() != null) {
    	    sb.append("[Class: ");
                sb.append( record.getSourceClassName());
    	    sb.append(" ]");
    	}
    	if (record.getSourceMethodName() != null) {
    	    sb.append("[Method: ");
                sb.append(record.getSourceMethodName());
    	    sb.append(" ]");
    	}
        if (record.getMessage() != null) {
            String message = formatMessage(record);
            sb.append(" ");
            sb.append(message);
    	}
    	if (record.getThrown() != null) {
    	    Throwable th = record.getThrown();
    	    sb.append("\n [Exception");
    	    sb.append(" : ");
            sb.append(th.toString());
    	    sb.append("]");
    	    StackTraceElement trace[] = th.getStackTrace();
    	    for (int i = 0; i < trace.length; i++) {
                StackTraceElement frame = trace[i];
                sb.append("\n  [stack trace:");
                sb.append("(");
                sb.append(frame.getClassName());
                sb.append(":");
                sb.append(frame.getMethodName());
                if (frame.getLineNumber() >= 0) {
                    sb.append(":");
                    sb.append(frame.getLineNumber());
                }
    	        sb.append(")]");
    	    }
    	}
        sb.append("\n");
        return sb.toString();
    }
}

panjang juga ya ternyata, hehe. Source diatas bisa langsung dicopy ke file SimpleLog.java, compile dan jalankan. Setelah selesai jalan, di folder yang sama akan muncul file log.txt yang akan berisi:

[2008-04-10 11:53:25][Class: SimpleLog ][Method: main ] trying to substring variabel string
[Exception : java.lang.NullPointerException]
[stack trace:(common.log.SimpleLog:main:56)]

di file tersebut terdapat 2 kelas, yaitu SimpleLog untuk loggingnya, dan SimpleLogFormatter untuk format text log yang akan tertulis. Nadamar sadar mungkin banyak tool-tool canggih diluar sana untuk keperluan logging, ini hanya contoh sederhana. Tapi contoh diatas tersebut, sudah cukup untuk keperluang logging Nadamar, hehe. Semoga bermanfaat.

Scripting di Java dengan LuaJava

Posted February 18, 2008 by nadamar
Categories: java

Sering kali kata Scripting terdengar oleh Nadamar. Karena penasaran, Nadamar mencari artinya di google, dan yang terpanjang adalah:

A high-level programming language that is interpreted by another program at runtime rather than compiled by the computer’s processor as other programming languages (such as C and C++) are. Scripting languages, which can be embedded within HTML, commonly are used to add functionality to a Web page, such as different menu styles or graphic displays or to serve dynamic advertisements. These types of languages are client-side scripting languages, affecting the data that the end user sees in a browser window. Other scripting languages are server-side scripting languages that manipulate the data, usually in a database, on the server. Scripting languages came about largely because of the development of the Internet as a communications tool. JavaScript, ASP, JSP, PHP, Perl, Tcl and Python are examples of scripting languages.

Namun suatu ketika, Nadamar diminta eksplorasi bagaimana java dapat memfasilitasi scripting. Setelah lama eksplorasi, ditemukan LuaJava. Kenapa Lua, bukan JavaScript atau lainnya? Karena Lua sering digunakan untuk Game scripting, dan karena ingin menguasai game programming (ingin, bukan sudah), dipilihlah Lua, biar sekalian, hehe.

Informasi lengkap mengenai gimana-gimananya bisa dilihat di websitenya LuaJava, namun akan diberikan juga sedikit contoh sederhana disini.

Untuk mencobanya, perlu di download dulu luajava-1.1.dll dan luajava-1.1.jar. Taruh pada suatu folder, katakanlah C:\Temp. Pada folder tersebut, buatlah file Script.java yang isinya:

//file: Script.java
import org.keplerproject.luajava.*;
public class Script {
  private int m_Member; 

  public void Script() {
    m_Member = 0;
  }

  public void setMember(int p_Int) {
    m_Member = p_Int;
  }

  public void printMember(String p_Executor) {
    System.out.println(&quot;Executor:&quot; + p_Executor);
    System.out.println(&quot;Member = &quot;+ m_Member);
  }

  public static void main(String [] args) {
    //ciptakan objek Script
    Script t_Script = new Script();

    //init objek untuk memproses script
    LuaState t_LuaState = LuaStateFactory.newLuaState();

    //masukkan object script ke script Environment
    t_LuaState.pushJavaObject(t_Script);
    //namakan object tersebut &quot;ScriptInstance&quot;
    t_LuaState.setGlobal(&quot;ScriptInstance&quot;);

    t_Script.printMember(&quot;Java&quot;);

    //eksekusi script
    t_LuaState.LdoFile(&quot;Hello.lua&quot;);
  }
}
//endfile

Kemudian buat juga file Hello.lua yang isinya:

ScriptInstance:setMember(5)
ScriptInstance:printMember("Lua")

compile dengan: javac -classpath luajava-1.1.jar Script.java ,jalankan dengan: java -cp luajava-1.1.jar;. Script. Apabila berhasil, akan mengeluarkan output:

Executor:Java
Member = 0
Executor:Lua
Member = 5

Main dari Script.java intinya:

  1. menciptakan objek Script
  2. menciptakan objek LuaState yang mengurusi scripting
  3. memasukkan object Script ke dalam script environment dan memberi nama untuk objek tersebut
  4. mengeksekusi script.

Untuk mengakses objek yang telah dimasukkan pada saat scripting, akses dengan nama yang diberikan, dalam contoh diatas, untuk mengakses t_Script, namanya adalah ScriptInstance.

Kegunaan dari LuaJava ini adalah…. entahlah :P , hehe. Mungkin agar program menjadi lebih mudah di modifikasi tanpa harus compile ulang, atau lainnya. Tapi mengutip pidato Steve Job (Founder Apple):

you cant connect the dots looking forward, you can only connect them looking backward

Jadi mungkin saja, suatu saat akan berguna dimasa depan, hehe.

Hanya Contoh Biasa RMI di Java

Posted February 15, 2008 by nadamar
Categories: java

Suatu ketika, Nadamar perlu membuat aplikasi Client-Server. Akhirnya dilakukan dengan memakai socket, karena client dan server tersebut hanya bertukar array of char. Kemudian terlintas dipikiran Nadamar, bagaimana jika Client ingin memanggil langsung method dari suatu object yang ada pada server. Setelah ber-googling cukup lama, Nadamar menemukan RMI, singkatan dari Remote Method Invocation.

Sederhananya, RMI memungkinkan client memanggil method dari suatu object yang hidup di server, ato technically, yang hidup di JVM yang berbeda dengan JVM client. Kata buku-buku, RMI biasanya dipakai untuk , EJB, J2EE dll. Tapi entahlah, bagi Nadamar si, kayaknya ini bisa dipake untuk Webservice. Ngapain susah-susah bikin sendiri kalo banyak yang nyediain? Kadang-kadang, yang tersedia itu sudah canggih, banyak fitur, dll. Ini bagus si, tapi menurut Nadamar, tool harus dipakai sebagaimana mestinya. Analoginya, buat apa naek mobil kalo cuma mau main ke tetangga sebelah rumah? iya ga? he he

Back to subject, untuk nyoba RMI, Nadamar melakukan langkah2 sebagai berikut:

  1. bikin folder C:\server dan C:\client
  2. bikin file IServer.java dan ServerImpl.java di folder server
  3. bikin file Client.java di  folder, copy IServer.java ke folder client
  4. nyalain 2 cmd(command prompt), yang satu mengacu ke C:\server, yang lain mengacu ke C:\client
  5. compile file java di folder server
  6. eksekusi C:\server> rmic -vcompat ServerImpl
  7. eksekusi C:\server> start rmiregistry. Ini akan memunculkan cmd baru
  8. eksekusi C:\server> java ServerImpl. Ini akan nge-hold cmd ini (ga munculin ‘C:\server>’ baru)
  9. copy ServerImpl_Stub.class ke folder client
  10. compile file java di folder client
  11. eksekusi C:\client> java Client

Sedangkan isi file-file tersebut adalah:

//file:IServer.java
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IServer extends Remote {
    public String execute(String p_String) throws RemoteException;
}
//endfile

//file:ServerImpl.java
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class ServerImpl extends UnicastRemoteObject implements IServer {

    public ServerImpl() throws RemoteException{ }
   
    @Override
    public String execute(String p_String) throws RemoteException {
        return "Republik"+p_String;
    }
   
    public static void main(String[] args) {       
        try {           
            IServer t_Server = new ServerImpl();
            Naming.rebind("RMI_test", t_Server);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
//endfile

//file:Client.java
import java.rmi.*;
public class Client {
    public void execute() {
        try{
            IServer service = (IServer) Naming.lookup("rmi://127.0.0.1/RMI_test");
            String s = service.execute(" Indonesia");
            System.out.println(s);
        }catch (java.io.IOException ei) {         
            ei.printStackTrace();
        } catch (NotBoundException eb) { 
            eb.printStackTrace();
        }

    }
    public static void main(String [] args) {
        new Client().execute();
    }
   
}
//endfile

Berhari-hari Nadamar nyoba ini, karena ketemu masalah :( , ya access denied lah, ya classNotFoundException lah, ya Naming not found lah, tapi itu semua rupanya karena ada beberapa JRE di komputer Nadamar dan semuanya diacu, jadi mungkin java-nya bingung, hehe. Kalo Rekan-rekan ketemu masalah, boleh dibagi, mungkin Nadamar bisa bantu, tapi pastinya ga akan sebaik Google, hehe.

Speaking about concept, kalo kata buku HeadFirst Java, beberapa point (kalo mo skip juga gapapa, hehe):

  • Object yang hidup di suatu JVM, normalnya tidak bisa mendapatkan reference ke object di beda JVM
  • RMI membuat seolah olah pemanggilan method dilakukan terhadap object beda JVM, padahal sebenarnya tidak demikian
  • Ketika remote method dipanggil, sebenernya client memanggil method dari representasi si remote object, yang disebut dengan ’stub’.
  • ‘Stub’ ini yang menangani keperluan low-level networking dan berkomunikasi dengan object sebenarnya di server
  • interface untuk remote service (IServer) harus turunan dari java.rmi.Remote, dan semua method throws RemoteException
  • implementasi remote service (ServerImpl) harus turunan dari UnicastRemoteObject. Meski banyak cara lain, tapi ini yang paling sederhana
  • remote service-nya harus di instansiasi, dan objectnya didaftarkan dengan RMI registry
  • mendaftarkannya dengan Naming.rebind(“nama_service”,instance_dari_service);
  • RMI registry harus berjalan pada JVM yang sama dengan remote service-nya
  • Client nyari remote service-nya dengan Naming.lookup(“rmi://Host/nama_service”);

Kalo ada yang salah, mohon diberitahu ya!! semoga bermanfaat.

Java Jar Loading saat Runtime

Posted February 13, 2008 by nadamar
Categories: java

Suatu ketika, nadamar dihadapkan pada persoalan yang mengharuskan program dapat nge-load file jar baru saat runtime. Setelah bersusah payah googling, Nadamar menemukan caranya, ?. Konsepnya kira kira gini: load JAR tersebut dengan java.net.URLClassLoader, kemudian iterasikan untuk me-load class-class yang terdapat di dalamnya.
Code-nya kira-kira seperti ini:

import java.io.*;
import java.lang.Exception;
import java.net.*;
import java.util.jar.*;

public class JarLoader {
  public static void loadJar() throws Exception{
    //acu directory tempat file ini dan jar berada
    File t_File = new File(".");
    if(!t_File.isDirectory()){
       System.out.println("no plugins loaded");      
    } else {
      //ciptakan filter untuk hanya mengambil file jar
      FilenameFilter t_JarFilter = new FilenameFilter() {
        public boolean accept(File dir, String name) {
          return name.endsWith(".jar") ? true : false;
        }};
      //ambil list file-file berekstensi jar
      String [] t_Files = t_File.list(t_JarFilter);    
      for(int i = 0; i < t_Files.length; ++i) {
        //akses jar file
        URL [] t_URL = new URL [] { new URL("file",null,t_Files[i]) };
        URLClassLoader t_UrlClassLoader =
          new URLClassLoader( t_URL );
        JarInputStream t_JarInputStream =
          new JarInputStream(new FileInputStream(t_Files[i]));
        JarEntry t_JarEntry = t_JarInputStream.getNextJarEntry();           
        //iterasikan kelas-kelas yang terdapat pada jar file
        while (t_JarEntry != null) {
          String t_String = t_JarEntry.getName();
          if (t_String.endsWith(".class")) {
            t_String = t_String.substring(0, t_String.length() - 6);
            t_String = t_String.replace('/', '.');
            try {
              System.out.println(t_String +" load");    
              //the loading code
              Class t_Class = t_UrlClassLoader.loadClass(t_String);                  
            } catch (Throwable t_Throwable) {
              System.out.println(" : gagal");              
            }
          }
          t_JarEntry = t_JarInputStream.getNextJarEntry();
        } //end while  
      } //end for
    } //end if
  } //end loadJar()
 
  public static void main(String [] args) {
    try {
      JarLoader.loadJar();
    } catch(Exception ex) {
      ex.printStackTrace();
    } //end try
  } //end main()    
} //end class

Dengan menaruh file tersebut dan JAR yang ingin di load pada path yang sama, maka file JAR akan terload dan dapat digunakan pada kelas-kelas di dalamnya.

Meski telah terload, misalkan kita ingin menggunakannya, program tidak akan lulus compile karena JAR ter-link saat runtime, bukan compile time, sehingga program diatas tidak terlalu berguna, ;P. Namun ceritanya akan lain apabila loading JAR digunakan untuk membuat program yang kita buat mendukung fitur Plugin, yang memungkinkan program dapat ditambahkan fungsionalitasnya tanpa perlu dicompile ulang. Bahkan mungkin saat runtime apabila dirancang dengan baik.

Untuk mengilustrasikannya dengan contoh diatas, kita buat interface Plugin, dengan code sebagai berikut:

interface Plugin {
  public void execute();
}

Kemudian buat sebuah JAR yang didalamnya terdapat kelas yang mengimplementasikan Plugin, misalnya seperti:

public class PluginTes implements Plugin {
  public void execute() {
    System.out.println("Print dari Plugin");
  }
}

Lalu pada kelas JarLoader tambahkan:

public class JarLoader {
  public static Plugin PLUGIN //tambahkan code ini
  ………
  ………
         try {
           System.out.print(t_String +" load ");    
           Class t_Class = t_UrlClassLoader.loadClass(t_String);              
           PLUGIN = (Plugin) t_Class.newInstance(); //tambahkan code ini              
         } catch (Throwable t_Throwable) {
           System.out.println(" : gagal");              
         }
  ……

public static void main(String [] args) {
    try {
      JarLoader.loadJar();
      JarLoader.PLUGIN.execute(); //tambahkan code ini
    }catch(Exception ex) {
      ex.printStackTrace();
    } //end try
  } //end main()    

Apabila di run, nantinya akan ada output  “Print dari Plugin” yang terdapat pada kelas PluginTes. Tergantung dari JAR yang diberikan, kelakuan execute() dapat berbeda-beda. Semoga bermanfaat.