写在前面:
昨天在博客记录自己抽空写的一个Socket聊天程序的初始设计,那是这个程序的整体设计,为了完整性,今天把服务端的设计细化记录一下,首页贴出Socket聊天程序的服务端大体设计图,如下图:
功能说明:
服务端主要有两个操作,一是阻塞接收客户端的socket并做响应处理,二是检测客户端的心跳,如果客户端一段时间内没有发送心跳则移除该客户端,由Server创建ServerSocket,然后启动两个线程池去处理这两件事(newFixedThreadPool,newScheduledThreadPool),对应的处理类分别是SocketDispatcher、SocketSchedule,其中SocketDispatcher根据socket不同的请求分发给不同SocketHandler去处理,而SocketWrapper则是对socket加了一层外壳包装,用lastAliveTime记录socket最新的交互时间,SocketHolder存储当前跟服务端交互的socket集合。
具体实现:
[Server.java]
Server是服务端的入口,由Server的start()方法启动ServerSocket,然后阻塞接收客户端的请求,交由SocketDispatcher去分发,SocketDispatcher由newFixedThread类型的线程池启动,当连接数超过最大数据时将被队列处理,使用scheduleAtFixedRate启动SocketSchedule定时循环去监听客户端的心跳包,这两个类型都实现了Runnable接口,下面给出服务端的代码:
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
|
package
yaolin.chat.server;
import
java.io.IOException;
import
java.net.ServerSocket;
import
java.util.Date;
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
import
java.util.concurrent.ScheduledExecutorService;
import
java.util.concurrent.TimeUnit;
import
yaolin.chat.common.ConstantValue;
import
yaolin.chat.util.LoggerUtil;
/**
* 服务器
* @author yaolin
*/
public
class
Server {
private
final
ServerSocket server;
private
final
ExecutorService pool;
public
Server()
throws
IOException {
server =
new
ServerSocket(ConstantValue.SERVER_PORT);
pool = Executors.newFixedThreadPool(ConstantValue.MAX_POOL_SIZE);
}
public
void
start() {
try
{
ScheduledExecutorService schedule = Executors.newScheduledThreadPool(
1
);
// Watch dog. Exception??
schedule.scheduleAtFixedRate(
new
SocketSchedule(),
10
, ConstantValue.TIME_OUT, TimeUnit.SECONDS);
while
(
true
) {
pool.execute(
new
SocketDispatcher(server.accept()));
LoggerUtil.info(
"ACCEPT A CLIENT AT "
+
new
Date());
}
}
catch
(IOException e) {
pool.shutdown();
}
}
public
static
void
main(String[] args) {
try
{
new
Server().start();
}
catch
(IOException e) {
LoggerUtil.error(
"Server start failed! -> "
+ e.getMessage(), e);
}
}
}
|
[SocketDispatcher.java]
Server只是服务端的入口,并指挥中心,SocketDispatcher才是服务端的指挥中心,对客户端不同的消息类型请求进行分发,让不同的SocketHandler去处理对应的消息请求,这里服务端和客户端的消息交互都是用JSON数据,所有消息类都继承BaseMessage,所以将接收到数据转换成BaseMessage类型,再判断其类型,(数据类型模块属于common模块),这里需要提一下的是当消息类型是文件类型的时候会睡眠配置执行的间隔时间,这样FileHandler才能有时间对文件流进行读取和重新发送给指定的客户端,而不会立即进入下一次循环对消息类型的判断(可能这里设计有点问题,不过暂时先这样做),下面给出SocketDispatcher的代码:
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
|
/**
* SocketDispatcher
*
* @author yaolin
*/
public
class
SocketDispatcher
implements
Runnable {
private
final
Socket socket;
public
SocketDispatcher(Socket socket) {
this
.socket = socket;
}
@Override
public
void
run() {
if
(socket !=
null
) {
while
(!socket.isClosed()) {
try
{
InputStream is = socket.getInputStream();
String line =
null
;
StringBuffer sb =
null
;
if
(is.available() >
0
) {
BufferedReader bufr =
new
BufferedReader(
new
InputStreamReader(is));
sb =
new
StringBuffer();
while
(is.available() >
0
&& (line = bufr.readLine()) !=
null
) {
sb.append(line);
}
LoggerUtil.trach(
"RECEIVE ["
+ sb.toString() +
"] AT "
+
new
Date());
BaseMessage message = JSON.parseObject(sb.toString(), BaseMessage.
class
);
switch
(message.getType()) {
case
MessageType.ALIVE:
HandlerFactory.getHandler(MessageType.ALIVE).handle(socket, sb.toString());
break
;
case
MessageType.CHAT:
HandlerFactory.getHandler(MessageType.CHAT).handle(socket, sb.toString());
break
;
case
MessageType.FILE:
HandlerFactory.getHandler(MessageType.FILE).handle(socket, sb.toString());
LoggerUtil.trach(
"SEVER:PAUSE TO RECEIVE FILE"
);
Thread.sleep(ConstantValue.MESSAGE_PERIOD);
break
;
case
MessageType.LOGIN:
HandlerFactory.getHandler(MessageType.LOGIN).handle(socket, sb.toString());
break
;
case
MessageType.LOGOUT:
break
;
case
MessageType.REGISTER:
HandlerFactory.getHandler(MessageType.REGISTER).handle(socket, sb.toString());
break
;
}
}
else
{
Thread.sleep(ConstantValue.MESSAGE_PERIOD);
}
}
catch
(Exception e) {
// catch all handler exception
LoggerUtil.error(
"SocketDispatcher Error!"
+ e.getMessage(), e);
}
}
}
}
}
|
[SocketSchedule.java]
跟Server有直接关系的另一个类(组件)是SocketSchedule,SocketSchedule主要负责检测客户端的最新一次跟服务端的交互时间是否超过系统配置允许最大的时间,如果超过了,则将该客户端socket从服务端移除,否则更新客户端的最新一次跟服务端的交互时间。下面是具体的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/**
* Remove socket from SocketHolder if lastAliveTime > TIME_OUT
* @author yaolin
*
*/
public
class
SocketSchedule
implements
Runnable {
@Override
public
void
run() {
for
(String key : SocketHolder.keySet()) {
SocketWrapper wrapper = SocketHolder.get(key);
if
(wrapper !=
null
&& wrapper.getLastAliveTime() !=
null
) {
if
(((
new
Date().getTime() - wrapper.getLastAliveTime().getTime()) /
1000
) > ConstantValue.TIME_OUT) {
// remove socket if timeout
SocketHolder.remove(key);
}
}
}
}
}
|
[SocketHolder.java、SocketWrapper.java]
从上面的代码可以看出,SocketSchedule#run()只是简单的对时间进行一次判断,真正有意义的其实是SocketHolder和SocketWrapper,SocketWrapper则是对socket加了一层外壳包装,SocketHolder的存储了当前有效时间内所有跟服务端有交互的客户端,SocketHolder以客户端的唯一标识(这里使用用户名),作为KEY,客户端所在的socket作为VALUE的键值对形式存储,其中SocketHolder#flushClientStatus()的处理逻辑是用于通知其他客户端当前客户端的上线/离线状态,下面给出这两个类的具体实现:
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
|
/**
* Wrap Socket, SocketSchedule remove socket if lastAliveTime > TIME_OUT
* @author yaolin
*
*/
public
class
SocketWrapper {
private
Socket socket;
private
Date lastAliveTime;
// full constructor
public
SocketWrapper(Socket socket, Date lastAliveTime) {
this
.socket = socket;
this
.lastAliveTime = lastAliveTime;
}
public
Socket getSocket() {
return
socket;
}
public
void
setSocket(Socket socket) {
this
.socket = socket;
}
public
Date getLastAliveTime() {
return
lastAliveTime;
}
public
void
setLastAliveTime(Date lastAliveTime) {
this
.lastAliveTime = lastAliveTime;
}
}
|
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
|
/**
* SocketHolder
* @author yaolin
*/
public
class
SocketHolder {
private
static
ConcurrentMap
new
ConcurrentHashMap
public
static
Set
return
listSocketWrap.keySet();
}
public
static
SocketWrapper get(String key) {
return
listSocketWrap.get(key);
}
public
static
void
put(String key, SocketWrapper value) {
listSocketWrap.put(key, value);
flushClientStatus(key,
true
);
}
public
static
SocketWrapper remove(String key) {
flushClientStatus(key,
false
);
return
listSocketWrap.remove(key);
}
public
static
void
clear() {
listSocketWrap.clear();
}
/**
*
* @param flag true:put,false:remove;
*/
private
static
void
flushClientStatus(String key,
boolean
flag) {
ClientNotifyDTO dto =
new
ClientNotifyDTO(flag, key);
ReturnMessage rm =
new
ReturnMessage().setKey(Key.NOTIFY).setSuccess(
true
).setContent(dto);
rm.setFrom(ConstantValue.SERVER_NAME);
for
(String toKey : listSocketWrap.keySet()) {
if
(!toKey.equals(key)) {
// not send to self
rm.setTo(toKey);
SocketWrapper wrap = listSocketWrap.get(toKey);
if
(wrap !=
null
) {
SendHelper.send(wrap.getSocket(), rm);
}
}
}
}
}
|
[SocketHandler.java、HandlerFactory.java、OtherHandlerImpl.java]
SocketDispatcher让不同的SocketHandler去处理对应的消息请求,SocketHandler的设计其实就是一套简单的工厂组件吧(其中ReturnHandler暂时由SendHelper实现信息传送,暂时没有用到,已经@Deprecated ,这里还是给出),完整类图如下:
下面给出这一块的代码,为了缩小篇幅,将所有Handler实现的代码收起来。
1
2
3
4
5
6
7
8
9
10
|
/**
* SocketHandler
* @author yaolin
*/
public
interface
SocketHandler {
/**
* Handle Client Socket
*/
public
Object handle(Socket client,Object data);
}
|
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
|
/**
* SocketHandlerFactory
* @author yaolin
*/
public
class
HandlerFactory {
// can not create instance
private
HandlerFactory(){}
public
static
SocketHandler getHandler(
int
type) {
switch
(type) {
case
MessageType.ALIVE:
// usually use
return
new
AliveHandler();
case
MessageType.CHAT:
return
new
ChatHandler();
case
MessageType.LOGIN:
return
new
LoginHandler();
// case MessageType.RETURN:
// return new ReturnHandler();
case
MessageType.LOGOUT:
return
new
LogoutHandler();
case
MessageType.REGISTER:
return
new
RegisterHandler();
case
MessageType.FILE:
return
new
FileHandler();
}
return
null
;
// NullPointException
}
}
|
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
|
/**
* AliveSocketHandler
* @author yaolin
*/
public
class
AliveHandler
implements
SocketHandler {
/**
* @return null
*/
@Override
public
Object handle(Socket client, Object data) {
if
(data !=
null
) {
BaseMessage message = JSON.parseObject(data.toString(), BaseMessage.
class
);
if
(StringUtil.isNotEmpty(message.getFrom())) {
SocketWrapper wrapper = SocketHolder.get(message.getFrom());
if
(wrapper !=
null
) {
wrapper.setLastAliveTime(
new
Date());
// KEEP SOCKET ...
SocketHolder.put(message.getFrom(), wrapper);
}
}
}
return
null
;
}
}
|
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
|
/**
* ChatHandler
*
* @author yaolin
*/
public
class
ChatHandler
implements
SocketHandler {
@Override
public
Object handle(Socket client, Object data) {
if
(data !=
null
) {
ChatMessage message = JSON.parseObject(data.toString(), ChatMessage.
class
);
if
(StringUtil.isNotEmpty(message.getFrom()) && StringUtil.isNotEmpty(message.getTo())) {
// exist & send
if
(SocketHolder.keySet().contains(message.getFrom())) {
String owner = message.getFrom();
message.setOwner(owner);
// owner will be display
if
(ConstantValue.TO_ALL.equals(message.getTo())) {
// one-to-all
// TO_ALL TAB will be select;
message.setFrom(ConstantValue.TO_ALL);
for
(String key : SocketHolder.keySet()) {
// also send to self
SocketWrapper wrapper = SocketHolder.get(key);
if
(wrapper !=
null
) {
SendHelper.send(wrapper.getSocket(), message);
}
}
}
else
{
// one-to-one
SocketWrapper wrapper = SocketHolder.get(message.getTo());
if
(wrapper !=
null
) {
// owner = from
SendHelper.send(wrapper.getSocket(), message);
// also send to self
// TO TAB will be select;
message.setFrom(message.getTo()).setTo(owner);
SendHelper.send(client, message);
}
}
}
}
}
return
null
;
}
}
|
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
|
public
class
FileHandler
implements
SocketHandler {
@Override
public
Object handle(Socket client, Object data) {
if
(client !=
null
) {
FileMessage message = JSON.parseObject(data.toString(), FileMessage.
class
);
if
(StringUtil.isNotEmpty(message.getFrom()) && StringUtil.isNotEmpty(message.getTo())) {
// exist & send
if
(SocketHolder.keySet().contains(message.getFrom())) {
if
(!ConstantValue.TO_ALL.equals(message.getTo())) {
// one-to-all
SocketWrapper wrapper = SocketHolder.get(message.getTo());
if
(wrapper !=
null
) {
SendHelper.send(wrapper.getSocket(), message);
try
{
if
(client !=
null
&& wrapper.getSocket() !=
null
&& message.getSize() >
0
) {
InputStream is = client.getInputStream();
OutputStream os = wrapper.getSocket().getOutputStream();
int
total =
0
;
while
(!client.isClosed() && !wrapper.getSocket().isClosed()) {
if
(is.available() >
0
) {
byte
[] buff =
new
byte
[ConstantValue.BUFF_SIZE];
int
len = -
1
;
while
(is.available() >
0
&& (len = is.read(buff)) != -
1
) {
os.write(buff,
0
, len);
total += len;
LoggerUtil.debug(
"SEND BUFF ["
+ len +
"]"
);
}
os.flush();
if
(total >= message.getSize()) {
LoggerUtil.info(
"SEND BUFF [OK]"
);
break
;
}
}
}
// AFTER SEND FILE
// SEND SUCCESSFULLY
ReturnMessage result =
new
ReturnMessage().setKey(Key.TIP)
.setSuccess(
true
)
.setContent(I18N.INFO_FILE_SEND_SUCCESSFULLY);
result.setFrom(message.getTo()).setTo(message.getFrom())
.setOwner(ConstantValue.SERVER_NAME);
SendHelper.send(client, result);
// RECEIVE SUCCESSFULLY
result.setContent(I18N.INFO_FILE_RECEIVE_SUCCESSFULLY)
.setFrom(message.getFrom())
.setTo(message.getTo());
SendHelper.send(wrapper.getSocket(), result);
}
}
catch
(Exception e) {
LoggerUtil.error(
"Handle file failed !"
+ e.getMessage(), e);
}
}
}
}
}
}
return
null
;
}
}
|
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
|
/**
* LoginHandler
*
* @author yaolin
*
*/
public
class
LoginHandler
implements
SocketHandler {
private
UsrService usrService =
new
UsrService();
@Override
public
Object handle(Socket client, Object data) {
ReturnMessage result =
new
ReturnMessage();
result.setSuccess(
false
);
if
(data !=
null
) {
LoginMessage message = JSON.parseObject(data.toString(), LoginMessage.
class
);
if
(StringUtil.isNotEmpty(message.getUsername()) && StringUtil.isNotEmpty(message.getPassword())) {
if
(usrService.login(message.getUsername(), message.getPassword()) !=
null
) {
result.setSuccess(
true
);
}
else
{
result.setMessage(I18N.INFO_LOGIN_ERROR_DATA);
}
result.setFrom(ConstantValue.SERVER_NAME).setTo(message.getUsername());
}
else
{
result.setMessage(I18N.INFO_LOGIN_EMPTY_DATA);
}
// AFTER LOGIN
result.setKey(Key.LOGIN);
if
(result.isSuccess()) {
// HOLD SOCKET
SocketHolder.put(result.getTo(),
new
SocketWrapper(client,
new
Date()));
}
SendHelper.send(client, result);
if
(result.isSuccess()) {
// SEND LIST USER
ClientListUserDTO dto =
new
ClientListUserDTO();
dto.setListUser(SocketHolder.keySet());
result.setContent(dto).setKey(Key.LISTUSER);
SendHelper.send(client, result);
}
}
return
null
;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public
class
LogoutHandler
implements
SocketHandler {
@Override
public
Object handle(Socket client, Object data) {
if
(data !=
null
) {
LogoutMessage message = JSON.parseObject(data.toString(), LogoutMessage.
class
);
if
(message !=
null
&& StringUtil.isNotEmpty(message.getFrom())) {
SocketWrapper wrapper = SocketHolder.get(message.getFrom());
Socket socket = wrapper.getSocket();
if
(socket !=
null
) {
try
{
socket.close();
socket =
null
;
}
catch
(Exception ignore) {
}
}
SocketHolder.remove(message.getFrom());
}
}
return
null
;
}
}
|
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
|
public
class
RegisterHandler
implements
SocketHandler {
private
UsrService usrService =
new
UsrService();
@Override
public
Object handle(Socket client, Object data) {
ReturnMessage result =
new
ReturnMessage();
result.setSuccess(
false
).setFrom(ConstantValue.SERVER_NAME);
if
(data !=
null
) {
RegisterMessage message = JSON.parseObject(data.toString(), RegisterMessage.
class
);
if
(StringUtil.isNotEmpty(message.getUsername()) && StringUtil.isNotEmpty(message.getPassword())) {
if
(usrService.register(message.getUsername(), message.getPassword()) !=
null
) {
result.setSuccess(
true
).setContent(I18N.INFO_REGISTER_OK);
}
else
{
result.setMessage(I18N.INFO_REGISTER_CLIENT_EXIST);
}
}
else
{
result.setMessage(I18N.INFO_REGISTER_EMPTY_DATA);
}
if
(StringUtil.isNotEmpty(message.getUsername())) {
result.setTo(message.getUsername());
}
// AFTER REGISTER
result.setKey(Key.REGISTER);
SendHelper.send(client, result);
}
return
null
;
}
}
|
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
|
/**
* Use SendHelper to send ReturnMessage,
* @see yaolin.chat.server.SocketDispatcher#run()
* @author yaolin
*/
@Deprecated
public
class
ReturnHandler
implements
SocketHandler {
/**
* @param data ReturnMessage
*/
@Override
public
Object handle(Socket client, Object data) {
if
(data !=
null
) {
ReturnMessage message = (ReturnMessage) data;
if
(StringUtil.isNotEmpty(message.getFrom()) && StringUtil.isNotEmpty(message.getTo())) {
SocketWrapper wrap = SocketHolder.get(message.getTo());
if
(wrap !=
null
) {
SendHelper.send(wrap.getSocket(), message);
}
}
}
return
null
;
}
}
|
用户业务:
服务端除了socket之外,还有一点点具体的业务,那就是用户的注册、登陆等,这里简单的列出Usr和UsrService这两个类,这些业务暂时没有怎么实现,我并不打算在这个程序中引入ORM框架,所以自己写一套DBUtil(待改善),在这里也一并贴出来。
这里只进行了简单的校验,没有持久化存储到DB中,下面是Usr和UsrService:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public
class
Usr {
private
long
id;
private
String username;
private
String password;
public
long
getId() {
return
id;
}
public
void
setId(
long
id) {
this
.id = id;
}
public
String getUsername() {
return
username;
}
public
void
setUsername(String username) {
this
.username = username;
}
public
String getPassword() {
return
password;
}
public
void
setPassword(String password) {
this
.password = password;
}
}
|
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
|
/**
* // TODO
* @see yaolin.chat.server.usr.repository.UsrRepository
* @author yaolin
*
*/
public
class
UsrService {
// TODO db
private
static
Map
new
HashMap
public
Usr register(String username, String password) {
if
(StringUtil.isEmpty(username) || StringUtil.isEmpty(password)) {
return
null
;
}
if
(db.containsKey(username)) {
return
null
;
// exist;
}
Usr usr =
new
Usr();
usr.setUsername(username);
usr.setPassword(MD5Util.getMD5Code(password));
db.put(username, usr);
return
usr;
}
public
Usr login(String username, String password) {
if
(StringUtil.isEmpty(username) || StringUtil.isEmpty(password)) {
return
null
;
}
if
(db.containsKey(username)) {
Usr usr = db.get(username);
if
(MD5Util.getMD5Code(password).equals(usr.getPassword())) {
return
usr;
}
}
return
null
;
}
}
|
下面是DBUtil工具:
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
/**
* DBUtils // TODO 有待调整&优化!!
* @author yaolin
*/
public
class
DBUtil {
// make connection used repeatedly
private
static
final
List
new
LinkedList
private
static
String url;
private
static
String driver;
private
static
String user;
private
static
String password;
private
static
Boolean debug;
static
{
InputStream is = DBUtil.
class
.getResourceAsStream(
"/db.properties"
);
try
{
Properties p =
new
Properties();
p.load(is);
url = p.getProperty(
"url"
);
driver = p.getProperty(
"driver"
);
user = p.getProperty(
"user"
);
password = p.getProperty(
"password"
);
// just for debug
try
{
debug = Boolean.valueOf(p.getProperty(
"debug"
));
}
catch
(Exception ignore) {
debug =
false
;
}
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
finally
{
if
(is !=
null
) {
try
{
is.close();
is =
null
;
}
catch
(Exception ignore) {
}
}
}
}
public
synchronized
static
Connection getConnection() {
if
(cache.isEmpty()) {
cache.add(makeConnection());
}
Connection conn =
null
;
int
i =
0
;
try
{
do
{
conn = cache.remove(i);
}
while
(conn !=
null
&& conn.isClosed() && i < cache.size());
}
catch
(Exception ignore) {
}
try
{
if
(conn ==
null
|| conn.isClosed()) {
cache.add(makeConnection());
conn = cache.remove(
0
);
}
return
conn;
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
}
public
synchronized
static
void
close(Connection connection) {
try
{
if
(connection !=
null
&& !connection.isClosed()) {
if
(debug)
debug(
"release connection!"
);
cache.add(connection);
}
}
catch
(SQLException ignore) {
}
}
public
static
Object query(String sql, ResultSetMapper mapper, Object... args) {
if
(debug)
debug(sql);
Connection conn = getConnection();
PreparedStatement ps =
null
;
ResultSet rs =
null
;
Object result =
null
;
try
{
ps = conn.prepareStatement(sql);
int
i =
1
;
for
(Object object : args) {
ps.setObject(i++, object);
}
rs = ps.executeQuery();
result = mapper.mapper(rs);
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
finally
{
try
{
if
(rs !=
null
) {
rs.close();
rs =
null
;
}
if
(ps !=
null
) {
ps.close();
ps =
null
;
}
}
catch
(Exception ignore) {
}
}
close(conn);
return
result;
}
public
static
int
modify(String sql, Object... args) {
if
(debug)
debug(sql);
Connection conn = getConnection();
PreparedStatement ps =
null
;
int
row =
0
;
try
{
ps = conn.prepareStatement(sql);
int
i =
1
;
for
(Object object : args) {
ps.setObject(i++, object);
}
row = ps.executeUpdate();
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
finally
{
try
{
if
(ps !=
null
) {
ps.close();
ps =
null
;
}
}
catch
(Exception ignore) {
}
}
close(conn);
return
row;
}
public
static
int
[] batch(List
if
(debug)
debug(sqls.toString());
Connection conn = getConnection();
Statement stmt =
null
;
int
[] row;
try
{
stmt = conn.createStatement();
for
(String sql : sqls) {
stmt.addBatch(sql);
}
row = stmt.executeBatch();
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
finally
{
try
{
if
(stmt !=
null
) {
stmt.close();
stmt =
null
;
}
}
catch
(Exception ignore) {
}
}
close(conn);
return
row;
}
public
static
int
[] batch(String sql, PreparedStatementSetter setter) {
if
(debug)
debug(sql);
Connection conn = getConnection();
PreparedStatement ps =
null
;
int
[] row;
try
{
ps = conn.prepareStatement(sql);
setter.setter(ps);
row = ps.executeBatch();
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
finally
{
try
{
if
(ps !=
null
) {
ps.close();
ps =
null
;
}
}
catch
(Exception ignore) {
}
}
close(conn);
return
row;
}
private
static
Connection makeConnection() {
try
{
Class.forName(driver).newInstance();
Connection conn = DriverManager.getConnection(url, user, password);
if
(debug)
debug(
"create connection!"
);
return
conn;
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
}
private
static
void
debug(String sqls) {
SimpleDateFormat sdf =
new
SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss"
);
System.out.println(sdf.format(
new
Date())
+
" DEBUG "
+ Thread.currentThread().getId()
+
" --- ["
+ Thread.currentThread().getName() +
"] "
+
"excute sqls : "
+ sqls);
}
}
|
1
2
3
4
5
6
7
|
/**
* PreparedStatementSetter
* @author yaolin
*/
public
interface
PreparedStatementSetter {
public
void
setter(PreparedStatement ps);
}
|
1
2
3
4
5
6
7
|
/**
* ResultSetMapper
* @author yaolin
*/
public
interface
ResultSetMapper {
public
Object mapper(ResultSet rs);
}
|
源码下载:demo
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。