Võ Văn Hải's blog

Chỉ có một điều tuyệt đối đó là mọi thứ đều tương đối…

Triệu gọi ứng dụng RMI trong Applet

Giới thiệu:

Trong rất nhiều mail gửi về hỏi những vấn đề liên quan đến RMI thì thường các bạn rất lúng túng trong việc code và thiết lập security để client có thể truy cập/triệu gọi đến các phương thức từ xa, đặc biệt là trên Applet.

Bởi vì lý do đó nên tôi viết bài này nhằm có thể hướng dẫn các bạn cách thức tạo/thực thi/triệu gọi 1 chương trình sử dụng công nghệ RMI, đặc biệt là trên Applet.

Bài này có liên quan đến java security, tuy nhiên tôi cố tình tránh nói đến nhiều mà chỉ nói cách bạn có thể làm và chạy được còn bạn nào muốn tìm hiểu sâu hơn thì ta có lẽ cần những bài khác.

Việc thiết lập biến môi trường để có thể chạy bằng command-line thì tôi đã giới thiệu ở 1 bài khác. Chạy trên 1 IDE nào đó thì dễ dàng hơn nhiều.

Bài này với nội dung là tạo ra 1 server tính toán và đăng ký trên registry. Khi client triệu gọi đến thì sẽ tính toán trên 4 phép tính cơ bản và trả kết quả về cho client

    1. Cấu trúc tổ chức code:

hinh01_src.png

Trong đó source code của các file như sau:

a. Calculator.java

			package vovanhai.wordpress.com;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Calcalutor extends Remote{
	public double Cong(double a,double b)throws RemoteException;
	public double Tru(double a,double b)throws RemoteException;
	public double Nhan(double a,double b)throws RemoteException;
	public double Chia(double a,double b)throws RemoteException;
}

			

b. CalculatorImpl.java

package vovanhai.wordpress.com.server;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

import vovanhai.wordpress.com.Calcalutor;

public class CalculatorImpl extends UnicastRemoteObject implements Calcalutor{
 public CalculatorImpl()throws RemoteException {
 }

 @Override
 public double Cong(double a, double b) throws RemoteException {
 return a+b;
 }

 @Override
 public double Tru(double a, double b) throws RemoteException {
 return a-b;
 }

 @Override
 public double Nhan(double a, double b) throws RemoteException {
 return a*b;
 }

 @Override
 public double Chia(double a, double b) throws RemoteException {
 return a/b;
 }
}

 

c. CalculatorServer.java

 package vovanhai.wordpress.com.server;

import java.rmi.registry.LocateRegistry;

import javax.naming.Context;
import javax.naming.InitialContext;

import vovanhai.wordpress.com.Calcalutor;

public class CalculatorServer {
 public static void main(String[] args) throws Exception{
 LocateRegistry.createRegistry(1099);
 System.out.println("Server started...");
 Context ctx=new InitialContext();
 Calcalutor cal=new CalculatorImpl();
 ctx.bind("rmi://localhost:1099/calc", cal);
 System.out.println("Object 'cal' bound in registry.");
 }
}

 

d. CalculatorClientGUI.java

 package vovanhai.wordpress.com.client;

import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

import vovanhai.wordpress.com.Calcalutor;

public class CalculatorClientGUI extends JFrame implements ActionListener{
 private JTextField tfA,tfB,tfKQ,tfSvr;
 private JButton btnCong,btnTru,btnNhan,btnChia;
 public CalculatorClientGUI() {
 this.setDefaultCloseOperation(EXIT_ON_CLOSE);
 setSize(290,190);setResizable(false);
 setTitle("Calculator");

 Box b=Box.createVerticalBox();
 Box b0=Box.createHorizontalBox();
 Box b1=Box.createHorizontalBox();
 Box b2=Box.createHorizontalBox();
 Box b3=Box.createHorizontalBox();
 Box b4=Box.createHorizontalBox();
 b.add(Box.createVerticalStrut(10));
 b.add(b0);b.add(Box.createVerticalStrut(10));
 b.add(b1);b.add(Box.createVerticalStrut(10));
 b.add(b2);b.add(Box.createVerticalStrut(10));
 b.add(b3);b.add(Box.createVerticalStrut(10));
 b.add(b4);b.add(Box.createVerticalStrut(10));
 JLabel l1,l2,l3,l4;
 b0.add(l1=new JLabel("Server Add:"));b0.add(tfSvr=new JTextField(20));
 tfSvr.setText("localhost");
 b1.add(l2=new JLabel("Số A:"));b1.add(tfA=new JTextField(20));
 b2.add(l3=new JLabel("Số B:"));b2.add(tfB=new JTextField(20));
 b4.add(l4=new JLabel("Kết quả:"));b4.add(tfKQ=new JTextField(20));
 b3.add(btnCong=new JButton("+"));btnCong.addActionListener(this);
 b3.add(Box.createHorizontalStrut(5));btnCong.setCursor(new Cursor(Cursor.HAND_CURSOR));
 b3.add(btnTru=new JButton("-"));btnTru.addActionListener(this);
 b3.add(Box.createHorizontalStrut(5));btnTru.setCursor(new Cursor(Cursor.HAND_CURSOR));
 b3.add(btnNhan=new JButton("*"));btnNhan.addActionListener(this);
 b3.add(Box.createHorizontalStrut(5));btnNhan.setCursor(new Cursor(Cursor.HAND_CURSOR));
 b3.add(btnChia=new JButton("/"));btnChia.addActionListener(this);
 b3.add(Box.createHorizontalStrut(5));btnChia.setCursor(new Cursor(Cursor.HAND_CURSOR));

 l2.setPreferredSize(l1.getPreferredSize());
 l3.setPreferredSize(l1.getPreferredSize());
 l4.setPreferredSize(l1.getPreferredSize());

 this.add(b);tfKQ.setEditable(false);
 }

 @Override
 public void actionPerformed(ActionEvent e) {

 JButton b=(JButton)e.getSource();
 String op=b.getText();
 String add=tfSvr.getText();
 try {
 double x=Double.parseDouble(tfA.getText());
 double y=Double.parseDouble(tfB.getText());
 double kq=0;

 String url="rmi://"+add+":1099/calc";
 Context ctx=new InitialContext();
 Object o=ctx.lookup(url);
 Calcalutor calc=(Calcalutor)o;
 if(op.equals("+")){
 kq=calc.Cong(x, y);
 }
 else if(op.equals("-")){
 kq=calc.Tru(x, y);
 }
 else if(op.equals("*")){
 kq=calc.Nhan(x, y);
 }
 else if(op.equals("/")){
 kq=calc.Chia(x, y);
 }
 tfKQ.setText(""+kq);
 } catch (NumberFormatException ex) {
 JOptionPane.showMessageDialog(null, "Lỗi chưa nhập liệu, nhập sai hoặc số quá lớn");
 }catch (Exception e2) {
 JOptionPane.showMessageDialog(null, "Lỗi:"+e2.getMessage());
 }
 }

 public static void main(String[] args) {
 new CalculatorClientGUI().setVisible(true);
 }
}

 

e. CalculatorClientApplet

 package vovanhai.wordpress.com.client;

import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.rmi.Naming;
import javax.swing.Box;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;

import vovanhai.wordpress.com.Calcalutor;

public class CalculatorClientApplet extends JApplet implements ActionListener{
 private JTextField tfA,tfB,tfKQ,tfSvr;
 private JButton btnCong,btnTru,btnNhan,btnChia;

 public void init(){
 setSize(290,190);
 Box b=Box.createVerticalBox();Box b0=Box.createHorizontalBox();
 Box b1=Box.createHorizontalBox();Box b2=Box.createHorizontalBox();
 Box b3=Box.createHorizontalBox();Box b4=Box.createHorizontalBox();
 b.add(Box.createVerticalStrut(10));
 b.add(b0);b.add(Box.createVerticalStrut(10));
 b.add(b1);b.add(Box.createVerticalStrut(10));
 b.add(b2);b.add(Box.createVerticalStrut(10));
 b.add(b3);b.add(Box.createVerticalStrut(10));
 b.add(b4);b.add(Box.createVerticalStrut(10));
 JLabel l1,l2,l3,l4;
 b0.add(l1=new JLabel("Server Add:"));b0.add(tfSvr=new JTextField(20));
 tfSvr.setText("localhost");
 b1.add(l2=new JLabel("Số A:"));b1.add(tfA=new JTextField(20));
 b2.add(l3=new JLabel("Số B:"));b2.add(tfB=new JTextField(20));
 b4.add(l4=new JLabel("Kết quả:"));b4.add(tfKQ=new JTextField(20));
 b3.add(btnCong=new JButton("+"));btnCong.addActionListener(this);
 b3.add(Box.createHorizontalStrut(5));btnCong.setCursor(new Cursor(Cursor.HAND_CURSOR));
 b3.add(btnTru=new JButton("-"));btnTru.addActionListener(this);
 b3.add(Box.createHorizontalStrut(5));btnTru.setCursor(new Cursor(Cursor.HAND_CURSOR));
 b3.add(btnNhan=new JButton("*"));btnNhan.addActionListener(this);
 b3.add(Box.createHorizontalStrut(5));btnNhan.setCursor(new Cursor(Cursor.HAND_CURSOR));
 b3.add(btnChia=new JButton("/"));btnChia.addActionListener(this);
 b3.add(Box.createHorizontalStrut(5));btnChia.setCursor(new Cursor(Cursor.HAND_CURSOR));

 l2.setPreferredSize(l1.getPreferredSize());
 l3.setPreferredSize(l1.getPreferredSize());
 l4.setPreferredSize(l1.getPreferredSize());

 this.add(b);tfKQ.setEditable(false);
 }

 @Override
 public void actionPerformed(ActionEvent e) {

 JButton b=(JButton)e.getSource();
 String op=b.getText();
 String add=tfSvr.getText();
 try {
 double x=Double.parseDouble(tfA.getText());
 double y=Double.parseDouble(tfB.getText());
 double kq=0;

 //String polfile=this.getParameter("policyfile");
 //System.setProperty("java.security.policy",polfile);

 String url="rmi://"+add+"/calc";
 Object o=Naming.lookup(url);
 Calcalutor calc=(Calcalutor)o;
 if(op.equals("+")){
 kq=calc.Cong(x, y);
 }
 else if(op.equals("-")){
 kq=calc.Tru(x, y);
 }
 else if(op.equals("*")){
 kq=calc.Nhan(x, y);
 }
 else if(op.equals("/")){
 kq=calc.Chia(x, y);
 }
 tfKQ.setText(""+kq);
 } catch (Exception e2) {
 tfKQ.setText(e2.getMessage());
 }

 }

}
 

2. Biên dịch chương trình

Tạo file build.bat có nội dung:

		rem tao thu muc chua cac files class
		md build
		rem start building...
		javac -d build -encoding "UTF-8" vovanhai/wordpress/com/*.java
		javac -d build -encoding "UTF-8" vovanhai/wordpress/com/server/*.java
		javac -d build -encoding "UTF-8" vovanhai/wordpress/com/client/*.java
		rem finished
		pause
	

Sau khi build ok, ta có kết quả là cây thư mục sau:

hinh02_afterbuilt.png

3. Thực thi chương trình

a. Chạy server

Trong thư mục build ta tạo file runserver.bat để start RMI server. File này có nội dung như sau:

			java vovanhai.wordpress.com.server.CalculatorServer
			pause
		

thực thi nó ta có màn hình sau:

hinh03_startserver.png

b. Chạy GUI client

Trong thư mục build tạo file runClient.bat với nội dung sau:

			java vovanhai.wordpress.com.client.CalculatorClientGUI
		

thực thi nó ta có màn hình sau:

hinh04_runclient.png

Bạn có thể chạy file runClient.bat vài lần để tạo ra vài cửa sổ rồi test thử

c. Chạy Applet Client

Tạo file CalculatorApplet.html có nội dung sau:

				<html>
	<head>
		<title>RMI Applet html page</title>
	</head>
	<body>
		<h3>
			<hr width="100%"/>
			RMI-Applet demo - <a href="https://vovanhai.wordpress.com">https://vovanhai.wordpress.com</a>
			<hr width="100%"/>
		</h3>
		<applet codebase="." code="vovanhai/wordpress/com/client/CalculatorClientApplet.class" width="450" height="180"/>
	</body>
</html>
			

Sau đó ta mở nó bằng trình duyệt (browser: IE,FireFox,Chrome,…) kết quả như sau:

hinh05_appletclient.png

Thử nhập số vào và nhấn 1 nút, bạn sẽ bị lỗi ngay. Lỗi không cho bạn kết nối. Có thể bạn nghĩ là thiếu file policy? Cũng đúng nhưng nếu bạn có file policy và đưa nó vào chương trình thì tôi chắc nó vẫn lỗi.

hinh06_clientError.png

Ta sẽ giải quyết trường hợp này như sau: có 2 cách giải quyết

Cách 1:
Sửa file policy của java: (Cách này mạo hiểm và không an toàn cho hệ thống của bạn – khuyên là không dùng) cách làm như sau:

Mở file JRE\lib\security\java.policy (thường là C:\Program Files\Java\jre6\lib\security\java.policy) lên và thêm dòng sau vào:

				grant{
					permission java.security.AllPermission;
				};
			

Lưu lại rồi sau đó chạy lại chương trình (server/applet) ta sẽ được phép làm việc.

hinh07_clientrunOK.png
Cách 2:

Bạn sign (ký – security) file jar của bạn với 1 chữ ký số (khuyên dùng) cách làm như sau:

i. Đóng gói ứng dụng thành file jar

Tạo file createJARfile.bat với nội dung:

				jar cvf Calculator.jar vovanhai/wordpress/com/*.class vovanhai/wordpress/com/client/*
				

ii. Tạo chữ ký số với công cụ keytool

Lưu ý: nếu bạn đã có thì khỏi chạy bước này và chỉ chạy 1 lần

Mở cửa sổ command-line lên, chuyển tới thư mục của bạn, chạy lệnh

					keytool -genkey -alias VoVanHai -keyalg RSA
				

Lưu ý: password mặc định là changeit

Kết quả như hình

hinh08_runKeyTool.png
Alias bây giờ của bạn là VoVanHai

iii. Sign file jar của bạn với công cụ jarsigner

tạo file signJARfile.bat với nội dung sau, sau đó thực thi nó (passphrase là changeit):

						jarsigner Calculator.jar VoVanHai
					

hinh09_signJARfile.png

Ta có thể verify lại file jar vừa mới sign bằng lệnh trong tập tin có tên verifyJAR.bat

					jarsigner.exe -verify -verbose Calculator.jar
				

hinh10_verifyJAR.png

Tạo file CalculatorSignedApplet.html có nội dung sau:

						<html>
	<head>
		<title>RMI Applet html page</title>
	</head>
	<body>
		<h3>
			<hr width="100%"/>
			RMI-Applet demo - <a href="https://vovanhai.wordpress.com">https://vovanhai.wordpress.com</a>
			<hr width="100%"/>
		</h3>
		<applet archive="Calculator.jar" code="vovanhai/wordpress/com/client/CalculatorClientApplet.class" width="450" height="180"/>
	</body>
</html>

					

Thực thi nó bạn sẽ thấy 1 cảnh báo:

hinh1_warningSecurity.png
Chấp nhận secirity cho chữ ký điện tử này, thực thi => OK

hinh11_appletOK.png
Cấu trúc đầy đủ của chương trình như sau:

hinh12_dirstruct.png


Nếu bạn muốn để nó lên 1 web server thì mọi việc rất đơn giản không gì khác ngoài copy/paste.

Chúc các bạn thành công!

One Response to “Triệu gọi ứng dụng RMI trong Applet”

  1. Phạm Văn Sỹ said

    Thầy cho em hỏi có cách nào để lấy được Mac Address của máy tính khách hàng thông qua Applets không? Em cảm ơn thày nhiều.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: