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 static org.opends.server.replication.protocol.OperationContext.*; 030 031 import org.opends.server.core.ModifyOperationBasis; 032 import org.opends.server.protocols.asn1.ASN1Exception; 033 import org.opends.server.protocols.asn1.ASN1OctetString; 034 import org.opends.server.protocols.ldap.LDAPAttribute; 035 import org.opends.server.protocols.ldap.LDAPModification; 036 import org.opends.server.protocols.asn1.ASN1Element; 037 import org.opends.server.protocols.internal.InternalClientConnection; 038 import org.opends.server.replication.common.ChangeNumber; 039 import org.opends.server.replication.plugin.Historical; 040 import org.opends.server.types.AbstractOperation; 041 import org.opends.server.types.Attribute; 042 import org.opends.server.types.AttributeType; 043 import org.opends.server.types.AttributeUsage; 044 import org.opends.server.types.DN; 045 import org.opends.server.types.LDAPException; 046 import org.opends.server.types.Modification; 047 import org.opends.server.types.RawModification; 048 import org.opends.server.types.operation.PostOperationModifyOperation; 049 050 051 import java.io.UnsupportedEncodingException; 052 import java.util.ArrayList; 053 import java.util.List; 054 import java.util.zip.DataFormatException; 055 056 /** 057 * Message used to send Modify information. 058 */ 059 public class ModifyMsg extends UpdateMessage 060 { 061 private static final long serialVersionUID = -4905520652801395185L; 062 private byte[] encodedMods = null; 063 private byte[] encodedMsg = null; 064 065 /** 066 * Creates a new Modify message from a ModifyOperation. 067 * 068 * @param op The operation to use for building the message 069 */ 070 public ModifyMsg(PostOperationModifyOperation op) 071 { 072 super((OperationContext) op.getAttachment(OperationContext.SYNCHROCONTEXT), 073 op.getRawEntryDN().stringValue()); 074 encodedMods = modsToByte(op.getModifications()); 075 } 076 077 /** 078 * Creates a new Modify message using the provided information. 079 * 080 * @param changeNumber The ChangeNumber for the operation. 081 * @param dn The baseDN of the operation. 082 * @param mods The mod of the operation. 083 * @param entryuuid The unique id of the entry on which the modification 084 * needs to apply. 085 */ 086 public ModifyMsg(ChangeNumber changeNumber, DN dn, List<Modification> mods, 087 String entryuuid) 088 { 089 super(new ModifyContext(changeNumber, entryuuid), 090 dn.toNormalizedString()); 091 this.encodedMods = modsToByte(mods); 092 } 093 094 /** 095 * Creates a new Modify message from a byte[]. 096 * 097 * @param in The byte[] from which the operation must be read. 098 * @throws DataFormatException If the input byte[] is not a valid modifyMsg 099 * @throws UnsupportedEncodingException If UTF8 is not supported by the JVM. 100 */ 101 public ModifyMsg(byte[] in) throws DataFormatException, 102 UnsupportedEncodingException 103 { 104 super(in); 105 encodedMsg = in; 106 } 107 108 /** 109 * Get the byte array representation of this Message. 110 * 111 * @return The byte array representation of this Message. 112 * 113 * @throws UnsupportedEncodingException When the encoding of the message 114 * failed because the UTF-8 encoding is not supported. 115 */ 116 @Override 117 public byte[] getBytes() throws UnsupportedEncodingException 118 { 119 if (encodedMsg == null) 120 { 121 encode(); 122 } 123 return encodedMsg; 124 } 125 126 /** 127 * {@inheritDoc} 128 */ 129 @Override 130 public AbstractOperation createOperation(InternalClientConnection connection, 131 String newDn) 132 throws LDAPException, ASN1Exception, DataFormatException 133 { 134 if (encodedMods == null) 135 { 136 decode(); 137 } 138 139 if (newDn == null) 140 newDn = getDn(); 141 142 ArrayList<RawModification> ldapmods; 143 144 ArrayList<ASN1Element> mods = null; 145 146 mods = ASN1Element.decodeElements(encodedMods); 147 148 ldapmods = new ArrayList<RawModification>(mods.size()); 149 for (ASN1Element elem : mods) 150 ldapmods.add(LDAPModification.decode(elem)); 151 152 ModifyOperationBasis mod = new ModifyOperationBasis(connection, 153 InternalClientConnection.nextOperationID(), 154 InternalClientConnection.nextMessageID(), null, 155 new ASN1OctetString(newDn), ldapmods); 156 ModifyContext ctx = new ModifyContext(getChangeNumber(), getUniqueId()); 157 mod.setAttachment(SYNCHROCONTEXT, ctx); 158 return mod; 159 } 160 161 /** 162 * Encode the Msg information into a byte array. 163 * 164 * @throws UnsupportedEncodingException If utf8 is not suported. 165 */ 166 private void encode() throws UnsupportedEncodingException 167 { 168 /* encode the header in a byte[] large enough to also contain the mods */ 169 encodedMsg = encodeHeader(MSG_TYPE_MODIFY_REQUEST, encodedMods.length + 1); 170 int pos = encodedMsg.length - (encodedMods.length + 1); 171 172 /* add the mods */ 173 pos = addByteArray(encodedMods, encodedMsg, pos); 174 } 175 176 /** 177 * Decode the encodedMsg into mods and dn. 178 * 179 * @throws DataFormatException when the encodedMsg is no a valid modify. 180 */ 181 private void decode() throws DataFormatException 182 { 183 int pos = decodeHeader(MSG_TYPE_MODIFY_REQUEST, encodedMsg); 184 185 /* Read the mods : all the remaining bytes but the terminating 0 */ 186 encodedMods = new byte[encodedMsg.length-pos-1]; 187 int i =0; 188 while (pos<encodedMsg.length-1) 189 { 190 encodedMods[i++] = encodedMsg[pos++]; 191 } 192 } 193 194 /** 195 * Encode an ArrayList of Modification into a byte[] suitable 196 * for storage in a database or send on the network. 197 * 198 * @param mods the ArrayList of Modification to be encoded. 199 */ 200 private byte[] modsToByte(List<Modification> mods) 201 { 202 ArrayList<ASN1Element> modsASN1; 203 204 modsASN1 = new ArrayList<ASN1Element>(mods.size()); 205 for (Modification mod : mods) 206 { 207 Attribute attr = mod.getAttribute(); 208 AttributeType type = attr.getAttributeType(); 209 if (type != null ) 210 { 211 if (AttributeUsage.DSA_OPERATION.equals(type.getUsage())) 212 { 213 // Attributes with a dsaOperation usage should not be synchronized. 214 // skip them. 215 continue; 216 } 217 } 218 219 if (!Historical.isHistoricalAttribute(attr)) 220 { 221 LDAPModification ldapmod = new LDAPModification( 222 mod.getModificationType(), new LDAPAttribute(mod.getAttribute())); 223 modsASN1.add(ldapmod.encode()); 224 } 225 } 226 227 return ASN1Element.encodeValue(modsASN1); 228 } 229 230 /** 231 * {@inheritDoc} 232 */ 233 @Override 234 public String toString() 235 { 236 return("MOD " + getDn() + " " + getChangeNumber()); 237 } 238 }