拥抱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开始(第二部分)相关推荐

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

    拥抱Jini:从Starter Kit 2.0开始(第一部分) Jini代表着分布式计算技术的深刻革命,其目的是通过对分布式资源的高效处理,将网络逐渐变成一个方便灵活且易于管理的工具, 这样用户或任何 ...

  2. .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的核心运行 ...

  3. ASP.NET 2.0 Club Web Site Starter Kit 补丁

    ASP.NET2.0  Club Web Site Starter Kit 具有一个很大的缺陷:不支持中文. 这里给出两种解决方案供大家参考 方法一: 1)由于大家大部分都是用SQL SERVER20 ...

  4. Starter Kit for ASP.NET 2.0 家族又添新丁!

    名为Small Business Site的新一款Starter Kit发布了,看名字就知道了,这套代码模板是为中小型企业建站提供的,基于此套模板可以个性化设置并生成您需要的网站. 主要功能和页面: ...

  5. 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 ...

  6. VC#2005 Starter Kit: Screen Saver 使用介绍

    如同标题显示的,这只是一篇使用介绍,未涉及源码分析.<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office: ...

  7. ASP.NET Report Starter Kit视频教程--1

    ASP.NET Report Starter Kit视频教程--1   本视频教程以希望出版社出版的<ASP.NET企业级开发案例精解> (http://www.china-pub.com ...

  8. ASP.NET Jumpstart:Media Share Library Starter Kit 简介

    摘要:了解如何使用 Microsoft ASP.NET 和 Microsoft Visual Studio 2005 或 Microsoft Visual Web Developer 2005 Exp ...

  9. MS讲座:可视化的软件架构设计和Portal Starter Kit挖宝记

    昨天下午参加了MS的讲座:可视化的软件架构设计和Portal Starter Kit挖宝记,主讲是广州嘉为的王兴明. 讲座听完了,没有什么特别的感受,"可视化的软件架构设计"讲的是 ...

最新文章

  1. 阿里云 OSS+CDN
  2. 边缘计算 — 与 AI
  3. linux c dup dup2 重定向函数简介
  4. 计算开始到结束的时间_阿里钉钉首次战胜微信,云计算的涨停潮只是开始,远未结束...
  5. 一起谈.NET技术,异步调用与多线程的区别
  6. java自定义错误码类_如何编写和应用Java的自定义异常类
  7. ann2snn的代码分析
  8. React和Vue的模块化
  9. java字体颜色编程_Java 字体颜色转换工具类 ColorUtil
  10. 自定义LinkedList实现
  11. Dev C++项目开发是添加背景音乐 CC++
  12. poj 1838 Banana
  13. excel同时冻结首行和首列怎么操作
  14. 使用wget从google drive下载
  15. GBIT51233-2016装配式木结构建筑技术标准
  16. 关于微信小程序授权登陆之后需要在个人信息页展示信息,如微信头像,昵称这件事
  17. win10自带ie和Edge浏览器无法上网解决方法 第三方浏览器和QQ可以使用
  18. 艾司博讯:拼多多增加自然访客的方法是什么?
  19. 微信小程序app.json全局配置项
  20. stata的固定效应,控制时间和个体的语句

热门文章

  1. js算法数组flat展平的几种方法
  2. leetcode 1419 数青蛙
  3. Plant Simulation之数字孪生
  4. Latex的Visual Studio Code+SumatraPDF环境配置(自用)
  5. 云南省增值税发票综合平台(新网址):https://fpdk.yunnan.chinatax.gov.cn/
  6. Qt中嵌入Directx11
  7. 群晖 Drive 的团队文件夹显示 “无法取得“
  8. 拼团不中返利模式开发(拼团商城返现系统源码设计)
  9. unable to resolve superclass of 解决方法
  10. #入坑keychron#Macbook外置机械键盘的不二之选-keychron