001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.server.replication.protocol; 018 019import java.io.IOException; 020import java.util.List; 021import java.util.zip.DataFormatException; 022 023import org.opends.server.core.ModifyOperation; 024import org.opends.server.core.ModifyOperationBasis; 025import org.opends.server.protocols.internal.InternalClientConnection; 026import org.opends.server.replication.common.CSN; 027import org.forgerock.opendj.ldap.DN; 028import org.opends.server.types.LDAPException; 029import org.opends.server.types.Modification; 030import org.opends.server.types.RawModification; 031import org.forgerock.opendj.ldap.ByteString; 032import org.opends.server.types.operation.PostOperationModifyOperation; 033 034import static org.opends.server.replication.protocol.OperationContext.*; 035 036/** 037 * Message used to send Modify information. 038 */ 039public class ModifyMsg extends ModifyCommonMsg 040{ 041 /** 042 * Creates a new Modify message from a ModifyOperation. 043 * 044 * @param op The operation to use for building the message 045 */ 046 ModifyMsg(PostOperationModifyOperation op) 047 { 048 super((OperationContext) op.getAttachment(OperationContext.SYNCHROCONTEXT), 049 op.getEntryDN()); 050 encodedMods = encodeMods(op.getModifications()); 051 } 052 053 /** 054 * Creates a new Modify message using the provided information. 055 * 056 * @param csn The CSN for the operation. 057 * @param dn The baseDN of the operation. 058 * @param mods The mod of the operation. 059 * @param entryUUID The unique id of the entry on which the modification 060 * needs to apply. 061 */ 062 public ModifyMsg(CSN csn, DN dn, List<Modification> mods, String entryUUID) 063 { 064 super(new ModifyContext(csn, entryUUID), dn); 065 this.encodedMods = encodeMods(mods); 066 } 067 068 /** 069 * Creates a new Modify message from a byte[]. 070 * 071 * @param in The byte[] from which the operation must be read. 072 * @throws DataFormatException If the input byte[] is not a valid ModifyMsg 073 */ 074 ModifyMsg(byte[] in) throws DataFormatException 075 { 076 final ByteArrayScanner scanner = new ByteArrayScanner(in); 077 decodeHeader(scanner, MSG_TYPE_MODIFY, MSG_TYPE_MODIFY_V1); 078 079 if (protocolVersion <= 3) 080 { 081 decodeBody_V123(scanner); 082 } 083 else 084 { 085 decodeBody_V4(scanner); 086 } 087 088 if (protocolVersion==ProtocolVersion.getCurrentVersion()) 089 { 090 bytes = in; 091 } 092 } 093 094 /** 095 * Creates a new Modify message from a V1 byte[]. 096 * 097 * @param in The byte[] from which the operation must be read. 098 * @return The created ModifyMsg. 099 * @throws DataFormatException If the input byte[] is not a valid ModifyMsg 100 */ 101 static ModifyMsg createV1(byte[] in) throws DataFormatException 102 { 103 ModifyMsg msg = new ModifyMsg(in); 104 105 // bytes is only for current version (of the protocol) bytes ! 106 msg.bytes = null; 107 108 return msg; 109 } 110 111 /** {@inheritDoc} */ 112 @Override 113 public ModifyOperation createOperation(InternalClientConnection connection, 114 DN newDN) throws LDAPException, IOException, DataFormatException 115 { 116 if (newDN == null) 117 { 118 newDN = getDN(); 119 } 120 121 List<RawModification> ldapmods = decodeRawMods(encodedMods); 122 123 ModifyOperation mod = new ModifyOperationBasis(connection, 124 InternalClientConnection.nextOperationID(), 125 InternalClientConnection.nextMessageID(), null, 126 ByteString.valueOfUtf8(newDN.toString()), ldapmods); 127 ModifyContext ctx = new ModifyContext(getCSN(), getEntryUUID()); 128 mod.setAttachment(SYNCHROCONTEXT, ctx); 129 return mod; 130 } 131 132 /** {@inheritDoc} */ 133 @Override 134 public String toString() 135 { 136 if (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V1) 137 { 138 return "ModifyMsg content: " + 139 " protocolVersion: " + protocolVersion + 140 " dn: " + dn + 141 " csn: " + csn + 142 " uniqueId: " + entryUUID + 143 " assuredFlag: " + assuredFlag + 144 (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V2 ? 145 " assuredMode: " + assuredMode + 146 " safeDataLevel: " + safeDataLevel + 147 " size: " + encodedMods.length 148 : ""); 149 /* Do not append mods, they can be too long */ 150 } 151 return "!!! Unknown version: " + protocolVersion + "!!!"; 152 } 153 154 /** {@inheritDoc} */ 155 @Override 156 public int size() 157 { 158 // The ModifyMsg can be very large when added or deleted attribute 159 // values are very large. 160 // We therefore need to count the whole encoded msg. 161 return encodedMods.length + encodedEclIncludes.length + headerSize(); 162 } 163 164 // ============ 165 // Msg Encoding 166 // ============ 167 168 /** {@inheritDoc} */ 169 @Override 170 public byte[] getBytes_V1() 171 { 172 final ByteArrayBuilder builder = encodeHeader_V1(MSG_TYPE_MODIFY_V1); 173 builder.appendByteArray(encodedMods); 174 return builder.toByteArray(); 175 } 176 177 /** {@inheritDoc} */ 178 @Override 179 public byte[] getBytes_V23() 180 { 181 final ByteArrayBuilder builder = 182 encodeHeader(MSG_TYPE_MODIFY, ProtocolVersion.REPLICATION_PROTOCOL_V3); 183 builder.appendByteArray(encodedMods); 184 return builder.toByteArray(); 185 } 186 187 /** {@inheritDoc} */ 188 @Override 189 public byte[] getBytes_V45(short protocolVersion) 190 { 191 final ByteArrayBuilder builder = 192 encodeHeader(MSG_TYPE_MODIFY, protocolVersion); 193 builder.appendIntUTF8(encodedMods.length); 194 builder.appendZeroTerminatedByteArray(encodedMods); 195 builder.appendIntUTF8(encodedEclIncludes.length); 196 builder.appendZeroTerminatedByteArray(encodedEclIncludes); 197 return builder.toByteArray(); 198 } 199 200 // ============ 201 // Msg decoding 202 // ============ 203 204 private void decodeBody_V123(ByteArrayScanner scanner) 205 throws DataFormatException 206 { 207 encodedMods = scanner.remainingBytes(); 208 } 209 210 private void decodeBody_V4(ByteArrayScanner scanner) 211 throws DataFormatException 212 { 213 final int modsLen = scanner.nextIntUTF8(); 214 this.encodedMods = scanner.nextByteArray(modsLen); 215 scanner.skipZeroSeparator(); 216 217 final int eclAttrLen = scanner.nextIntUTF8(); 218 encodedEclIncludes = scanner.nextByteArray(eclAttrLen); 219 } 220}