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