1 /*
2 * #%L
3 * AbstractMessage.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 package com.allanbank.mongodb.client.message;
21
22 import com.allanbank.mongodb.ReadPreference;
23 import com.allanbank.mongodb.bson.io.BsonOutputStream;
24 import com.allanbank.mongodb.bson.io.BufferingBsonOutputStream;
25 import com.allanbank.mongodb.client.Message;
26 import com.allanbank.mongodb.client.Operation;
27 import com.allanbank.mongodb.client.VersionRange;
28
29 /**
30 * Base class for a MongoDB message.
31 *
32 * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
33 * mutated in incompatible ways between any two releases of the driver.
34 * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved
35 */
36 public abstract class AbstractMessage implements Message {
37
38 /** The size of a message header. */
39 public static final int HEADER_SIZE = Header.SIZE;
40
41 /** The name of the collection to operate on. */
42 protected String myCollectionName;
43
44 /** The name of the database to operate on. */
45 protected String myDatabaseName;
46
47 /** The details on which servers may be sent the message. */
48 private final ReadPreference myReadPreference;
49
50 /** The required version of the server to support processing the message. */
51 private final VersionRange myRequiredServerVersionRange;
52
53 /**
54 * Create a new AbstractMessage.
55 */
56 public AbstractMessage() {
57 myDatabaseName = "";
58 myCollectionName = "";
59 myReadPreference = ReadPreference.PRIMARY;
60 myRequiredServerVersionRange = null;
61 }
62
63 /**
64 * Create a new AbstractMessage.
65 *
66 * @param databaseName
67 * The name of the database.
68 * @param collectionName
69 * The name of the collection.
70 * @param readPreference
71 * The preferences for which servers to send the message.
72 */
73 public AbstractMessage(final String databaseName,
74 final String collectionName, final ReadPreference readPreference) {
75 myDatabaseName = databaseName;
76 myCollectionName = collectionName;
77 myReadPreference = readPreference;
78 myRequiredServerVersionRange = null;
79 }
80
81 /**
82 * Creates a new AbstractMessage.
83 *
84 * @param databaseName
85 * The name of the database.
86 * @param collectionName
87 * The name of the collection.
88 * @param readPreference
89 * The preferences for which servers to send the message.
90 * @param versionRange
91 * The required range of versions of the server to support
92 * processing the message.
93 */
94 public AbstractMessage(final String databaseName,
95 final String collectionName, final ReadPreference readPreference,
96 final VersionRange versionRange) {
97 myDatabaseName = databaseName;
98 myCollectionName = collectionName;
99 myReadPreference = readPreference;
100 myRequiredServerVersionRange = versionRange;
101 }
102
103 /**
104 * Determines if the passed object is of this same type as this object and
105 * if so that its fields are equal.
106 *
107 * @param object
108 * The object to compare to.
109 *
110 * @see java.lang.Object#equals(java.lang.Object)
111 */
112 @Override
113 public boolean equals(final Object object) {
114 boolean result = false;
115
116 // This should never return false as derived classes should have
117 // verified.
118 if ((object != null) && (getClass() == object.getClass())) {
119 final AbstractMessage other = (AbstractMessage) object;
120
121 result = myCollectionName.equals(other.myCollectionName)
122 && myDatabaseName.equals(other.myDatabaseName)
123 && myReadPreference.equals(other.myReadPreference);
124 }
125 return result;
126 }
127
128 /**
129 * Returns the name of the collection.
130 *
131 * @return The name of the collection.
132 */
133 public String getCollectionName() {
134 return myCollectionName;
135 }
136
137 /**
138 * Returns the name of the database.
139 *
140 * @return The name of the database.
141 */
142 @Override
143 public String getDatabaseName() {
144 return myDatabaseName;
145 }
146
147 /**
148 * {@inheritDoc}
149 * <p>
150 * Returns the message's read preference.
151 * </p>
152 */
153 @Override
154 public ReadPreference getReadPreference() {
155 return myReadPreference;
156 }
157
158 /**
159 * {@inheritDoc}
160 */
161 @Override
162 public VersionRange getRequiredVersionRange() {
163 return myRequiredServerVersionRange;
164 }
165
166 /**
167 * Computes a reasonable hash code.
168 *
169 * @return The hash code value.
170 */
171 @Override
172 public int hashCode() {
173 int result = 1;
174 result = (31 * result) + myCollectionName.hashCode();
175 result = (31 * result) + myDatabaseName.hashCode();
176 result = (31 * result) + myReadPreference.hashCode();
177 return result;
178 }
179
180 /**
181 * Writes the MsgHeader messageLengthField in the header <tt>stream</tt>.
182 *
183 * <pre>
184 * <code>
185 * struct MsgHeader {
186 * int32 messageLength; // total message size, including this
187 * int32 requestID; // identifier for this message
188 * int32 responseTo; // requestID from the original request
189 * // (used in reponses from db)
190 * int32 opCode; // request type - see table below
191 * }
192 * </code>
193 * </pre>
194 *
195 * @param stream
196 * The stream to write to.
197 * @param start
198 * The position of the start of the header for the message.
199 */
200 protected void finishHeader(final BufferingBsonOutputStream stream,
201 final long start) {
202
203 final long end = stream.getPosition();
204
205 stream.writeIntAt(start, (int) (end - start));
206 }
207
208 /**
209 * Initializes the database and collection name from the full database name.
210 *
211 * @param name
212 * The full database name.
213 */
214 protected void init(final String name) {
215 final int firstDot = name.indexOf('.');
216 if (firstDot >= 0) {
217 myDatabaseName = name.substring(0, firstDot);
218 myCollectionName = name.substring(firstDot + 1);
219 }
220 else {
221 myDatabaseName = name;
222 }
223 }
224
225 /**
226 * Writes the MsgHeader to the <tt>stream</tt>.
227 *
228 * <pre>
229 * <code>
230 * struct MsgHeader {
231 * int32 messageLength; // total message size, including this
232 * int32 requestID; // identifier for this message
233 * int32 responseTo; // requestID from the original request
234 * // (used in reponses from db)
235 * int32 opCode; // request type - see table below
236 * }
237 * </code>
238 * </pre>
239 *
240 * @param stream
241 * The stream to write to.
242 * @param requestId
243 * The requestID from above.
244 * @param responseTo
245 * The responseTo from above.
246 * @param op
247 * The operation for the opCode field.
248 * @param length
249 * The length of the message including the header.
250 */
251 protected void writeHeader(final BsonOutputStream stream,
252 final int requestId, final int responseTo, final Operation op,
253 final int length) {
254 stream.writeInt(length);
255 stream.writeInt(requestId);
256 stream.writeInt(responseTo);
257 stream.writeInt(op.getCode());
258 }
259
260 /**
261 * Writes the MsgHeader to the <tt>stream</tt>.
262 *
263 * <pre>
264 * <code>
265 * struct MsgHeader {
266 * int32 messageLength; // total message size, including this
267 * int32 requestID; // identifier for this message
268 * int32 responseTo; // requestID from the original request
269 * // (used in reponses from db)
270 * int32 opCode; // request type - see table below
271 * }
272 * </code>
273 * </pre>
274 *
275 * @param stream
276 * The stream to write to.
277 * @param requestId
278 * The requestID from above.
279 * @param responseTo
280 * The responseTo from above.
281 * @param op
282 * The operation for the opCode field.
283 * @return The position of the start of the header for the message.
284 */
285 protected long writeHeader(final BufferingBsonOutputStream stream,
286 final int requestId, final int responseTo, final Operation op) {
287
288 final long start = stream.getPosition();
289
290 stream.writeInt(0);
291 stream.writeInt(requestId);
292 stream.writeInt(responseTo);
293 stream.writeInt(op.getCode());
294
295 return start;
296 }
297
298 }