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 }