[转载]拥抱Jini:从Starter Kit 2.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.javaimport 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.javaimport 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.javaimport 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.javaimport 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 *.classD:\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.jarCalculatorServiceService 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是计算器客户端程序运行时的效果:
图8 基于Jini的计算器
|
|
小结
Jini 在很大程度上改变了人们对网络的看法,它以Java技术为依托,把网络上的各种硬件设备和软件组件联合成单一的、动态的分布式系统,从而使网络更易于操纵 和管理,并具有更高的可配置性。Jini的目标是将所有可以联网的软硬件资源组织成一个完全自发的网络,使网络中不再需要手工的设备配制、驱动安装等专业 性较强的工作。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-130159/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/374079/viewspace-130159/
[转载]拥抱Jini:从Starter Kit 2.0开始(第二部分)相关推荐
- [转载]拥抱Jini:从Starter Kit 2.0开始(第一部分)
拥抱Jini:从Starter Kit 2.0开始(第一部分) Jini代表着分布式计算技术的深刻革命,其目的是通过对分布式资源的高效处理,将网络逐渐变成一个方便灵活且易于管理的工具, 这样用户或任何 ...
- .NET 4.0 的Web Form和EF的例子 Employee Info Starter Kit (v4.0.0)
ASP.NET 4.0改进了许多不同的场景集(set of scenarios),如Webforms ,Dynamic Data以及基于AJAX的Web开发.此外还有许多对支撑ASP.NET的核心运行 ...
- ASP.NET 2.0 Club Web Site Starter Kit 补丁
ASP.NET2.0 Club Web Site Starter Kit 具有一个很大的缺陷:不支持中文. 这里给出两种解决方案供大家参考 方法一: 1)由于大家大部分都是用SQL SERVER20 ...
- Starter Kit for ASP.NET 2.0 家族又添新丁!
名为Small Business Site的新一款Starter Kit发布了,看名字就知道了,这套代码模板是为中小型企业建站提供的,基于此套模板可以个性化设置并生成您需要的网站. 主要功能和页面: ...
- Unity Game Starter Kit for Windows Store and Windows Phone Store games
原地址:http://digitalerr0r.wordpress.com/2013/09/30/unity-game-starter-kit-for-windows-store-and-window ...
- VC#2005 Starter Kit: Screen Saver 使用介绍
如同标题显示的,这只是一篇使用介绍,未涉及源码分析.<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office: ...
- ASP.NET Report Starter Kit视频教程--1
ASP.NET Report Starter Kit视频教程--1 本视频教程以希望出版社出版的<ASP.NET企业级开发案例精解> (http://www.china-pub.com ...
- ASP.NET Jumpstart:Media Share Library Starter Kit 简介
摘要:了解如何使用 Microsoft ASP.NET 和 Microsoft Visual Studio 2005 或 Microsoft Visual Web Developer 2005 Exp ...
- MS讲座:可视化的软件架构设计和Portal Starter Kit挖宝记
昨天下午参加了MS的讲座:可视化的软件架构设计和Portal Starter Kit挖宝记,主讲是广州嘉为的王兴明. 讲座听完了,没有什么特别的感受,"可视化的软件架构设计"讲的是 ...
最新文章
- 阿里云 OSS+CDN
- 边缘计算 — 与 AI
- linux c dup dup2 重定向函数简介
- 计算开始到结束的时间_阿里钉钉首次战胜微信,云计算的涨停潮只是开始,远未结束...
- 一起谈.NET技术,异步调用与多线程的区别
- java自定义错误码类_如何编写和应用Java的自定义异常类
- ann2snn的代码分析
- React和Vue的模块化
- java字体颜色编程_Java 字体颜色转换工具类 ColorUtil
- 自定义LinkedList实现
- Dev C++项目开发是添加背景音乐 CC++
- poj 1838 Banana
- excel同时冻结首行和首列怎么操作
- 使用wget从google drive下载
- GBIT51233-2016装配式木结构建筑技术标准
- 关于微信小程序授权登陆之后需要在个人信息页展示信息,如微信头像,昵称这件事
- win10自带ie和Edge浏览器无法上网解决方法 第三方浏览器和QQ可以使用
- 艾司博讯:拼多多增加自然访客的方法是什么?
- 微信小程序app.json全局配置项
- stata的固定效应,控制时间和个体的语句
热门文章
- js算法数组flat展平的几种方法
- leetcode 1419 数青蛙
- Plant Simulation之数字孪生
- Latex的Visual Studio Code+SumatraPDF环境配置(自用)
- 云南省增值税发票综合平台(新网址):https://fpdk.yunnan.chinatax.gov.cn/
- Qt中嵌入Directx11
- 群晖 Drive 的团队文件夹显示 “无法取得“
- 拼团不中返利模式开发(拼团商城返现系统源码设计)
- unable to resolve superclass of 解决方法
- #入坑keychron#Macbook外置机械键盘的不二之选-keychron