本文主要介绍两个案例,第一个是使用Hessian来实现远程过程调用,第二个是通过Hessian提供的二进制RPC协议进行和Servlet进行数据交互,Hessian本身即是基于Http的RPC实现。
案例一:
1.准备工作
这里建立一个Maven项目,其中包含四个模块,父模块(仅用来聚合其它模块,不做实际使用),服务器端模块,客户端模块,API模块(远程过程接口,供服务器和客户端使用)。目录结构见下图:
2.添加Hessian的依赖
由于客户端和服务器都要依赖Hessian的包,这里可以添加到父模块的pom.xml中去。
|
1
2
3
4
5
|
<
dependency
>
<
groupId
>com.caucho</
groupId
>
<
artifactId
>hessian</
artifactId
>
<
version
>4.0.38</
version
>
</
dependency
>
|
关于其它的具体依赖配置,这里不做多余展示,本文末尾附有完整的下载地址。
3.在hessian-api模块定义过程接口
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package
secondriver.hessian.api;
import
java.io.InputStream;
import
java.util.List;
import
secondriver.hessian.api.bean.Person;
public
interface
HelloHessian {
public
String sayHello();
public
String sayHello(String name);
public
List<Person> getPersons();
public
Person getPersonById(
int
id);
public
boolean
uploadFile(String fileName, InputStream data);
public
byte
[] downloadFile(String fileName);
}
|
上面的接口中定义的六个方法,有重载方法,有获取集合,有获取对象,有上传文件,下载文件等。需要注意的是在Hessian中定义过程接口的方法时输入输出流对象参数应该放置到方法的最后一个参数。另外在实际操作中发现对于流的处理通过远程调用还是会出现一个莫名其妙的问题,加上Hessian缺乏完整的文档(除Hessian的序列号协议)。
关于下载文件这个方法的定义使用了返回byte数组,这将受限与虚拟机的内存或其他因素。网上有说关于文件下载Hessian不支持(未能证实。引申案例二的方式,传输二进制就可以实现下载,不过这将与Hessian的RPC实现无关,而是应用到Hessian的序列号协议),这个可以算是一种策略,So,关于远程文件下载,谁会使用这样的方式呢?!!
4.服务器端实现具体功能
服务器端实现接口
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
package
secondriver.hessian.server.bo;
import
java.io.File;
import
java.io.FileInputStream;
import
java.io.FileNotFoundException;
import
java.io.IOException;
import
java.io.InputStream;
import
java.util.Arrays;
import
java.util.Calendar;
import
java.util.Date;
import
java.util.List;
import
java.util.Random;
import
org.apache.commons.io.FileUtils;
import
org.apache.commons.io.IOUtils;
import
secondriver.hessian.api.HelloHessian;
import
secondriver.hessian.api.bean.Person;
public
class
HelloHessianImpl
implements
HelloHessian {
private
static
Person[] persons =
new
Person[
5
];
static
{
Random random =
new
Random();
for
(
int
i =
0
, l = persons.length; i < l; i++) {
persons[i] =
new
Person();
persons[i].setId(i);
persons[i].setGender(random.nextBoolean());
persons[i].setName(
"name-"
+ i);
persons[i].setPhone(random.nextLong());
persons[i].setHeight(random.nextDouble());
persons[i].setWeight(random.nextFloat());
persons[i].setAddress(
new
String[] {
"Address"
+ random.nextInt(),
"Address"
+ random.nextInt() });
Calendar c = Calendar.getInstance();
c.set(Calendar.DATE, i +
1
);
persons[i].setBrithday(c.getTime());
}
}
@Override
public
String sayHello() {
return
"Hello Hession "
+
new
Date().toString();
}
@Override
public
String sayHello(String name) {
return
"Welcome "
+ name;
}
@Override
public
List<Person> getPersons() {
return
Arrays.asList(persons);
}
@Override
public
Person getPersonById(
int
id) {
for
(Person p : persons) {
if
(p.getId() == id) {
return
p;
}
}
return
null
;
}
@Override
public
boolean
uploadFile(String fileName, InputStream data) {
List<String> temp;
try
{
temp = IOUtils.readLines(data);
String filePath = System.getProperty(
"user.dir"
) +
"/temp/"
+ fileName;
FileUtils.writeLines(
new
File(filePath), temp);
System.out.println(
"Upload file to "
+ filePath);
return
true
;
}
catch
(IOException e) {
e.printStackTrace();
return
false
;
}
}
@Override
public
byte
[] downloadFile(String fileName) {
String filePath = System.getProperty(
"user.dir"
) +
"/temp/"
+ fileName;
InputStream data =
null
;
try
{
data =
new
FileInputStream(filePath);
int
size = data.available();
byte
[] buffer =
new
byte
[size];
IOUtils.read(data, buffer);
return
buffer;
}
catch
(FileNotFoundException e) {
e.printStackTrace();
return
null
;
}
catch
(IOException e) {
e.printStackTrace();
return
null
;
}
finally
{
IOUtils.closeQuietly(data);
}
}
}
|
5.服务器端配置远程服务地址
Hessian是Remote On Http工具,服务端是典型的Java Web应用,我们需要在web.xml中配置服务的请求地址。
|
1
2
3
4
5
6
7
8
9
10
11
12
|
<
servlet
>
<
servlet-name
>HelloHessian</
servlet-name
>
<
servlet-class
>com.caucho.hessian.server.HessianServlet</
servlet-class
>
<!-- RPC HessianServlet处理类 -->
<
init-param
>
<
param-name
>home-class</
param-name
>
<!-- 远程服务实现类 -->
<
param-value
>secondriver.hessian.server.bo.HelloHessianImpl</
param-value
>
</
init-param
>
<
init-param
>
<
param-name
>home-api</
param-name
>
<!-- 远程服务接口 -->
<
param-value
>secondriver.hessian.api.HelloHessian</
param-value
>
</
init-param
>
</
servlet
>
|
6.客户端调用远程对象方法
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
package
secondriver.hessian.client;
import
java.io.BufferedInputStream;
import
java.io.FileInputStream;
import
java.io.FileNotFoundException;
import
java.io.FileWriter;
import
java.io.IOException;
import
java.io.InputStream;
import
java.net.MalformedURLException;
import
java.util.List;
import
org.apache.commons.io.IOUtils;
import
secondriver.hessian.api.HelloHessian;
import
secondriver.hessian.api.bean.Person;
import
com.caucho.hessian.client.HessianProxyFactory;
/**
* Hessian RPC
*/
public
class
HelloHessianClient {
public
static
String urlName =
"http://localhost:8080/hessian-server/HelloHessian"
;
public
static
void
main(String[] args)
throws
MalformedURLException {
HessianProxyFactory factory =
new
HessianProxyFactory();
// 开启方法重载
factory.setOverloadEnabled(
true
);
HelloHessian helloHession = (HelloHessian) factory.create(
HelloHessian.
class
, urlName);
// 调用方法
System.out.println(
"call sayHello():"
+ helloHession.sayHello());
System.out.println(
"call sayHello(\"Tom\"):"
+ helloHession.sayHello(
"Tom"
));
System.out.println(
"call getPersons():"
);
// 调用方法获取集合对象
List<Person> persons = helloHession.getPersons();
if
(
null
!= persons && persons.size() >
0
) {
for
(Person p : persons) {
System.out.println(p.toString());
}
}
else
{
System.out.println(
"No person."
);
}
// 通过参数调用方法获取对象
int
id =
2
;
System.out.println(String.format(
"call getPersonById(%d)"
, id));
Person person = helloHession.getPersonById(id);
if
(
null
!= person) {
System.out.println(person.toString());
}
else
{
System.out.println(
"Id is "
+ id +
" person not exist."
);
}
// 上传文件
String fileName =
"upload.txt"
;
String filePath = System.getProperty(
"user.dir"
) +
"/temp/"
+ fileName;
InputStream data =
null
;
try
{
data =
new
BufferedInputStream(
new
FileInputStream(filePath));
if
(helloHession.uploadFile(fileName, data)) {
System.out.println(
"Upload file "
+ filePath +
" succeed."
);
}
else
{
System.out.println(
"Upload file "
+ filePath +
" failed."
);
}
}
catch
(FileNotFoundException e) {
e.printStackTrace();
}
finally
{
IOUtils.closeQuietly(data);
}
// 下载文件
fileName =
"download.txt"
;
filePath = System.getProperty(
"user.dir"
) +
"/temp/"
+ fileName;
try
{
byte
[] temp = helloHession.downloadFile(fileName);
if
(
null
!= temp) {
FileWriter output =
new
FileWriter(filePath);
IOUtils.write(temp, output,
"UTF-8"
);
System.out.println(
"Download file "
+ filePath +
" succeed."
);
output.close();
}
else
{
System.out.println(
"Download file "
+ filePath +
" failed."
);
}
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
|
通过客户端调用可以看出,远程方法的具体实现对于客户端来说是透明的。
7.运行程序
7.1启动服务器
7.2运行客户端程序
客户端请求远程服务对象方法调用结果:
|
1
2
3
4
5
6
7
8
9
|
call sayHello():Hello Hession Sat Jan 10 12:31:21 CST 2015
call sayHello("Tom"):Welcome Tom
call getPersons():
Person [id=0, gender=true, name=name-0, brithday=Thu Jan 01 12:31:21 CST 2015, height=0.04756537639163749, weight=0.24559122, phone=1359341198036930408, address=[Address2145973789, Address486043733]]
....省略
call getPersonById(2)
Person [id=2, gender=false, name=name-2, brithday=Sat Jan 03 12:31:21 CST 2015, height=0.07169451440704722, weight=0.8515671, phone=2732762596557481818, address=[Address8744397, Address-1690316438]]
Upload file F:\__eclipse\tomsworkspace\hessian-L-parent\hessian-client/temp/upload.txt succeed.
Download file F:\__eclipse\tomsworkspace\hessian-L-parent\hessian-client/temp/download.txt succeed.
|
案例二:
1. 客户端通过Hessian序列号协议序列化对象,通过Http Post方式提交到服务器端,然后通过反序列化服务器端响应数据。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
package
secondriver.hessian.data;
import
java.io.ByteArrayOutputStream;
import
java.io.InputStream;
import
java.util.Date;
import
org.apache.http.HttpEntity;
import
org.apache.http.client.methods.CloseableHttpResponse;
import
org.apache.http.client.methods.HttpPost;
import
org.apache.http.entity.ByteArrayEntity;
import
org.apache.http.entity.ContentType;
import
org.apache.http.impl.client.CloseableHttpClient;
import
org.apache.http.impl.client.HttpClients;
import
secondriver.hessian.api.bean.Person;
import
com.caucho.hessian.io.Hessian2Input;
import
com.caucho.hessian.io.Hessian2Output;
public
class
Test3 {
public
static
String urlName =
"http://localhost:8080/hessian-server/PostDataServlet"
;
public
static
void
main(String[] args)
throws
Throwable {
// 序列化
ByteArrayOutputStream os =
new
ByteArrayOutputStream();
Hessian2Output h2o =
new
Hessian2Output(os);
h2o.startMessage();
h2o.writeObject(getPerson());
h2o.writeString(
"I am client."
);
h2o.completeMessage();
h2o.close();
byte
[] buffer = os.toByteArray();
os.close();
ByteArrayEntity byteArrayEntity =
new
ByteArrayEntity(buffer,
ContentType.create(
"x-application/hessian"
,
"UTF-8"
));
CloseableHttpClient client = HttpClients.createDefault();
HttpPost post =
new
HttpPost(urlName);
post.setEntity(byteArrayEntity);
CloseableHttpResponse response = client.execute(post);
System.out.println(
"response status:\n"
+ response.getStatusLine().getStatusCode());
HttpEntity body = response.getEntity();
InputStream is = body.getContent();
Hessian2Input h2i =
new
Hessian2Input(is);
h2i.startMessage();
Person person = (Person) h2i.readObject();
System.out.println(
"response:\n"
+ person.toString());
System.out.println(h2i.readString());
h2i.completeMessage();
h2i.close();
is.close();
}
public
static
Person getPerson() {
Person person =
new
Person();
person.setAddress(
new
String[] {
"Beijing"
,
"TaiWan"
,
"GuangZhou"
});
person.setBrithday(
new
Date());
person.setGender(
false
);
person.setHeight(
168
.5D);
person.setId(
300
);
person.setName(
"Jack"
);
person.setPhone(
188888888
);
person.setWeight(
55
.2F);
return
person;
}
}
|
2.服务器端通过Hessian序列化协议反序列化对象,通过HttpServletResponse对请求进行响应,响应数据是Hessian序列化的二进制结果。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
package
secondriver.hessian.server.servlet;
import
java.io.ByteArrayOutputStream;
import
java.io.IOException;
import
java.util.Date;
import
javax.servlet.ServletException;
import
javax.servlet.ServletInputStream;
import
javax.servlet.ServletOutputStream;
import
javax.servlet.http.HttpServlet;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
secondriver.hessian.api.bean.Person;
import
com.caucho.hessian.io.Hessian2Input;
import
com.caucho.hessian.io.Hessian2Output;
public
class
PostDataServlet
extends
HttpServlet {
private
static
final
long
serialVersionUID = -4461061053732328507L;
@Override
protected
void
doPost(HttpServletRequest req, HttpServletResponse resp)
throws
ServletException, IOException {
// 处理请求
ServletInputStream sis = req.getInputStream();
Hessian2Input h2i =
new
Hessian2Input(sis);
h2i.startMessage();
Person person = (Person) h2i.readObject();
System.out.println(
"receive:\n"
+ person.toString());
System.out.println(h2i.readString());
h2i.completeMessage();
h2i.close();
sis.close();
// 发送响应
resp.setCharacterEncoding(
"UTF-8"
);
resp.setContentType(
"x-application/hessian"
);
ByteArrayOutputStream bos =
new
ByteArrayOutputStream();
Hessian2Output h2o =
new
Hessian2Output(bos);
h2o.startMessage();
h2o.writeObject(getPerson());
h2o.writeString(
"I am server."
);
h2o.completeMessage();
h2o.close();
ServletOutputStream sos = resp.getOutputStream();
sos.write(bos.toByteArray());
sos.flush();
bos.close();
sos.close();
}
public
static
Person getPerson() {
Person person =
new
Person();
person.setAddress(
new
String[] {
"ShangHai"
,
"ShenZhen"
,
"ChengDu"
});
person.setBrithday(
new
Date());
person.setGender(
true
);
person.setHeight(
178
.5D);
person.setId(
301
);
person.setName(
"Tom"
);
person.setPhone(
188218888
);
person.setWeight(
55
.2F);
return
person;
}
}
|
3.在web.xml中配置PostDataServlet,此处略去具体配置信息,可以参考上面HelloHessian Servlet配置。
4.运行程序
4.1启动服务器
4.2运行客户端程序Test3.
运行结果:
服务器端输出:
|
1
2
3
4
|
[INFO] Restart completed at Sat Jan 10 12:34:51 CST 2015
receive:
Person [id=300, gender=false, name=Jack, brithday=Sat Jan 10 12:35:03 CST 2015, height=168.5, weight=55.2, phone=188888888, address=[Beijing, TaiWan, GuangZhou]]
I am client.
|
客户端输出:
|
1
2
3
4
5
|
response status:
200
response:
Person [id=
301
, gender=
true
, name=Tom, brithday=Sat Jan
10
12
:
35
:
03
CST
2015
, height=
178.5
, weight=
55.2
, phone=
188218888
, address=[ShangHai, ShenZhen, ChengDu]]
I am server.
|
分析输出结果可见Hessian序列化对象通过Http的方式传输,并成功反序列化。
关于Hessian序列号协议可以参见:http://hessian.caucho.com/doc/hessian-serialization.html
写在最后:Hessian的相关资料网上还是比较少,而且例子不尽相同,而且官方文档错误之处清晰可见(仔细阅读方可一览无余),本文从Hessian的RPC使用和Hessian序列化和反序列化对象两个方面提供了示例,完整示例下载地址可见本文附件:HessionRPC示例(Eclipse Luna版工程,需要Mavn支持),另外带有源码的Hessian的API文档下载地址:http://down.51cto.com/data/1973896 。
一次不太愉快的Hessian体验使得对RPC的理解更加深刻,无论何种框架,对象的序列化和反序列化,数据的传输协议都是实现RPC的工作重点。
本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1601574,如需转载请自行联系原作者
