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