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