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-2009 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2015 ForgeRock AS.
016 */
017package org.opends.server.replication.protocol;
018
019import java.util.zip.DataFormatException;
020
021import org.opends.server.replication.common.AssuredMode;
022import org.opends.server.replication.common.CSN;
023
024import static org.opends.server.replication.protocol.ByteArrayBuilder.*;
025
026/**
027 * Abstract class that must be extended to define a message
028 * used for sending Updates between servers.
029 */
030public class UpdateMsg extends ReplicationMsg
031                                    implements Comparable<UpdateMsg>
032{
033  /** Protocol version. */
034  protected short protocolVersion;
035
036  /** The CSN of this update. */
037  protected CSN csn;
038
039  /** True when the update must use assured replication. */
040  protected boolean assuredFlag;
041
042  /** When assuredFlag is true, defines the requested assured mode. */
043  protected AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE;
044
045  /** When assured mode is safe data, gives the requested level. */
046  protected byte safeDataLevel = 1;
047
048  /** The payload that must be encoded in this message. */
049  private final byte[] payload;
050
051  /**
052   * Creates a new empty UpdateMsg.
053   */
054  protected UpdateMsg()
055  {
056    payload = null;
057  }
058
059  /**
060   * Creates a new UpdateMsg with the given information.
061   *
062   * @param bytes A Byte Array with the encoded form of the message.
063   *
064   * @throws DataFormatException If bytes is not valid.
065   */
066  UpdateMsg(byte[] bytes) throws DataFormatException
067  {
068    final ByteArrayScanner scanner = new ByteArrayScanner(bytes);
069    decodeHeader(MSG_TYPE_GENERIC_UPDATE, scanner);
070    // Read the payload : all the remaining bytes but the terminating 0
071    payload = scanner.remainingBytes();
072  }
073
074  /**
075   * Creates a new UpdateMsg with the given informations.
076   * <p>
077   * This constructor is only used for testing.
078   *
079   * @param csn  The CSN associated with the change encoded in this message.
080   * @param payload       The payload that must be encoded in this message.
081   */
082  public UpdateMsg(CSN csn, byte[] payload)
083  {
084    this.payload = payload;
085    this.protocolVersion = ProtocolVersion.getCurrentVersion();
086    this.csn = csn;
087  }
088
089  /**
090   * Get the CSN from the message.
091   * @return the CSN
092   */
093  public CSN getCSN()
094  {
095    return csn;
096  }
097
098  /**
099   * Get a boolean indicating if the Update must be processed as an
100   * Asynchronous or as an assured replication.
101   *
102   * @return Returns the assuredFlag.
103   */
104  public boolean isAssured()
105  {
106    return assuredFlag;
107  }
108
109  /**
110   * Set the Update message as an assured message.
111   *
112   * @param assured If the message is assured or not. Using true implies
113   * setAssuredMode method must be called.
114   */
115  public void setAssured(boolean assured)
116  {
117    assuredFlag = assured;
118  }
119
120  /** {@inheritDoc} */
121  @Override
122  public boolean equals(Object obj)
123  {
124    return obj != null
125        && obj.getClass() == getClass()
126        && csn.equals(((UpdateMsg) obj).csn);
127  }
128
129  /** {@inheritDoc} */
130  @Override
131  public int hashCode()
132  {
133    return csn.hashCode();
134  }
135
136  /** {@inheritDoc} */
137  @Override
138  public int compareTo(UpdateMsg msg)
139  {
140    return csn.compareTo(msg.getCSN());
141  }
142
143  /**
144   * Get the assured mode in this message.
145   * @return The assured mode in this message
146   */
147  public AssuredMode getAssuredMode()
148  {
149    return assuredMode;
150  }
151
152  /**
153   * Get the safe data level in this message.
154   * @return The safe data level in this message
155   */
156  public byte getSafeDataLevel()
157  {
158    return safeDataLevel;
159  }
160
161  /**
162   * Set the assured mode. Assured boolean must be set to true for this field
163   * to mean something.
164   * @param assuredMode The chosen assured mode.
165   */
166  public void setAssuredMode(AssuredMode assuredMode)
167  {
168    this.assuredMode = assuredMode;
169  }
170
171  /**
172   * Set the safe data level. Assured mode should be set to safe data for this
173   * field to mean something.
174   * @param safeDataLevel The chosen safe data level.
175   */
176  public void setSafeDataLevel(byte safeDataLevel)
177  {
178    this.safeDataLevel = safeDataLevel;
179  }
180
181  /**
182   * Get the version included in the update message. Means the replication
183   * protocol version with which this update message was instantiated.
184   *
185   * @return The version with which this update message was instantiated.
186   */
187  public short getVersion()
188  {
189    return protocolVersion;
190  }
191
192  /**
193   * Return the number of bytes used by this message.
194   *
195   * @return The number of bytes used by this message.
196   */
197  public int size()
198  {
199    return 10 + payload.length;
200  }
201
202  /**
203   * Encode the common header for all the UpdateMsg. This uses the current
204   * protocol version.
205   *
206   * @param msgType The type of UpdateMsg to encode.
207   * @param protocolVersion The ProtocolVersion to use when encoding.
208   * @return a byte array builder containing the common header
209   */
210  protected ByteArrayBuilder encodeHeader(byte msgType, short protocolVersion)
211  {
212    final ByteArrayBuilder builder = new ByteArrayBuilder(bytes(6) + csnsUTF8(1));
213    builder.appendByte(msgType);
214    builder.appendByte(ProtocolVersion.getCurrentVersion());
215    builder.appendCSNUTF8(getCSN());
216    builder.appendBoolean(assuredFlag);
217    builder.appendByte(assuredMode.getValue());
218    builder.appendByte(safeDataLevel);
219    return builder;
220  }
221
222  /**
223   * Decode the Header part of this Update message, and check its type.
224   *
225   * @param allowedType The allowed type of this Update Message.
226   * @param scanner The encoded form of the UpdateMsg.
227   * @throws DataFormatException
228   *           if the scanner does not contain a valid common header.
229   */
230  protected void decodeHeader(byte allowedType, ByteArrayScanner scanner)
231      throws DataFormatException
232  {
233    /* The message header is stored in the form :
234     * <operation type><protocol version><CSN><assured>
235     * <assured mode> <safe data level>
236     */
237    final byte msgType = scanner.nextByte();
238    if (allowedType != msgType)
239    {
240      throw new DataFormatException("byte[] is not a valid update msg: "
241          + msgType);
242    }
243
244    protocolVersion = scanner.nextByte();
245    csn = scanner.nextCSNUTF8();
246    assuredFlag = scanner.nextBoolean();
247    assuredMode = AssuredMode.valueOf(scanner.nextByte());
248    safeDataLevel = scanner.nextByte();
249  }
250
251  /**
252   * Returns the encoded representation of this update message using the current
253   * protocol version.
254   *
255   * @return The encoded representation of this update message.
256   */
257  public byte[] getBytes()
258  {
259    return getBytes(ProtocolVersion.getCurrentVersion());
260  }
261
262  /**
263   * This implementation is only called during unit testing, so we are free to
264   * force the protocol version. Underlying implementations override this method
265   * in order to provide version specific encodings.
266   *
267   * {@inheritDoc}
268   */
269  @Override
270  public byte[] getBytes(short protocolVersion)
271  {
272    final ByteArrayBuilder builder = encodeHeader(MSG_TYPE_GENERIC_UPDATE,
273        ProtocolVersion.getCurrentVersion());
274    builder.appendByteArray(payload);
275    return builder.toByteArray();
276  }
277
278  /**
279   * Get the payload of the UpdateMsg.
280   *
281   * @return The payload of the UpdateMsg.
282   */
283  public byte[] getPayload()
284  {
285    return payload;
286  }
287
288  /**
289   * Whether the current message can update the "ds-sync-state" attribute.
290   *
291   * @return true if current message can update the "ds-sync-state" attribute, false otherwise.
292   */
293  public boolean contributesToDomainState()
294  {
295    return true;
296  }
297}