View Javadoc
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 }