/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
*/
package org.opends.server.replication.protocol;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.zip.DataFormatException;
import org.forgerock.opendj.io.ASN1;
import org.forgerock.opendj.io.ASN1Writer;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.DecodeException;
import org.opends.server.core.AddOperation;
import org.opends.server.core.AddOperationBasis;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.ldap.LDAPAttribute;
import org.opends.server.replication.common.CSN;
import org.opends.server.replication.plugin.EntryHistorical;
import org.opends.server.types.*;
import org.opends.server.types.operation.PostOperationAddOperation;
import static org.opends.server.replication.protocol.OperationContext.*;
/**
* This class is used to exchange Add operation between LDAP servers
* and replication servers.
*/
public class AddMsg extends LDAPUpdateMsg
{
/** Attributes are managed encoded. */
private byte[] encodedAttributes;
/** Parent is managed decoded. */
private String parentEntryUUID;
/**
* Creates a new AddMessage.
* @param op the operation to use when creating the message
*/
AddMsg(PostOperationAddOperation op)
{
super((AddContext) op.getAttachment(SYNCHROCONTEXT), op.getEntryDN());
AddContext ctx = (AddContext) op.getAttachment(SYNCHROCONTEXT);
// Stores parentUniqueID not encoded
this.parentEntryUUID = ctx.getParentEntryUUID();
// Stores attributes encoded
this.encodedAttributes = encodeAttributes(op.getObjectClasses(),
op.getUserAttributes(), op.getOperationalAttributes());
}
/**
* Creates a new AddMessage.
*
* @param csn CSN of the add.
* @param dn DN of the added entry.
* @param entryUUID The Unique identifier of the added entry.
* @param parentEntryUUID The unique Id of the parent of the added
* entry.
* @param objectClasses objectclass of the added entry.
* @param userAttributes user attributes of the added entry.
* @param operationalAttributes operational attributes of the added entry.
*/
public AddMsg(CSN csn,
DN dn,
String entryUUID,
String parentEntryUUID,
Map<ObjectClass, String> objectClasses,
Map<AttributeType,List<Attribute>> userAttributes,
Map<AttributeType,List<Attribute>> operationalAttributes)
{
super (csn, entryUUID, dn);
// Stores parentUniqueID not encoded
this.parentEntryUUID = parentEntryUUID;
// Stores attributes encoded
this.encodedAttributes = encodeAttributes(objectClasses, userAttributes,
operationalAttributes);
}
/**
* Creates a new AddMessage.
*
* @param csn CSN of the add.
* @param dn DN of the added entry.
* @param uniqueId The Unique identifier of the added entry.
* @param parentId The unique Id of the parent of the added entry.
* @param objectClass objectclass of the added entry.
* @param userAttributes user attributes of the added entry.
* @param operationalAttributes operational attributes of the added entry.
*/
public AddMsg(CSN csn,
DN dn,
String uniqueId,
String parentId,
Attribute objectClass,
Collection<Attribute> userAttributes,
Collection<Attribute> operationalAttributes)
{
super (csn, uniqueId, dn);
// Stores parentUniqueID not encoded
this.parentEntryUUID = parentId;
// Stores attributes encoded
this.encodedAttributes = encodeAttributes(objectClass, userAttributes,
operationalAttributes);
}
/**
* 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 AddMsg
*/
public AddMsg(byte[] in) throws DataFormatException
{
final ByteArrayScanner scanner = new ByteArrayScanner(in);
decodeHeader(scanner, MSG_TYPE_ADD, MSG_TYPE_ADD_V1);
if (protocolVersion <= 3)
{
decodeBody_V123(scanner);
}
else
{
decodeBody_V4(scanner);
}
if (protocolVersion==ProtocolVersion.getCurrentVersion())
{
bytes = in;
}
}
/** {@inheritDoc} */
@Override
public AddOperation createOperation(
InternalClientConnection connection, DN newDN)
throws LDAPException, DecodeException
{
List<RawAttribute> attr = decodeRawAttributes(encodedAttributes);
AddOperation add = new AddOperationBasis(connection,
InternalClientConnection.nextOperationID(),
InternalClientConnection.nextMessageID(), null,
ByteString.valueOfUtf8(newDN.toString()), attr);
AddContext ctx = new AddContext(getCSN(), getEntryUUID(), parentEntryUUID);
add.setAttachment(SYNCHROCONTEXT, ctx);
return add;
}
// ============
// Msg encoding
// ============
/** {@inheritDoc} */
@Override
public byte[] getBytes_V1()
{
final ByteArrayBuilder builder = encodeHeader_V1(MSG_TYPE_ADD_V1);
builder.appendString(parentEntryUUID);
builder.appendByteArray(encodedAttributes);
return builder.toByteArray();
}
/** {@inheritDoc} */
@Override
public byte[] getBytes_V23()
{
final ByteArrayBuilder builder =
encodeHeader(MSG_TYPE_ADD, ProtocolVersion.REPLICATION_PROTOCOL_V3);
builder.appendString(parentEntryUUID);
builder.appendByteArray(encodedAttributes);
return builder.toByteArray();
}
/** {@inheritDoc} */
@Override
public byte[] getBytes_V45(short protocolVersion)
{
final ByteArrayBuilder builder =
encodeHeader(MSG_TYPE_ADD, protocolVersion);
builder.appendString(parentEntryUUID);
builder.appendIntUTF8(encodedAttributes.length);
builder.appendZeroTerminatedByteArray(encodedAttributes);
builder.appendIntUTF8(encodedEclIncludes.length);
builder.appendZeroTerminatedByteArray(encodedEclIncludes);
return builder.toByteArray();
}
private byte[] encodeAttributes(
Map<ObjectClass, String> objectClasses,
Map<AttributeType, List<Attribute>> userAttributes,
Map<AttributeType, List<Attribute>> operationalAttributes)
{
ByteStringBuilder byteBuilder = new ByteStringBuilder();
ASN1Writer writer = ASN1.getWriter(byteBuilder);
try
{
// Encode the object classes (SET OF LDAPString).
AttributeBuilder builder = new AttributeBuilder(
DirectoryServer.getObjectClassAttributeType());
builder.addAllStrings(objectClasses.values());
new LDAPAttribute(builder.toAttribute()).write(writer);
// Encode the user and operational attributes (AttributeList).
encodeAttributes(userAttributes, writer);
encodeAttributes(operationalAttributes, writer);
}
catch(Exception e)
{
// TODO: DO something
}
// Encode the sequence.
return byteBuilder.toByteArray();
}
private void encodeAttributes(Map<AttributeType, List<Attribute>> attributes,
ASN1Writer writer) throws Exception
{
for (List<Attribute> list : attributes.values())
{
for (Attribute a : list)
{
if (!a.isVirtual() && !EntryHistorical.isHistoricalAttribute(a))
{
new LDAPAttribute(a).write(writer);
}
}
}
}
private byte[] encodeAttributes(
Attribute objectClass,
Collection<Attribute> userAttributes,
Collection<Attribute> operationalAttributes)
{
ByteStringBuilder byteBuilder = new ByteStringBuilder();
ASN1Writer writer = ASN1.getWriter(byteBuilder);
try
{
new LDAPAttribute(objectClass).write(writer);
for (Attribute a : userAttributes)
{
new LDAPAttribute(a).write(writer);
}
if (operationalAttributes != null)
{
for (Attribute a : operationalAttributes)
{
new LDAPAttribute(a).write(writer);
}
}
}
catch(Exception e)
{
// Do something
}
return byteBuilder.toByteArray();
}
// ============
// Msg decoding
// ============
private void decodeBody_V123(ByteArrayScanner scanner)
throws DataFormatException
{
parentEntryUUID = scanner.nextString();
encodedAttributes = scanner.remainingBytes();
}
private void decodeBody_V4(ByteArrayScanner scanner)
throws DataFormatException
{
parentEntryUUID = scanner.nextString();
final int attrLen = scanner.nextIntUTF8();
encodedAttributes = scanner.nextByteArray(attrLen);
scanner.skipZeroSeparator();
final int eclAttrLen = scanner.nextIntUTF8();
encodedEclIncludes = scanner.nextByteArray(eclAttrLen);
}
/** {@inheritDoc} */
@Override
public String toString()
{
if (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V1)
{
return "AddMsg content: " +
" protocolVersion: " + protocolVersion +
" dn: " + dn +
" csn: " + csn +
" uniqueId: " + entryUUID +
" assuredFlag: " + assuredFlag +
(protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V2 ?
" assuredMode: " + assuredMode +
" safeDataLevel: " + safeDataLevel
: "");
}
return "!!! Unknown version: " + protocolVersion + "!!!";
}
/**
* Add the specified attribute/attribute value in the entry contained
* in this AddMsg.
*
* @param name The name of the attribute to add.
* @param value The value of the attribute to add.
* @throws DecodeException When this Msg is not valid.
*/
public void addAttribute(String name, String value) throws DecodeException
{
ByteStringBuilder byteBuilder = new ByteStringBuilder();
byteBuilder.appendBytes(encodedAttributes);
ASN1Writer writer = ASN1.getWriter(byteBuilder);
try
{
new LDAPAttribute(name, value).write(writer);
encodedAttributes = byteBuilder.toByteArray();
}
catch(Exception e)
{
// DO SOMETHING
}
}
/**
* Get the attributes of this add msg.
* @throws LDAPException In case of LDAP decoding exception
* @throws DecodeException In case of ASN1 decoding exception
* @return the list of attributes
*/
public List<Attribute> getAttributes() throws LDAPException, DecodeException
{
return decodeAttributes(encodedAttributes);
}
/**
* Set the parent unique id of this add msg.
*
* @param entryUUID the parent unique id.
*/
public void setParentEntryUUID(String entryUUID)
{
parentEntryUUID = entryUUID;
}
/**
* Get the parent unique id of this add msg.
* @return the parent unique id.
*/
public String getParentEntryUUID()
{
return parentEntryUUID;
}
/** {@inheritDoc} */
@Override
public int size()
{
return encodedAttributes.length + encodedEclIncludes.length + headerSize();
}
}