本项目主要是在windows平台上基于eclipse的开发学习。对于需要安装和配置的工具,以下分别作出说明。
JDK版本:1.7.0_21或任何与之兼容的版本。
安装路径:默认安装在“C:\ProgramFiles\Java\”下,本文修改安装路径为“D:\Java\jdk1.7.0_21”。
系统环境设置:
l 选“我的电脑(MyComputer)”,鼠标右键选“属性(Properties)”,在“高级(Advanced)的下方可找到环境变量(Environment Variables)。
l 在系统环境变量PATH里添加JDK的bin目录路径如“D:\Java\jdk1.7.0_21\bin;”以便在任意路径启用JAVA相关命令。
l 在系统环境变量里新加一个变量名JAVA_HOME,变量值为JDK安装路径如 “D:\Java\jdk1.7.0_21。
图1 系统环境变量里Path路径添加
J2EE是Java 2平台企业版,用于企业级应用开发。后续版本为Java EE (Enterprise Edition)。
Java EE版本:6以及任何兼容版本,如Glassfish 3。
安装路径:默认路径如C盘的根目录下,建议路径名不带空格。
Glassfish自身带有Java EE应用服务器,但我们主要是利用其包括的一些Jar包文件。
Eclipse为项目工程的集成开发环境。
Eclipse版本:JEE-Indigo-SR2或任何兼容版本。
安装路径:直接解压到指定目录,建议路径名不带空格。
安装:
官网:http://www.zeroc.com
下载:Windows下安装,到http://www.zeroc.com/download.html下载Windows Installers下的Ice-3.5.0-1.msi。
安装:点击下一步安装即可。(会让选择安装目录和demo目录。这里我们的安装目录安装在D:\ZeroC\Ice-3.5.0;demo目录安装在E:\ZeroC\Ice-3.5.0-demos);
环境配置:需要Jdk5或6,配置
classpath (D:\ZeroC\Ice-3.5.0\lib;.;D:\Java\jdk1.7.0_21\lib\dt.jar;D:\Java\jdk1.7.0_21\lib\tools.jar;D:\ZeroC\Ice-3.5.0\lib\Ice.jar;D:\ZeroC\Ice-3.5.0\lib\db.jar; D:\ZeroC\Ice-3.5.0\lib\Freeze.jar;)
path(;D:\ZeroC\Ice-3.5.0\bin)
测试demo:
Cmd打开 cd进入E:\ZeroC\Ice-3.5.0-demos\demoj\Ice\hello
执行:Slice2java hello.ice(生成Demo文件夹,里面都是生成的*.java文件)
执行:javac Server.java
执行:java Server
新开cmd窗口,cd进入E:\ZeroC\Ice-3.5.0-demos\demoj\Ice\hello
执行:javac Client.java
执行:java Client
(会发现Demo文件夹下生成了对应的class文件)
结果显示:
图2 测试demo(1)
表示成功。
也可以在命令行输入slice2java –v,会出现ice的版本号,如图:
图3 测试demo(2)
前面已经将Ice-3.5.0-1.msi安装到D:\ZeroC\Ice-3.5.0。官方下载地址:http://www.zeroc.com/download.html。
图4 Ice-3.5.0-1.msi下载界面
安装Slice2Java插件
图5 安装Slice2Java插件(1)
安装方法:打开Eclipse----->help-install new software-work with... ,右侧add按钮,点击add -Add Site -输入http://www.zeroc.com/download/eclipse/添加好即可。
图6 安装Slice2Java插件(2)
注:本文选的是Slice2Java Plug-in 3.4.2 for Ice 3.4.2,因为我装的Ice 是3.5.0,所以开始选的是Slice2Java Plug-in 3.4.2 for Ice 3.5.0。但是在之后的Slice2Java Builder时报错,改为Slice2Java Plug-in 3.4.2 for Ice 3.4.2后成功building。
安装成功后,点击Eclipse的Window——preferences,如下图:
图7 安装Slice2Java插件(3)
上图表示安装成功,如果指定的路径不对,则显示如下:
图8 安装Slice2Java插件(4)
此时,修改一下指定的路径即可。
建立slice文件夹,在其目录下建立:Printer.ice,内容如下:
图9 建立Java工程:testIce
右键点击工程“testIce”——Slice2Java——AddSlice2Java builder,自动生成generated文件夹下的java文件:
图10 使用Slice2Java插件
在完成这一步后,如果eclipse里面generated文件夹下的.java文件好多出现了“”,就是说好多文件出现错误,可能是JDK的版本不同导致的报错。解决办法:在项目名上右键,选中properties,在弹出的对话框中选择Java compiler 在下拉列表中选择1.5,问题就解决了。
编写作为servant的PrinterI类
图11 PrinterI类
编写应用文件PrinterI,该文件是你要作为接口方法的定义,必须继承_PrinterDisp.这个_PrinterDisp文件是在我们编写完Printer.ice后通过AddSlice2Java builder生成的,包含的是服务器端骨架类的定义,所用接口定义都要继承这个东西,这里的接口指供客户端调用的接口。
但是,什么是servant?Ice 对象(servant)是一种具有类型、标识,以及寻址信息的概念性实体。在服务器端提供操作调用的行为的制品叫作servant。一个servant 提供一个或多个Ice 对象的实质内容实际上,servant 就是服务器开发者编写的类的实例,这些类作为一个或多个Ice 对象的servant、向服务器端runtime 进行注册。类的方法对应于Ice 对象的接口上的操作,并且提供这些操作的行为。在这里,我们服务器提供的服务就是
System.out.println("server接收到client 传入数据:"+ s);
System.out.println("server返回给client处理结果:" + compute(s));
即:打印客户发过来的数据,并打印要返回给客户端的数据(注意,这里只打印数据,实际上没有返回客户端)。
图12 server类
代码执行的步骤:
1)通过调用Ice.initialize()初始化Ice run time状态,传递args参数是因为服务器要接受一些命令行参数,调用initialize返回的结果是一个Ice::Communicator的引用。这是Ice run time 的主句柄。
2)我们调用Communicator实例上的createObjectAdapterWithEndpoints,创建一个对象适配器,传入的参数是“SimplePrinterAdapter"(适配器的名字)和“default -p 1888",后者是要适配器用缺省协议(Tcp/ip)在端口1888处侦听进来的请求。
3)第三步,服务器端的run time已经初始化了,我们实例化一个PrinterI对象,为我们的Printer接口创建一个子类,这个类包含着服务器的行为。
4)通过调用适配器的add方法,通知适配器有了一个新的子类(servant)存在,传入add的参数是我们刚刚实例化的子类(servant),再加上个标识符。在这里,“SimplePrinter"是servant的名字(如果我们有多个打印机,每个打印机都可以有不同的名字,更正确的说法是,都有不同的对象标识)。
5)接下来,我们调用适配器的activate方法来激活适配器(适配器最初是在holding 状态下创建的,如果我们有多个子类(servant),它们共享一个适配器,而在所有servant实例化之前我们又不想处理请求的话,这是很有用的)。
6)最后,我们调用waitForShutdown。这个方法挂起发出调用请求的线程,直到服务器实现终止。或者通过发出一个调用关闭run time,或者是对某个信号作出响应。(现在,当我们不再需要服务器是,我们会简单地在命令行上中断它)。
图13 client客户端
代码执行顺序如下:
1)和在服务器中一样,我们调用Ice::initialize初始化Ice run time。
2)下一步是取得远程打印机的代理。我们通过在communicator上调用stringToProxy创建一个代理,它带有一个字符串参数“SimplePrinter:default -h 127.0.0.1 -p 1888”,这个字符串里包含了服务器中用到的对象标识符、服务器IP地址和端口号。
3)stringToProxy返回的代理类型时Ice::ObjectPrx,这种类型是接口和类的继承树上的根节点。但是说到我们实际用的printer,我们要的是Demo::Print,而不是一个Object接口的代理。为此,我们要调用Demo.PrinterPrx.checkedCast进行向下转换,这个checked方法会发送一条消息给服务器,并询问:“这是一个Demo::Printer接口的代理吗?”,如果是,这个调用会返回一个Demo.PrintPrx类型的代理;否则,如果代理表示是其他的类型,这个调用会返回None。
4)我们测试向下转型是否成功,如果不成功,就抛出错误消息,中止。
5)好了,现在我们在我们的地址空间里有个活动的代理,可以调用printString方法,把"client->server计算请求!"字符串传递给服务器,服务器就会在终端上打印这个字符串。
在一台机器上运行Server,另一台运行Client,在运行Server的机器上输出:
server接收到client 传入数据:client->server计算请求!
server返回给client处理结果:<server_to_client>client->server计算请求!</server_to_client>
如果客户端不断地请求,则服务端不断地处理来自客户端的请求,并把计算结果返回给客户端。客户端只是调用接口函数,并不知道是哪个计算节点提供的计算服务