View Javadoc
1   /*
2    * #%L
3    * MultipleCursorCallback.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.ArrayList;
24  import java.util.Collection;
25  import java.util.List;
26  import java.util.concurrent.atomic.AtomicBoolean;
27  
28  import com.allanbank.mongodb.Callback;
29  import com.allanbank.mongodb.MongoDbException;
30  import com.allanbank.mongodb.MongoIterator;
31  import com.allanbank.mongodb.bson.Document;
32  import com.allanbank.mongodb.client.Client;
33  import com.allanbank.mongodb.client.MongoIteratorImpl;
34  import com.allanbank.mongodb.client.message.CursorableMessage;
35  import com.allanbank.mongodb.client.message.Query;
36  import com.allanbank.mongodb.client.message.Reply;
37  
38  /**
39   * Callback to convert a {@link CursorableMessage} {@link Reply} into a
40   * collection of {@link MongoIteratorImpl}.
41   * 
42   * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
43   *         mutated in incompatible ways between any two releases of the driver.
44   * @copyright 2014, Allanbank Consulting, Inc., All Rights Reserved
45   */
46  public final class MultipleCursorCallback extends
47          AbstractReplyCallback<Collection<MongoIterator<Document>>> implements
48          AddressAware {
49  
50      /** The server the original request was sent to. */
51      private volatile String myAddress;
52  
53      /** The original query. */
54      private final Client myClient;
55  
56      /** The original message to start the cursor. */
57      private final CursorableMessage myMessage;
58  
59      /** The reply to the query. */
60      private volatile Reply myReply;
61  
62      /**
63       * Initially set to false. Set to true for the first of address or reply
64       * being set. The second fails and {@link #trigger() triggers} the callback.
65       */
66      private final AtomicBoolean mySetOther;
67  
68      /**
69       * Create a new CursorCallback.
70       * 
71       * @param client
72       *            The client interface to the server.
73       * @param message
74       *            The original query.
75       * @param results
76       *            The callback to update once the first set of results are
77       *            ready.
78       */
79      public MultipleCursorCallback(final Client client,
80              final CursorableMessage message,
81              final Callback<Collection<MongoIterator<Document>>> results) {
82  
83          super(results);
84  
85          myClient = client;
86          myMessage = message;
87  
88          mySetOther = new AtomicBoolean(false);
89      }
90  
91      /**
92       * Returns the server the original request was sent to.
93       * 
94       * @return The server the original request was sent to.
95       */
96      public String getAddress() {
97          return myAddress;
98      }
99  
100     /**
101      * Sets the value of the server the original request was sent to.
102      * 
103      * @param address
104      *            The new value for the server the original request was sent to.
105      */
106     @Override
107     public void setAddress(final String address) {
108         myAddress = address;
109         trigger();
110     }
111 
112     /**
113      * {@inheritDoc}
114      * <p>
115      * Overridden to add the {@link Query} to the exception.
116      * </p>
117      * 
118      * @see AbstractReplyCallback#asError(Reply, int, int, String)
119      */
120     @Override
121     protected MongoDbException asError(final Reply reply, final int okValue,
122             final int errorNumber, final String errorMessage) {
123         return super.asError(reply, okValue, errorNumber, false, errorMessage,
124                 myMessage);
125     }
126 
127     /**
128      * {@inheritDoc}
129      * <p>
130      * Overridden to construct a {@link MongoIteratorImpl} around the reply.
131      * </p>
132      * 
133      * @see AbstractReplyCallback#convert(Reply)
134      */
135     @Override
136     protected Collection<MongoIterator<Document>> convert(final Reply reply)
137             throws MongoDbException {
138         final List<Reply> results = CommandCursorTranslator.translateAll(reply);
139         final List<MongoIterator<Document>> iters = new ArrayList<MongoIterator<Document>>(
140                 results.size());
141         for (final Reply r : results) {
142             iters.add(new MongoIteratorImpl(myMessage, myClient, myAddress, r));
143         }
144         return iters;
145     }
146 
147     /**
148      * {@inheritDoc}
149      * <p>
150      * Overridden to check if the server address has been set and if so then
151      * pass the converted reply to the {@link #getForwardCallback() forward
152      * callback}. Otherwise the call is dropped.
153      * </p>
154      */
155     @Override
156     protected void handle(final Reply reply) {
157         myReply = reply;
158         trigger();
159     }
160 
161     /**
162      * Triggers the callback when the address and reply are set.
163      */
164     private void trigger() {
165         if (!mySetOther.compareAndSet(false, true)) {
166             super.handle(myReply);
167         }
168     }
169 }