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 2008 Sun Microsystems, Inc.
025 *      Portions copyright 2013-2015 ForgeRock AS.
026 */
027package org.opends.server.util;
028
029import static org.opends.messages.ToolMessages.*;
030import static org.opends.server.config.ConfigConstants.*;
031
032import java.io.BufferedReader;
033import java.io.FileNotFoundException;
034import java.io.FileReader;
035import java.io.IOException;
036import java.nio.file.Paths;
037import java.util.Arrays;
038
039import org.forgerock.util.Utils;
040import org.opends.server.core.DirectoryServer;
041import org.opends.server.types.InitializationException;
042
043/**
044 * Represents a particular version of OpenDJ useful for making comparisons
045 * between versions.
046 */
047@org.opends.server.types.PublicAPI(
048    stability = org.opends.server.types.StabilityLevel.VOLATILE,
049    mayInstantiate = false, mayExtend = false, mayInvoke = true)
050public final class BuildVersion implements Comparable<BuildVersion>
051{
052
053  private final int major;
054  private final int minor;
055  private final int point;
056  private final long rev;
057  private static final BuildVersion BINARY_VERSION = new BuildVersion(
058      DynamicConstants.MAJOR_VERSION, DynamicConstants.MINOR_VERSION,
059      DynamicConstants.POINT_VERSION, DynamicConstants.REVISION_NUMBER);
060
061  /**
062   * Returns the build version as specified by the dynamic constants.
063   *
064   * @return The build version as specified by the dynamic constants.
065   */
066  public static BuildVersion binaryVersion()
067  {
068    return BINARY_VERSION;
069  }
070
071  /**
072   * Reads the instance version from config/buildinfo.
073   *
074   * @return The instance version from config/buildinfo.
075   * @throws InitializationException
076   *           If an error occurred while reading or parsing the version.
077   */
078  public static BuildVersion instanceVersion() throws InitializationException
079  {
080    final String buildInfo = Paths.get(DirectoryServer.getInstanceRoot(), CONFIG_DIR_NAME, "buildinfo").toString();
081    try (final BufferedReader reader = new BufferedReader(new FileReader(buildInfo)))
082    {
083      final String s = reader.readLine();
084      if (s == null)
085      {
086        throw new InitializationException(ERR_BUILDVERSION_MALFORMED.get(buildInfo));
087      }
088      return valueOf(s);
089    }
090    catch (FileNotFoundException e)
091    {
092      throw new InitializationException(ERR_INSTANCE_NOT_CONFIGURED.get(), e);
093    }
094    catch (IOException e)
095    {
096      throw new InitializationException(ERR_BUILDVERSION_NOT_FOUND.get(buildInfo));
097    }
098    catch (final IllegalArgumentException e)
099    {
100      throw new InitializationException(ERR_BUILDVERSION_MALFORMED.get(buildInfo));
101    }
102  }
103
104  /**
105   * Checks if the binary version is the same than the instance version.
106   *
107   * @throws InitializationException
108   *           Sends an exception if the version mismatch.
109   */
110  public static void checkVersionMismatch() throws InitializationException
111  {
112    if (!BuildVersion.binaryVersion().toString().equals(BuildVersion.instanceVersion().toString()))
113    {
114      throw new InitializationException(
115          ERR_BUILDVERSION_MISMATCH.get(BuildVersion.binaryVersion(), BuildVersion.instanceVersion()));
116    }
117  }
118
119  /**
120   * Parses the string argument as a build version. The string must be of the
121   * form:
122   *
123   * <pre>
124   * major.minor.point.rev
125   * </pre>
126   *
127   * @param s
128   *          The string to be parsed as a build version.
129   * @return The parsed build version.
130   * @throws IllegalArgumentException
131   *           If the string does not contain a parsable build version.
132   */
133  public static BuildVersion valueOf(final String s) throws IllegalArgumentException
134  {
135    final String[] fields = s.split("\\.");
136    if (fields.length != 4)
137    {
138      throw new IllegalArgumentException("Invalid version string " + s);
139    }
140    final int major = Integer.parseInt(fields[0]);
141    final int minor = Integer.parseInt(fields[1]);
142    final int point = Integer.parseInt(fields[2]);
143    final long rev = Long.parseLong(fields[3]);
144    return new BuildVersion(major, minor, point, rev);
145  }
146
147  /**
148   * Creates a new build version using the provided version information.
149   *
150   * @param major
151   *          Major release version number.
152   * @param minor
153   *          Minor release version number.
154   * @param point
155   *          Point release version number.
156   * @param rev
157   *          VCS revision number.
158   */
159  public BuildVersion(final int major, final int minor, final int point, final long rev)
160  {
161    this.major = major;
162    this.minor = minor;
163    this.point = point;
164    this.rev = rev;
165  }
166
167  @Override
168  public int compareTo(final BuildVersion version)
169  {
170    if (major == version.major)
171    {
172      if (minor == version.minor)
173      {
174        if (point == version.point)
175        {
176          if (rev == version.rev)
177          {
178            return 0;
179          }
180          else if (rev < version.rev)
181          {
182            return -1;
183          }
184        }
185        else if (point < version.point)
186        {
187          return -1;
188        }
189      }
190      else if (minor < version.minor)
191      {
192        return -1;
193      }
194    }
195    else if (major < version.major)
196    {
197      return -1;
198    }
199    return 1;
200  }
201
202  @Override
203  public boolean equals(final Object obj)
204  {
205    if (this == obj)
206    {
207      return true;
208    }
209    else if (obj instanceof BuildVersion)
210    {
211      final BuildVersion other = (BuildVersion) obj;
212      return major == other.major && minor == other.minor && point == other.point && rev == other.rev;
213    }
214    else
215    {
216      return false;
217    }
218  }
219
220  /**
221   * Returns the major release version number.
222   *
223   * @return The major release version number.
224   */
225  public int getMajorVersion()
226  {
227    return major;
228  }
229
230  /**
231   * Returns the minor release version number.
232   *
233   * @return The minor release version number.
234   */
235  public int getMinorVersion()
236  {
237    return minor;
238  }
239
240  /**
241   * Returns the point release version number.
242   *
243   * @return The point release version number.
244   */
245  public int getPointVersion()
246  {
247    return point;
248  }
249
250  /**
251   * Returns the VCS revision number.
252   *
253   * @return The VCS revision number.
254   */
255  public long getRevisionNumber()
256  {
257    return rev;
258  }
259
260  @Override
261  public int hashCode()
262  {
263    return Arrays.hashCode(new int[] { major, minor, point, (int) (rev >>> 32), (int) (rev & 0xFFFFL) });
264  }
265
266  @Override
267  public String toString()
268  {
269    return Utils.joinAsString(".", major, minor, point, rev);
270  }
271}