BuildInformation.java revision 2f6d798e90520dd1b83ac30e53838ae6fd41a150
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
*/
package org.opends.quicksetup;
import static org.opends.messages.QuickSetupMessages.*;
import static org.opends.server.util.SetupUtils.*;
import static com.forgerock.opendj.util.OperatingSystem.isWindows;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.quicksetup.util.Utils;
import org.opends.server.util.DynamicConstants;
import org.opends.server.util.SetupUtils;
import org.opends.server.util.StaticUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* Represents information about the current build that is
* publicly obtainable by invoking start-ds -F.
*/
public class BuildInformation implements Comparable<BuildInformation> {
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
/**
* Reads build information for a particular installation by reading the
* output from invoking the start-ds tool with the full information option.
* @param installation from which to gather build information
* @return BuildInformation object populated with information
* @throws ApplicationException if all or some important information could
* not be determined
*/
public static BuildInformation create(Installation installation)
throws ApplicationException {
BuildInformation bi = new BuildInformation();
List<String> args = new ArrayList<String>();
args.add(Utils.getScriptPath(
Utils.getPath(installation.getServerStartCommandFile())));
args.add("-F"); // full verbose
ProcessBuilder pb = new ProcessBuilder(args);
InputStream is = null;
OutputStream out = null;
final boolean[] done = {false};
try {
Map<String, String> env = pb.environment();
env.put(SetupUtils.OPENDJ_JAVA_HOME, System.getProperty("java.home"));
// This is required in order the return code to be valid.
env.put("OPENDJ_EXIT_NO_BACKGROUND", "true");
final Process process = pb.start();
is = process.getInputStream();
out = process.getOutputStream();
final OutputStream fOut = out;
if (isWindows())
{
// In windows if there is an error we wait the user to click on
// return to continue.
Thread t = new Thread(new Runnable()
{
@Override
public void run()
{
while (!done[0])
{
try
{
Thread.sleep(15000);
if (!done[0])
{
fOut.write(Constants.LINE_SEPARATOR.getBytes());
fOut.flush();
}
}
catch (Throwable t)
{
logger.warn(LocalizableMessage.raw("Error writing to process: "+t, t));
}
}
}
});
t.start();
}
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = reader.readLine();
bi.values.put(NAME, line);
StringBuilder sb = new StringBuilder();
while (null != (line = reader.readLine())) {
if (sb.length() > 0)
{
sb.append('\n');
}
sb.append(line);
int colonIndex = line.indexOf(':');
if (-1 != colonIndex) {
String name = line.substring(0, colonIndex).trim();
String value = line.substring(colonIndex + 1).trim();
bi.values.put(name, value);
}
}
int resultCode = process.waitFor();
if (resultCode != 0)
{
if (sb.length() == 0)
{
throw new ApplicationException(
ReturnCode.START_ERROR,
INFO_ERROR_CREATING_BUILD_INFO.get(), null);
}
else
{
try
{
checkNotNull(bi.values,
NAME,
MAJOR_VERSION,
MINOR_VERSION,
POINT_VERSION,
REVISION_NUMBER);
}
catch (Throwable t)
{
// We did not get the required information.
throw new ApplicationException(
ReturnCode.START_ERROR,
INFO_ERROR_CREATING_BUILD_INFO_MSG.get(sb),
null);
}
}
}
} catch (IOException | InterruptedException e) {
throw new ApplicationException(
ReturnCode.START_ERROR,
INFO_ERROR_CREATING_BUILD_INFO.get(), e);
} finally {
done[0] = true;
StaticUtils.close(is, out);
}
// Make sure we got values for important properties that are used
// in compareTo, equals, and hashCode
checkNotNull(bi.values,
NAME,
MAJOR_VERSION,
MINOR_VERSION,
POINT_VERSION,
REVISION_NUMBER);
return bi;
}
/**
* Creates an instance from a string representing a build number
* of the for MAJOR.MINOR.POINT.REVISION where MAJOR, MINOR, POINT,
* and REVISION are integers.
* @param bn String representation of a build number
* @return a BuildInformation object populated with the information
* provided in <code>bn</code>
* @throws IllegalArgumentException if <code>bn</code> is not a build
* number
*/
public static BuildInformation fromBuildString(String bn) throws
IllegalArgumentException
{
// -------------------------------------------------------
// NOTE: if you change this be sure to change getBuildString()
// -------------------------------------------------------
// Allow negative revision number for cases where there is no
// VCS available.
Pattern p = Pattern.compile("((\\d+)\\.(\\d+)\\.(\\d+)\\.(-?\\d+))");
Matcher m = p.matcher(bn);
if (!m.matches()) {
throw new IllegalArgumentException("'" + bn + "' is not a build string");
}
BuildInformation bi = new BuildInformation();
try {
bi.values.put(MAJOR_VERSION, m.group(2));
bi.values.put(MINOR_VERSION, m.group(3));
bi.values.put(POINT_VERSION, m.group(4));
bi.values.put(REVISION_NUMBER, m.group(5));
} catch (Exception e) {
throw new IllegalArgumentException("Error parsing build number " + bn);
}
return bi;
}
/**
* Creates an instance from constants present in the current build.
* @return BuildInformation created from current constant values
* @throws ApplicationException if all or some important information could
* not be determined
*/
public static BuildInformation getCurrent() throws ApplicationException {
BuildInformation bi = new BuildInformation();
bi.values.put(NAME, DynamicConstants.FULL_VERSION_STRING);
bi.values.put(BUILD_ID, DynamicConstants.BUILD_ID);
bi.values.put(MAJOR_VERSION,
String.valueOf(DynamicConstants.MAJOR_VERSION));
bi.values.put(MINOR_VERSION,
String.valueOf(DynamicConstants.MINOR_VERSION));
bi.values.put(POINT_VERSION,
String.valueOf(DynamicConstants.POINT_VERSION));
bi.values.put(VERSION_QUALIFIER,
String.valueOf(DynamicConstants.VERSION_QUALIFIER));
bi.values.put(REVISION_NUMBER,
String.valueOf(DynamicConstants.REVISION_NUMBER));
bi.values.put(URL_REPOSITORY,
String.valueOf(DynamicConstants.URL_REPOSITORY));
bi.values.put(FIX_IDS, DynamicConstants.FIX_IDS);
bi.values.put(DEBUG_BUILD, String.valueOf(DynamicConstants.DEBUG_BUILD));
bi.values.put(BUILD_OS, DynamicConstants.BUILD_OS);
bi.values.put(BUILD_USER, DynamicConstants.BUILD_USER);
bi.values.put(BUILD_JAVA_VERSION, DynamicConstants.BUILD_JAVA_VERSION);
bi.values.put(BUILD_JAVA_VENDOR, DynamicConstants.BUILD_JAVA_VENDOR);
bi.values.put(BUILD_JVM_VERSION, DynamicConstants.BUILD_JVM_VERSION);
bi.values.put(BUILD_JVM_VENDOR, DynamicConstants.BUILD_JVM_VENDOR);
// Make sure we got values for important properties that are used
// in compareTo, equals, and hashCode
checkNotNull(bi.values,
NAME,
MAJOR_VERSION,
MINOR_VERSION,
POINT_VERSION,
REVISION_NUMBER);
return bi;
}
private Map<String, String> values = new HashMap<String, String>();
/**
* Gets the name of this build. This is the first line of the output
* from invoking start-ds -F.
* @return String representing the name of the build
*/
public String getName() {
return values.get(NAME);
}
/**
* Gets the build ID which is the 14 digit number code like 20070420110336.
*
* @return String representing the build ID
*/
public String getBuildId() {
return values.get(BUILD_ID);
}
/**
* Gets the major version.
*
* @return String representing the major version
*/
public Integer getMajorVersion() {
return Integer.valueOf(values.get(MAJOR_VERSION));
}
/**
* Gets the minor version.
*
* @return String representing the minor version
*/
public Integer getMinorVersion() {
return Integer.valueOf(values.get(MINOR_VERSION));
}
/**
* Gets the point version.
*
* @return String representing the point version
*/
public Integer getPointVersion() {
return Integer.valueOf(values.get(POINT_VERSION));
}
/**
* Gets the SVN revision number.
*
* @return Integer representing the SVN revision number
*/
public Integer getRevisionNumber() {
return Integer.valueOf(values.get(REVISION_NUMBER));
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getName());
String id = getBuildId();
if (id != null) {
sb.append(" (")
.append(INFO_GENERAL_BUILD_ID.get())
.append(": ")
.append(id)
.append(")");
}
return sb.toString();
}
/** {@inheritDoc} */
@Override
public int compareTo(BuildInformation bi) {
if (getMajorVersion().equals(bi.getMajorVersion())) {
if (getMinorVersion().equals(bi.getMinorVersion())) {
if (getPointVersion().equals(bi.getPointVersion())) {
if (getRevisionNumber().equals(bi.getRevisionNumber())) {
return 0;
} else if (getRevisionNumber() < bi.getRevisionNumber()) {
return -1;
}
} else if (getPointVersion() < bi.getPointVersion()) {
return -1;
}
} else if (getMinorVersion() < bi.getMinorVersion()) {
return -1;
}
} else if (getMajorVersion() < bi.getMajorVersion()) {
return -1;
}
return 1;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
return o != null
&& getClass() == o.getClass()
&& compareTo((BuildInformation)o) == 0;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
int hc = 11;
hc = 31 * hc + getMajorVersion().hashCode();
hc = 31 * hc + getMinorVersion().hashCode();
hc = 31 * hc + getPointVersion().hashCode();
hc = 31 * hc + getRevisionNumber().hashCode();
return hc;
}
private static void checkNotNull(Map<?, ?> values, String... props)
throws ApplicationException {
for (String prop : props) {
if (null == values.get(prop)) {
throw new ApplicationException(
ReturnCode.TOOL_ERROR,
INFO_ERROR_PROP_VALUE.get(prop), null);
}
}
}
}