/* * 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<>(); 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); } 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); 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+)\\.(-?.+))"); 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, 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, DynamicConstants.REVISION); 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); return bi; } private Map<String, String> values = new HashMap<>(); /** * 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 VCS revision. * * @return String representing the VCS revision */ public String getRevision() { return values.get(REVISION); } /** {@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 (getRevision().equals(bi.getRevision())) { return 0; } else if (getRevision().compareTo(bi.getRevision()) < 0) { 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 + getRevision().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); } } } }