From 94823dbc53a839ef282609746efaa51938af9afd Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Wed, 08 Jul 2026 16:04:58 +0000
Subject: [PATCH] Add CI install-test for the Windows MSI + document MSI install/upgrade/uninstall (#664)
---
opendj-server-legacy/resource/bin/start-ds.bat | 10 +-
opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc | 44 +++++++++++
opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc | 29 +++++++
opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs | 2
opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc | 47 +++++++++++
.github/workflows/build.yml | 52 +++++++++++++
opendj-server-legacy/resource/bin/_script-util.bat | 19 ++--
opendj-server-legacy/resource/bin/setcp.bat | 11 +-
8 files changed, 195 insertions(+), 19 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ec8d21f..8d77743 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -562,3 +562,55 @@
*.jmeter.out
if-no-files-found: warn
retention-days: 90
+
+ test-msi:
+ needs: build-maven
+ runs-on: 'windows-latest'
+ steps:
+ - name: Download artifacts
+ uses: actions/download-artifact@v8
+ with:
+ name: windows-latest-11
+ - name: Set up Java
+ uses: actions/setup-java@v5
+ with:
+ java-version: '25'
+ distribution: 'zulu'
+ - name: Install MSI (silent)
+ shell: pwsh
+ run: |
+ $msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName
+ if (-not $msi) { throw "MSI not found in the windows-latest-11 artifact" }
+ Write-Host "MSI: $msi"
+ # No OPENDJ property: use the default install directory (a path with spaces),
+ # which the server scripts must handle.
+ $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart /l*v install.log"
+ if ($p.ExitCode -ne 0) { Get-Content install.log -Tail 80; throw "msiexec /i failed: $($p.ExitCode)" }
+ $root = @("C:\Program Files (x86)\OpenDJ","C:\Program Files\OpenDJ") | Where-Object { Test-Path "$_\setup.bat" } | Select-Object -First 1
+ if (-not $root) { Get-Content install.log -Tail 80; throw "OpenDJ install root with setup.bat not found" }
+ Write-Host "Installed to $root"
+ "OPENDJ_ROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Append
+ - name: Setup and start/stop the Windows service
+ shell: pwsh
+ run: |
+ $root = $env:OPENDJ_ROOT
+ $env:OPENDJ_JAVA_ARGS = "-server -Xmx512m"
+ & "$root\setup.bat" -h localhost -p 1389 --ldapsPort 1636 --adminConnectorPort 4444 --enableStartTLS --generateSelfSignedCertificate --rootUserDN "cn=Directory Manager" --rootUserPassword password --baseDN dc=example,dc=com --addBaseEntry --cli --acceptLicense --no-prompt --doNotStart
+ if ($LASTEXITCODE -ne 0) { throw "setup.bat failed: $LASTEXITCODE" }
+ & "$root\bat\windows-service.bat" --enableService
+ if ($LASTEXITCODE -ne 0) { throw "windows-service --enableService failed: $LASTEXITCODE" }
+ net start "OpenDJ Server"
+ if ($LASTEXITCODE -ne 0) { throw "net start failed: $LASTEXITCODE" }
+ for ($i=0; $i -lt 12; $i++) { try { $c = New-Object System.Net.Sockets.TcpClient('localhost', 1636); $c.Close(); break } catch { Start-Sleep -Seconds 5 } }
+ & "$root\bat\ldapsearch.bat" --hostname localhost --port 1636 --bindDN "cn=Directory Manager" --bindPassword password --useSsl --trustAll --baseDN "dc=example,dc=com" --searchScope base "(objectClass=*)" 1.1
+ if ($LASTEXITCODE -ne 0) { throw "ldapsearch failed: $LASTEXITCODE" }
+ net stop "OpenDJ Server"
+ if ($LASTEXITCODE -ne 0) { throw "net stop failed: $LASTEXITCODE" }
+ & "$root\bat\windows-service.bat" --disableService
+ - name: Uninstall MSI
+ shell: pwsh
+ run: |
+ $msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName
+ $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/x `"$msi`" /quiet /qn /norestart /l*v uninstall.log"
+ if ($p.ExitCode -ne 0) { Get-Content uninstall.log -Tail 80; throw "msiexec /x failed: $($p.ExitCode)" }
+ Write-Host "Uninstalled OK"
diff --git a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc
index 92be08f..f976103 100644
--- a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc
+++ b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-install.adoc
@@ -40,6 +40,8 @@
* xref:#install-rpm["To Install From the RPM Package"]
+* xref:#install-msi["To Install With the Windows Installer (MSI)"]
+
* xref:#install-properties-file["To Install OpenDJ Directory Server With a Properties File"]
* xref:#pdb-to-je["To Move Data from a PDB Backend to a JE Backend"]
@@ -635,6 +637,48 @@
====
+[#install-msi]
+.To Install With the Windows Installer (MSI)
+====
+On Windows you can install OpenDJ directory server from the `.msi` package. The installer only copies the server files to disk: it does not configure or start a server, it does not register a Windows service, and it does not install a Java runtime.
+
+. Make sure a supported Java runtime is available, as described in xref:#before-you-install["To Prepare For Installation"].
++
+The installer does not check for Java. If your default Java environment is not appropriate, set `OPENDJ_JAVA_HOME` to the correct Java installation (or `OPENDJ_JAVA_BIN` to the absolute path of the `java` command), or make sure `java` is on the `PATH`, before you run `setup` or start the server.
+
+. Install the package, either with the GUI or silently:
++
+* GUI: double-click `opendj-{opendj-version}.msi` and follow the wizard.
++
+* Silent: run the following command (optionally set the installation directory with the `OPENDJ` property):
++
+
+[source, console, subs="attributes"]
+----
+C:\> msiexec /i opendj-{opendj-version}.msi /quiet OPENDJ="C:\opendj"
+----
++
+By default the package installs under `C:\Program Files\OpenDJ` (the 32-bit installer uses `C:\Program Files (x86)\OpenDJ` on 64-bit Windows).
+
+. Configure OpenDJ directory server by running the `setup` command, described in xref:../reference/admin-tools-ref.adoc#setup-1[setup(1)] in the __Reference__, from the installation directory. Use `setup.bat` for the GUI wizard or `setup.bat --cli` for the command-line:
++
+
+[source, console]
+----
+C:\path\to\opendj> setup.bat --cli
+----
+
+. (Optional) Register OpenDJ as a Windows service and start it. The MSI does not register the service; use the `windows-service` command:
++
+
+[source, console]
+----
+C:\path\to\opendj\bat> windows-service.bat --enableService
+C:\> net start "OpenDJ Server"
+----
+
+====
+
[#install-properties-file]
.To Install OpenDJ Directory Server With a Properties File
====
diff --git a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc
index 19cc3a3..17e0413 100644
--- a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc
+++ b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-uninstall.adoc
@@ -12,7 +12,7 @@
information: "Portions copyright [year] [name of copyright owner]".
Copyright 2017 ForgeRock AS.
- Portions Copyright 2024 3A Systems LLC.
+ Portions Copyright 2024-2026 3A Systems LLC.
////
:figure-caption!:
@@ -33,6 +33,8 @@
* xref:#uninstall-rpm["To Uninstall the RPM Package"]
+* xref:#uninstall-msi["To Uninstall the Windows MSI Package"]
+
[#uninstall-gui]
.To Remove OpenDJ With the GUI Uninstaller
@@ -157,3 +159,28 @@
====
+[#uninstall-msi]
+.To Uninstall the Windows MSI Package
+====
+Remove OpenDJ directory server installed from the `.msi` package like any other Windows program.
+
+. If OpenDJ is registered as a Windows service, remove the service first:
++
+
+[source, console]
+----
+C:\path\to\opendj\bat> windows-service.bat --disableService
+----
+
+. Uninstall the package, either through __Settings > Apps__ (or __Control Panel > Programs and Features__) by selecting OpenDJ and choosing Uninstall, or from the command-line:
++
+
+[source, console, subs="attributes"]
+----
+C:\> msiexec /x opendj-{opendj-version}.msi /quiet
+----
++
+Uninstalling removes the files installed by the package. Your configured instance data under the installation directory (for example `config`, `db`, and `logs`) is not removed; delete the installation directory manually to remove all files.
+
+====
+
diff --git a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc
index cf0268d..c514e71 100644
--- a/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc
+++ b/opendj-doc-generated-ref/src/main/asciidoc/install-guide/chap-upgrade.adoc
@@ -45,6 +45,8 @@
* xref:#upgrade-zip-example["Upgrading to OpenDJ {opendj-version-short}"]
+* xref:#upgrade-msi["To Upgrade the Windows MSI Installation"]
+
* xref:#upgrade-repl["To Upgrade Replicated Servers"]
* xref:#new-repl-mixed-topology["To Add a New Replica to an Existing Topology"]
@@ -243,6 +245,51 @@
----
====
+[#upgrade-msi]
+.To Upgrade the Windows MSI Installation
+====
+Before starting this procedure, follow the steps in xref:#before-you-upgrade["Before You Upgrade"]. Installing the newer `.msi` performs a major upgrade that replaces the installed program files, so make a full file-system backup of the current installation first.
+
+. Stop the current OpenDJ server.
+
+. If OpenDJ is registered as a Windows service, disable the service:
++
+
+[source, console]
+----
+C:\path\to\opendj\bat> windows-service.bat --disableService
+----
+
+. Back up the file-system directory where OpenDJ is installed.
+
+. Install the newer package (GUI or silent), using the same installation directory as the current server. Your configured instance data (`config`, `db`, `logs`) is kept; only the program files are replaced:
++
+
+[source, console, subs="attributes"]
+----
+C:\> msiexec /i opendj-{opendj-version}.msi /quiet OPENDJ="C:\path\to\opendj"
+----
+
+. Run the `upgrade` command, described in xref:../reference/admin-tools-ref.adoc#upgrade-1[upgrade(1)] in the __Reference__, to bring the configuration and application data up to date with the new binary and script files:
++
+
+[source, console]
+----
+C:\path\to\opendj> upgrade.bat --no-prompt --acceptLicense
+----
+
+. Start the upgraded OpenDJ server.
+
+. If you disabled the Windows service, enable it again:
++
+
+[source, console]
+----
+C:\path\to\opendj\bat> windows-service.bat --enableService
+----
+
+====
+
[#upgrade-repl]
.To Upgrade Replicated Servers
====
diff --git a/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs b/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs
index 23f4dd4..f77dda7 100644
--- a/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs
+++ b/opendj-packages/opendj-msi/opendj-msi-standard/resources/msi/package.wxs
@@ -13,7 +13,7 @@
! information: "Portions Copyright [year] [name of copyright owner]".
!
! Copyright 2013-2016 ForgeRock AS.
- ! Portion Copyright 2018 Open Identity Platform Community
+ ! Portions Copyright 2018-2026 3A Systems, LLC
! -->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Codepage="1252" Language="1033" Manufacturer="Open Identity Platform Community"
diff --git a/opendj-server-legacy/resource/bin/_script-util.bat b/opendj-server-legacy/resource/bin/_script-util.bat
index f6bacbb..bea162b7 100644
--- a/opendj-server-legacy/resource/bin/_script-util.bat
+++ b/opendj-server-legacy/resource/bin/_script-util.bat
@@ -13,7 +13,7 @@
rem
rem Copyright 2008-2010 Sun Microsystems, Inc.
rem Portions Copyright 2011-2016 ForgeRock AS.
-rem Portions Copyright 2020-2025 3A Systems, LLC.
+rem Portions Copyright 2020-2026 3A Systems, LLC.
set SET_JAVA_HOME_AND_ARGS_DONE=false
set SET_ENVIRONMENT_VARS_DONE=false
@@ -56,10 +56,10 @@
rem it also helps comparing the two paths
FOR /F "delims=" %%i IN ("%INSTALL_ROOT%") DO set INSTALL_ROOT=%%~dpnxi
FOR /F "delims=" %%i IN ("%INSTANCE_ROOT%") DO set INSTANCE_ROOT=%%~dpnxi
-call "%INSTALL_ROOT%\lib\setcp.bat" %INSTALL_ROOT%\lib\bootstrap-client.jar
+call "%INSTALL_ROOT%\lib\setcp.bat" "%INSTALL_ROOT%\lib\bootstrap-client.jar"
set CLASSPATH=%INSTANCE_ROOT%\classes;%CLASSPATH%
if "%INSTALL_ROOT%" == "%INSTANCE_ROOT%" goto setClassPathDone
-FOR %%x in ("%INSTANCE_ROOT%\lib\*.jar") DO call "%INSTANCE_ROOT%\lib\setcp.bat" %%x
+FOR %%x in ("%INSTANCE_ROOT%\lib\*.jar") DO call "%INSTANCE_ROOT%\lib\setcp.bat" "%%x"
:setClassPathDone
set SET_CLASSPATH_DONE=true
goto scriptBegin
@@ -71,7 +71,7 @@
rem it also helps comparing the two paths
FOR /F "delims=" %%i IN ("%INSTALL_ROOT%") DO set INSTALL_ROOT=%%~dpnxi
FOR /F "delims=" %%i IN ("%INSTANCE_ROOT%") DO set INSTANCE_ROOT=%%~dpnxi
-call "%INSTALL_ROOT%\lib\setcp.bat" %INSTALL_ROOT%\lib\*
+call "%INSTALL_ROOT%\lib\setcp.bat" "%INSTALL_ROOT%\lib\*"
set CLASSPATH=%INSTANCE_ROOT%\classes;%CLASSPATH%
set SET_CLASSPATH_DONE=true
goto scriptBegin
@@ -100,7 +100,8 @@
set SCRIPT_JAVA_ARGS_PROPERTY=%SCRIPT_NAME%.java-args
call:readProperty %SCRIPT_JAVA_ARGS_PROPERTY%
set OPENDJ_JAVA_ARGS=%OPENDJ_JAVA_ARGS% %PROPERTY_VALUE%
-if not "%OPENDJ_JAVA_ARGS%" == "" goto checkEnvJavaHome
+rem "if defined" comparisons: the value may contain quotes (java.io.tmpdir), which break "%VAR%" == "" checks.
+if defined OPENDJ_JAVA_ARGS goto checkEnvJavaHome
call:readProperty default.java-args
set OPENDJ_JAVA_ARGS=%OPENDJ_JAVA_ARGS% %PROPERTY_VALUE
if "%OPENDJ_JAVA_BIN%" == "" goto checkEnvJavaHome
@@ -186,12 +187,12 @@
if %SET_TEMP_DIR_DONE% == "true" goto end
set OPENDJ_TMP_DIR=%INSTANCE_ROOT%\tmp
if not exist "%OPENDJ_TMP_DIR%" mkdir "%OPENDJ_TMP_DIR%"
-set OPENDJ_JAVA_ARGS=%OPENDJ_JAVA_ARGS% -Djava.io.tmpdir=%OPENDJ_TMP_DIR%
+set OPENDJ_JAVA_ARGS=%OPENDJ_JAVA_ARGS% -Djava.io.tmpdir="%OPENDJ_TMP_DIR%"
set SET_TEMP_DIR_DONE=true
goto scriptBegin
:testJava
-if "%OPENDJ_JAVA_ARGS%" == "" goto checkLegacyArgs
+if not defined OPENDJ_JAVA_ARGS goto checkLegacyArgs
:continueTestJava
"%OPENDJ_JAVA_BIN%" %OPENDJ_JAVA_ARGS% org.opends.server.tools.CheckJVMVersion > NUL 2>&1
set RESULT_CODE=%errorlevel%
@@ -200,12 +201,12 @@
goto end
:checkLegacyArgs
-if "%OPENDS_JAVA_ARGS%" == "" goto continueTestJava
+if not defined OPENDS_JAVA_ARGS goto continueTestJava
set OPENDJ_JAVA_ARGS=%OPENDS_JAVA_ARGS%
goto continueTestJava
:noValidJavaHome
-if NOT "%OPENDJ_JAVA_ARGS%" == "" goto noValidHomeWithArgs
+if defined OPENDJ_JAVA_ARGS goto noValidHomeWithArgs
echo ERROR: The detected Java version could not be used. The detected
echo Java binary is:
echo %OPENDJ_JAVA_BIN%
diff --git a/opendj-server-legacy/resource/bin/setcp.bat b/opendj-server-legacy/resource/bin/setcp.bat
index a47d4e6..eca179c 100644
--- a/opendj-server-legacy/resource/bin/setcp.bat
+++ b/opendj-server-legacy/resource/bin/setcp.bat
@@ -12,14 +12,17 @@
rem information: "Portions Copyright [year] [name of copyright owner]".
rem
rem Copyright 2006-2008 Sun Microsystems, Inc.
+rem Portions Copyright 2026 3A Systems, LLC
-set CLASSPATHCOMPONENT=%1
-if ""%1""=="""" goto gotAllArgs
+rem Use %~1 and quoted comparisons so paths containing spaces and parentheses
+rem (for example C:\Program Files (x86)\OpenDJ) do not break the parser.
+set CLASSPATHCOMPONENT=%~1
+if "%~1"=="" goto gotAllArgs
shift
:argCheck
-if ""%1""=="""" goto gotAllArgs
-set CLASSPATHCOMPONENT=%CLASSPATHCOMPONENT% %1
+if "%~1"=="" goto gotAllArgs
+set CLASSPATHCOMPONENT=%CLASSPATHCOMPONENT% %~1
shift
goto argCheck
diff --git a/opendj-server-legacy/resource/bin/start-ds.bat b/opendj-server-legacy/resource/bin/start-ds.bat
index 7f7be60..f591502 100644
--- a/opendj-server-legacy/resource/bin/start-ds.bat
+++ b/opendj-server-legacy/resource/bin/start-ds.bat
@@ -14,7 +14,7 @@
rem
rem Copyright 2006-2010 Sun Microsystems, Inc.
rem Portions Copyright 2011-2014 ForgeRock AS.
-rem Portions Copyright 2025 3A Systems LLC.
+rem Portions Copyright 2025-2026 3A Systems LLC.
setlocal
set DIR_HOME=%~dp0..
@@ -61,10 +61,12 @@
rem cleanup the tmp directory
set CUR_DIR=%CD%
set OPENDJ_TMP_DIR=%INSTANCE_ROOT%\tmp
-dir /b /s /a %OPENDJ_TMP_DIR% | findstr .>nul && (
- cd /d %OPENDJ_TMP_DIR%
+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%
+ cd /d "%CUR_DIR%"
)
"%OPENDJ_JAVA_BIN%" -client %SCRIPT_NAME_ARG% org.opends.server.core.DirectoryServer --configFile "%INSTANCE_ROOT%\config\config.ldif" --checkStartability %*
--
Gitblit v1.10.0