/*
|
* 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 e) {
|
throw new ApplicationException(
|
ReturnCode.START_ERROR,
|
INFO_ERROR_CREATING_BUILD_INFO.get(), e);
|
|
} catch (InterruptedException ie) {
|
throw new ApplicationException(
|
ReturnCode.START_ERROR,
|
INFO_ERROR_CREATING_BUILD_INFO.get(), ie);
|
} 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);
|
}
|
}
|
}
|
|
}
|