GAPI 所基于 MINA 是一个异步网络通信框架,因此要实现 API 调用的同步方式返回,需要采用锁机制,即在主线程启动 filter 后进入“睡眠”状态, 当filter 处理任务完毕后唤醒主线程继续运行。具体实现的方法如下(以 execScript() 方法为例)。
测试用例:
public class SimpleExpScriptTest {
private IConnection conn;
@Test
public void testPWD() throws Exception {
conn = ConnFactory.getInstance().getConnection(Constants.PORT_CONNECT_GODU_DATA);
conn.setSyncType(Constants.TYPE_DATA_CMD_SYNC);
if (conn.getConnection("
System.out.println("Connect GODU OK!");
}
conn.sendGoduCmd("settelnetcodec switch=y");
conn.set_script_timeout(500);
Map
params.put("user_name", "test");
params.put("old_pwd", "godu123");
params.put("new_pwd", "testgodu");
IResult result = conn. execScript ("-1200187081", "script/PWD", "", params);
System.out.println("***result is:\n" + result.getString());
}
}
execScript 方法:
package com.boco.godu.gapi.conn;
import ...
public class GoduDataConnection extends AbstractConnection {
private static final GapiLogger logger = LogUtils.getLogger(this.getClass().getName());
private final Object lock = new Object(); //对象锁
public IResult execScript(String ne_id, String scriptPath, String name, Map
// check if connected GODU successfully
...
// parse script file
GoduScript gs = new GoduScript(scriptPath, script_buffer_size, params);
// add execute_script filter
session.getFilterChain().addBefore(...);
// open NE
...
// 在执行完脚本或者超时之前阻止代码继续运行,在ExecScriptFilter.messageReceived方法中解锁
GapiIosessionUtils. lockSession (session, lock , script_timeout );
GoduReturnResult res = new GoduReturnResult();
String script_result = (String) session.getAttribute(
ExecScriptFilter.EXEC_SCRIPT_RESULT, "");
res.setArrays(script_result.getBytes());
int return_status = (Integer) session.getAttribute(
ExecScriptFilter.EXEC_SCRIPT_RETURN_STATUS, 0);
res.setReturnStatus(return_status);
return res;
}
}
这样代码运行到lockSession时暂停,直到超时时间到,或者被其他线程上持有同一个锁的“唤醒”方法唤醒,这实现了同步方式的前一半:下面是同步方式的后一半:filter执行脚本完毕后唤醒主线程继续运行:
package com.boco.godu.gapi.mina.filter.data;
import ...
public class ExecScriptFilter extends AGapiFilter {
@Override
public void messageReceived(NextFilter nextFilter, IoSession session,
Object message) throws Exception {
...
if (...) { // 如果脚本执行完毕:
...
GapiIosessionUtils. unlockSession (session);
return;
}
...
}
}
那么 lockSession 和 unlockSession 方法又是如何实现的呢,继续往下看:
public class GapiIosessionUtils {
public static void lockSession(final IoSession session, final Object lock , final int timeout ) throws ... {
try {
if (session.isConnected() && !session.isClosing()) {
long beforeLockTime = System.currentTimeMillis();
session.setAttribute(GoduFilterConstants. GODU_DATA_LOCK , lock );
session.getConfig().setBothIdleTime(timeout);
ThreadUtil. waitIt ( lock , timeout * 1000 + 1000);
long afterLockTime = System.currentTimeMillis();
logger.debug("lockTime=" + (afterLockTime - beforeLockTime) + "ms");
}
} catch (Exception e) {
e.printStackTrace();
logger.error("lockSession exception: ", e);
throw new GapiException(e);
}
}
public static void unlockSession(final IoSession session) throws ... {
try {
if (session.isConnected() && !session.isClosing()) {
Object status = session.getAttribute(GoduFilterConstants. GODU_DATA_LOCK );
ThreadUtil.notifyIt(status);
}
} catch (Exception e) {
...
}
}
}
可以看到加锁的时候将锁(lock对象)放在了session的一个attribute上,解锁的时候从这个attribute上取出这个锁,然后解锁。这里用到的 冻结线程的 waitIt 方法以及唤醒线程的 notifyIt 方法实现如下:
package com.boco.godu.common.util;
public class ThreadUtil {
public static void waitIt (final Object o, final long time) {
synchronized (o) {
try {
if (time == -1) {
o.wait();
} else {
o.wait(time);
}
} catch (InterruptedException iex) {
iex.printStackTrace();
}
}
}
public static void notifyIt (final Object o) {
synchronized (o) {
o.notifyAll();
}
}
}
当代码到 o.wait(time); 时进入阻塞状态(停止运行,实际上是java.lang.Object.wait()方法),直到下列情况之一出现:
-
超时时间time到;
-
被唤醒,也就是调用同一个对象的notifyAll方法(即java.lang.Object.notifyAll()方法)。