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-2016 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 String rev;
057  private static final BuildVersion BINARY_VERSION = new BuildVersion(
058      DynamicConstants.MAJOR_VERSION, DynamicConstants.MINOR_VERSION,
059      DynamicConstants.POINT_VERSION, DynamicConstants.REVISION);
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    final int nbFields = fields.length;
137    if (!(nbFields == 3 || nbFields == 4))
138    {
139      throw new IllegalArgumentException("Invalid version string " + s);
140    }
141    final int major = Integer.parseInt(fields[0]);
142    final int minor = Integer.parseInt(fields[1]);
143    final int point = Integer.parseInt(fields[2]);
144
145    if (nbFields == 4)
146    {
147      return new BuildVersion(major, minor, point, fields[3]);
148    }
149    else
150    {
151      return new BuildVersion(major, minor, point);
152    }
153  }
154
155  /**
156   * Creates a new build version using the provided version information.
157   *
158   * @param major
159   *          Major release version number.
160   * @param minor
161   *          Minor release version number.
162   * @param point
163   *          Point release version number.
164   */
165  public BuildVersion(final int major, final int minor, final int point)
166  {
167    this(major, minor, point, "");
168  }
169
170  /**
171   * Creates a new build version using the provided version information.
172   *
173   * @param major
174   *          Major release version number.
175   * @param minor
176   *          Minor release version number.
177   * @param point
178   *          Point release version number.
179   * @param rev
180   *          VCS revision.
181   */
182  public BuildVersion(final int major, final int minor, final int point, final String rev)
183  {
184    this.major = major;
185    this.minor = minor;
186    this.point = point;
187    this.rev = rev;
188  }
189
190  @Override
191  public int compareTo(final BuildVersion version)
192  {
193    if (major == version.major)
194    {
195      if (minor == version.minor)
196      {
197        if (point == version.point)
198        {
199          return 0;
200        }
201        else if (point < version.point)
202        {
203          return -1;
204        }
205      }
206      else if (minor < version.minor)
207      {
208        return -1;
209      }
210    }
211    else if (major < version.major)
212    {
213      return -1;
214    }
215    return 1;
216  }
217
218  @Override
219  public boolean equals(final Object obj)
220  {
221    if (this == obj)
222    {
223      return true;
224    }
225    else if (obj instanceof BuildVersion)
226    {
227      final BuildVersion other = (BuildVersion) obj;
228      return major == other.major && minor == other.minor && point == other.point;
229    }
230    else
231    {
232      return false;
233    }
234  }
235
236  /**
237   * Returns the major release version number.
238   *
239   * @return The major release version number.
240   */
241  public int getMajorVersion()
242  {
243    return major;
244  }
245
246  /**
247   * Returns the minor release version number.
248   *
249   * @return The minor release version number.
250   */
251  public int getMinorVersion()
252  {
253    return minor;
254  }
255
256  /**
257   * Returns the point release version number.
258   *
259   * @return The point release version number.
260   */
261  public int getPointVersion()
262  {
263    return point;
264  }
265
266  /**
267   * Returns the VCS revision.
268   *
269   * @return The VCS revision.
270   */
271  public String getRevision()
272  {
273    return rev;
274  }
275
276  @Override
277  public int hashCode()
278  {
279    return Arrays.hashCode(new int[] { major, minor, point });
280  }
281
282  @Override
283  public String toString()
284  {
285    if (!rev.isEmpty())
286    {
287      return Utils.joinAsString(".", major, minor, point, rev);
288    }
289    return Utils.joinAsString(".", major, minor, point);
290  }
291}