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-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS.
026 */
027package org.opends.server.replication.protocol;
028
029import static org.opends.server.util.StaticUtils.*;
030
031import java.util.zip.DataFormatException;
032
033import org.forgerock.i18n.LocalizableMessage;
034import org.forgerock.i18n.slf4j.LocalizedLogger;
035
036/**
037 * This message is part of the replication protocol.
038 * This message is sent by a server or a replication server when an error
039 * is detected in the context of a total update.
040 */
041public class ErrorMsg extends RoutableMsg
042{
043  /** The tracer object for the debug logger. */
044  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
045
046  /** Specifies the messageID built from the error that was detected. */
047  private final String msgID;
048  /** Specifies the complementary details about the error that was detected. */
049  private final LocalizableMessage details;
050
051  /**
052   * The time of creation of this message.
053   * <p>
054   * protocol version previous to V4
055   */
056  private long creationTime = System.currentTimeMillis();
057
058  /**
059   * Creates an ErrorMsg providing the destination server.
060   *
061   * @param sender The server ID of the server that send this message.
062   * @param destination The destination server or servers of this message.
063   * @param details The message containing the details of the error.
064   */
065  public ErrorMsg(int sender, int destination, LocalizableMessage details)
066  {
067    super(sender, destination);
068    this.msgID  = getMessageId(details);
069    this.details = details;
070    this.creationTime = System.currentTimeMillis();
071
072    if (logger.isTraceEnabled())
073    {
074      logger.trace(" Creating error message" + this
075          + " " + stackTraceToSingleLineString(new Exception("trace")));
076    }
077  }
078
079  /**
080   * Creates an ErrorMsg.
081   *
082   * @param destination replication server id
083   * @param details details of the error
084   */
085  public ErrorMsg(int destination, LocalizableMessage details)
086  {
087    this(-2, destination, details);
088  }
089
090  /** Returns the unique message Id. */
091  private String getMessageId(LocalizableMessage details)
092  {
093    return details.resourceName() + "-" + details.ordinal();
094  }
095
096  /**
097   * Creates a new ErrorMsg by decoding the provided byte array.
098   *
099   * @param  in A byte array containing the encoded information for the message
100   * @param version The protocol version to use to decode the msg.
101   * @throws DataFormatException If the in does not contain a properly
102   *                             encoded message.
103   */
104  ErrorMsg(byte[] in, short version) throws DataFormatException
105  {
106    final ByteArrayScanner scanner = new ByteArrayScanner(in);
107    final byte msgType = scanner.nextByte();
108    if (msgType != MSG_TYPE_ERROR)
109    {
110      throw new DataFormatException("input is not a valid "
111          + getClass().getCanonicalName());
112    }
113    senderID = scanner.nextIntUTF8();
114    destination = scanner.nextIntUTF8();
115    msgID = scanner.nextString();
116    details = LocalizableMessage.raw(scanner.nextString());
117
118    if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V4)
119    {
120      creationTime = scanner.nextLongUTF8();
121    }
122  }
123
124  /**
125   * Get the details from this message.
126   *
127   * @return the details from this message.
128   */
129  public LocalizableMessage getDetails()
130  {
131    return details;
132  }
133
134  /**
135   * Get the msgID from this message.
136   *
137   * @return the msgID from this message.
138   */
139  public String getMsgID()
140  {
141    return msgID;
142  }
143
144  // ============
145  // Msg encoding
146  // ============
147
148  /** {@inheritDoc} */
149  @Override
150  public byte[] getBytes(short version)
151  {
152    final ByteArrayBuilder builder = new ByteArrayBuilder();
153    builder.appendByte(MSG_TYPE_ERROR);
154    builder.appendIntUTF8(senderID);
155    builder.appendIntUTF8(destination);
156    builder.appendString(msgID);
157    builder.appendString(details.toString());
158    if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V4)
159    {
160      builder.appendLongUTF8(creationTime);
161    }
162    return builder.toByteArray();
163  }
164
165  /**
166   * Returns a string representation of the message.
167   *
168   * @return the string representation of this message.
169   */
170  @Override
171  public String toString()
172  {
173    return "ErrorMessage=["+
174      " sender=" + this.senderID +
175      " destination=" + this.destination +
176      " msgID=" + this.msgID +
177      " details=" + this.details +
178      " creationTime=" + this.creationTime + "]";
179  }
180
181  /**
182   * Get the creation time of this message.
183   * When several attempts of initialization are done sequentially, it helps
184   * sorting the good ones, from the ones that relate to ended initialization
185   * when they are received.
186   *
187   * @return the creation time of this message.
188   */
189  public long getCreationTime()
190  {
191    return creationTime;
192  }
193
194  /**
195   * Get the creation time of this message.
196   * @param creationTime the creation time of this message.
197   */
198  public void setCreationTime(long creationTime)
199  {
200    this.creationTime = creationTime;
201  }
202}