ITPub博客

首页 > Linux操作系统 > Linux操作系统 > [转载]拥抱Jini:从Starter Kit 2.0开始(第二部分)

[转载]拥抱Jini:从Starter Kit 2.0开始(第二部分)

原创 Linux操作系统 作者:dinner1007 时间:2019-06-01 15:21:07 0 删除 编辑

拥抱Jini:从Starter Kit 2.0开始(第二部分)


Jini代表着分布式计算技术的深刻革命,其目的是通过对分布式资源的高效处理,将网络逐渐变成一个方便灵活且易于管理的工具, 这样用户或任何其它可计算实体在Jini系统中都能够很方便地发现对其有用的资源,从而完成各种分布式计算。继本文第一部分介绍了Jini的基本概念和实 现原理后,本文第二部分将介绍如何借助Jini Technology Starter Kit v2.0开发基于Jini的软件系统。

怎样开发Jini?

下 面以一个简单的计算器程序为例,讲述如何开发基于Jini的分布式应用。从这个例子可以看出Jini的一些核心思想:如果有人将计算器作为一种服务成功地 加入到Jini网络中,那么所有需要对两个操作数进行加、减、乘、除这类四则运算的硬件设备或者软件组件,就都可以使用这一服务了。或许这个范例本身的功 能还微不足道,但只要稍微类比一下打印服务、购书服务或者订餐服务,就不难体会到Jini的真正强大之处。




回页首


服务接口

Java 语言中的接口(Interface)是实现分布计算的关键,一个类通过共同定义的接口(如计算器接口Calculator),能够调用位于另一台计算机上 的服务。一个实现了Calculator接口的类承诺它的行为是一个计算器,并且清楚如何响应客户端提交的请求。在Jini中,接口可以看成是服务器和客 户端之间的服务合同(contract),因此两者都必须清楚接口的具体细节。下面定义的计算器接口Calculator,无论是服务器还是客户端都需要 用到。

清单8:Calculator.java
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* 计算器服务接口
*/
public interface Calculator extends Remote {
// 加法操作接口
public double add(double firstOperand, double secondOperand)
throws RemoteException;
// 减法操作接口
public double subtract(double firstOperand, double secondOperand)
throws RemoteException;
// 乘法操作接口
public double multiply(double firstOperand, double secondOperand)
throws RemoteException;
// 除法操作接口
public double divide(double firstOperand, double secondOperand)
throws RemoteException;
}

需要注意的是,由于Jini在实现远程计算时所使用的底层机制是RMI,因此Calculator接口必须继承java.rmi.Remote,并且接口中的每个方法都必须抛出java.rmi.RemoteException异常。




回页首


服务代理

在Jini 这一分布式环境中,对服务的访问总是通过一个由服务自身所提供的对象,即服务代理(Proxy)来完成的。当一个客户需要使用某个Jini服务时,服务代 理将被下载到客户端,这样客户就可以像调用其他普通对象一样来调用它了,从而达到使用和控制Jini服务的目的。从某种意义上说,Jini的作用就是协助 客户端从网络上下载Java字节码,并且安全地执行它们。

这种可下载的服务代理的思想,是使得Jini在不显示 安装任何驱动程序或者软件的情况下,仍然能够使用服务或者设备的关键原因。例如,一台打印机负责在Jini网络中公布自己的服务代理,并且保证该服务代理 拥有控制这台打印机的能力,这样凡是需要使用打印机的应用程序就只用下载服务代理,就完全能够使用这台打印机了,而不必关心服务代理是如何实现的,也不必 了解它是如何与设备进行交互的。

具体到计算器服务来说,它的服务代理必须能够完成对两个操作数的加、减、乘、除运算,其完整的代码如清单9所示:

				
清单9:CalculatorProxy.java
import java.io.Serializable;
import java.rmi.RemoteException;
import net.jini.core.lookup.ServiceID;
import net.jini.lookup.ServiceIDListener;
/**
* 计算器服务代理
*/
public class CalculatorProxy
implements Calculator, ServiceIDListener, Serializable {
public CalculatorProxy() throws RemoteException {
super();
}
// 获得服务标识符
public void serviceIDNotify(ServiceID id) {
System.out.println("Service ID is: " + id);
}
// 加法操作
public double add(double firstOperand, double secondOperand)
throws RemoteException {
return firstOperand + secondOperand;
}
// 减法操作
public double subtract(double firstOperand, double secondOperand)
throws RemoteException {
return firstOperand - secondOperand;
}
// 乘法操作
public double multiply(double firstOperand, double secondOperand)
throws RemoteException {
return firstOperand * secondOperand;
}
// 除法操作
public double divide(double firstOperand, double secondOperand)
throws RemoteException {
return firstOperand / secondOperand;
}
}

计 算器的服务代理CalculatorProxy实现了Calculator接口,它给出了加、减、乘、除运算的具体实现。 ServiceIDListener是CalculatorProxy实现的另外一个接口,该接口中的方法serviceIDNotify将在服务成功加 入Jini网络中时被调用,通过它可以获得该服务的唯一标识符,Jini网络中的每个服务都有自己唯一的标识符,查找服务利用它来区分不同的服务。此外, 由于CalculatorProxy对象需要通过网络进行传送,因此CalculatorProxy还必须实现Serializable接口。




回页首


服务发布

当服务接口和服务代理都定义好后,为了使其他硬件设备或者软件组件可以找到并使用该服务,首先需要在Jini网络中发布它,这个过程称为加入。通常的做法是给出一个所谓的"封装"程序(Wrapper),由它来负责定位一个查找服务,并通过查找服务来发布自己的服务代理。

封装程序要做的第一件事就是为这个服务配置一个安全管理器。在运行封装程序时,可以将安全策略文件作为命令行参数传入,但如果用户忘记输入安全策略文件,封装程序应该为他创建一个默认的安全管理器java.rmi.RMISecurityManager:

private void initializeSecurityManager() {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
}

接下去封装程序应该在Jini网络中找到一个查找服务,或者说向Jini体系结构请求一个查找服务。定位查找服务比较简单的做法是使用LookupDiscoveryManager类,它可以用来发现附近网络上的查找服务

LookupDiscoveryManager manager = new
LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS, null, null);

LookupDiscoveryManager能驱动多播和单播发现,它的构造函数接受三个参数

  • 一个字符串数组,指明用来驱动多播发现的组;
  • 一个LookupLocator数组,指明用来驱动单播发现的特定查找服务;
  • 一个实现了DiscoveryListener接口的接收器。

最后,封装程序通过JoinManager将服务代理加入到Jini网络中:

JoinManager joinManager =
new JoinManager(proxy, null, (CalculatorProxy) proxy, manager, null);

清单10是封装程序的完整代码:

				
清单10:CalculatorService.java
import java.io.IOException;
import java.rmi.RMISecurityManager;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lookup.JoinManager;
/**
* Jini计算器服务
*/
public class CalculatorService {
public CalculatorService() throws IOException {
initializeSecurityManager();
initializeJoinManager();
}
// 设置安全管理器
private void initializeSecurityManager() {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
}
// 设置加入管理器
private void initializeJoinManager() {
try {
// 要加入到Jini网络中的服务
CalculatorProxy proxy = new CalculatorProxy();
// 定位Jini查找服务
LookupDiscoveryManager manager =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS, null, null);
// 将服务加入到Jini网络
JoinManager joinManager =
new JoinManager(proxy, null, (CalculatorProxy) proxy, manager, null);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
try {
new CalculatorService();
// 确保Jini服务始终有效
Object keepAlive = new Object();
synchronized (keepAlive) {
keepAlive.wait();
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}




回页首


服务客户

一旦服务通过某个查找服务成功地加入了Jini网络之后,客户就可以访问它了。对Jini客户来讲,它首先要做的一件事也是配置恰当的安全管理器:

private void initializeSecurityManager() {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
}

接下去同样是定位一个查找服务:

private void initializeServiceRegister()
throws MalformedURLException, ClassNotFoundException, IOException {
LookupLocator locator =
new LookupLocator("jini://" + ConfigUtil.getHostName());
serviceRegistrar = locator.getRegistrar();
}

Jini客户使用服务模板 ServiceTemplate来描述自己感兴趣的服务,这是向查找服务提交查询请求的一种常用方法。服务模板的作用就像是数据库中的SQL语句,它是查 找服务进行搜索时的依据。当Jini客户提交一个服务模板之后,查找服务就会搜索它所有已经注册的服务,找到一个与之配置的服务。下面的代码创建了一个根 据服务类型进行搜索的模板:

private void initializeServiceTemplate() {
Class[] types = new Class[] { Calculator.class };
serviceTemplate = new ServiceTemplate(null, types, null);
}

实际的搜索是通过代表一个查找服务的ServiceRegistrar对象上的lookup方法来完成的,该方法连接查找服务并执行搜索,如果找到一个匹配的服务,查找服务就把该服务的服务代理传递给Jini客户:

private void searchServiceItem() throws RemoteException {
serviceItem = (Calculator) serviceRegistrar.lookup(serviceTemplate);
}

Jini客户通过服务代理可以获得该服务提供的所有功能,清单11是客户程序的完整代码:

				
清单11:CalculatorClient.java
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import com.sun.jini.config.ConfigUtil;
import net.jini.core.discovery.LookupLocator;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
/**
* Jini计算器客户
*/
public class CalculatorClient {
private ServiceTemplate serviceTemplate;
private ServiceRegistrar serviceRegistrar;
private Calculator serviceItem;
public CalculatorClient() throws IOException {
try {
initializeSecurityManager();
initializeServiceRegister();
initializeServiceTemplate();
searchServiceItem();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 设置安全管理器
private void initializeSecurityManager() {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
}
// 定位Jini查找服务
private void initializeServiceRegister()
throws MalformedURLException, ClassNotFoundException, IOException {
LookupLocator locator =
new LookupLocator("jini://" + ConfigUtil.getHostName());
serviceRegistrar = locator.getRegistrar();
}
// 设置服务搜索模板
private void initializeServiceTemplate() {
Class[] types = new Class[] { Calculator.class };
serviceTemplate = new ServiceTemplate(null, types, null);
}
// 搜索服务
private void searchServiceItem() throws RemoteException {
serviceItem = (Calculator) serviceRegistrar.lookup(serviceTemplate);
}
public static void main(String[] args) {
try {
CalculatorClient calculator = new CalculatorClient();
CalculatorFrame frame = calculator.new CalculatorFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
} catch (IOException e) {
e.printStackTrace();
}
}
class CalculatorFrame extends JFrame {
public static final int WIDTH = 350;
public static final int HEIGHT = 200;
public static final String TITLE = "Jini Calculator";
private JLabel operand1Label;
private JLabel operand2Label;
private JLabel operatorLabel;
private JLabel resultLabel;
private JTextField operand1TextField;
private JTextField operand2TextField;
private JTextField resultTextField;
private JComboBox operatorComboBox;
private JButton calculateButton;
public CalculatorFrame() {
initializeUI();
}
// 构造用户界面
private void initializeUI() {
setTitle(TITLE);
setSize(WIDTH, HEIGHT);
Container contentPane = getContentPane();
contentPane.setLayout(null);
operand1Label = new JLabel("Operand 1", SwingConstants.CENTER);
operand1Label.setBounds(20, 20, 100, 20);
operand1TextField = new JTextField("", 1);
operand1TextField.setBounds(20, 50, 100, 20);
operatorLabel = new JLabel("Operator", SwingConstants.CENTER);
operatorLabel.setBounds(120, 20, 100, 20);
operatorComboBox = new JComboBox();
operatorComboBox.addItem(" + ");
operatorComboBox.addItem(" - ");
operatorComboBox.addItem(" * ");
operatorComboBox.addItem(" / ");
operatorComboBox.setBounds(145, 50, 50, 20);
operand2Label = new JLabel("Operand 2", SwingConstants.CENTER);
operand2Label.setBounds(220, 20, 100, 20);
operand2TextField = new JTextField("", 1);
operand2TextField.setBounds(220, 50, 100, 20);
calculateButton = new JButton("Calculate");
calculateButton.setBounds(20, 100, 100, 20);
calculateButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
compute();
}
});
resultLabel = new JLabel("Result:", SwingConstants.CENTER);
resultLabel.setBounds(120, 100, 100, 20);
resultTextField = new JTextField("", 1);
resultTextField.setBounds(220, 100, 100, 20);
resultTextField.setEditable(false);
contentPane.add(operand1Label);
contentPane.add(operand1TextField);
contentPane.add(operatorLabel);
contentPane.add(operatorComboBox);
contentPane.add(operand2Label);
contentPane.add(operand2TextField);
contentPane.add(calculateButton);
contentPane.add(resultLabel);
contentPane.add(resultTextField);
}
// 调用Jini服务完成计算
private void compute() {
double operand1, operand2, result;
if (operand1TextField.getText().length() > 0) {
operand1 = Double.parseDouble(operand1TextField.getText());
} else {
operand1 = 0.0;
}
if (operand2TextField.getText().length() > 0) {
operand2 = Double.parseDouble(operand2TextField.getText());
} else {
operand2 = 0.0;
}
try {
switch (operatorComboBox.getSelectedIndex()) {
case 0 :
result = serviceItem.add(operand1, operand2);
resultTextField.setText(String.valueOf(result));
break;
case 1 :
result = serviceItem.subtract(operand1, operand2);
resultTextField.setText(String.valueOf(result));
break;
case 2 :
result = serviceItem.multiply(operand1, operand2);
resultTextField.setText(String.valueOf(result));
break;
case 3 :
result = serviceItem.divide(operand1, operand2);
resultTextField.setText(String.valueOf(result));
break;
}
} catch (RemoteException ex) {
ex.printStackTrace();
}
}
}
}




回页首


编译运行

假设所有源代码都放在d:\jini2_0\calculator\目录下,首先在命令行方面下用javac将它们编译成Java字节码:

D:\jini2_0\calculator> javac  -classpath d:\jini2_0\lib\jini-core.jar;
d:\jini2_0\lib\jini-ext.jar;d:\jini2_0\lib\sun-util.jar *.java

由于CalculatorProxy需要在网络中通过RMI进行传输,因此要用rmic命令为它生成相应的存根代码(stub)和框架代码(skelecton):

D:\jini2_0\calculator> rmic -classpath d:\jini2_0\lib\jini-core.jar;
d:\jini2_0\lib\jini-ext.jar;. CalculatorProxy

接着将编译好的Java字节码打包成calculator.jar,并复制到d:\jini2_0\lib\目录下:

D:\jini2_0\calculator> jar cvf calculator.jar *.class
D:\jini2_0\calculator> copy calculator.jar d:\jini2_0\lib\

如果此时HTTP服务器、RMI激活守护进程和Reggie查找服务都已经正常运行起来了,那么就可以在命令行方式下执行下面的命令来启动Calculator服务:

D:\jini2_0> java -cp lib\jini-core.jar;lib\jini-ext.jar;lib\calculator.jar
-Djava.security.policy=config\all.policy -Djava.rmi.server.
codebase=http://%computername%:8080/calculator.jar
CalculatorService
Service ID is: 45f2798b-bf05-428f-8377-0c620e0916b1

而要启动Calculator客户端程序,则应该使用下面的命令:

D:\jini2_0> java -cp lib\jini-core.jar;lib\jini-ext.jar;
lib\sun-util.jar;lib\calculator.jar
-Djava.security.policy=config\all.policy CalculatorClient

虽然此处为了简化问题,是在同一台计算机上运行Jini的服务端和客户端,但在实际运用时是可以在一台计算机上运行CalculatorService,而在另一台计算机器运行CalculatorClient的。图8是计算器客户端程序运行时的效果:







回页首


小结

Jini 在很大程度上改变了人们对网络的看法,它以Java技术为依托,把网络上的各种硬件设备和软件组件联合成单一的、动态的分布式系统,从而使网络更易于操纵 和管理,并具有更高的可配置性。Jini的目标是将所有可以联网的软硬件资源组织成一个完全自发的网络,使网络中不再需要手工的设备配制、驱动安装等专业 性较强的工作。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/374079/viewspace-130159/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-08-23

  • 博文量
    1714
  • 访问量
    1477771