[#1030] Keep the setup log out of the way of start-ds, and say when it is gone (#1032)
23 files modified
4 files added
| | |
| | | --baseDN dc=example,dc=com --sampleData 5000 --cli --acceptLicense --no-prompt \ |
| | | --propertiesFilePath /tmp/opendj-setup.properties.bcfks --doNotStart |
| | | |
| | | # start-ds sweeps the native libraries the BC FIPS loader leaves in tmp/ and nothing |
| | | # else: the rest of tmp/ belongs to tools that may still be running, setup among them |
| | | # (issue #1030). Without these two probes a sweep back to "tmp/*" passes every cell. |
| | | mkdir -p opendj-server-legacy/target/package/opendj/tmp/bc-fips-jni_123 |
| | | touch opendj-server-legacy/target/package/opendj/tmp/keep.me |
| | | |
| | | opendj-server-legacy/target/package/opendj/bin/start-ds |
| | | |
| | | test ! -e opendj-server-legacy/target/package/opendj/tmp/bc-fips-jni_123 |
| | | test -e opendj-server-legacy/target/package/opendj/tmp/keep.me |
| | | |
| | | opendj-server-legacy/target/package/opendj/bin/status --hostname localhost --bindDN "cn=Directory Manager" --bindPassword password --trustAll |
| | | opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1 |
| | | opendj-server-legacy/target/package/opendj/bin/ldapsearch --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "ou=people,dc=example,dc=com" --searchScope sub "(uid=user.*)" dn | grep ^dn: | wc -l | grep -q 5000 |
| | |
| | | opendj-server-legacy\target\package\opendj\upgrade.bat |
| | | opendj-server-legacy\target\package\opendj\bat\import-ldif.bat --offline --ldifFile test.ldif --backendID=example2 |
| | | opendj-server-legacy\target\package\opendj\bat\rebuild-index.bat --offline --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll |
| | | # The same probe as the Unix FIPS step, for start-ds.bat: the sweep takes the BC FIPS |
| | | # directories and leaves everything else in tmp\ alone (issue #1030). The runner's |
| | | # install path has no space in it, so this does not settle the quoting of the for /D line. |
| | | New-Item -ItemType Directory -Force opendj-server-legacy\target\package\opendj\tmp\bc-fips-jni_123 | Out-Null |
| | | New-Item -ItemType File -Force opendj-server-legacy\target\package\opendj\tmp\keep.me | Out-Null |
| | | opendj-server-legacy\target\package\opendj\bat\start-ds.bat |
| | | if (Test-Path opendj-server-legacy\target\package\opendj\tmp\bc-fips-jni_123) { throw "start-ds.bat left a bc-fips-jni_* directory behind" } |
| | | if (-not (Test-Path opendj-server-legacy\target\package\opendj\tmp\keep.me)) { throw "start-ds.bat removed a tmp file that is not its own" } |
| | | opendj-server-legacy\target\package\opendj\bat\rebuild-index.bat --bindDN "cn=Directory Manager" --bindPassword password --baseDN "dc=example2,dc=com" --rebuildAll --trustAll |
| | | opendj-server-legacy\target\package\opendj\bat\ldapsearch.bat --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example2,dc=com" --searchScope sub "(uid=user.*)" dn | find /c '"dn:"' | findstr "10000" |
| | | opendj-server-legacy\target\package\opendj\bat\stop-ds.bat |
| | |
| | | if ($LASTEXITCODE -ne 0) { throw "net stop 'OpenDJ Server' failed with exit code $LASTEXITCODE" } |
| | | opendj-server-legacy\target\package\opendj\bat\windows-service.bat --disableService |
| | | |
| | | # A test step that fails leaves its instances behind. The server-side story of a |
| | | # failed start lives in logs/server.out and logs/errors, and nothing else prints it |
| | | # (setup only has the client-side view, see issue #1030). |
| | | - name: Dump the server logs of a failed test |
| | | if: failure() |
| | | shell: bash |
| | | run: | |
| | | for f in opendj-server-legacy/target/package/opendj*/logs/server.out opendj-server-legacy/target/package/opendj*/logs/errors; do |
| | | [ -f "$f" ] || continue |
| | | echo "===== $f" |
| | | cat "$f" |
| | | done |
| | | |
| | | - name: Upload artifacts OpenDJ Server |
| | | uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 |
| | | with: |
| | |
| | | # |
| | | # Copyright 2006-2009 Sun Microsystems, Inc. |
| | | # Portions Copyright 2011-2014 ForgeRock AS. |
| | | # Portions Copyright 2025 3A Systems LLC. |
| | | # Portions Copyright 2025-2026 3A Systems LLC. |
| | | |
| | | |
| | | # Capture the current working directory so that we can change to it later. |
| | |
| | | LOG_FILE=${INSTANCE_ROOT}/logs/server.out |
| | | STARTING_FILE=${INSTANCE_ROOT}/logs/server.starting |
| | | |
| | | # Cleanup the tmp directory |
| | | # Remove the native libraries the BC FIPS loader extracts into the tmp directory |
| | | # on every start, so that they do not pile up. Nothing else in there is ours to |
| | | # remove: the tmp directory is java.io.tmpdir for every tool, and a tool may |
| | | # still be running - setup starts the server through this script and keeps its |
| | | # own log open (issue #1030). The logs a failed dsreplication or status run |
| | | # leaves there (opendj-replication-*.log, opendj-status-*.log) are the |
| | | # operator's to read and to remove; starting the server no longer takes them. |
| | | OPENDJ_TMP_DIR="${INSTANCE_ROOT}/tmp" |
| | | if [ -d "${OPENDJ_TMP_DIR}" ]; then |
| | | rm -rf ${OPENDJ_TMP_DIR}/* |
| | | rm -rf "${OPENDJ_TMP_DIR}"/bc-fips-jni_* |
| | | fi |
| | | |
| | | # See if the provided set of arguments were sufficient for us to be able to |
| | |
| | | |
| | | echo %SCRIPT%: PATH=%PATH% >> %LOG% |
| | | |
| | | rem cleanup the tmp directory |
| | | set CUR_DIR=%CD% |
| | | rem Remove the native libraries the BC FIPS loader extracts into the tmp |
| | | rem directory on every start, so that they do not pile up. Nothing else in |
| | | rem there is ours to remove: the tmp directory is java.io.tmpdir for every |
| | | rem tool, and a tool may still be running - setup starts the server through |
| | | rem this script and keeps its own log open (issue #1030). The logs a failed |
| | | rem dsreplication or status run leaves there (opendj-replication-*.log, |
| | | rem opendj-status-*.log) are the operator's to read and to remove; starting |
| | | rem the server no longer takes them. |
| | | set OPENDJ_TMP_DIR=%INSTANCE_ROOT%\tmp |
| | | rem The paths must be quoted: an unquoted parenthesis (e.g. from |
| | | rem "C:\Program Files (x86)") terminates the ( ) block at parse time. |
| | | dir /b /s /a "%OPENDJ_TMP_DIR%" | findstr .>nul && ( |
| | | cd /d "%OPENDJ_TMP_DIR%" |
| | | for /F "delims=" %%i in ('dir /b') do (rmdir "%%i" /s/q>NUL 2>&1 || del "%%i" /s/q>NUL 2>&1) |
| | | cd /d "%CUR_DIR%" |
| | | ) |
| | | rem %%~i drops the quotes cmd would hand back if it ever kept them: an install path with a |
| | | rem space would otherwise split the rmdir argument, and the redirect would hide it. |
| | | for /D %%i in ("%OPENDJ_TMP_DIR%\bc-fips-jni_*") do rmdir "%%~i" /s/q>NUL 2>&1 |
| | | |
| | | "%OPENDJ_JAVA_BIN%" -client %SCRIPT_NAME_ARG% org.opends.server.core.DirectoryServer --configFile "%INSTANCE_ROOT%\config\config.ldif" --checkStartability %* |
| | | |
| | |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011-2016 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.guitools.uninstaller; |
| | | |
| | |
| | | @Override |
| | | protected void guiLaunchFailed() { |
| | | System.err.println( |
| | | tempLogFile.isEnabled() ? ERR_UNINSTALL_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS.get(tempLogFile.getPath()) |
| | | : ERR_UNINSTALL_LAUNCHER_GUI_LAUNCHED_FAILED.get()); |
| | | hasTempLogFile() ? ERR_UNINSTALL_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS.get(getTempLogFile().getPath()) |
| | | : ERR_UNINSTALL_LAUNCHER_GUI_LAUNCHED_FAILED.get()); |
| | | } |
| | | |
| | | @Override |
| | |
| | | /** Actually performs the uninstall in this thread. The thread is blocked. */ |
| | | @Override |
| | | public void run() { |
| | | // The uninstall begins here, as the install begins in Installer.run(): the errors below go |
| | | // to the error logger, and only the log file puts a publisher on it. Without it a failed |
| | | // uninstall in the wizard leaves no record anywhere (issue #1030). The command line |
| | | // uninstaller is given no supplier - its launcher has created the log already. |
| | | openTempLogFile(); |
| | | status = STARTED; |
| | | logger.info(LocalizableMessage.raw("run of the Uninstaller started")); |
| | | |
| | |
| | | import java.lang.reflect.InvocationTargetException; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.function.Supplier; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.LocalizableMessageBuilder; |
| | |
| | | private final ErrorPrintStream err = new ErrorPrintStream(); |
| | | private final OutputPrintStream out = new OutputPrintStream(); |
| | | |
| | | /** Temporary log file where messages will be logged. */ |
| | | /** |
| | | * Temporary log file where messages will be logged, once {@link #openTempLogFile()} has |
| | | * asked for it. It stays {@code null} until then: creating it costs a file - and the |
| | | * directory it lives in - that nothing removes unless the operation succeeds, so the roads |
| | | * which attempt nothing must not create one (issue #1030). |
| | | */ |
| | | protected TempLogFile tempLogFile; |
| | | |
| | | /** Supplies the temporary log file, see {@link #openTempLogFile()}. */ |
| | | private Supplier<TempLogFile> tempLogFileSupplier; |
| | | |
| | | /** |
| | | * Creates an application by instantiating the Application class |
| | | * denoted by the System property |
| | |
| | | } |
| | | |
| | | /** |
| | | * Sets the temporary log file where messages will be logged. |
| | | * Sets where the temporary log file of this application comes from, without creating it. |
| | | * <p> |
| | | * The wizard and the CLI hand the supplier over as they start: the file itself is created by |
| | | * {@link #openTempLogFile()}, on the road that runs the operation, so that a run which does |
| | | * nothing - a quit at any step of the wizard, a server which turns out to be installed |
| | | * already - leaves no log behind (issue #1030). |
| | | * <p> |
| | | * There is deliberately no setter taking the file itself: a caller holding a supplier could |
| | | * then resolve it on the spot, and the log would be created before anything is attempted |
| | | * again. |
| | | * |
| | | * @param tempLogFile |
| | | * temporary log file where messages will be logged. |
| | | * supplies the temporary log file where messages will be logged. |
| | | */ |
| | | public void setTempLogFile(final TempLogFile tempLogFile) |
| | | public void setTempLogFile(final Supplier<TempLogFile> tempLogFile) |
| | | { |
| | | this.tempLogFile = tempLogFile; |
| | | this.tempLogFile = null; |
| | | this.tempLogFileSupplier = tempLogFile; |
| | | } |
| | | |
| | | /** |
| | | * Creates the temporary log file of this application, unless it has one already. |
| | | * <p> |
| | | * Called where the operation begins and not before: from that point on the log is worth |
| | | * keeping, because there is something which can fail and be reported. |
| | | * <p> |
| | | * An application nobody handed a supplier to has no log of its own: the command line |
| | | * uninstaller is built that way, and its launcher creates the log before running it. |
| | | * |
| | | * @return the temporary log file of this application, or {@code null} if it was given none. |
| | | */ |
| | | protected TempLogFile openTempLogFile() |
| | | { |
| | | if (tempLogFile == null && tempLogFileSupplier != null) |
| | | { |
| | | tempLogFile = tempLogFileSupplier.get(); |
| | | } |
| | | return tempLogFile; |
| | | } |
| | | |
| | | /** |
| | |
| | | /** The relative path where the database files are. */ |
| | | private static final String DATABASES_PATH_RELATIVE = "db"; |
| | | /** The relative path where the log files are. */ |
| | | private static final String LOGS_PATH_RELATIVE = "logs"; |
| | | public static final String LOGS_PATH_RELATIVE = "logs"; |
| | | /** The relative path where the LDIF files are. */ |
| | | private static final String LDIFS_PATH_RELATIVE = "ldif"; |
| | | /** The relative path where the backup files are. */ |
| | |
| | | * |
| | | * Copyright 2008-2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013-2016 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.quicksetup; |
| | | |
| | |
| | | import static org.opends.messages.QuickSetupMessages.*; |
| | | import static org.opends.server.util.DynamicConstants.*; |
| | | |
| | | import java.io.File; |
| | | import java.io.PrintStream; |
| | | import java.util.function.Supplier; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | |
| | | /** Arguments with which this launcher was invoked. */ |
| | | protected final String[] args; |
| | | |
| | | /** The temporary log file which will be kept if an error occurs. */ |
| | | protected final TempLogFile tempLogFile; |
| | | private final String tempLogFilePrefix; |
| | | private final File tempLogFileDirectory; |
| | | /** |
| | | * The temporary log file which will be kept if an error occurs; see {@link #getTempLogFile()}. |
| | | * Volatile because the GUI road asks for it from the thread that runs the splash screen and |
| | | * the roads after it read it from the main thread. |
| | | */ |
| | | private volatile TempLogFile tempLogFile; |
| | | /** Why the GUI did not come up, kept until there is a log to write it to. */ |
| | | private volatile Throwable guiLaunchFailure; |
| | | |
| | | /** |
| | | * Creates a Launcher. |
| | |
| | | * temporary log file path where messages will be logged |
| | | */ |
| | | public Launcher(final String[] args, final String tempLogFilePrefix) { |
| | | this(args, tempLogFilePrefix, null); |
| | | } |
| | | |
| | | /** |
| | | * Creates a Launcher whose temporary log file lives in the given directory. |
| | | * |
| | | * @param args |
| | | * String[] of argument passes from the command line |
| | | * @param tempLogFilePrefix |
| | | * temporary log file path where messages will be logged |
| | | * @param tempLogFileDirectory |
| | | * the directory to create the temporary log file in, or {@code null} for the OS |
| | | * temporary directory |
| | | */ |
| | | public Launcher(final String[] args, final String tempLogFilePrefix, final File tempLogFileDirectory) { |
| | | if (args == null) { |
| | | throw new IllegalArgumentException("args cannot be null"); |
| | | } |
| | | this.args = args; |
| | | this.tempLogFile = TempLogFile.newTempLogFile(tempLogFilePrefix); |
| | | this.tempLogFilePrefix = tempLogFilePrefix; |
| | | this.tempLogFileDirectory = tempLogFileDirectory; |
| | | } |
| | | |
| | | /** |
| | | * The temporary log file of this launcher, created the first time it is asked for. |
| | | * <p> |
| | | * Creating it costs a file - and, with a directory of the caller's choosing, the directory |
| | | * as well - that nothing removes afterwards unless the operation succeeds. So it is created |
| | | * on the first road that can fail an operation and not before: {@code --help}, |
| | | * {@code --version}, a usage error and the other roads that attempt nothing leave no log |
| | | * behind (issue #1030). |
| | | * |
| | | * @return the temporary log file, creating it if this is the first call. |
| | | */ |
| | | protected synchronized TempLogFile getTempLogFile() { |
| | | if (tempLogFile == null) { |
| | | tempLogFile = TempLogFile.newTempLogFile(tempLogFilePrefix, tempLogFileDirectory); |
| | | logGuiLaunchFailure(); |
| | | } |
| | | return tempLogFile; |
| | | } |
| | | |
| | | /** |
| | | * Whether there is a log to name, without creating one to answer. |
| | | * |
| | | * @return {@code true} if a temporary log file has been created and can be used to log |
| | | * messages. |
| | | */ |
| | | protected boolean hasTempLogFile() { |
| | | return tempLogFile != null && tempLogFile.isEnabled(); |
| | | } |
| | | |
| | | /** Writes the reason the GUI did not come up, now that there is a log to hold it. */ |
| | | private void logGuiLaunchFailure() { |
| | | Throwable failure = guiLaunchFailure; |
| | | if (failure == null) { |
| | | return; |
| | | } |
| | | logger.warn(LocalizableMessage.raw("Error launching GUI: " + failure)); |
| | | StringBuilder buf = new StringBuilder(); |
| | | while (failure != null) |
| | | { |
| | | for (StackTraceElement aStack : failure.getStackTrace()) { |
| | | buf.append(aStack).append("\n"); |
| | | } |
| | | |
| | | failure = failure.getCause(); |
| | | if (failure != null) |
| | | { |
| | | buf.append("Root cause:\n"); |
| | | } |
| | | } |
| | | logger.warn(LocalizableMessage.raw(buf)); |
| | | } |
| | | |
| | | /** |
| | |
| | | { |
| | | try |
| | | { |
| | | SplashScreen.main(tempLogFile, args); |
| | | startSplashScreen(Launcher.this::getTempLogFile, args); |
| | | returnValue[0] = 0; |
| | | } |
| | | catch (Throwable t) |
| | | { |
| | | if (tempLogFile.isEnabled()) |
| | | { |
| | | logger.warn(LocalizableMessage.raw("Error launching GUI: "+t)); |
| | | StringBuilder buf = new StringBuilder(); |
| | | while (t != null) |
| | | { |
| | | for (StackTraceElement aStack : t.getStackTrace()) { |
| | | buf.append(aStack).append("\n"); |
| | | } |
| | | |
| | | t = t.getCause(); |
| | | if (t != null) |
| | | { |
| | | buf.append("Root cause:\n"); |
| | | } |
| | | } |
| | | logger.warn(LocalizableMessage.raw(buf)); |
| | | } |
| | | // Kept rather than logged: a GUI which does not come up is not by itself an |
| | | // operation that failed, and creating a log here would leave one behind on every |
| | | // headless road that installs nothing (issue #1030). It goes into the log as soon |
| | | // as something asks for one. |
| | | guiLaunchFailure = t; |
| | | } |
| | | } |
| | | }); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Shows the splash screen and, behind it, builds the wizard. |
| | | * <p> |
| | | * The log file is handed over as a supplier and not as a file: the splash screen comes up |
| | | * before the user has said anything, and a log created there outlives every road that |
| | | * installs nothing - a quit at any wizard step, a server which is configured already |
| | | * (issue #1030). The application asks for it when it starts the operation. |
| | | * <p> |
| | | * Package-private so that a test can drive {@link #launchGui(String[])} without a display. |
| | | * |
| | | * @param tempLogFile |
| | | * supplies the temporary log file of the application |
| | | * @param args |
| | | * the arguments to pass to the splash screen |
| | | */ |
| | | void startSplashScreen(final Supplier<TempLogFile> tempLogFile, final String[] args) |
| | | { |
| | | SplashScreen.main(tempLogFile, args); |
| | | } |
| | | |
| | | /** |
| | | * Gets the frame title of the GUI application that will be used |
| | | * in some operating systems. |
| | | * @return internationalized String representing the frame title |
| | |
| | | } |
| | | System.exit(ReturnCode.SUCCESSFUL.getReturnCode()); |
| | | } else if (isCli()) { |
| | | // An operation is about to run: from here on there is something worth logging, and |
| | | // preExit() names the file. The roads above attempt nothing and leave no log behind. |
| | | getTempLogFile(); |
| | | CliApplication cliApp = createCliApplication(); |
| | | int exitCode = launchCli(cliApp); |
| | | preExit(cliApp); |
| | |
| | | willLaunchGui(); |
| | | int exitCode = launchGui(args); |
| | | if (exitCode != 0) { |
| | | // The GUI did not come up and the operation runs on the command line after all: from |
| | | // here on there is something worth logging, the reason the GUI failed included. The |
| | | // log comes first, so that guiLaunchFailed() can name the file holding that reason. |
| | | getTempLogFile(); |
| | | guiLaunchFailed(); |
| | | CliApplication cliApp = createCliApplication(); |
| | | exitCode = launchCli(cliApp); |
| | |
| | | |
| | | // Add an extra space systematically |
| | | System.out.println(); |
| | | if (tempLogFile.isEnabled()) { |
| | | System.out.println(INFO_GENERAL_SEE_FOR_DETAILS.get(tempLogFile.getPath())); |
| | | if (hasTempLogFile()) { |
| | | System.out.println(INFO_GENERAL_SEE_FOR_DETAILS.get(getTempLogFile().getPath())); |
| | | } |
| | | } |
| | | } |
| | |
| | | import java.awt.MediaTracker; |
| | | import java.awt.Toolkit; |
| | | import java.awt.Window; |
| | | import java.util.function.Supplier; |
| | | |
| | | import javax.swing.SwingUtilities; |
| | | |
| | |
| | | private Object quickSetup; |
| | | |
| | | private Class<?> quickSetupClass; |
| | | private TempLogFile tempLogFile; |
| | | /** |
| | | * Supplies the temporary log file of the application behind this splash screen. Nothing |
| | | * here asks for it: the splash screen shows before anything is attempted, and a log created |
| | | * at that point outlives every road that attempts nothing (issue #1030). |
| | | */ |
| | | private Supplier<TempLogFile> tempLogFile; |
| | | |
| | | /** Constant for the display of the splash screen. */ |
| | | private static final int MIN_SPLASH_DISPLAY = 3000; |
| | |
| | | * It can be called from the event thread and outside the event thread. |
| | | * |
| | | * @param tempLogFile |
| | | * temporary log file where messages will be logged |
| | | * supplies the temporary log file where messages will be logged |
| | | * @param args |
| | | * arguments to be passed to the method QuickSetup.initialize |
| | | */ |
| | | public static void main(final TempLogFile tempLogFile, String[] args) |
| | | public static void main(final Supplier<TempLogFile> tempLogFile, String[] args) |
| | | { |
| | | SplashScreen screen = new SplashScreen(); |
| | | screen.tempLogFile = tempLogFile; |
| | |
| | | { |
| | | quickSetupClass = Class.forName("org.opends.quicksetup.ui.QuickSetup"); |
| | | quickSetup = quickSetupClass.getDeclaredConstructor().newInstance(); |
| | | quickSetupClass.getMethod("initialize", new Class[] { TempLogFile.class, String[].class }) |
| | | quickSetupClass.getMethod("initialize", new Class[] { Supplier.class, String[].class }) |
| | | .invoke(quickSetup, tempLogFile, args); |
| | | } catch (Exception e) |
| | | { |
| | |
| | | import java.io.File; |
| | | import java.io.FileOutputStream; |
| | | import java.io.IOException; |
| | | import java.nio.charset.Charset; |
| | | import java.nio.file.Files; |
| | | import java.util.Date; |
| | | import java.text.DateFormat; |
| | |
| | | */ |
| | | public static TempLogFile newTempLogFile(final String prefix) |
| | | { |
| | | return newTempLogFile(prefix, null); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new temporary log file in the given directory. |
| | | * <p> |
| | | * The directory is created if it does not exist yet. When it is {@code null} or cannot be |
| | | * used, the log file goes to the OS temporary directory instead, as with |
| | | * {@link #newTempLogFile(String)}. The name of the file follows the pattern |
| | | * prefix-[RANDOM_NUMBER_STRING].log either way. |
| | | * |
| | | * @param prefix |
| | | * log file prefix to which log messages will be written. |
| | | * @param directory |
| | | * the directory to create the log file in, or {@code null} for the OS temporary |
| | | * directory. |
| | | * @return a new temporary log file. |
| | | */ |
| | | public static TempLogFile newTempLogFile(final String prefix, final File directory) |
| | | { |
| | | IOException fallbackReason = null; |
| | | if (directory != null) |
| | | { |
| | | try |
| | | { |
| | | Files.createDirectories(directory.toPath()); |
| | | return new TempLogFile(Files.createTempFile(directory.toPath(), prefix, ".log").toFile()); |
| | | } |
| | | catch (final IOException e) |
| | | { |
| | | // Nothing can be logged yet: the first publisher is the one the constructor installs |
| | | // below, so the warning has to wait until there is a log to write it to. |
| | | fallbackReason = e; |
| | | } |
| | | } |
| | | try |
| | | { |
| | | return new TempLogFile(Files.createTempFile(prefix, ".log").toFile()); |
| | | final TempLogFile tempLogFile = new TempLogFile(Files.createTempFile(prefix, ".log").toFile()); |
| | | if (fallbackReason != null) |
| | | { |
| | | localizedLogger.warn(LocalizableMessage.raw("Unable to create temp log file in " + directory |
| | | + " because: " + fallbackReason.getMessage() + ", falling back to the temporary directory"), |
| | | fallbackReason); |
| | | } |
| | | return tempLogFile; |
| | | } |
| | | catch (final IOException e) |
| | | { |
| | |
| | | { |
| | | this.logFile = null; |
| | | this.writer=null; |
| | | this.startupErrorLogPublisher = null; |
| | | this.startupDebugLogPublisher = null; |
| | | } |
| | | |
| | | final TextWriter writer; |
| | | /** Kept so that they can be taken off the logger singletons again, see {@link #deleteLogFileAfterSuccess()}. */ |
| | | private final ErrorLogPublisher startupErrorLogPublisher; |
| | | private final DebugLogPublisher startupDebugLogPublisher; |
| | | |
| | | private TempLogFile(final File file) throws IOException |
| | | { |
| | |
| | | }else { |
| | | writer=new TextWriter.STREAM(new FileOutputStream(file)); |
| | | } |
| | | ErrorLogPublisher startupErrorLogPublisher = TextErrorLogPublisher.getServerStartupTextErrorPublisher(writer); |
| | | startupErrorLogPublisher = TextErrorLogPublisher.getServerStartupTextErrorPublisher(writer); |
| | | ErrorLogger.getInstance().addLogPublisher(startupErrorLogPublisher); |
| | | DebugLogger.getInstance().addPublisherIfRequired(writer); |
| | | startupDebugLogPublisher = DebugLogger.getInstance().addPublisherIfRequired(writer); |
| | | |
| | | localizedLogger.info(LocalizableMessage.raw("QuickSetup application launched " + DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date()), null)); |
| | | } |
| | |
| | | return logFile; |
| | | } |
| | | |
| | | /** Closes the log file handler and delete the temp log file . */ |
| | | /** |
| | | * Closes the log file handler and delete the temp log file . |
| | | * <p> |
| | | * The publishers installed by the constructor go with it: they are held by the logger |
| | | * singletons, which outlive this object, and once the writer is shut everything they are |
| | | * handed is written to a closed stream and swallowed. |
| | | */ |
| | | public void deleteLogFileAfterSuccess() |
| | | { |
| | | if (isEnabled()) |
| | | { |
| | | if (startupErrorLogPublisher != null) { |
| | | ErrorLogger.getInstance().removeLogPublisher(startupErrorLogPublisher); |
| | | } |
| | | if (startupDebugLogPublisher != null) { |
| | | DebugLogger.getInstance().removeLogPublisher(startupDebugLogPublisher); |
| | | } |
| | | if (writer!=null) { |
| | | writer.shutdown(); |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * Return {@code true} if the temp log file is still on disk and can be read. |
| | | * <p> |
| | | * Unlike {@link #isEnabled()} this is about the file, not the logger: something else may have |
| | | * removed the file while the logger still writes to it (see issue #1030), and then there is |
| | | * nothing to hand over to whoever needs the log. |
| | | * |
| | | * @return {@code true} if the temp log file is there and readable. |
| | | */ |
| | | public boolean isReadable() |
| | | { |
| | | return logFile != null && Files.isReadable(logFile.toPath()) && Files.isRegularFile(logFile.toPath()); |
| | | } |
| | | |
| | | /** |
| | | * Reads the whole temp log file. |
| | | * <p> |
| | | * The file is decoded with the default charset of the JVM, which is the one |
| | | * {@link TextWriter.STREAM} wrote it with: reader and writer are the same JVM, so a |
| | | * non-ASCII path or base DN in a report comes back as it was logged. |
| | | * |
| | | * @return the contents of the temp log file. |
| | | * @throws IOException |
| | | * if the file cannot be read, for instance because it is no longer there. |
| | | */ |
| | | public String readContents() throws IOException |
| | | { |
| | | if (logFile == null) |
| | | { |
| | | throw new IOException("No temp log file"); |
| | | } |
| | | return new String(Files.readAllBytes(logFile.toPath()), Charset.defaultCharset()); |
| | | } |
| | | |
| | | /** |
| | | * Return the absolute path of the temp log file. |
| | | * @return the absolute path of the temp log file. |
| | | */ |
| | |
| | | import java.io.FileWriter; |
| | | import java.io.IOException; |
| | | import java.io.PrintStream; |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Paths; |
| | | import java.security.KeyStore; |
| | | import java.security.KeyStoreException; |
| | | import java.security.NoSuchAlgorithmException; |
| | |
| | | @Override |
| | | public void run() |
| | | { |
| | | // The install begins here: this is the first point where something can fail and be worth |
| | | // a report, so this is where the log file is created. Every road which stops before this |
| | | // one - a quit at any step of the wizard, "already configured", a refused licence, a |
| | | // cancel at the prompt - leaves neither the log nor its directory behind (issue #1030). |
| | | openTempLogFile(); |
| | | applicationException = null; |
| | | PrintStream origErr = System.err; |
| | | PrintStream origOut = System.out; |
| | |
| | | uninstall(); |
| | | setCurrentProgressStep(InstallProgressStep.FINISHED_CANCELED); |
| | | notifyListeners(null); |
| | | // Nothing names this log on this road - notifyListenersOfExistingLogFile() belongs to |
| | | // handleInstallationError() below - and uninstall() has just taken the installation |
| | | // back, so keeping the file would leave a report nobody is told about (issue #1030). |
| | | tempLogFile.deleteLogFileAfterSuccess(); |
| | | } else { |
| | | handleInstallationError(ex); |
| | | } |
| | |
| | | return Utils.getInstancePathFromInstallPath(installPath); |
| | | } |
| | | |
| | | private void notifyListenersOfExistingLogFile() |
| | | /** Package-private so that {@code InstallerTest} can drive every road of this report. */ |
| | | void notifyListenersOfExistingLogFile() |
| | | { |
| | | if (tempLogFile.isEnabled()) |
| | | if (!tempLogFile.isEnabled()) |
| | | { |
| | | final String tempLogFilePath = tempLogFile.getPath(); |
| | | notifyListeners(getFormattedProgress(INFO_GENERAL_PROVIDE_LOG_IN_ERROR.get(tempLogFilePath))); |
| | | //write log |
| | | try { |
| | | notifyListeners(getLineBreak()); |
| | | notifyListeners(LocalizableMessage.valueOf(new String(Files.readAllBytes(Paths.get(tempLogFilePath)),"UTF-8"))); |
| | | } catch (UnsupportedEncodingException e) { |
| | | e.printStackTrace(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | notifyListeners(getLineBreak()); |
| | | return; |
| | | } |
| | | final String tempLogFilePath = tempLogFile.getPath(); |
| | | if (!tempLogFile.isReadable()) |
| | | { |
| | | // Something removed the log while it was being written (issue #1030): say so |
| | | // rather than ask for a file that is not there. |
| | | notifyListeners(getFormattedWarning(INFO_GENERAL_LOG_IN_ERROR_MISSING.get(tempLogFilePath))); |
| | | notifyListeners(getLineBreak()); |
| | | return; |
| | | } |
| | | notifyListeners(getFormattedProgress(INFO_GENERAL_PROVIDE_LOG_IN_ERROR.get(tempLogFilePath))); |
| | | notifyListeners(getLineBreak()); |
| | | // Write the log out as well, so that a report has it even when the file is not attached. |
| | | try |
| | | { |
| | | notifyListeners(LocalizableMessage.raw(tempLogFile.readContents())); |
| | | } |
| | | catch (final IOException e) |
| | | { |
| | | notifyListeners(getFormattedWarning(INFO_GENERAL_LOG_IN_ERROR_UNREADABLE.get(tempLogFilePath, e))); |
| | | } |
| | | notifyListeners(getLineBreak()); |
| | | } |
| | | |
| | | /** Creates a default instance. */ |
| | |
| | | * |
| | | * Copyright 2008-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2014-2016 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.quicksetup.installer; |
| | | |
| | |
| | | |
| | | import static com.forgerock.opendj.util.StaticUtils.registerBcProvider; |
| | | |
| | | import java.io.File; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.opends.quicksetup.CliApplication; |
| | | import org.opends.quicksetup.Installation; |
| | |
| | | * @param args the arguments passed by the command lines. |
| | | */ |
| | | public SetupLauncher(String[] args) { |
| | | super(args, LOG_FILE_PREFIX); |
| | | super(args, LOG_FILE_PREFIX, instanceLogsDirectory()); |
| | | if (System.getProperty(PROPERTY_SCRIPT_NAME) == null) |
| | | { |
| | | System.setProperty(PROPERTY_SCRIPT_NAME, Installation.getSetupFileName()); |
| | |
| | | initializeParser(); |
| | | } |
| | | |
| | | /** |
| | | * The {@code logs/} directory of the instance being set up, where the setup log is kept. |
| | | * <p> |
| | | * The launcher scripts point {@code java.io.tmpdir} at {@code <instance>/tmp}, the scratch |
| | | * space of every tool, which {@code start-ds} - run by setup itself to start the server - |
| | | * used to sweep clean (issue #1030). The log of a failed setup belongs next to the server's |
| | | * own logs instead, where {@code server.out} tells the other half of the story. |
| | | * <p> |
| | | * Neither the directory nor the log is created here: {@link Launcher#getTempLogFile()} |
| | | * creates both when an install is about to run, so that a road which installs nothing - |
| | | * {@code setup --help} on a package whose instance directory is not laid down yet, for one - |
| | | * leaves nothing behind. |
| | | * |
| | | * @return the logs directory of the instance, or {@code null} when the launcher is not |
| | | * running from an installation and the OS temporary directory has to do. |
| | | */ |
| | | private static File instanceLogsDirectory() |
| | | { |
| | | final String installPath = Utils.getInstallPathFromClasspath(); |
| | | if (installPath == null) |
| | | { |
| | | return null; |
| | | } |
| | | return new File(Utils.getInstancePathFromInstallPath(installPath), Installation.LOGS_PATH_RELATIVE); |
| | | } |
| | | |
| | | /** Initialize the contents of the argument parser. */ |
| | | protected void initializeParser() |
| | | { |
| | |
| | | else if (isCli()) |
| | | { |
| | | Utils.checkJavaVersion(); |
| | | System.exit(InstallDS.mainCLI(args, tempLogFile)); |
| | | System.exit(InstallDS.mainCLI(args, this::getTempLogFile)); |
| | | } |
| | | else |
| | | { |
| | |
| | | if (exitCode != 0) { |
| | | guiLaunchFailed(); |
| | | Utils.checkJavaVersion(); |
| | | System.exit(InstallDS.mainCLI(args, tempLogFile)); |
| | | System.exit(InstallDS.mainCLI(args, this::getTempLogFile)); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | protected void guiLaunchFailed() { |
| | | System.err.println( |
| | | tempLogFile.isEnabled() ? INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS.get(tempLogFile.getPath()) |
| | | : INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED.get()); |
| | | // No log is named here: none exists yet, and creating one to name would leave it behind on |
| | | // every command line road that installs nothing (issue #1030). The reason the GUI failed |
| | | // goes into the log of the install that follows, and a failed install names that log. |
| | | System.err.println(INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED.get()); |
| | | } |
| | | |
| | | @Override |
| | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.function.Supplier; |
| | | import java.util.logging.Handler; |
| | | |
| | | import javax.swing.SwingUtilities; |
| | |
| | | * blocked. |
| | | * |
| | | * @param tempLogFile |
| | | * temporary log file where messages will be logged. |
| | | * supplies the temporary log file where messages will be logged. The wizard does |
| | | * not create it: the application does, when it starts the operation, so that a |
| | | * wizard the user quits leaves no log behind (issue #1030). |
| | | * @param args |
| | | * for the moment this parameter is not used but we keep it in order |
| | | * to (in case of need) pass parameters through the command line. |
| | | */ |
| | | public void initialize(final TempLogFile tempLogFile, String[] args) |
| | | public void initialize(final Supplier<TempLogFile> tempLogFile, String[] args) |
| | | { |
| | | ProgressMessageFormatter formatter = new HtmlProgressMessageFormatter(); |
| | | |
| | |
| | | import java.util.Collections; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.function.Supplier; |
| | | |
| | | import com.forgerock.opendj.util.FipsStaticUtils; |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | |
| | | private Integer lastResetAdminConnectorPort; |
| | | private Integer lastResetJmxPort; |
| | | |
| | | private final TempLogFile tempLogFile; |
| | | /** |
| | | * The temporary log file where messages will be logged, asked for only once an install is |
| | | * about to run: the roads which return before that one leave no log behind (issue #1030). |
| | | */ |
| | | private final Supplier<TempLogFile> tempLogFile; |
| | | |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | |
| | | * @param tempLogFile |
| | | * the temporary log file where messages will be logged. |
| | | */ |
| | | private InstallDS(PrintStream out, PrintStream err, TempLogFile tempLogFile) |
| | | private InstallDS(PrintStream out, PrintStream err, Supplier<TempLogFile> tempLogFile) |
| | | { |
| | | super(out, err); |
| | | this.tempLogFile = tempLogFile; |
| | |
| | | * @param args |
| | | * the command-line arguments provided to this program. |
| | | * @param tempLogFile |
| | | * the temporary log file where messages will be logged. |
| | | * supplies the temporary log file where messages will be logged. It is called on |
| | | * the road that runs the install and not on the roads which return before it, so |
| | | * that a run which installs nothing creates no log. |
| | | * @return The error code. |
| | | */ |
| | | public static int mainCLI(String[] args, final TempLogFile tempLogFile) |
| | | public static int mainCLI(String[] args, final Supplier<TempLogFile> tempLogFile) |
| | | { |
| | | return mainCLI(args, System.out, System.err, tempLogFile); |
| | | } |
| | |
| | | // |
| | | // *NOTE* this method has been kept public because it is used by OpenAM. |
| | | // |
| | | return mainCLI(args, outStream, errStream, () -> tempLogFile); |
| | | } |
| | | |
| | | /** |
| | | * Parses the provided command-line arguments and uses that information to run |
| | | * the setup tool. |
| | | * |
| | | * @param args |
| | | * The command-line arguments provided to this program. |
| | | * @param outStream |
| | | * The output stream to use for standard output, or <CODE>null</CODE> |
| | | * if standard output is not needed. |
| | | * @param errStream |
| | | * The output stream to use for standard error, or <CODE>null</CODE> |
| | | * if standard error is not needed. |
| | | * @param tempLogFile |
| | | * supplies the temporary log file where messages will be logged, see |
| | | * {@link #mainCLI(String[], Supplier)}. |
| | | * @return The error code. |
| | | */ |
| | | public static int mainCLI( |
| | | String[] args, OutputStream outStream, OutputStream errStream, Supplier<TempLogFile> tempLogFile) |
| | | { |
| | | final PrintStream out = NullOutputStream.wrapOrNullStream(outStream); |
| | | |
| | | System.setProperty(Constants.CLI_JAVA_PROPERTY, "true"); |
| | |
| | | |
| | | System.setProperty(Constants.CLI_JAVA_PROPERTY, "true"); |
| | | final Installer installer = new Installer(); |
| | | // The supplier is handed over, not the file: the installer creates it when it starts to |
| | | // run, which is the first point where something can fail. The roads above - a usage |
| | | // error, "already installed", a refused licence, a cancel at the prompt - return without |
| | | // asking for it and leave no log behind (issue #1030). |
| | | installer.setTempLogFile(tempLogFile); |
| | | installer.setUserData(uData); |
| | | installer.setProgressMessageFormatter(formatter); |
| | |
| | | INFO_GENERAL_SEE_FOR_DETAILS=See %s for a detailed log of this operation. |
| | | INFO_GENERAL_PROVIDE_LOG_IN_ERROR=See %s for a detailed log of the failed operation.\ |
| | | \nPlease report this error and provide the log file mentioned above. |
| | | INFO_GENERAL_LOG_IN_ERROR_MISSING=The log file %s of the failed operation is no \ |
| | | longer there, so it cannot be provided with the error report. |
| | | INFO_GENERAL_LOG_IN_ERROR_UNREADABLE=The log file %s of the failed operation \ |
| | | could not be read: %s |
| | | INFO_GENERAL_SERVER_STARTED=started |
| | | INFO_GENERAL_SERVER_STOPPED=stopped |
| | | INFO_GENERAL_WARNING=Warning |
| | |
| | | INFO_SERVER_SETTINGS_STEP=Server Settings |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED=%n%nThe graphical Setup launch \ |
| | | failed.%n%nLaunching command line setup... |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS=%n%nThe graphical Setup \ |
| | | launch failed. Check file %s for more details.%n%nLaunching command line \ |
| | | setup... |
| | | INFO_SETUP_LAUNCHER_LAUNCHING_GUI=Launching graphical setup... |
| | | INFO_SHOW_EXCEPTION_DETAILS=Show Details |
| | | # Only translate if the image is specific to the local |
| | |
| | | # |
| | | # Copyright 2006-2010 Sun Microsystems, Inc. |
| | | # Portions Copyright 2010-2016 ForgeRock AS. |
| | | # Portions Copyright 2026 3A Systems, LLC. |
| | | |
| | | |
| | | |
| | |
| | | INFO_SERVER_SETTINGS_PANEL_TITLE=Servereinstellungen |
| | | INFO_SERVER_SETTINGS_STEP=Servereinstellungen |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED=%n%nSetup-Start \u00fcber grafische Benutzeroberfl\u00e4che ist fehlgeschlagen.%n%nSetup \u00fcber Befehlszeile wird gestartet... |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS=%n%nSetup-Start \u00fcber grafische Benutzeroberfl\u00e4che ist fehlgeschlagen. Weitere Details finden Sie in der Datei %s.%n%nSetup \u00fcber Befehlszeile wird gestartet... |
| | | INFO_SETUP_LAUNCHER_LAUNCHING_GUI=Setup \u00fcber grafische Benutzeroberfl\u00e4che wird gestartet... |
| | | INFO_SHOW_EXCEPTION_DETAILS=Details anzeigen |
| | | # Only translate if the image is specific to the local |
| | |
| | | # |
| | | # Copyright 2006-2010 Sun Microsystems, Inc. |
| | | # Portions Copyright 2010-2016 ForgeRock AS. |
| | | # Portions Copyright 2026 3A Systems, LLC. |
| | | |
| | | |
| | | |
| | |
| | | INFO_SERVER_SETTINGS_PANEL_TITLE=Configuraci\u00f3n del servidor |
| | | INFO_SERVER_SETTINGS_STEP=Configuraci\u00f3n del servidor |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED=%n%nError al iniciar la instalaci\u00f3n gr\u00e1fica.%n%nIniciando la instalaci\u00f3n mediante l\u00ednea de comandos... |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS=%n%nError al iniciar la instalaci\u00f3n gr\u00e1fica. Consulte el archivo %s para obtener m\u00e1s informaci\u00f3n.%n%nIniciando la instalaci\u00f3n mediante l\u00ednea de comandos... |
| | | INFO_SETUP_LAUNCHER_LAUNCHING_GUI=Iniciando configuraci\u00f3n gr\u00e1fica... |
| | | INFO_SHOW_EXCEPTION_DETAILS=Mostrar detalles |
| | | # Only translate if the image is specific to the local |
| | |
| | | # |
| | | # Copyright 2006-2010 Sun Microsystems, Inc. |
| | | # Portions Copyright 2010-2016 ForgeRock AS. |
| | | # Portions Copyright 2026 3A Systems, LLC. |
| | | |
| | | |
| | | |
| | |
| | | INFO_SERVER_SETTINGS_PANEL_TITLE=Param\u00e8tres du serveur |
| | | INFO_SERVER_SETTINGS_STEP=Param\u00e8tres du serveur |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED=%n%n\u00c9chec du lancement de la configuration graphique.%n%nD\u00e9marrage de la configuration via la ligne de commande... |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS=%n%n\u00c9chec du lancement de la configuration graphique. Reportez-vous au fichier %s pour plus d'informations.%n%nD\u00e9marrage de la configuration via la ligne de commande... |
| | | INFO_SETUP_LAUNCHER_LAUNCHING_GUI=D\u00e9marrage de la configuration graphique... |
| | | INFO_SHOW_EXCEPTION_DETAILS=Afficher les d\u00e9tails |
| | | # Only translate if the image is specific to the local |
| | |
| | | # |
| | | # Copyright 2006-2010 Sun Microsystems, Inc. |
| | | # Portions Copyright 2010-2016 ForgeRock AS. |
| | | # Portions Copyright 2026 3A Systems, LLC. |
| | | |
| | | |
| | | |
| | |
| | | INFO_SERVER_SETTINGS_PANEL_TITLE=\u30b5\u30fc\u30d0\u30fc\u8a2d\u5b9a |
| | | INFO_SERVER_SETTINGS_STEP=\u30b5\u30fc\u30d0\u30fc\u8a2d\u5b9a |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED=%n%n\u30b0\u30e9\u30d5\u30a3\u30ab\u30eb\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u306e\u8d77\u52d5\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002%n%n\u30b3\u30de\u30f3\u30c9\u884c\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u8d77\u52d5\u3057\u3066\u3044\u307e\u3059... |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS=%n%n\u30b0\u30e9\u30d5\u30a3\u30ab\u30eb\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u306e\u8d77\u52d5\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 \u8a73\u7d30\u306f\u30d5\u30a1\u30a4\u30eb %s \u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002%n%n\u30b3\u30de\u30f3\u30c9\u884c\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u8d77\u52d5\u3057\u3066\u3044\u307e\u3059... |
| | | INFO_SETUP_LAUNCHER_LAUNCHING_GUI=\u30b0\u30e9\u30d5\u30a3\u30ab\u30eb\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u8d77\u52d5\u3057\u3066\u3044\u307e\u3059... |
| | | INFO_SHOW_EXCEPTION_DETAILS=\u8a73\u7d30\u3092\u8868\u793a |
| | | # Only translate if the image is specific to the local |
| | |
| | | # |
| | | # Copyright 2006-2009 Sun Microsystems, Inc. |
| | | # Portions Copyright 2010-2016 ForgeRock AS. |
| | | # Portions Copyright 2026 3A Systems, LLC. |
| | | |
| | | |
| | | |
| | |
| | | INFO_SERVER_SETTINGS_PANEL_TITLE=\uc11c\ubc84 \uc124\uc815 |
| | | INFO_SERVER_SETTINGS_STEP=\uc11c\ubc84 \uc124\uc815 |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED=%n%n\uadf8\ub798\ud53d \uc124\uce58\ub97c \uc2dc\uc791\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.%n%n\uba85\ub839\uc904 \uc124\uce58\ub97c \uc2dc\uc791\ud558\ub294 \uc911... |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS=%n%n\uadf8\ub798\ud53d \uc124\uce58\ub97c \uc2dc\uc791\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4. \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 %s \ud30c\uc77c\uc744 \ucc38\uc870\ud558\uc2ed\uc2dc\uc624.%n%n\uba85\ub839\uc904 \uc124\uce58\ub97c \uc2dc\uc791\ud558\ub294 \uc911... |
| | | INFO_SETUP_LAUNCHER_LAUNCHING_GUI=\uadf8\ub798\ud53d \uc124\uce58\ub97c \uc2dc\uc791\ud558\ub294 \uc911... |
| | | INFO_SHOW_EXCEPTION_DETAILS=\uc138\ubd80 \uc815\ubcf4 \ud45c\uc2dc |
| | | # Only translate if the image is specific to the local |
| | |
| | | # |
| | | # Copyright 2006-2009 Sun Microsystems, Inc. |
| | | # Portions Copyright 2010-2016 ForgeRock AS. |
| | | # Portions Copyright 2026 3A Systems, LLC. |
| | | |
| | | |
| | | |
| | |
| | | INFO_CERTIFICATE_LABEL=Certyfikat: |
| | | ERR_CANNOT_CONNECT_TO_LOCAL_GENERIC=Could not connect to the server. Check that the server is running and that the provided credentials are valid.%nSzczeg\u00f3\u0142y b\u0142\u0119du:%n%s |
| | | INFO_CERTIFICATE_DIALOG_ACCEPT_FOR_SESSION_BUTTON_TOOLTIP=Zamknij to okienko i zaakceptuj certyfikat tylko dla tej sesji. |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS=%n%nThe graphical Setup launch failed. Sprawd\u017a plik %s aby uzyska\u0107 szczeg\u00f3\u0142owe informacje.%n%nLaunching command line setup... |
| | | INFO_CANCEL_BUTTON_LABEL=Anuluj |
| | | INFO_CERTIFICATE_DIALOG_ACCEPT_PERMANENTLY_BUTTON_TOOLTIP=Zamknij to okienko i zaakceptuj certyfikat na sta\u0142e. |
| | | INFO_CANNOT_USE_DEFAULT_ADMIN_CONNECTOR_PORT=Nie mo\u017cna u\u017cy\u0107 4444. Port w u\u017cyciu lub nieautoryzowany u\u017cytkownik. |
| | |
| | | # |
| | | # Copyright 2006-2010 Sun Microsystems, Inc. |
| | | # Portions Copyright 2010-2016 ForgeRock AS. |
| | | # Portions Copyright 2026 3A Systems, LLC. |
| | | |
| | | |
| | | |
| | |
| | | INFO_SERVER_SETTINGS_PANEL_TITLE=\u670d\u52a1\u5668\u8bbe\u7f6e |
| | | INFO_SERVER_SETTINGS_STEP=\u670d\u52a1\u5668\u8bbe\u7f6e |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED=%n%n\u542f\u52a8\u56fe\u5f62\u5316\u5b89\u88c5\u5931\u8d25\u3002%n%n\u6b63\u5728\u542f\u52a8\u547d\u4ee4\u884c\u5b89\u88c5... |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS=%n%n\u542f\u52a8\u56fe\u5f62\u5316\u5b89\u88c5\u5931\u8d25\u3002\u8bf7\u68c0\u67e5\u6587\u4ef6 %s\uff0c\u4ee5\u83b7\u53d6\u66f4\u591a\u7684\u8be6\u7ec6\u4fe1\u606f\u3002%n%n\u6b63\u5728\u542f\u52a8\u547d\u4ee4\u884c\u5b89\u88c5... |
| | | INFO_SETUP_LAUNCHER_LAUNCHING_GUI=\u6b63\u5728\u542f\u52a8\u56fe\u5f62\u5316\u5b89\u88c5... |
| | | INFO_SHOW_EXCEPTION_DETAILS=\u663e\u793a\u8be6\u7ec6\u8d44\u6599 |
| | | # Only translate if the image is specific to the local |
| | |
| | | # |
| | | # Copyright 2006-2009 Sun Microsystems, Inc. |
| | | # Portions Copyright 2010-2016 ForgeRock AS. |
| | | # Portions Copyright 2026 3A Systems, LLC. |
| | | |
| | | |
| | | |
| | |
| | | INFO_SERVER_SETTINGS_PANEL_TITLE=\u4f3a\u670d\u5668\u8a2d\u5b9a |
| | | INFO_SERVER_SETTINGS_STEP=\u4f3a\u670d\u5668\u8a2d\u5b9a |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED=%n%n\u5716\u5f62\u5316\u8a2d\u5b9a\u555f\u52d5\u5931\u6557\u3002%n%n\u6b63\u5728\u555f\u52d5\u6307\u4ee4\u884c\u8a2d\u5b9a... |
| | | INFO_SETUP_LAUNCHER_GUI_LAUNCHED_FAILED_DETAILS=%n%n\u5716\u5f62\u5316\u8a2d\u5b9a\u555f\u52d5\u5931\u6557\u3002\u6aa2\u67e5\u6a94\u6848 %s \u4ee5\u53d6\u5f97\u66f4\u591a\u8a73\u7d30\u8cc7\u6599\u3002%n%n\u6b63\u5728\u555f\u52d5\u6307\u4ee4\u884c\u8a2d\u5b9a... |
| | | INFO_SETUP_LAUNCHER_LAUNCHING_GUI=\u6b63\u5728\u555f\u52d5\u5716\u5f62\u5316\u8a2d\u5b9a... |
| | | INFO_SHOW_EXCEPTION_DETAILS=\u986f\u793a\u8a73\u7d30\u8cc7\u8a0a |
| | | # Only translate if the image is specific to the local |
| New file |
| | |
| | | /* |
| | | * The contents of this file are subject to the terms of the Common Development and |
| | | * Distribution License (the License). You may not use this file except in compliance with the |
| | | * License. |
| | | * |
| | | * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the |
| | | * specific language governing permission and limitations under the License. |
| | | * |
| | | * When distributing Covered Software, include this CDDL Header Notice in each file and include |
| | | * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.quicksetup; |
| | | |
| | | import static org.testng.Assert.assertEquals; |
| | | import static org.testng.Assert.assertFalse; |
| | | import static org.testng.Assert.assertSame; |
| | | import static org.testng.Assert.assertTrue; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.function.Supplier; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.opends.server.DirectoryServerTestCase; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.testng.SkipException; |
| | | import org.testng.annotations.AfterClass; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import com.forgerock.opendj.cli.ArgumentParser; |
| | | |
| | | /** |
| | | * Tests when a launcher creates its temporary log file. |
| | | * <p> |
| | | * Nothing removes that file unless the operation it belongs to succeeds, so a road which |
| | | * attempts nothing - {@code setup --help}, {@code --version}, a usage error - must not create |
| | | * one, nor the directory it would live in (issue #1030). |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | @Test(groups = { "precommit", "quicksetup" }, sequential = true) |
| | | public class LauncherTest extends DirectoryServerTestCase |
| | | { |
| | | private static final String PREFIX = "opendj-setup-"; |
| | | |
| | | private File tempDir; |
| | | private final List<TempLogFile> created = new ArrayList<>(); |
| | | |
| | | @BeforeClass |
| | | public void setUp() throws IOException |
| | | { |
| | | tempDir = TestCaseUtils.createTemporaryDirectory("launcherTest"); |
| | | } |
| | | |
| | | @AfterClass |
| | | public void tearDown() throws IOException |
| | | { |
| | | for (TempLogFile logFile : created) |
| | | { |
| | | logFile.deleteLogFileAfterSuccess(); |
| | | } |
| | | TestCaseUtils.deleteDirectory(tempDir); |
| | | } |
| | | |
| | | @Test |
| | | public void testBuildingALauncherLeavesNothingOnDisk() throws Exception |
| | | { |
| | | final File instance = new File(tempDir, "not-yet-laid-down"); |
| | | final File logs = new File(instance, "logs"); |
| | | |
| | | final TestLauncher launcher = new TestLauncher(logs); |
| | | |
| | | assertFalse(launcher.hasTempLogFile(), "the log must wait for a road that can fail"); |
| | | assertFalse(logs.exists(), logs.getPath()); |
| | | assertFalse(instance.exists(), instance.getPath()); |
| | | } |
| | | |
| | | @Test |
| | | public void testAskingForTheLogCreatesItOnce() throws Exception |
| | | { |
| | | final File logs = new File(tempDir, "asked-for/logs"); |
| | | final TestLauncher launcher = new TestLauncher(logs); |
| | | |
| | | final TempLogFile logFile = launcher.getTempLogFile(); |
| | | created.add(logFile); |
| | | |
| | | assertTrue(logFile.isReadable(), logFile.getPath()); |
| | | assertEquals(logFile.getLogFile().getCanonicalFile().getParentFile(), logs.getCanonicalFile()); |
| | | assertTrue(launcher.hasTempLogFile()); |
| | | assertSame(launcher.getTempLogFile(), logFile, "a second ask must not create a second log"); |
| | | } |
| | | |
| | | /** |
| | | * The wizard road: the splash screen comes up before the user has said anything, so the log |
| | | * cannot be created on the way to it - it is the application that asks for one, when it |
| | | * starts the install. A wizard quit at any step leaves nothing behind. |
| | | */ |
| | | @Test |
| | | public void testLaunchingTheGuiCreatesNoLog() throws Exception |
| | | { |
| | | final File instance = new File(tempDir, "quit-at-the-first-step"); |
| | | final File logs = new File(instance, "logs"); |
| | | final TestLauncher launcher = new TestLauncher(logs); |
| | | |
| | | // The wizard behind the splash screen quits without installing anything. |
| | | launcher.launchGui(new String[0]); |
| | | |
| | | assertFalse(launcher.hasTempLogFile(), "the splash screen must not cost a log"); |
| | | assertFalse(logs.exists(), logs.getPath()); |
| | | assertFalse(instance.exists(), instance.getPath()); |
| | | |
| | | // What the wizard was handed is the launcher's own log, made on the first ask. |
| | | final TempLogFile logFile = launcher.splashLogFile.get(); |
| | | created.add(logFile); |
| | | assertTrue(logFile.isReadable(), logFile.getPath()); |
| | | assertSame(logFile, launcher.getTempLogFile()); |
| | | } |
| | | |
| | | /** |
| | | * A GUI which does not come up costs no log either, and its reason is not lost for that: it |
| | | * goes into the log as soon as something asks for one - on the headless road, the operation |
| | | * which runs on the command line instead. |
| | | */ |
| | | @Test |
| | | public void testAGuiWhichDoesNotComeUpIsLoggedOnceThereIsALog() throws Exception |
| | | { |
| | | if ("true".equalsIgnoreCase(System.getenv("OPENDJ_LOG_TO_STDOUT"))) |
| | | { |
| | | // The reason is written to the log, which then goes to stdout and leaves the file empty. |
| | | throw new SkipException("OPENDJ_LOG_TO_STDOUT writes the log to stdout, not to the file"); |
| | | } |
| | | final File logs = new File(tempDir, "headless/logs"); |
| | | final TestLauncher launcher = new TestLauncher(logs); |
| | | launcher.splashFailure = new IllegalStateException("no display at all"); |
| | | |
| | | assertTrue(launcher.launchGui(new String[0]) != 0, "a GUI which does not come up is reported"); |
| | | |
| | | assertFalse(launcher.hasTempLogFile(), "a GUI which does not come up must not cost a log"); |
| | | assertFalse(logs.exists(), logs.getPath()); |
| | | |
| | | final TempLogFile logFile = launcher.getTempLogFile(); |
| | | created.add(logFile); |
| | | logFile.writer.shutdown(); |
| | | final String contents = logFile.readContents(); |
| | | assertTrue(contents.contains("no display at all"), contents); |
| | | } |
| | | |
| | | /** A launcher with nothing in it but the log file behaviour under test. */ |
| | | private static final class TestLauncher extends Launcher |
| | | { |
| | | /** What {@link Launcher#launchGui(String[])} handed the splash screen. */ |
| | | private Supplier<TempLogFile> splashLogFile; |
| | | /** What the splash screen throws, if it is not to come up. */ |
| | | private RuntimeException splashFailure; |
| | | |
| | | TestLauncher(final File tempLogFileDirectory) |
| | | { |
| | | super(new String[0], PREFIX, tempLogFileDirectory); |
| | | } |
| | | |
| | | @Override |
| | | void startSplashScreen(final Supplier<TempLogFile> tempLogFile, final String[] args) |
| | | { |
| | | // No display here, and no install either: the wizard is quit at its first step - or it |
| | | // does not come up at all. |
| | | splashLogFile = tempLogFile; |
| | | if (splashFailure != null) |
| | | { |
| | | throw splashFailure; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public ArgumentParser getArgumentParser() |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | protected LocalizableMessage getFrameTitle() |
| | | { |
| | | return LocalizableMessage.raw("test"); |
| | | } |
| | | |
| | | @Override |
| | | protected CliApplication createCliApplication() |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | protected void willLaunchGui() |
| | | { |
| | | // nothing is launched here |
| | | } |
| | | |
| | | @Override |
| | | protected void guiLaunchFailed() |
| | | { |
| | | // nothing is launched here |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * The contents of this file are subject to the terms of the Common Development and |
| | | * Distribution License (the License). You may not use this file except in compliance with the |
| | | * License. |
| | | * |
| | | * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the |
| | | * specific language governing permission and limitations under the License. |
| | | * |
| | | * When distributing Covered Software, include this CDDL Header Notice in each file and include |
| | | * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.quicksetup; |
| | | |
| | | import static java.nio.charset.Charset.defaultCharset; |
| | | import static java.nio.file.StandardOpenOption.APPEND; |
| | | import static org.testng.Assert.assertEquals; |
| | | import static org.testng.Assert.assertFalse; |
| | | import static org.testng.Assert.assertNotEquals; |
| | | import static org.testng.Assert.assertTrue; |
| | | import static org.testng.Assert.fail; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.lang.reflect.Method; |
| | | import java.nio.file.Files; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | |
| | | import org.opends.server.DirectoryServerTestCase; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.loggers.ErrorLogger; |
| | | import org.testng.SkipException; |
| | | import org.testng.annotations.AfterClass; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.Test; |
| | | |
| | | /** |
| | | * Tests that a {@link TempLogFile} can be placed in a directory of the caller's choosing (the |
| | | * instance {@code logs/} directory for setup, so that {@code start-ds} does not sweep it away |
| | | * with the rest of {@code tmp/}, see issue #1030), and that it tells whether the file is still |
| | | * there to be read. |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | @Test(groups = { "precommit", "quicksetup" }, sequential = true) |
| | | public class TempLogFileTest extends DirectoryServerTestCase |
| | | { |
| | | private static final String PREFIX = "opendj-setup-"; |
| | | |
| | | private File tempDir; |
| | | private final List<TempLogFile> created = new ArrayList<>(); |
| | | |
| | | @BeforeClass |
| | | public void setUp() throws IOException |
| | | { |
| | | tempDir = TestCaseUtils.createTemporaryDirectory("tempLogFileTest"); |
| | | } |
| | | |
| | | @AfterClass |
| | | public void tearDown() throws IOException |
| | | { |
| | | for (TempLogFile logFile : created) |
| | | { |
| | | logFile.deleteLogFileAfterSuccess(); |
| | | } |
| | | TestCaseUtils.deleteDirectory(tempDir); |
| | | } |
| | | |
| | | private TempLogFile track(TempLogFile logFile) |
| | | { |
| | | created.add(logFile); |
| | | return logFile; |
| | | } |
| | | |
| | | private static File parentOf(TempLogFile logFile) throws IOException |
| | | { |
| | | return logFile.getLogFile().getCanonicalFile().getParentFile(); |
| | | } |
| | | |
| | | /** The directory does not exist before setup lays the instance down: it has to be created. */ |
| | | @Test |
| | | public void testLogFileIsCreatedInTheRequestedDirectory() throws Exception |
| | | { |
| | | final File logs = new File(tempDir, "not-yet-laid-down/logs"); |
| | | assertFalse(logs.exists()); |
| | | |
| | | final TempLogFile logFile = track(TempLogFile.newTempLogFile(PREFIX, logs)); |
| | | |
| | | assertTrue(logFile.isEnabled()); |
| | | assertTrue(logFile.isReadable()); |
| | | assertEquals(parentOf(logFile), logs.getCanonicalFile()); |
| | | assertTrue(logFile.getLogFile().getName().startsWith(PREFIX), logFile.getPath()); |
| | | assertTrue(logFile.getLogFile().getName().endsWith(".log"), logFile.getPath()); |
| | | } |
| | | |
| | | @Test |
| | | public void testReadContentsReturnsWhatIsInTheFile() throws Exception |
| | | { |
| | | final TempLogFile logFile = track(TempLogFile.newTempLogFile(PREFIX, new File(tempDir, "logs"))); |
| | | // The log's own stream is not in append mode and sits at the end of its own bytes, so a |
| | | // record written after the marker would be written over it: shut the writer first, and |
| | | // the marker is the last thing in the file whatever else the JVM logs. |
| | | logFile.writer.shutdown(); |
| | | final String marker = "the last line written before the failure"; |
| | | Files.write(logFile.getLogFile().toPath(), (marker + "\n").getBytes(defaultCharset()), APPEND); |
| | | |
| | | assertTrue(logFile.readContents().endsWith(marker + "\n")); |
| | | } |
| | | |
| | | /** Being enabled means messages are logged; being readable means the file is there to hand over. */ |
| | | @Test |
| | | public void testIsReadableFollowsTheFileNotTheLogger() throws Exception |
| | | { |
| | | final TempLogFile logFile = track(TempLogFile.newTempLogFile(PREFIX, new File(tempDir, "logs"))); |
| | | assertTrue(logFile.isReadable()); |
| | | |
| | | // Not File.delete(): the writer still holds the file, and Windows does not delete a file |
| | | // that is open. deleteLogFileAfterSuccess() shuts the writer first, as setup does. |
| | | logFile.deleteLogFileAfterSuccess(); |
| | | assertFalse(logFile.getLogFile().exists()); |
| | | |
| | | assertTrue(logFile.isEnabled()); |
| | | assertFalse(logFile.isReadable()); |
| | | try |
| | | { |
| | | logFile.readContents(); |
| | | fail("reading a deleted log must fail"); |
| | | } |
| | | catch (IOException expected) |
| | | { |
| | | // the caller reports it instead of promising the file |
| | | } |
| | | } |
| | | |
| | | /** A directory where the log was is not a log: there is nothing to hand over either. */ |
| | | @Test |
| | | public void testADirectoryAtTheLogPathIsNotReadable() throws Exception |
| | | { |
| | | final TempLogFile logFile = track(TempLogFile.newTempLogFile(PREFIX, new File(tempDir, "logs"))); |
| | | logFile.deleteLogFileAfterSuccess(); |
| | | assertTrue(logFile.getLogFile().mkdir()); |
| | | |
| | | assertTrue(Files.isReadable(logFile.getLogFile().toPath())); |
| | | assertFalse(logFile.isReadable()); |
| | | } |
| | | |
| | | @Test |
| | | public void testNoDirectoryMeansTheTemporaryDirectory() throws Exception |
| | | { |
| | | final TempLogFile logFile = track(TempLogFile.newTempLogFile(PREFIX, null)); |
| | | |
| | | assertTrue(logFile.isEnabled()); |
| | | assertEquals(parentOf(logFile), new File(System.getProperty("java.io.tmpdir")).getCanonicalFile()); |
| | | } |
| | | |
| | | /** A directory that cannot be used must not cost the log: fall back to the temporary directory. */ |
| | | @Test |
| | | public void testUnusableDirectoryFallsBackToTheTemporaryDirectory() throws Exception |
| | | { |
| | | final File notADirectory = new File(tempDir, "not-a-directory"); |
| | | assertTrue(notADirectory.createNewFile()); |
| | | |
| | | final TempLogFile logFile = track(TempLogFile.newTempLogFile(PREFIX, notADirectory)); |
| | | |
| | | assertTrue(logFile.isEnabled()); |
| | | assertTrue(logFile.isReadable()); |
| | | assertNotEquals(parentOf(logFile), notADirectory.getCanonicalFile()); |
| | | assertEquals(parentOf(logFile), new File(System.getProperty("java.io.tmpdir")).getCanonicalFile()); |
| | | |
| | | // Why the log is not where it was asked for is warned about once there is a log to carry |
| | | // the warning: at the point the directory failed, no publisher was installed yet. |
| | | assumeTheLogGoesToTheFile(); |
| | | logFile.writer.shutdown(); |
| | | final String contents = logFile.readContents(); |
| | | assertTrue(contents.contains("falling back to the temporary directory"), contents); |
| | | assertTrue(contents.contains(notADirectory.toString()), contents); |
| | | } |
| | | |
| | | /** |
| | | * The publishers the constructor puts on the logger singletons come off again with the log: |
| | | * the singletons outlive the file, and what a leaked publisher is handed goes to a closed |
| | | * stream and is swallowed. |
| | | */ |
| | | @Test |
| | | public void testDeletingTheLogTakesItsPublisherOffTheLogger() throws Exception |
| | | { |
| | | final int before = errorLogPublishers(); |
| | | |
| | | final TempLogFile logFile = track(TempLogFile.newTempLogFile(PREFIX, new File(tempDir, "logs"))); |
| | | assertEquals(errorLogPublishers(), before + 1, "the log logs through a publisher of its own"); |
| | | |
| | | logFile.deleteLogFileAfterSuccess(); |
| | | |
| | | assertEquals(errorLogPublishers(), before, "the publisher must not outlive the log"); |
| | | } |
| | | |
| | | /** How many publishers the error logger holds; {@code getLogPublishers()} is protected. */ |
| | | private static int errorLogPublishers() throws Exception |
| | | { |
| | | final Method getLogPublishers = ErrorLogger.class.getDeclaredMethod("getLogPublishers"); |
| | | getLogPublishers.setAccessible(true); |
| | | return ((Collection<?>) getLogPublishers.invoke(ErrorLogger.getInstance())).size(); |
| | | } |
| | | |
| | | /** |
| | | * {@code OPENDJ_LOG_TO_STDOUT} sends the records to stdout and leaves the file empty, so a |
| | | * case which reads the log back has nothing to look at. |
| | | */ |
| | | private static void assumeTheLogGoesToTheFile() |
| | | { |
| | | if ("true".equalsIgnoreCase(System.getenv("OPENDJ_LOG_TO_STDOUT"))) |
| | | { |
| | | throw new SkipException("OPENDJ_LOG_TO_STDOUT writes the log to stdout, not to the file"); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * The contents of this file are subject to the terms of the Common Development and |
| | | * Distribution License (the License). You may not use this file except in compliance with the |
| | | * License. |
| | | * |
| | | * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the |
| | | * specific language governing permission and limitations under the License. |
| | | * |
| | | * When distributing Covered Software, include this CDDL Header Notice in each file and include |
| | | * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.quicksetup.installer; |
| | | |
| | | import static org.mockito.Mockito.mock; |
| | | import static org.mockito.Mockito.when; |
| | | import static org.opends.messages.QuickSetupMessages.*; |
| | | import static org.testng.Assert.assertEquals; |
| | | import static org.testng.Assert.assertFalse; |
| | | import static org.testng.Assert.assertNull; |
| | | import static org.testng.Assert.assertTrue; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.opends.quicksetup.TempLogFile; |
| | | import org.opends.quicksetup.UserData; |
| | | import org.opends.quicksetup.event.ProgressUpdateEvent; |
| | | import org.opends.quicksetup.event.ProgressUpdateListener; |
| | | import org.opends.quicksetup.util.PlainTextProgressMessageFormatter; |
| | | import org.opends.server.DirectoryServerTestCase; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.testng.annotations.AfterClass; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.Test; |
| | | |
| | | /** |
| | | * Tests what a failed installation tells its listeners about the log file. |
| | | * <p> |
| | | * This report is the one place the diagnosis of a failed setup lives, and it used to promise |
| | | * the file without looking at it - printing a {@code NoSuchFileException} stack when the file |
| | | * was gone (issue #1030). The roads below are the whole of that decision. |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | @Test(groups = { "precommit", "quicksetup" }, sequential = true) |
| | | public class InstallerTest extends DirectoryServerTestCase |
| | | { |
| | | private static final String PREFIX = "opendj-setup-"; |
| | | /** Marks a line the report sends out as a warning, see {@link MarkedArmsFormatter}. */ |
| | | private static final String WARNING = "[W]"; |
| | | /** Marks a line the report sends out as progress. */ |
| | | private static final String PROGRESS = "[P]"; |
| | | |
| | | private File tempDir; |
| | | private final List<TempLogFile> created = new ArrayList<>(); |
| | | |
| | | @BeforeClass |
| | | public void setUp() throws IOException |
| | | { |
| | | tempDir = TestCaseUtils.createTemporaryDirectory("installerTest"); |
| | | } |
| | | |
| | | @AfterClass |
| | | public void tearDown() throws IOException |
| | | { |
| | | for (TempLogFile logFile : created) |
| | | { |
| | | logFile.deleteLogFileAfterSuccess(); |
| | | } |
| | | TestCaseUtils.deleteDirectory(tempDir); |
| | | } |
| | | |
| | | /** A log that is there is named, as before, and named as progress rather than as a warning. */ |
| | | @Test |
| | | public void testAReadableLogIsHandedOver() throws Exception |
| | | { |
| | | final TempLogFile logFile = newLogFile(); |
| | | |
| | | final String report = reportOf(logFile); |
| | | |
| | | assertContains(report, PROGRESS, INFO_GENERAL_PROVIDE_LOG_IN_ERROR.get(logFile.getPath())); |
| | | assertDoesNotContain(report, INFO_GENERAL_LOG_IN_ERROR_MISSING.get(logFile.getPath())); |
| | | } |
| | | |
| | | /** |
| | | * The contents of the log reach the report: attaching the file is up to whoever reads it, |
| | | * so the report carries the log itself. |
| | | * <p> |
| | | * What the file holds is not the point here and is mocked away - with |
| | | * {@code OPENDJ_LOG_TO_STDOUT} set, a real log is empty and would pin nothing. |
| | | */ |
| | | @Test |
| | | public void testTheContentsOfTheLogReachTheReport() throws Exception |
| | | { |
| | | final String contents = "QuickSetup application launched\nthe last line before the failure\n"; |
| | | final TempLogFile logFile = mock(TempLogFile.class); |
| | | when(logFile.isEnabled()).thenReturn(true); |
| | | when(logFile.isReadable()).thenReturn(true); |
| | | when(logFile.getPath()).thenReturn(new File(tempDir, "readable.log").getAbsolutePath()); |
| | | when(logFile.readContents()).thenReturn(contents); |
| | | |
| | | final String report = reportOf(logFile); |
| | | |
| | | assertTrue(report.contains(contents), report); |
| | | } |
| | | |
| | | /** A log something else removed is reported as gone, instead of being asked for. */ |
| | | @Test |
| | | public void testAMissingLogIsReportedAsMissing() throws Exception |
| | | { |
| | | final TempLogFile logFile = newLogFile(); |
| | | final String path = logFile.getPath(); |
| | | logFile.deleteLogFileAfterSuccess(); |
| | | assertFalse(logFile.isReadable()); |
| | | |
| | | final String report = reportOf(logFile); |
| | | |
| | | assertContains(report, WARNING, INFO_GENERAL_LOG_IN_ERROR_MISSING.get(path)); |
| | | // The line that asks for the file must not go out when there is no file to provide. |
| | | assertDoesNotContain(report, INFO_GENERAL_PROVIDE_LOG_IN_ERROR.get(path)); |
| | | } |
| | | |
| | | /** A log which is there but cannot be read costs the report its contents, not its diagnosis. */ |
| | | @Test |
| | | public void testAnUnreadableLogIsReportedAsUnreadable() throws Exception |
| | | { |
| | | final String path = new File(tempDir, "unreadable.log").getAbsolutePath(); |
| | | final IOException failure = new IOException("Input/output error"); |
| | | final TempLogFile logFile = mock(TempLogFile.class); |
| | | when(logFile.isEnabled()).thenReturn(true); |
| | | when(logFile.isReadable()).thenReturn(true); |
| | | when(logFile.getPath()).thenReturn(path); |
| | | when(logFile.readContents()).thenThrow(failure); |
| | | |
| | | final String report = reportOf(logFile); |
| | | |
| | | assertContains(report, WARNING, INFO_GENERAL_LOG_IN_ERROR_UNREADABLE.get(path, failure)); |
| | | assertDoesNotContain(report, INFO_GENERAL_LOG_IN_ERROR_MISSING.get(path)); |
| | | } |
| | | |
| | | /** Nothing is said about a log that was never created. */ |
| | | @Test |
| | | public void testNoLogMeansNoReport() throws Exception |
| | | { |
| | | final TempLogFile logFile = mock(TempLogFile.class); |
| | | when(logFile.isEnabled()).thenReturn(false); |
| | | |
| | | assertTrue(reportOf(logFile).isEmpty(), "a launcher without a log has nothing to report"); |
| | | } |
| | | |
| | | /** |
| | | * A cancelled install takes its log with it - and asks for it only when it begins. |
| | | * <p> |
| | | * Nothing names the log on that road - the report belongs to the failure road - and the |
| | | * cancel has just taken the installation back, so a log kept there is a report nobody is |
| | | * ever pointed at (issue #1030). The installer is handed the log the way the wizard and the |
| | | * command line hand it over, as a supplier: nothing asks for it before {@code run()}. |
| | | */ |
| | | @Test |
| | | public void testACancelledInstallTakesItsLogWithIt() throws Exception |
| | | { |
| | | final TempLogFile logFile = newLogFile(); |
| | | final File instance = new File(tempDir, "cancelled"); |
| | | // A locks directory the lock file can be taken in: without it the cancel road reads the |
| | | // server as running and goes off to stop it. |
| | | assertTrue(new File(instance, "locks").mkdirs()); |
| | | |
| | | final TestInstaller installer = new TestInstaller(instance); |
| | | installer.setProgressMessageFormatter(new MarkedArmsFormatter()); |
| | | final int[] asked = { 0 }; |
| | | installer.setTempLogFile(() -> { |
| | | asked[0]++; |
| | | return logFile; |
| | | }); |
| | | installer.setUserData(new UserData()); |
| | | installer.cancel(); |
| | | assertEquals(asked[0], 0, "nothing is logged before the install begins"); |
| | | |
| | | installer.run(); |
| | | |
| | | assertEquals(asked[0], 1, "the install asks for its log once, as it begins"); |
| | | assertEquals(installer.getCurrentProgressStep(), InstallProgressStep.FINISHED_CANCELED); |
| | | assertFalse(logFile.getLogFile().exists(), logFile.getPath()); |
| | | } |
| | | |
| | | /** |
| | | * An application nobody handed a log to has none, and asking for it is not a failure: the |
| | | * command line uninstaller is built that way, its launcher having created the log already. |
| | | */ |
| | | @Test |
| | | public void testAnApplicationGivenNoLogHasNone() throws Exception |
| | | { |
| | | assertNull(new TestInstaller(null).openTempLogFile()); |
| | | } |
| | | |
| | | private TempLogFile newLogFile() |
| | | { |
| | | final TempLogFile logFile = TempLogFile.newTempLogFile(PREFIX, new File(tempDir, "logs")); |
| | | created.add(logFile); |
| | | return logFile; |
| | | } |
| | | |
| | | /** What the listeners of a failed installation are told about the log file. */ |
| | | private static String reportOf(final TempLogFile logFile) |
| | | { |
| | | final TestInstaller installer = new TestInstaller(null); |
| | | installer.setProgressMessageFormatter(new MarkedArmsFormatter()); |
| | | final StringBuilder report = new StringBuilder(); |
| | | installer.addProgressUpdateListener(new ProgressUpdateListener() |
| | | { |
| | | @Override |
| | | public void progressUpdate(final ProgressUpdateEvent ev) |
| | | { |
| | | if (ev.getNewLogs() != null) |
| | | { |
| | | report.append(ev.getNewLogs()); |
| | | } |
| | | } |
| | | }); |
| | | installer.setTempLogFile(() -> logFile); |
| | | // The report is made by an install which has begun, and so has asked for its log. |
| | | installer.openTempLogFile(); |
| | | installer.notifyListenersOfExistingLogFile(); |
| | | return report.toString(); |
| | | } |
| | | |
| | | private static void assertContains(final String report, final String arm, final LocalizableMessage expected) |
| | | { |
| | | assertTrue(report.contains(arm + expected), "expected <" + arm + expected + "> in <" + report + ">"); |
| | | } |
| | | |
| | | private static void assertDoesNotContain(final String report, final LocalizableMessage unexpected) |
| | | { |
| | | assertFalse(report.contains(unexpected.toString()), "unexpected <" + unexpected + "> in <" + report + ">"); |
| | | } |
| | | |
| | | /** An installer which can be told where it lives, and whose log can be asked for. */ |
| | | private static final class TestInstaller extends Installer |
| | | { |
| | | /** The installation, or {@code null} for the one the class path names. */ |
| | | private final File instance; |
| | | |
| | | TestInstaller(final File instance) |
| | | { |
| | | this.instance = instance; |
| | | } |
| | | |
| | | /** |
| | | * An installation under the temporary directory rather than the one the class path names: |
| | | * the installer takes both paths from there, and in a test run there is none. |
| | | */ |
| | | @Override |
| | | public String getInstallationPath() |
| | | { |
| | | return instance != null ? instance.getAbsolutePath() : super.getInstallationPath(); |
| | | } |
| | | |
| | | @Override |
| | | public String getInstancePath() |
| | | { |
| | | return instance != null ? instance.getAbsolutePath() : super.getInstancePath(); |
| | | } |
| | | |
| | | @Override |
| | | protected TempLogFile openTempLogFile() |
| | | { |
| | | return super.openTempLogFile(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * A formatter that marks the arm each line goes out on. |
| | | * <p> |
| | | * The plain text formatter returns warnings and progress messages unchanged - only the |
| | | * wizard's HTML formatter tells them apart - so without a mark a report which says "the log |
| | | * is gone" as an ordinary progress line reads exactly like one which warns about it. |
| | | */ |
| | | private static final class MarkedArmsFormatter extends PlainTextProgressMessageFormatter |
| | | { |
| | | @Override |
| | | public LocalizableMessage getFormattedWarning(final LocalizableMessage text, final boolean applyMargin) |
| | | { |
| | | return LocalizableMessage.raw(WARNING + super.getFormattedWarning(text, applyMargin)); |
| | | } |
| | | |
| | | @Override |
| | | public LocalizableMessage getFormattedProgress(final LocalizableMessage text) |
| | | { |
| | | return LocalizableMessage.raw(PROGRESS + super.getFormattedProgress(text)); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * The contents of this file are subject to the terms of the Common Development and |
| | | * Distribution License (the License). You may not use this file except in compliance with the |
| | | * License. |
| | | * |
| | | * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the |
| | | * specific language governing permission and limitations under the License. |
| | | * |
| | | * When distributing Covered Software, include this CDDL Header Notice in each file and include |
| | | * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.tools; |
| | | |
| | | import static org.testng.Assert.assertEquals; |
| | | import static org.testng.Assert.assertTrue; |
| | | |
| | | import java.io.File; |
| | | import java.lang.reflect.Field; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | import java.util.function.Supplier; |
| | | |
| | | import org.opends.quicksetup.Constants; |
| | | import org.opends.quicksetup.Installation; |
| | | import org.opends.quicksetup.TempLogFile; |
| | | import org.opends.server.DirectoryServerTestCase; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.testng.annotations.Test; |
| | | |
| | | /** |
| | | * Tests when the CLI setup asks for its log file. |
| | | * <p> |
| | | * Nothing removes that file unless the install succeeds, so the roads which install nothing - |
| | | * a usage request, a usage error, a server which is configured already, a refused licence, a |
| | | * cancel at the prompt - must not ask for one: asking creates it, and the instance |
| | | * {@code logs/} directory with it (issue #1030). |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | @Test(groups = { "precommit", "tools" }, sequential = true) |
| | | public class InstallDSTest extends DirectoryServerTestCase |
| | | { |
| | | /** {@code InstallReturnCode.SUCCESSFUL_NOP}: the usage was displayed and nothing was done. */ |
| | | private static final int SUCCESSFUL_NOP = 0; |
| | | /** {@code InstallReturnCode.ERROR_USER_DATA}: the arguments could not be parsed. */ |
| | | private static final int ERROR_USER_DATA = 2; |
| | | /** {@code InstallReturnCode.ERROR_SERVER_ALREADY_INSTALLED}: the server is configured already. */ |
| | | private static final int ERROR_SERVER_ALREADY_INSTALLED = 3; |
| | | |
| | | /** {@code setup --help} displays the usage and returns before anything can fail. */ |
| | | @Test |
| | | public void testAUsageRequestAsksForNoLog() throws Exception |
| | | { |
| | | assertAsksForNoLog(SUCCESSFUL_NOP, "--help"); |
| | | } |
| | | |
| | | /** An argument the parser does not know is reported, and no install is attempted. */ |
| | | @Test |
| | | public void testAUsageErrorAsksForNoLog() throws Exception |
| | | { |
| | | assertAsksForNoLog(ERROR_USER_DATA, "--no-such-option"); |
| | | } |
| | | |
| | | /** |
| | | * A server which is configured already is reported as such, and no install is attempted. |
| | | * <p> |
| | | * This is the first road past the argument parser - the licence and the prompts come after |
| | | * it - so it is the one which tells a log asked for as soon as the arguments are known from |
| | | * one asked for where the install begins. |
| | | */ |
| | | @Test |
| | | public void testAServerWhichIsConfiguredAlreadyAsksForNoLog() throws Exception |
| | | { |
| | | // The installation the setup looks at: one whose configuration directory holds something. |
| | | // Neither the class path of a test run nor the server the tests run against will do - the |
| | | // former is no installation, and the latter is not to be taken as one - so the setup is |
| | | // pointed at a directory of its own. A locks directory keeps it from reading as running. |
| | | final File instance = TestCaseUtils.createTemporaryDirectory("installDSTest"); |
| | | assertTrue(new File(instance, "locks").mkdirs()); |
| | | assertTrue(new File(instance, "config").mkdirs()); |
| | | assertTrue(new File(instance, "config/config.ldif").createNewFile()); |
| | | |
| | | // Installation.getLocal() keeps what it resolved first in a static field. |
| | | final Field local = Installation.class.getDeclaredField("local"); |
| | | local.setAccessible(true); |
| | | final Object previous = local.get(null); |
| | | local.set(null, new Installation(instance, instance)); |
| | | try |
| | | { |
| | | assertAsksForNoLog(ERROR_SERVER_ALREADY_INSTALLED); |
| | | } |
| | | finally |
| | | { |
| | | local.set(null, previous); |
| | | TestCaseUtils.deleteDirectory(instance); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Runs the CLI setup with a supplier which counts the asks and hands out nothing, so that a |
| | | * road which asks for the log is red here rather than a file left behind on a real run. |
| | | */ |
| | | private static void assertAsksForNoLog(final int expectedReturnCode, final String... args) |
| | | { |
| | | final AtomicInteger asked = new AtomicInteger(); |
| | | final Supplier<TempLogFile> countingSupplier = () -> { |
| | | asked.incrementAndGet(); |
| | | return null; |
| | | }; |
| | | // mainCLI() sets this property for the run; put back what the rest of the JVM had. |
| | | final String cliProperty = System.getProperty(Constants.CLI_JAVA_PROPERTY); |
| | | try |
| | | { |
| | | assertEquals(InstallDS.mainCLI(args, null, null, countingSupplier), expectedReturnCode); |
| | | assertEquals(asked.get(), 0, "a run which installs nothing must not create a log"); |
| | | } |
| | | finally |
| | | { |
| | | if (cliProperty != null) |
| | | { |
| | | System.setProperty(Constants.CLI_JAVA_PROPERTY, cliProperty); |
| | | } |
| | | else |
| | | { |
| | | System.clearProperty(Constants.CLI_JAVA_PROPERTY); |
| | | } |
| | | } |
| | | } |
| | | } |