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