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
031/**
032 * This abstract message class is the superclass for start messages used
033 * by LDAP servers and Replication servers to initiate their communications.
034 * This class specifies a message header that contains the Replication
035 * Protocol version.
036 */
037public abstract class StartMsg extends ReplicationMsg
038{
039  /** Protocol version. */
040  protected short protocolVersion;
041  /** Generation id of data set we want to work with. */
042  protected long  generationId;
043  /** Group id of the replicated domain. */
044  protected byte groupId = -1;
045
046  /**
047   * Create a new StartMsg.
048   */
049  protected StartMsg()
050  {
051    // Nothing to do.
052  }
053
054  /**
055   * Create a new StartMsg.
056   *
057   * @param protocolVersion The Replication Protocol version of the server
058   *                        for which the StartMsg is created.
059   * @param generationId    The generationId for this server.
060   *
061   */
062  StartMsg(short protocolVersion, long generationId)
063  {
064    this.protocolVersion = protocolVersion;
065    this.generationId = generationId;
066  }
067
068  /**
069   * Encode the header for the start message.
070   *
071   * @param msgType The type of the message to create.
072   * @param builder Additional length needed to encode the remaining
073   *                         part of the UpdateMessage.
074   * @param protocolVersion  The version to use when encoding the header.
075   */
076  void encodeHeader(byte msgType, ByteArrayBuilder builder, short protocolVersion)
077  {
078    /* The message header is stored in the form :
079     * <message type><protocol version><generation id><group id>
080     */
081    builder.appendByte(msgType);
082    builder.appendByte(protocolVersion);
083    builder.appendLongUTF8(generationId);
084    builder.appendByte(groupId);
085  }
086
087  /**
088   * Encode the header for the start message. This uses the version 1 of the
089   * replication protocol (used for compatibility purpose).
090   *
091   * @param msgType The type of the message to create.
092   * @param builder The builder where to append the remaining part of the
093   *                UpdateMessage.
094   */
095  void encodeHeader_V1(byte msgType, ByteArrayBuilder builder)
096  {
097    /* The message header is stored in the form :
098     * <message type><protocol version><generation id>
099     */
100    builder.appendByte(msgType);
101    builder.appendByte(ProtocolVersion.REPLICATION_PROTOCOL_V1_REAL);
102    builder.appendByte(0);
103    builder.appendLongUTF8(generationId);
104  }
105
106  /**
107   * Decode the Header part of this message, and check its type.
108   *
109   * @param scanner where to read the message from.
110   * @param allowedTypes The allowed types of this message.
111   * @throws DataFormatException if the encodedMsg does not contain a valid
112   *         common header.
113   */
114  void decodeHeader(final ByteArrayScanner scanner, byte... allowedTypes)
115      throws DataFormatException
116  {
117    final byte msgType = scanner.nextByte();
118    if (!isTypeAllowed(allowedTypes, msgType))
119    {
120      throw new DataFormatException("byte[] is not a valid start msg: "
121          + msgType);
122    }
123
124    final byte version = scanner.nextByte();
125
126    // Filter for supported old versions PDUs
127    if (msgType == MSG_TYPE_REPL_SERVER_START_V1)
128    {
129      if (version != ProtocolVersion.REPLICATION_PROTOCOL_V1_REAL)
130      {
131        throw new DataFormatException("Not a valid message: type is " + msgType
132            + " but protocol version byte is " + version + " instead of "
133            + ProtocolVersion.REPLICATION_PROTOCOL_V1_REAL);
134      }
135
136      // Force version to V1
137      // We need to translate the MSG_TYPE_REPL_SERVER_START_V1 version
138      // into REPLICATION_PROTOCOL_V1 so that we only see V1 everywhere.
139      protocolVersion = ProtocolVersion.REPLICATION_PROTOCOL_V1;
140
141      // In V1, version was 1 (49) in string, so with a null
142      // terminating string. Let's position the cursor at the next byte
143      scanner.skipZeroSeparator();
144      generationId = scanner.nextLongUTF8();
145    }
146    else
147    {
148      if (version < ProtocolVersion.REPLICATION_PROTOCOL_V2)
149      {
150        throw new DataFormatException("Not a valid message: type is " + msgType
151            + " but protocol version byte is " + version + " instead of "
152            + ProtocolVersion.getCurrentVersion());
153      }
154      protocolVersion = version;
155      generationId = scanner.nextLongUTF8();
156      groupId = scanner.nextByte();
157    }
158  }
159
160  private boolean isTypeAllowed(byte[] allowedTypes, final byte msgType)
161  {
162    for (byte allowedType : allowedTypes)
163    {
164      if (msgType == allowedType)
165      {
166        return true;
167      }
168    }
169    return false;
170  }
171
172  /**
173   * Get the version included in the Start message mean the replication
174   * protocol version used by the server that created the message.
175   *
176   * @return The version used by the server that created the message.
177   */
178  public short getVersion()
179  {
180    return protocolVersion;
181  }
182
183  /**
184   * Get the generationId from this message.
185   * @return The generationId.
186   */
187  public long getGenerationId()
188  {
189    return generationId;
190  }
191
192  /**
193   * Get the group id in this message.
194   * @return The group id in this message
195   */
196  public byte getGroupId()
197  {
198    return groupId;
199  }
200
201  /**
202   * Set the group id in this message (For test purpose).
203   * @param groupId The group id to set.
204   */
205  public void setGroupId(byte groupId)
206  {
207    this.groupId = groupId;
208  }
209}