/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. */ package org.opends.server.replication.protocol; import java.util.zip.DataFormatException; import org.opends.server.controls.SubtreeDeleteControl; import org.opends.server.core.DeleteOperation; import org.opends.server.core.DeleteOperationBasis; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.replication.common.CSN; import org.forgerock.opendj.ldap.DN; import org.opends.server.types.operation.PostOperationDeleteOperation; import static org.opends.server.replication.protocol.OperationContext.*; /** * Object used when sending delete information to replication servers. */ public class DeleteMsg extends LDAPUpdateMsg { private String initiatorsName; /** Whether the DEL operation is a subtree DEL. */ private boolean isSubtreeDelete; /** * Creates a new delete message. * * @param operation the Operation from which the message must be created. */ DeleteMsg(PostOperationDeleteOperation operation) { super((OperationContext) operation.getAttachment(SYNCHROCONTEXT), operation.getEntryDN()); try { isSubtreeDelete = operation.getRequestControl(SubtreeDeleteControl.DECODER) != null; } catch(Exception e) {/* do nothing */} } /** * Creates a new delete message. * * @param dn The dn with which the message must be created. * @param csn The CSN with which the message must be created. * @param entryUUID The unique id with which the message must be created. */ public DeleteMsg(DN dn, CSN csn, String entryUUID) { super(new DeleteContext(csn, entryUUID), dn); } /** * Creates a new Add message from a byte[]. * * @param in The byte[] from which the operation must be read. * @throws DataFormatException The input byte[] is not a valid DeleteMsg */ DeleteMsg(byte[] in) throws DataFormatException { final ByteArrayScanner scanner = new ByteArrayScanner(in); decodeHeader(scanner, MSG_TYPE_DELETE, MSG_TYPE_DELETE_V1); if (protocolVersion >= 4) { decodeBody_V4(scanner); } else { // Keep the previous protocol version behavior - when we don't know the // truth, we assume 'subtree' isSubtreeDelete = true; } } /** {@inheritDoc} */ @Override public DeleteOperation createOperation(InternalClientConnection connection, DN newDN) { DeleteOperation del = new DeleteOperationBasis(connection, InternalClientConnection.nextOperationID(), InternalClientConnection.nextMessageID(), null, newDN); if (isSubtreeDelete) { del.addRequestControl(new SubtreeDeleteControl(false)); } DeleteContext ctx = new DeleteContext(getCSN(), getEntryUUID()); del.setAttachment(SYNCHROCONTEXT, ctx); return del; } // ============ // Msg encoding // ============ /** {@inheritDoc} */ @Override public byte[] getBytes_V1() { return encodeHeader_V1(MSG_TYPE_DELETE_V1) .toByteArray(); } /** {@inheritDoc} */ @Override public byte[] getBytes_V23() { return encodeHeader(MSG_TYPE_DELETE,ProtocolVersion.REPLICATION_PROTOCOL_V3) .toByteArray(); } /** {@inheritDoc} */ @Override public byte[] getBytes_V45(short protocolVersion) { final ByteArrayBuilder builder = encodeHeader(MSG_TYPE_DELETE, protocolVersion); builder.appendString(initiatorsName); builder.appendIntUTF8(encodedEclIncludes.length); builder.appendZeroTerminatedByteArray(encodedEclIncludes); builder.appendBoolean(isSubtreeDelete); return builder.toByteArray(); } // ============ // Msg decoding // ============ private void decodeBody_V4(ByteArrayScanner scanner) throws DataFormatException { initiatorsName = scanner.nextString(); final int eclAttrLen = scanner.nextIntUTF8(); encodedEclIncludes = scanner.nextByteArray(eclAttrLen); scanner.skipZeroSeparator(); isSubtreeDelete = scanner.nextBoolean(); } /** {@inheritDoc} */ @Override public String toString() { if (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V1) { return "DeleteMsg content: " + " protocolVersion: " + protocolVersion + " dn: " + dn + " csn: " + csn + " uniqueId: " + entryUUID + " assuredFlag: " + assuredFlag + (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V2 ? " assuredMode: " + assuredMode + " safeDataLevel: " + safeDataLevel : ""); } return "!!! Unknown version: " + protocolVersion + "!!!"; } /** {@inheritDoc} */ @Override public int size() { return encodedEclIncludes.length + headerSize(); } /** * Set the initiator's name of this change. * * @param iname the initiator's name. */ public void setInitiatorsName(String iname) { initiatorsName = iname; } /** * Get the initiator's name of this change. * @return the initiator's name. */ public String getInitiatorsName() { return initiatorsName; } /** * Set the subtree flag. * @param subtreeDelete the subtree flag. */ public void setSubtreeDelete(boolean subtreeDelete) { this.isSubtreeDelete = subtreeDelete; } /** * Get the subtree flag. * @return the subtree flag. */ public boolean isSubtreeDelete() { return this.isSubtreeDelete; } }