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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.server.replication.protocol;
028
029import java.util.zip.DataFormatException;
030
031import org.opends.server.controls.SubtreeDeleteControl;
032import org.opends.server.core.DeleteOperation;
033import org.opends.server.core.DeleteOperationBasis;
034import org.opends.server.protocols.internal.InternalClientConnection;
035import org.opends.server.replication.common.CSN;
036import org.opends.server.types.DN;
037import org.opends.server.types.operation.PostOperationDeleteOperation;
038
039import static org.opends.server.replication.protocol.OperationContext.*;
040
041/**
042 * Object used when sending delete information to replication servers.
043 */
044public class DeleteMsg extends LDAPUpdateMsg
045{
046  private String initiatorsName;
047
048  /** Whether the DEL operation is a subtree DEL. */
049  private boolean isSubtreeDelete;
050
051  /**
052   * Creates a new delete message.
053   *
054   * @param operation the Operation from which the message must be created.
055   */
056  DeleteMsg(PostOperationDeleteOperation operation)
057  {
058    super((OperationContext) operation.getAttachment(SYNCHROCONTEXT),
059           operation.getEntryDN());
060    try
061    {
062      isSubtreeDelete =
063          operation.getRequestControl(SubtreeDeleteControl.DECODER) != null;
064    }
065    catch(Exception e)
066    {/* do nothing */}
067  }
068
069  /**
070   * Creates a new delete message.
071   *
072   * @param dn           The dn with which the message must be created.
073   * @param csn          The CSN with which the message must be created.
074   * @param entryUUID    The unique id with which the message must be created.
075   */
076  public DeleteMsg(DN dn, CSN csn, String entryUUID)
077  {
078    super(new DeleteContext(csn, entryUUID), dn);
079  }
080
081  /**
082   * Creates a new Add message from a byte[].
083   *
084   * @param in The byte[] from which the operation must be read.
085   * @throws DataFormatException The input byte[] is not a valid DeleteMsg
086   */
087  DeleteMsg(byte[] in) throws DataFormatException
088  {
089    final ByteArrayScanner scanner = new ByteArrayScanner(in);
090    decodeHeader(scanner, MSG_TYPE_DELETE, MSG_TYPE_DELETE_V1);
091
092    if (protocolVersion >= 4)
093    {
094      decodeBody_V4(scanner);
095    }
096    else
097    {
098      // Keep the previous protocol version behavior - when we don't know the
099      // truth, we assume 'subtree'
100      isSubtreeDelete = true;
101    }
102  }
103
104  /** {@inheritDoc} */
105  @Override
106  public DeleteOperation createOperation(InternalClientConnection connection,
107      DN newDN)
108  {
109    DeleteOperation del =  new DeleteOperationBasis(connection,
110        InternalClientConnection.nextOperationID(),
111        InternalClientConnection.nextMessageID(), null, newDN);
112
113    if (isSubtreeDelete)
114    {
115      del.addRequestControl(new SubtreeDeleteControl(false));
116    }
117
118    DeleteContext ctx = new DeleteContext(getCSN(), getEntryUUID());
119    del.setAttachment(SYNCHROCONTEXT, ctx);
120    return del;
121  }
122
123  // ============
124  // Msg encoding
125  // ============
126
127  /** {@inheritDoc} */
128  @Override
129  public byte[] getBytes_V1()
130  {
131    return encodeHeader_V1(MSG_TYPE_DELETE_V1)
132        .toByteArray();
133  }
134
135  /** {@inheritDoc} */
136  @Override
137  public byte[] getBytes_V23()
138  {
139    return encodeHeader(MSG_TYPE_DELETE,ProtocolVersion.REPLICATION_PROTOCOL_V3)
140        .toByteArray();
141  }
142
143  /** {@inheritDoc} */
144  @Override
145  public byte[] getBytes_V45(short protocolVersion)
146  {
147    final ByteArrayBuilder builder =
148        encodeHeader(MSG_TYPE_DELETE, protocolVersion);
149    builder.appendString(initiatorsName);
150    builder.appendIntUTF8(encodedEclIncludes.length);
151    builder.appendZeroTerminatedByteArray(encodedEclIncludes);
152    builder.appendBoolean(isSubtreeDelete);
153    return builder.toByteArray();
154  }
155
156  // ============
157  // Msg decoding
158  // ============
159
160  private void decodeBody_V4(ByteArrayScanner scanner)
161      throws DataFormatException
162  {
163    initiatorsName = scanner.nextString();
164
165    final int eclAttrLen = scanner.nextIntUTF8();
166    encodedEclIncludes = scanner.nextByteArray(eclAttrLen);
167    scanner.skipZeroSeparator();
168
169    isSubtreeDelete = scanner.nextBoolean();
170  }
171
172  /** {@inheritDoc} */
173  @Override
174  public String toString()
175  {
176    if (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V1)
177    {
178      return "DeleteMsg content: " +
179        " protocolVersion: " + protocolVersion +
180        " dn: " + dn +
181        " csn: " + csn +
182        " uniqueId: " + entryUUID +
183        " assuredFlag: " + assuredFlag +
184        (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V2 ?
185          " assuredMode: " + assuredMode +
186          " safeDataLevel: " + safeDataLevel
187          : "");
188    }
189    return "!!! Unknown version: " + protocolVersion + "!!!";
190  }
191
192  /** {@inheritDoc} */
193  @Override
194  public int size()
195  {
196    return encodedEclIncludes.length + headerSize();
197  }
198
199  /**
200   * Set the initiator's name of this change.
201   *
202   * @param iname the initiator's name.
203   */
204  public void setInitiatorsName(String iname)
205  {
206    initiatorsName = iname;
207  }
208
209  /**
210   * Get the initiator's name of this change.
211   * @return the initiator's name.
212   */
213  public String getInitiatorsName()
214  {
215    return initiatorsName;
216  }
217
218  /**
219   * Set the subtree flag.
220   * @param subtreeDelete the subtree flag.
221   */
222  public void setSubtreeDelete(boolean subtreeDelete)
223  {
224    this.isSubtreeDelete = subtreeDelete;
225  }
226
227  /**
228   * Get the subtree flag.
229   * @return the subtree flag.
230   */
231  public boolean isSubtreeDelete()
232  {
233    return this.isSubtreeDelete;
234  }
235}