001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.replication.protocol;
028
029 import java.io.Serializable;
030 import java.io.UnsupportedEncodingException;
031 import java.util.zip.DataFormatException;
032
033 import org.opends.server.protocols.asn1.ASN1Exception;
034 import org.opends.server.protocols.internal.InternalClientConnection;
035 import org.opends.server.replication.common.ChangeNumber;
036 import org.opends.server.types.AbstractOperation;
037 import org.opends.server.types.LDAPException;
038 import org.opends.server.types.operation.PostOperationAddOperation;
039 import org.opends.server.types.operation.PostOperationDeleteOperation;
040 import org.opends.server.types.operation.PostOperationModifyDNOperation;
041 import org.opends.server.types.operation.PostOperationModifyOperation;
042 import org.opends.server.types.operation.PostOperationOperation;
043
044 /**
045 * Abstract class that must be extended to define a message
046 * used for sending Updates between servers.
047 */
048 public abstract class UpdateMessage extends ReplicationMessage
049 implements Serializable,
050 Comparable<UpdateMessage>
051 {
052 /**
053 * The ChangeNumber of this update.
054 */
055 private ChangeNumber changeNumber;
056
057 /**
058 * The DN on which the update was originally done.
059 */
060 private String dn = null;
061
062 /**
063 * True when the update must use assured replication.
064 */
065 private boolean assuredFlag = false;
066
067 /**
068 * The UniqueId of the entry that was updated.
069 */
070 private String UniqueId;
071
072 /**
073 * Creates a new UpdateMessage with the given informations.
074 *
075 * @param ctx The replication Context of the operation for which the
076 * update message must be created,.
077 * @param dn The dn of the entry on which the change
078 * that caused the creation of this object happened
079 */
080 public UpdateMessage(OperationContext ctx, String dn)
081 {
082 this.changeNumber = ctx.getChangeNumber();
083 this.UniqueId = ctx.getEntryUid();
084 this.dn = dn;
085 }
086
087 /**
088 * Creates a new UpdateMessage from an ecoded byte array.
089 *
090 * @param in The encoded byte array containind the UpdateMessage.
091 * @throws DataFormatException if the encoded byte array is not valid.
092 * @throws UnsupportedEncodingException if UTF-8 is not supprted.
093 */
094 protected UpdateMessage(byte[] in) throws DataFormatException,
095 UnsupportedEncodingException
096 {
097 /* read the changeNumber */
098 int pos = 1;
099 int length = getNextLength(in, pos);
100 String changenumberStr = new String(in, pos, length, "UTF-8");
101 this.changeNumber = new ChangeNumber(changenumberStr);
102 }
103
104 /**
105 * Generates an Update Message which the provided information.
106 *
107 * @param op The operation for which the message must be created.
108 * @param isAssured flag indicating if the operation is an assured operation.
109 * @return The generated message.
110 */
111 public static UpdateMessage generateMsg(
112 PostOperationOperation op, boolean isAssured)
113 {
114 UpdateMessage msg = null;
115 switch (op.getOperationType())
116 {
117 case MODIFY :
118 msg = new ModifyMsg((PostOperationModifyOperation) op);
119 if (isAssured)
120 msg.setAssured();
121 break;
122
123 case ADD:
124 msg = new AddMsg((PostOperationAddOperation) op);
125 if (isAssured)
126 msg.setAssured();
127 break;
128
129 case DELETE :
130 msg = new DeleteMsg((PostOperationDeleteOperation) op);
131 if (isAssured)
132 msg.setAssured();
133 break;
134
135 case MODIFY_DN :
136 msg = new ModifyDNMsg( (PostOperationModifyDNOperation) op);
137 if (isAssured)
138 msg.setAssured();
139 break;
140 }
141
142 return msg;
143 }
144
145 /**
146 * Get the ChangeNumber from the message.
147 * @return the ChangeNumber
148 */
149 public ChangeNumber getChangeNumber()
150 {
151 return changeNumber;
152 }
153
154 /**
155 * Get the DN on which the operation happened.
156 *
157 * @return The DN on which the operations happened.
158 */
159 public String getDn()
160 {
161 return dn;
162 }
163
164 /**
165 * Set the DN.
166 * @param dn The dn that must now be used for this message.
167 */
168 public void setDn(String dn)
169 {
170 this.dn = dn;
171 }
172
173 /**
174 * Get the Unique Identifier of the entry on which the operation happened.
175 *
176 * @return The Unique Identifier of the entry on which the operation happened.
177 */
178 public String getUniqueId()
179 {
180 return UniqueId;
181 }
182
183 /**
184 * Get a boolean indicating if the Update must be processed as an
185 * Asynchronous or as an assured replication.
186 *
187 * @return Returns the assuredFlag.
188 */
189 public boolean isAssured()
190 {
191 return assuredFlag;
192 }
193
194 /**
195 * Set the Update message as an assured message.
196 */
197 public void setAssured()
198 {
199 assuredFlag = true;
200 }
201
202 /**
203 * {@inheritDoc}
204 */
205 @Override
206 public boolean equals(Object obj)
207 {
208 if (obj != null)
209 {
210 if (obj.getClass() != this.getClass())
211 return false;
212 return changeNumber.equals(((UpdateMessage) obj).changeNumber);
213 }
214 else
215 {
216 return false;
217 }
218 }
219
220 /**
221 * {@inheritDoc}
222 */
223 @Override
224 public int hashCode()
225 {
226 return changeNumber.hashCode();
227 }
228
229 /**
230 * {@inheritDoc}
231 */
232 public int compareTo(UpdateMessage msg)
233 {
234 return changeNumber.compareTo(msg.getChangeNumber());
235 }
236
237 /**
238 * Create and Operation from the message.
239 *
240 * @param conn connection to use when creating the message
241 * @return the created Operation
242 * @throws LDAPException In case of LDAP decoding exception.
243 * @throws ASN1Exception In case of ASN1 decoding exception.
244 * @throws DataFormatException In case of bad msg format.
245 */
246 public AbstractOperation createOperation(InternalClientConnection conn)
247 throws LDAPException, ASN1Exception, DataFormatException
248 {
249 return createOperation(conn, dn);
250 }
251
252
253 /**
254 * Create and Operation from the message using the provided DN.
255 *
256 * @param conn connection to use when creating the message.
257 * @param newDn the DN to use when creating the operation.
258 * @return the created Operation.
259 * @throws LDAPException In case of LDAP decoding exception.
260 * @throws ASN1Exception In case of ASN1 decoding exception.
261 * @throws DataFormatException In case of bad msg format.
262 */
263 public abstract AbstractOperation createOperation(
264 InternalClientConnection conn, String newDn)
265 throws LDAPException, ASN1Exception, DataFormatException;
266
267 /**
268 * Encode the common header for all the UpdateMessage.
269 *
270 * @param type the type of UpdateMessage to encode.
271 * @param additionalLength additional length needed to encode the remaining
272 * part of the UpdateMessage.
273 * @return a byte array containing the common header and enough space to
274 * encode the reamining bytes of the UpdateMessage as was specified
275 * by the additionalLength.
276 * (byte array length = common header length + additionalLength)
277 * @throws UnsupportedEncodingException if UTF-8 is not supported.
278 */
279 public byte[] encodeHeader(byte type, int additionalLength)
280 throws UnsupportedEncodingException
281 {
282 byte[] byteDn = dn.getBytes("UTF-8");
283 byte[] changeNumberByte =
284 this.getChangeNumber().toString().getBytes("UTF-8");
285 byte[] byteEntryuuid = getUniqueId().getBytes("UTF-8");
286
287 /* The message header is stored in the form :
288 * <operation type>changenumber><dn><assured><entryuuid><change>
289 * the length of result byte array is therefore :
290 * 1 + change number length + 1 + dn length + 1 + 1 +
291 * uuid length + 1 + additional_length
292 */
293 int length = 5 + changeNumberByte.length + byteDn.length
294 + byteEntryuuid.length + additionalLength;
295
296 byte[] encodedMsg = new byte[length];
297
298 /* put the type of the operation */
299 encodedMsg[0] = type;
300 int pos = 1;
301
302 /* put the ChangeNumber */
303 pos = addByteArray(changeNumberByte, encodedMsg, pos);
304
305 /* put the assured information */
306 encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
307
308 /* put the DN and a terminating 0 */
309 pos = addByteArray(byteDn, encodedMsg, pos);
310
311 /* put the entry uuid and a terminating 0 */
312 pos = addByteArray(byteEntryuuid, encodedMsg, pos);
313
314 return encodedMsg;
315 }
316
317 /**
318 * Decode the Header part of this Update Message, and check its type.
319 *
320 * @param type The type of this Update Message.
321 * @param encodedMsg the encoded form of the UpdateMessage.
322 * @return the position at which the remaining part of the message starts.
323 * @throws DataFormatException if the encodedMsg does not contain a valid
324 * common header.
325 */
326 public int decodeHeader(byte type, byte [] encodedMsg)
327 throws DataFormatException
328 {
329 /* first byte is the type */
330 if (encodedMsg[0] != type)
331 throw new DataFormatException("byte[] is not a valid msg");
332
333 try
334 {
335 /* read the changeNumber */
336 int pos = 1;
337 int length = getNextLength(encodedMsg, pos);
338 String changenumberStr = new String(encodedMsg, pos, length, "UTF-8");
339 pos += length + 1;
340 changeNumber = new ChangeNumber(changenumberStr);
341
342 /* read the assured information */
343 if (encodedMsg[pos++] == 1)
344 assuredFlag = true;
345 else
346 assuredFlag = false;
347
348 /* read the dn */
349 length = getNextLength(encodedMsg, pos);
350 dn = new String(encodedMsg, pos, length, "UTF-8");
351 pos += length + 1;
352
353 /* read the entryuuid */
354 length = getNextLength(encodedMsg, pos);
355 UniqueId = new String(encodedMsg, pos, length, "UTF-8");
356 pos += length + 1;
357
358 return pos;
359 } catch (UnsupportedEncodingException e)
360 {
361 throw new DataFormatException("UTF-8 is not supported by this jvm.");
362 }
363
364 }
365 }