/src/main/java/jp/ameba/mongo/MongoConnectionImpl.java

http://github.com/suguru/mongo-java-async-driver · Java · 303 lines · 176 code · 28 blank · 99 comment · 13 complexity · 73a60308adac3fdd084ffebdcde91f08 MD5 · raw file

  1. package jp.ameba.mongo;
  2. import java.net.SocketAddress;
  3. import java.util.concurrent.ExecutionException;
  4. import java.util.concurrent.TimeUnit;
  5. import jp.ameba.mongo.protocol.Consistency;
  6. import jp.ameba.mongo.protocol.Delete;
  7. import jp.ameba.mongo.protocol.GetMore;
  8. import jp.ameba.mongo.protocol.Insert;
  9. import jp.ameba.mongo.protocol.KillCursors;
  10. import jp.ameba.mongo.protocol.Query;
  11. import jp.ameba.mongo.protocol.Request;
  12. import jp.ameba.mongo.protocol.RequestFuture;
  13. import jp.ameba.mongo.protocol.Response;
  14. import jp.ameba.mongo.protocol.Update;
  15. import org.bson.BSONObject;
  16. import org.jboss.netty.channel.Channel;
  17. import org.jboss.netty.channel.ChannelFuture;
  18. import org.jboss.netty.channel.ChannelFutureListener;
  19. /**
  20. * MongoDB との非同期通信を行うクライアント
  21. *
  22. * @author suguru
  23. */
  24. public class MongoConnectionImpl implements MongoConnection {
  25. // Netty用の ChannelFactory
  26. private Channel channel;
  27. // 初期接続先アドレス一覧
  28. private SocketAddress serverAddress;
  29. // 接続設定
  30. private MongoConfiguration connectionConfig;
  31. /**
  32. * {@link MongoConnectionImpl} を構成します。
  33. */
  34. public MongoConnectionImpl(
  35. Channel channel,
  36. SocketAddress serverAddress,
  37. MongoConfiguration connectionConfig) {
  38. this.channel = channel;
  39. this.serverAddress = serverAddress;
  40. this.connectionConfig = connectionConfig;
  41. }
  42. @Override
  43. public int getChannelId() {
  44. return channel.getId();
  45. }
  46. /**
  47. * MongoDB へ接続します。
  48. */
  49. public void open() {
  50. MongoFuture future = openAsync();
  51. try {
  52. future.await(connectionConfig.getConnectTimeout(), TimeUnit.MILLISECONDS);
  53. } catch (InterruptedException ex) {
  54. }
  55. if (!future.isSuccess()) {
  56. throw new MongoException("No available MongoDB servers.");
  57. }
  58. }
  59. /**
  60. * 非同期処理で MongoDB へ接続します。
  61. * @return
  62. */
  63. public MongoFuture openAsync() {
  64. MongoFuture mongoFuture = new MongoFuture();
  65. ChannelFuture channelFuture = channel.connect(serverAddress);
  66. channelFuture.addListener(new OpenChannelFutureListener(mongoFuture));
  67. return mongoFuture;
  68. }
  69. /**
  70. * MongoDB への接続を閉じます。
  71. */
  72. public void close() {
  73. MongoFuture future = closeAsync();
  74. try {
  75. future.await(connectionConfig.getOperationTimeout(), TimeUnit.MILLISECONDS);
  76. } catch (InterruptedException ex) {
  77. }
  78. if (!future.isSuccess()) {
  79. throw new MongoException("Failed to close connections");
  80. }
  81. }
  82. /**
  83. * 非同期処理で MongoDB への接続を閉じます。
  84. * @return
  85. */
  86. public MongoFuture closeAsync() {
  87. MongoFuture mongoFuture = new MongoFuture();
  88. CloseChannelFutureListener channelFutureListener = new CloseChannelFutureListener(mongoFuture);
  89. ChannelFuture channelFuture = channel.close();
  90. channelFuture.addListener(channelFutureListener);
  91. return mongoFuture;
  92. }
  93. /**
  94. * 接続完了処理のための非同期リスナ
  95. */
  96. private class OpenChannelFutureListener implements ChannelFutureListener {
  97. private MongoFuture mongoFuture;
  98. public OpenChannelFutureListener(MongoFuture mongoFuture) {
  99. this.mongoFuture = mongoFuture;
  100. }
  101. public synchronized void operationComplete(ChannelFuture channelFuture) throws Exception {
  102. if (channelFuture.isSuccess()) {
  103. // 1つでも接続に成功していれば、成功とみなす
  104. mongoFuture.setSuccess(true);
  105. }
  106. // 待ち受けスレッドへ通知
  107. synchronized (mongoFuture) {
  108. mongoFuture.notifyAll();
  109. }
  110. };
  111. }
  112. /**
  113. * 切断処理のための非同期リスナ
  114. * @author suguru
  115. */
  116. private class CloseChannelFutureListener implements ChannelFutureListener {
  117. private MongoFuture mongoFuture;
  118. public CloseChannelFutureListener(MongoFuture mongoFuture) {
  119. this.mongoFuture = mongoFuture;
  120. }
  121. @Override
  122. public synchronized void operationComplete(ChannelFuture channelFuture) throws Exception {
  123. synchronized (this) {
  124. if (channelFuture.isSuccess()) {
  125. mongoFuture.setSuccess(true);
  126. }
  127. // 待ち受けスレッドへ通知
  128. synchronized (mongoFuture) {
  129. mongoFuture.notifyAll();
  130. }
  131. }
  132. }
  133. }
  134. /**
  135. * 接続がオープンであるか確認します。
  136. * @return
  137. */
  138. public boolean isOpen() {
  139. return channel.isOpen();
  140. }
  141. /**
  142. * 接続中であるか確認します。
  143. * @return
  144. */
  145. public boolean isConnected() {
  146. return channel.isConnected();
  147. }
  148. /**
  149. * 更新を実行します。
  150. * @param databaseName
  151. * @param collectionName
  152. * @param selector
  153. * @param update
  154. * @param upsert
  155. * @param multiUpdate
  156. * @param consistency
  157. */
  158. public void update(Update update) {
  159. sendUpdateRequest(update);
  160. }
  161. /**
  162. * 新しいオブジェクトを新規に永続化します。
  163. * @param insert
  164. */
  165. public void insert(Insert insert) {
  166. sendUpdateRequest(insert);
  167. }
  168. /**
  169. * 指定のオブジェクトを削除します。
  170. * @param delete
  171. */
  172. public void delete(Delete delete) {
  173. sendUpdateRequest(delete);
  174. }
  175. /**
  176. * クエリを送信します。
  177. * @param query
  178. * @return
  179. */
  180. public Response query(Query query) {
  181. return sendQueryRequest(query);
  182. }
  183. /**
  184. * カーソルの追加取得を送信します。
  185. * @param getMore
  186. * @return
  187. */
  188. public Response getMore(GetMore getMore) {
  189. return sendQueryRequest(getMore);
  190. }
  191. /**
  192. * カーソルを取得します。
  193. * @param query
  194. * @return
  195. */
  196. public MongoCursor cursor(String databaseName, String collectionName) {
  197. return new MongoCursor(this, databaseName, collectionName);
  198. }
  199. /**
  200. * カーソルクリアを送信します。
  201. * @param kilLCursors
  202. */
  203. public void killCursors(KillCursors killCursors) {
  204. sendUpdateRequest(killCursors);
  205. }
  206. /**
  207. * 更新系リクエストを送信します。
  208. * {@link Consistency} の状況によって、サーバーからの
  209. * 返却までの間、処理をブロックします。
  210. *
  211. * @param request
  212. */
  213. private void sendUpdateRequest(Request request) {
  214. channel.write(request);
  215. Consistency consistency = request.getConsistency();
  216. BSONObject getErrorQuery = consistency.getLastErrorQuery();
  217. if (getErrorQuery != null) {
  218. waitLastError(request);
  219. }
  220. }
  221. /**
  222. * クエリリクエストを送信し、レスポンスを取得するまでブロックします。
  223. * @param request
  224. * @return
  225. */
  226. private Response sendQueryRequest(Request request) {
  227. channel.write(request);
  228. RequestFuture requestFuture = request.getFuture();
  229. try {
  230. Response response = requestFuture.get();
  231. if (!response.isOk()) {
  232. throw new MongoException("Query failure: " + response.getErrorMessage());
  233. } else {
  234. return response;
  235. }
  236. } catch (ExecutionException ex) {
  237. throw new MongoException(ex);
  238. } catch (InterruptedException ex) {
  239. throw new MongoException(ex);
  240. }
  241. }
  242. /**
  243. * 指定のリクエストの getLastError の返りを待ちます。
  244. *
  245. * @param request
  246. */
  247. private void waitLastError(Request request) {
  248. // ResponseFuture を取得
  249. RequestFuture requestFuture = request.getFuture();
  250. if (requestFuture != null) {
  251. try {
  252. Response response = requestFuture.get();
  253. BSONObject object = response.getDocuments().get(0);
  254. // OK でない場合は、例外を発する
  255. if (!response.isOk()) {
  256. String error = (String) object.get("errmsg");
  257. throw new MongoException(error);
  258. }
  259. String err = (String) object.get("err");
  260. if (err != null) {
  261. Integer code = (Integer) object.get("code");
  262. throw new MongoException(err, code);
  263. }
  264. } catch (ExecutionException ex) {
  265. throw new MongoException(ex);
  266. } catch (InterruptedException ex) {
  267. throw new MongoException(ex);
  268. }
  269. }
  270. }
  271. @Override
  272. protected void finalize() throws Throwable {
  273. closeAsync();
  274. }
  275. }