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.forgerock.opendj.config.dsconfig;
028
029import static com.forgerock.opendj.ldap.config.ConfigMessages.*;
030import static org.forgerock.util.Utils.closeSilently;
031
032import java.io.BufferedReader;
033import java.io.File;
034import java.io.FileReader;
035import java.io.IOException;
036import java.util.Arrays;
037
038import org.forgerock.opendj.config.ConfigurationFramework;
039import org.forgerock.opendj.config.server.ConfigException;
040import org.forgerock.opendj.ldap.Connection;
041import org.forgerock.opendj.ldap.LdapException;
042import org.forgerock.opendj.ldap.responses.SearchResultEntry;
043import org.forgerock.util.Utils;
044
045/**
046 * Represents a particular version of OpenDJ useful for making comparisons between versions. FIXME TODO Move this file
047 * in ? package.
048 */
049public class BuildVersion implements Comparable<BuildVersion> {
050
051    private final int major;
052    private final int minor;
053    private final int point;
054    private final String rev;
055
056    /**
057     * Creates a new build version using the provided version information.
058     *
059     * @param major
060     *            Major release version number.
061     * @param minor
062     *            Minor release version number.
063     * @param point
064     *            Point release version number.
065     * @param rev
066     *            VCS revision.
067     */
068    public BuildVersion(final int major, final int minor, final int point, final String rev) {
069        this.major = major;
070        this.minor = minor;
071        this.point = point;
072        this.rev = rev;
073    }
074
075    /**
076     * Returns the build version as specified in the entry "cn=Version,cn=monitor".
077     *
078     * @param connection
079     *            The connection to use to read the entry.
080     * @return The build version as specified in the current installation configuration.
081     * @throws ConfigException
082     *             Sends an exception if it is impossible to retrieve the version configuration entry.
083     */
084    public static BuildVersion binaryVersion(final Connection connection) throws ConfigException {
085        try {
086            final SearchResultEntry entry = connection.readEntry("", "fullVendorVersion");
087            return valueOf(entry.getAttribute("fullVendorVersion").firstValueAsString());
088        } catch (LdapException e) {
089            throw new ConfigException(ERR_CONFIGVERSION_NOT_FOUND.get());
090        }
091    }
092
093    /**
094     * Checks if the binary version is the same than the instance version. If not, a configuration exception is thrown.
095     *
096     * @param connection
097     *            The connection to use to read the configuration entry.
098     * @throws ConfigException
099     *             Sends an exception if the version mismatch.
100     */
101    public static void checkVersionMismatch(final Connection connection) throws ConfigException {
102        final BuildVersion binaryVersion = BuildVersion.binaryVersion(connection);
103        final BuildVersion instanceVersion = BuildVersion.instanceVersion();
104        if (!binaryVersion.toString().equals(instanceVersion.toString())) {
105            throw new ConfigException(ERR_BUILDVERSION_MISMATCH.get(binaryVersion, instanceVersion));
106        }
107    }
108
109    /**
110     * Reads the instance version from config/buildinfo.
111     *
112     * @return The instance version from config/buildinfo.
113     * @throws ConfigException
114     *             If an error occurred while reading or parsing the version.
115     */
116    public static BuildVersion instanceVersion() throws ConfigException {
117        final String buildInfo = ConfigurationFramework.getInstance().getInstancePath() + File.separator + "config"
118                + File.separator + "buildinfo";
119        BufferedReader reader = null;
120        try {
121            reader = new BufferedReader(new FileReader(buildInfo));
122            final String s = reader.readLine();
123            if (s != null) {
124                return valueOf(s);
125            } else {
126                throw new ConfigException(ERR_BUILDVERSION_MALFORMED.get(buildInfo));
127            }
128        } catch (IOException e) {
129            throw new ConfigException(ERR_BUILDVERSION_NOT_FOUND.get(buildInfo));
130        } catch (final IllegalArgumentException e) {
131            throw new ConfigException(ERR_BUILDVERSION_MALFORMED.get(buildInfo));
132        } finally {
133            closeSilently(reader);
134        }
135    }
136
137    /**
138     * Parses the string argument as a build version. The string must be of the form:
139     *
140     * <pre>
141     * major.minor.point.rev
142     * </pre>
143     *
144     * @param s
145     *            The string to be parsed as a build version.
146     * @return The parsed build version.
147     * @throws IllegalArgumentException
148     *             If the string does not contain a parsable build version.
149     */
150    public static BuildVersion valueOf(final String s) {
151        final String[] fields = s.split("\\.");
152        if (fields.length != 4) {
153            throw new IllegalArgumentException("Invalid version string " + s);
154        }
155        final int major = Integer.parseInt(fields[0]);
156        final int minor = Integer.parseInt(fields[1]);
157        final int point = Integer.parseInt(fields[2]);
158        final String rev = fields[3];
159        return new BuildVersion(major, minor, point, rev);
160    }
161
162    /**
163     * Returns the major release version number.
164     *
165     * @return The major release version number.
166     */
167    public int getMajorVersion() {
168        return major;
169    }
170
171    /**
172     * Returns the minor release version number.
173     *
174     * @return The minor release version number.
175     */
176    public int getMinorVersion() {
177        return minor;
178    }
179
180    /**
181     * Returns the point release version number.
182     *
183     * @return The point release version number.
184     */
185    public int getPointVersion() {
186        return point;
187    }
188
189    /** {@inheritDoc} */
190    public boolean equals(final Object obj) {
191        if (this == obj) {
192            return true;
193        } else if (obj instanceof BuildVersion) {
194            final BuildVersion other = (BuildVersion) obj;
195            return major == other.major && minor == other.minor && point == other.point && rev.equals(other.rev);
196        } else {
197            return false;
198        }
199    }
200
201    /** {@inheritDoc} */
202    public int compareTo(final BuildVersion version) {
203        if (major == version.major) {
204            if (minor == version.minor) {
205                if (point == version.point) {
206                    if (rev == version.rev) {
207                        return 0;
208                    } else if (rev.compareTo(version.rev) < 0) {
209                        return -1;
210                    }
211                } else if (point < version.point) {
212                    return -1;
213                }
214            } else if (minor < version.minor) {
215                return -1;
216            }
217        } else if (major < version.major) {
218            return -1;
219        }
220        return 1;
221    }
222
223    /**
224     * Returns the VCS revision.
225     *
226     * @return The VCS revision.
227     */
228    public String getRevision() {
229        return rev;
230    }
231
232    /** {@inheritDoc} */
233    public int hashCode() {
234        return Arrays.hashCode(new int[] { major, minor, point, rev.hashCode() });
235    }
236
237    /**
238     * Returns the string representation of the version. E.g:
239     *
240     * <pre>
241     * version : 2.8.0.1022
242     * </pre>
243     *
244     * @return The string representation of the version.
245     */
246    public String toString() {
247        return Utils.joinAsString(".", major, minor, point, rev);
248    }
249}