1 /*
2 * #%L
3 * CursorCallback.java - mongodb-async-driver - Allanbank Consulting, Inc.
4 * %%
5 * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
6 * %%
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * #L%
19 */
20
21 package com.allanbank.mongodb.client.callback;
22
23 import java.util.concurrent.atomic.AtomicBoolean;
24
25 import com.allanbank.mongodb.Callback;
26 import com.allanbank.mongodb.MongoDbException;
27 import com.allanbank.mongodb.MongoIterator;
28 import com.allanbank.mongodb.bson.Document;
29 import com.allanbank.mongodb.client.Client;
30 import com.allanbank.mongodb.client.MongoIteratorImpl;
31 import com.allanbank.mongodb.client.message.CursorableMessage;
32 import com.allanbank.mongodb.client.message.Query;
33 import com.allanbank.mongodb.client.message.Reply;
34
35 /**
36 * Callback to convert a {@link CursorableMessage} {@link Reply} into a
37 * {@link MongoIteratorImpl}.
38 *
39 * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
40 * mutated in incompatible ways between any two releases of the driver.
41 * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved
42 */
43 public final class CursorCallback extends
44 AbstractReplyCallback<MongoIterator<Document>> implements AddressAware {
45
46 /** The server the original request was sent to. */
47 private volatile String myAddress;
48
49 /** The original query. */
50 private final Client myClient;
51
52 /** If true then the callback should expect a command formated cursor reply. */
53 private final boolean myCommand;
54
55 /** The original message to start the cursor. */
56 private final CursorableMessage myMessage;
57
58 /** The reply to the query. */
59 private volatile Reply myReply;
60
61 /**
62 * Initially set to false. Set to true for the first of address or reply
63 * being set. The second fails and {@link #trigger() triggers} the callback.
64 */
65 private final AtomicBoolean mySetOther;
66
67 /**
68 * Create a new CursorCallback.
69 *
70 * @param client
71 * The client interface to the server.
72 * @param message
73 * The original query.
74 * @param command
75 * If true then the callback should expect a command formated
76 * cursor reply.
77 * @param results
78 * The callback to update once the first set of results are
79 * ready.
80 */
81 public CursorCallback(final Client client, final CursorableMessage message,
82 final boolean command,
83 final Callback<MongoIterator<Document>> results) {
84
85 super(results);
86
87 myClient = client;
88 myMessage = message;
89 myCommand = command;
90
91 mySetOther = new AtomicBoolean(false);
92 }
93
94 /**
95 * Returns the server the original request was sent to.
96 *
97 * @return The server the original request was sent to.
98 */
99 public String getAddress() {
100 return myAddress;
101 }
102
103 /**
104 * Sets the value of the server the original request was sent to.
105 *
106 * @param address
107 * The new value for the server the original request was sent to.
108 */
109 @Override
110 public void setAddress(final String address) {
111 myAddress = address;
112 trigger();
113 }
114
115 /**
116 * {@inheritDoc}
117 * <p>
118 * Overridden to add the {@link Query} to the exception.
119 * </p>
120 *
121 * @see AbstractReplyCallback#asError(Reply, int, int, String)
122 */
123 @Override
124 protected MongoDbException asError(final Reply reply, final int okValue,
125 final int errorNumber, final String errorMessage) {
126 return super.asError(reply, okValue, errorNumber, false, errorMessage,
127 myMessage);
128 }
129
130 /**
131 * {@inheritDoc}
132 * <p>
133 * Overridden to construct a {@link MongoIteratorImpl} around the reply.
134 * </p>
135 *
136 * @see AbstractReplyCallback#convert(Reply)
137 */
138 @Override
139 protected MongoIterator<Document> convert(final Reply reply)
140 throws MongoDbException {
141 Reply result = reply;
142 if (isCommand()) {
143 result = CommandCursorTranslator.translate(reply);
144 }
145 return new MongoIteratorImpl(myMessage, myClient, myAddress, result);
146 }
147
148 /**
149 * {@inheritDoc}
150 * <p>
151 * Overridden to check if the server address has been set and if so then
152 * pass the converted reply to the {@link #getForwardCallback() forward
153 * callback}. Otherwise the call is dropped.
154 * </p>
155 */
156 @Override
157 protected void handle(final Reply reply) {
158 myReply = reply;
159 trigger();
160 }
161
162 /**
163 * Returns true if the callback should expect a command formated cursor
164 * reply.
165 *
166 * @return True if the callback should expect a command formated cursor
167 * reply.
168 */
169 protected boolean isCommand() {
170 return myCommand;
171 }
172
173 /**
174 * Triggers the callback when the address and reply are set.
175 */
176 private void trigger() {
177 if (!mySetOther.compareAndSet(false, true)) {
178 super.handle(myReply);
179 }
180 }
181 }