Pernahkah anda dalam suatu kebutuhan menjalan aplikasi lain dari dalam aplikasi Java anda? Misalnya, ketika anda harus membuka aplikasi Microsoft Word dari dalam aplikasi anda? Atau mungkin menjalankan sebuah perintah seperti seolah-olah berada dalam Command Line ?
Java menyediakan sebuah class Runtime
untuk keperluan ini. Runtime adalah class yang memungkinkan terjadinya hubungan/komunikasi antara aplikasi (Kita batasi disini Java) dengan Environment tempat aplikasi tersebut berjalan. Lebih mendetail mengenai teknis dan JavaDoc bisa dilihat disini.
Saya akan memberikan contoh aplikasi yang sangat sederhana, yaitu PING. Anda pasti sudah tahu, atau bahkan mungkin sering menggunakan tool yang satu ini. Saya membuat sebuah aplikasi sederhana, yang bertujuan mendemokan penggunakan Runtime.exec()
milik Java, dan bagaimana berinteraksi dengan instance Process
yang dihasilkan exec()
tersebut. Saya namakan, JPinger.
JPinger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | package lee.gaismamedia; import java.io.*; /** * Created with IntelliJ IDEA. * User: lee ( lee@konglie.web.id ) * Date: 7/10/14 * Time: 12:56 PM */ public class JPinger { // external command which to be executed private final String PING_CMD; private Process pinger; private JPingerListener listener; private Boolean isPinging; public JPinger(JPingerListener l){ String OS = System.getProperty("os.name"); System.out.println(OS); if(OS.toLowerCase().indexOf("win") >= 0){ // -t for ping until stopped // -a for resolve ip to hostname PING_CMD = "ping -t -a"; } else { // on linux, this is the default behaviour PING_CMD = "ping"; } this.listener = l; this.isPinging = false; } private Thread processReader; public void ping(String target){ // only take the first-before-space destination // this actually affect nothing, as we are passing everything to PING String pingCommand = String.format("%s %s", PING_CMD, target.trim().split(" ")[0]); stopPing(); try{ listener.PingOutput("Execute: " + pingCommand + "\n"); // create the Process instance pinger = Runtime.getRuntime().exec(pingCommand); // to make it easier, // we will pipe the InputStream and Error Stream to one single channel // so we will only focus accepting whatever the process are printing out PipedInputStream in = new PipedInputStream(1024 * 1024); PipedOutputStream out = new PipedOutputStream(in); final BufferedReader io = new BufferedReader(new InputStreamReader(in)); // pipe the inputStream new StreamReader(pinger.getInputStream(), out).start(); // pipe the errorStrem new StreamReader(pinger.getErrorStream(), out).start(); // what about the OutputStream ?? // well, in this case, PING does not accept any input from us once it is running // so, we don't care about the OutputStream // read and pass the output, line by line, to our listener processReader = new Thread(new Runnable(){ @Override public void run() { String line; try{ while( (line = io.readLine()) != null && !Thread.interrupted()){ listener.PingOutput(line); } } catch (Exception e){ } } }); processReader.start(); } catch (Exception e){ e.printStackTrace(); } } public void stopPing(){ try { // destroy everything pinger.destroy(); processReader.interrupt(); } catch (Exception e){ return; } } } class StreamReader extends Thread { private InputStream in; private OutputStream out; public StreamReader(InputStream in, OutputStream out) { this.in = in; this.out = out; } public void run() { try { BufferedReader reader = new BufferedReader(new InputStreamReader(in)); PrintStream printStream = new PrintStream(out); String line; // get line from the stream, and write it to the destination pipe. while ( (line = reader.readLine()) != null) { printStream.println(line); } } catch (IOException ioe) { ioe.printStackTrace(); } } } |
Dan untuk memudahkan saya dalam berkomunikasi dengan class JPinger ini, saya membuat sebuah Interface, sederhana saja, yang menjadi tempat bagi JPinger memberikan output dari perintah PING yang dijalankan.
JPingerListener
1 2 3 4 5 6 7 8 9 10 11 | package lee.gaismamedia; /** * Created with IntelliJ IDEA. * User: lee ( lee@konglie.web.id ) * Date: 7/10/14 * Time: 1:02 PM */ public interface JPingerListener { public void PingOutput(String line); } |
Kunci utama dari aplikasi ini adalah di baris
pinger = Runtime.getRuntime().exec(pingCommand);
dimana kita membuat sebuah instance class Process
(ditampung di variable) pinger
.
Sebuah Process
akan memiliki 3 buah Stream, yaitu InputStream
, OutputStream
, dan ErrorStream
( CMIIW ). InputStream adalah stream tempat Process mengeluarkan seluruh output ke StdOut, ErrorStream untuk mengeluarkan Error ke StdErr, dan OutputStream adalah tempat Process menerima input dari StdIn.
Lho, kok terbalik? InputStream ke StdOut dan OutputStream ke StdIn ?
Iya benar. Yang dimaksud Stream disini adalah dari sudut pandang si instance Process yang kita miliki, bukan dari instance command yang berjalan misalnya dalam kasus kita adalah si PING.
Sehingga apabila kita mengeluarkan sesuatu (Output) dari Process, itu akan menjadi masukan (Input) ke PING, dan sebaliknya, masukan (Input) ke Process adalah hasil dari keluaran (Output) dari PING. Are you still with me?
ping out -> process in ping err -> process in ping inTapi dalam kasus ping, memang saya tidak memproses input nya, kenapa? Karena setau saya PING tidak menerima input apapun setelah dijalankan.
So, demo aplikasinya? Download disini.
Sebagai catatan, untuk menghindari penggunaan memori terlalu besar, saya batasi output yang dikeluarkan hanya 100 baris terakhir saja. Dan
core
aplikasi ini adalah menggunakan class JPinger di atas. Selebihnya hanya standar desain Swing saja.Semoga bisa berguna. Enjoy.