/*
* 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
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* 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
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 2008-2009 Sun Microsystems, Inc.
* Portions Copyright 2013 ForgeRock AS.
*/
package org.opends.server.replication.server;
import java.io.UnsupportedEncodingException;
import java.util.zip.DataFormatException;
import org.opends.server.replication.common.AssuredMode;
import org.opends.server.replication.common.ChangeNumber;
import org.opends.server.replication.protocol.UpdateMsg;
import org.opends.server.replication.protocol.LDAPUpdateMsg;
import org.opends.server.replication.protocol.ProtocolVersion;
/**
* This is a facility class that is in fact an hack to optimize replication
* server performances in case of assured replication usage:
* When received from a server by a server reader, an update message is to be
* posted in the queues of the server writers. Thus, they receive the same
* reference of update message. As we want to transform an assured update
* message to an equivalent not assured one for some servers but not for all,
* instead of performing a painful clone of the message, we use this special
* class to keep a reference to the real object, but that will overwrite the
* assured flag value to false when serializing the message, and return false
* when calling the isAssured() method.
*
*/
public class NotAssuredUpdateMsg extends UpdateMsg
{
// The real update message this message represents
private UpdateMsg realUpdateMsg = null;
// V1 serialized form of the real message with assured flag set to false.
// Ready to be sent.
private byte[] realUpdateMsgNotAssuredBytesV1 = null;
// VLatest serialized form of the real message with assured flag set to false.
// Ready to be sent.
private byte[] realUpdateMsgNotAssuredBytesVLatest = null;
/**
* Creates a new empty UpdateMsg.
* This class is only used by replication server code so constructor is not
* public by security.
* @param updateMsg The real underlying update message this object represents.
* @throws UnsupportedEncodingException When the pre-encoding of the message
* failed because the UTF-8 encoding is not supported or the
* requested protocol version to use is not supported by this PDU.
*
*/
NotAssuredUpdateMsg(UpdateMsg updateMsg) throws UnsupportedEncodingException
{
realUpdateMsg = updateMsg;
/**
* Prepare serialized forms
*/
if (realUpdateMsg instanceof LDAPUpdateMsg)
{
/**
* Prepare V1 serialized form of the message:
* Get the encoding form of the real message then overwrite the assured
* flag to always be false.
*/
byte[] origBytes = realUpdateMsg.getBytes(
ProtocolVersion.REPLICATION_PROTOCOL_V1);
// Clone the byte array to be able to modify it without problems
// (ModifyMsg messages for instance do not return a cloned version of
// their byte array)
byte[] bytes = new byte[origBytes.length];
System.arraycopy(origBytes, 0, bytes, 0, origBytes.length);
int maxLen = bytes.length;
int pos;
int nZeroFound = 0; // Number of 0 value found
boolean found = false;
/* Look for assured flag position:
* The message header is stored in the form :
* <operation type><changenumber><dn><assured><entryuuid><change>
* the length of result byte array is therefore :
* 1 + change number length + 1 + dn length + 1 + 1 +
* uuid length + 1 + additional_length
* See LDAPUpdateMsg.encodeHeader_V1() for more information
*/
// Find end of change number then end of dn
for (pos = 1; pos < maxLen; pos++)
{
if (bytes[pos] == (byte) 0)
{
nZeroFound++;
if (nZeroFound == 2) // 2 end of string to find
{
found = true;
break;
}
}
}
if (!found)
throw new UnsupportedEncodingException("Could not find end of " +
"change number.");
pos++;
if (pos >= maxLen)
throw new UnsupportedEncodingException("Reached end of packet.");
// Force assured flag to false
bytes[pos] = (byte) 0;
// Store computed V1 serialized form
realUpdateMsgNotAssuredBytesV1 = bytes;
/**
* Prepare VLATEST serialized form of the message:
* Get the encoding form of the real message then overwrite the assured
* flag to always be false.
*/
origBytes = realUpdateMsg.getBytes(ProtocolVersion.getCurrentVersion());
// Clone the byte array to be able to modify it without problems
// (ModifyMsg messages for instance do not return a cloned version of
// their byte array)
bytes = new byte[origBytes.length];
System.arraycopy(origBytes, 0, bytes, 0, origBytes.length);
maxLen = bytes.length;
nZeroFound = 0; // Number of 0 value found
found = false;
/* Look for assured flag position:
* The message header is stored in the form :
* <operation type><protocol version><changenumber><dn><entryuuid>
* <assured> <assured mode> <safe data level>
* the length of result byte array is therefore :
* 1 + 1 + change number length + 1 + dn length + 1 + uuid length +
* 1 + 1 + 1 + 1 + additional_length
* See LDAPUpdateMsg.encodeHeader() for more information
*/
// Find end of change number then end of dn then end of uuid
for (pos = 2; pos < maxLen; pos++)
{
if (bytes[pos] == (byte) 0)
{
nZeroFound++;
if (nZeroFound == 3) // 3 end of string to find
{
found = true;
break;
}
}
}
if (!found)
throw new UnsupportedEncodingException("Could not find end of " +
"change number.");
pos++;
if (pos >= maxLen)
throw new UnsupportedEncodingException("Reached end of packet.");
// Force assured flag to false
bytes[pos] = (byte) 0;
// Store computed VLATEST serialized form
realUpdateMsgNotAssuredBytesVLatest = bytes;
} else
{
/**
* Prepare VLATEST serialized form of the message:
* Get the encoding form of the real message then overwrite the assured
* flag to always be false.
*/
byte[] origBytes = realUpdateMsg.getBytes(
ProtocolVersion.getCurrentVersion());
// Clone the byte array to be able to modify it without problems
// (ModifyMsg messages for instance do not return a cloned version of
// their byte array)
byte[] bytes = new byte[origBytes.length];
System.arraycopy(origBytes, 0, bytes, 0, origBytes.length);
int maxLen = bytes.length;
int pos;
int nZeroFound = 0; // Number of 0 value found
boolean found = false;
// This is a generic update message
/* Look for assured flag position:
* The message header is stored in the form :
* <operation type><protocol version><changenumber><assured>
* <assured mode> <safe data level>
* the length of result byte array is therefore :
* 1 + 1 + change number length + 1 + 1
* + 1 + 1 + additional_length
* See UpdateMsg.encodeHeader() for more information
*/
// Find end of change number
for (pos = 2; pos < maxLen; pos++)
{
if (bytes[pos] == (byte) 0)
{
nZeroFound++;
if (nZeroFound == 1) // 1 end of string to find
{
found = true;
break;
}
}
}
if (!found)
throw new UnsupportedEncodingException("Could not find end of " +
"change number.");
pos++;
if (pos >= maxLen)
throw new UnsupportedEncodingException("Reached end of packet.");
// Force assured flag to false
bytes[pos] = (byte) 0;
// Store computed VLatest serialized form
realUpdateMsgNotAssuredBytesVLatest = bytes;
}
}
/**
* {@inheritDoc}
*/
@Override
public ChangeNumber getChangeNumber()
{
return realUpdateMsg.getChangeNumber();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAssured()
{
// Always return false as we represent a not assured message
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void setAssured(boolean assured)
{
// No impact for this method as semantic is that assured is always false
// and we do not want to change the original real update message settings
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj)
{
// Compare with the underlying real update message
if (obj != null)
{
if (obj.getClass() != realUpdateMsg.getClass())
return false;
return realUpdateMsg.getChangeNumber().
equals(((UpdateMsg)obj).getChangeNumber());
}
else
{
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode()
{
return realUpdateMsg.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public int compareTo(UpdateMsg msg)
{
return realUpdateMsg.compareTo(msg);
}
/**
* {@inheritDoc}
*/
@Override
public byte[] getBytes(short reqProtocolVersion)
throws UnsupportedEncodingException
{
if (reqProtocolVersion == ProtocolVersion.REPLICATION_PROTOCOL_V1)
return realUpdateMsgNotAssuredBytesV1;
else
return realUpdateMsgNotAssuredBytesVLatest;
}
/**
* {@inheritDoc}
*/
@Override
public AssuredMode getAssuredMode()
{
return realUpdateMsg.getAssuredMode();
}
/**
* {@inheritDoc}
*/
@Override
public byte getSafeDataLevel()
{
return realUpdateMsg.getSafeDataLevel();
}
/**
* {@inheritDoc}
*/
@Override
public void setAssuredMode(AssuredMode assuredMode)
{
// No impact for this method as semantic is that assured is always false
// and we do not want to change the original real update message settings
}
/**
* {@inheritDoc}
*/
@Override
public void setSafeDataLevel(byte safeDataLevel)
{
// No impact for this method as semantic is that assured is always false
// and we do not want to change the original real update message settings
}
/**
* {@inheritDoc}
*/
@Override
public short getVersion()
{
return realUpdateMsg.getVersion();
}
/**
* {@inheritDoc}
*/
@Override
public int size()
{
return realUpdateMsg.size();
}
/**
* {@inheritDoc}
*/
@Override
protected byte[] encodeHeader(byte type, int additionalLength, short version)
throws UnsupportedEncodingException
{
// Not called as only used by constructors using bytes
return null;
}
/**
* {@inheritDoc}
*/
@Override
public int decodeHeader(byte type, byte[] encodedMsg)
throws DataFormatException
{
// Not called as only used by getBytes methods
return -1;
}
/**
* {@inheritDoc}
*/
@Override
public byte[] getPayload()
{
return realUpdateMsg.getPayload();
}
}