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