diff --git a/.gitattributes b/.gitattributes
index e195b512dd..464d174671 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,6 @@
+# WiX sources must stay LF so the WiX toolset parses them consistently across runners.
+*.wxs text eol=lf
+*.wxi text eol=lf
# Keep HTML checked out with LF on all platforms so javadoc doclint
# (JDK 25/26) does not treat CR (from CRLF) as part of a multi-line tag name.
*.html text eol=lf
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8691a0e2c6..ae780952da 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -23,6 +23,11 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
+# The build only checks out sources and publishes artifacts through the actions
+# API; no job needs to write back to the repository.
+permissions:
+ contents: read
+
jobs:
build-maven:
runs-on: ${{ matrix.os }}
@@ -39,19 +44,12 @@ jobs:
- { os: 'windows-latest', java: '26' }
fail-fast: false
steps:
- - name: Install wine+rpm for distribution
+ - name: Install rpm for distribution
if: runner.os == 'Linux'
shell: bash
run: |
- sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
- sudo dpkg --add-architecture i386
- sudo mkdir -pm755 /etc/apt/keyrings && sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
- sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -c -s)/winehq-$(lsb_release -c -s).sources
sudo apt-get update
- sudo apt install --install-recommends winehq-stable || sudo apt install --install-recommends winehq-staging
- wine --version
- version="9.4.0"; sudo wget "https://dl.winehq.org/wine/wine-mono/$version/wine-mono-$version-x86.msi" -O /tmp/wine-mono.msi
- wine msiexec /i /tmp/wine-mono.msi
+ sudo apt-get install -y rpm
- uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -90,6 +88,19 @@ jobs:
run: |
echo "MAVEN_PROFILE_FLAG=-P precommit" >> $GITHUB_OUTPUT
+ - name: Setup WiX (.NET tool) for MSI
+ if: runner.os == 'Windows'
+ shell: bash
+ run: |
+ # The MSI builds on Windows only (WiX cannot author MSIs on Linux/macOS). WiX 5 ships as a
+ # net6.0 tool; allow it to run on the newer .NET runtime present on the runner.
+ echo "DOTNET_ROLL_FORWARD=Major" >> "$GITHUB_ENV"
+ export DOTNET_ROLL_FORWARD=Major
+ dotnet tool install --global wix --version 5.0.2 || dotnet tool update --global wix --version 5.0.2
+ echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH"
+ export PATH="$HOME/.dotnet/tools:$PATH"
+ wix --version
+ wix extension add -g WixToolset.UI.wixext/5.0.2 || true
- name: Build with Maven
timeout-minutes: 180
env:
@@ -594,6 +605,34 @@ jobs:
uses: actions/download-artifact@v8
with:
name: windows-latest-11
+ - name: Install must fail without a JRE
+ shell: pwsh
+ run: |
+ # Simulate a machine with no Java. The launch condition is evaluated by the Windows
+ # Installer service, which sees the MACHINE environment - so hide Java there too and
+ # restart msiserver to make it pick the change up; restore everything afterwards.
+ Stop-Service msiserver -Force -ErrorAction SilentlyContinue
+ $oldJH = [Environment]::GetEnvironmentVariable('JAVA_HOME','Machine')
+ $oldPM = [Environment]::GetEnvironmentVariable('Path','Machine')
+ [Environment]::SetEnvironmentVariable('JAVA_HOME',$null,'Machine')
+ [Environment]::SetEnvironmentVariable('Path',((($oldPM -split ';') | Where-Object { $_ -notmatch 'java|jdk|jre|zulu|javapath' }) -join ';'),'Machine')
+ $env:JAVA_HOME = ""
+ $env:PATH = (($env:PATH -split ';') | Where-Object { $_ -notmatch 'java|jdk|jre|zulu|javapath' }) -join ';'
+ try {
+ $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" }
+ $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart /l*v install-nojre.log"
+ if ($p.ExitCode -eq 0) {
+ Get-Content install-nojre.log -Tail 60
+ throw "installer succeeded without a JRE, but the Java launch condition must fail it"
+ }
+ Write-Host "Installer refused to install without a JRE as expected (exit $($p.ExitCode))"
+ Select-String -Path install-nojre.log -Pattern "requires Java" | Select-Object -First 1
+ } finally {
+ [Environment]::SetEnvironmentVariable('JAVA_HOME',$oldJH,'Machine')
+ [Environment]::SetEnvironmentVariable('Path',$oldPM,'Machine')
+ Stop-Service msiserver -Force -ErrorAction SilentlyContinue
+ }
- name: Set up Java
uses: actions/setup-java@v5
with:
@@ -605,12 +644,12 @@ jobs:
$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.
+ # No OPENDJ property: exercise the x64 default C:\Program Files\OpenDJ (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" }
+ $root = "C:\Program Files\OpenDJ"
+ if (-not (Test-Path "$root\setup.bat")) { Get-Content install.log -Tail 80; throw "OpenDJ not installed into the x64 default $root" }
Write-Host "Installed to $root"
"OPENDJ_ROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Setup and start/stop the Windows service
@@ -620,8 +659,7 @@ jobs:
$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" }
+ # The service is already registered by the MSI (WiX ServiceInstall); just start it.
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 } }
@@ -629,7 +667,6 @@ jobs:
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: |
@@ -637,3 +674,91 @@ jobs:
$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"
+
+ # Upgrade path: released 5.1.1 x86 MSI (wine-built, WiX3) -> this build's x64 MSI.
+ # Verifies the new installer detects the legacy Program Files (x86) install, keeps the
+ # instance data in place, replaces the windows-service.bat service with the MSI-managed
+ # one, and the upgraded server starts with the old data.
+ test-msi-upgrade:
+ 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 released 5.1.1 MSI and configure an instance
+ shell: pwsh
+ run: |
+ Invoke-WebRequest -Uri "https://github.com/OpenIdentityPlatform/OpenDJ/releases/download/5.1.1/opendj-5.1.1.msi" -OutFile opendj-5.1.1.msi
+ # The released 5.1.1 scripts cannot run from a directory with spaces (unquoted
+ # java.io.tmpdir), so put the old install into C:\opendj.
+ $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i opendj-5.1.1.msi /quiet /qn /norestart OPENDJ=C:\opendj /l*v install-old.log"
+ if ($p.ExitCode -ne 0) { Get-Content install-old.log -Tail 80; throw "msiexec /i (5.1.1) failed: $($p.ExitCode)" }
+ $root = "C:\opendj"
+ if (-not (Test-Path "$root\setup.bat")) { Get-Content install-old.log -Tail 80; throw "5.1.1 install root not found at $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 (5.1.1) failed: $LASTEXITCODE" }
+ # Register the LEGACY service the pre-MSI way and prove it works, then stop it.
+ & "$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 (5.1.1) failed: $LASTEXITCODE" }
+ net stop "OpenDJ Server"
+ if ($LASTEXITCODE -ne 0) { throw "net stop (5.1.1) failed: $LASTEXITCODE" }
+ - name: Upgrade with the newly built MSI (same directory passed explicitly)
+ 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" }
+ $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart OPENDJ=C:\opendj /l*v upgrade.log"
+ if ($p.ExitCode -ne 0) { Get-Content upgrade.log -Tail 120; throw "msiexec /i (upgrade) failed: $($p.ExitCode)" }
+ $root = "C:\opendj"
+ # New package files landed in the old directory, not the x64 default
+ if (-not (Test-Path "$root\setup.bat")) { throw "upgrade did not keep the old install dir" }
+ if (Test-Path "C:\Program Files\OpenDJ") { throw "upgrade unexpectedly installed into the x64 default dir" }
+ # Instance data survived
+ if (-not (Test-Path "$root\config\config.ldif")) { throw "instance data (config\config.ldif) lost by the upgrade" }
+ # Legacy service replaced by the MSI-managed one: display name maps to key 'OpenDJ'
+ $key = (sc.exe getkeyname "OpenDJ Server" | Select-String -Pattern "Name = (.+)").Matches[0].Groups[1].Value.Trim()
+ if ($key -ne "OpenDJ") { sc.exe query; throw "expected MSI-managed service key 'OpenDJ', got '$key'" }
+ sc.exe qc OpenDJ
+ "OPENDJ_ROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Append
+ - name: Run upgrade.bat and start the upgraded server
+ shell: pwsh
+ run: |
+ $root = $env:OPENDJ_ROOT
+ $env:OPENDJ_JAVA_ARGS = "-server -Xmx512m"
+ & "$root\upgrade.bat" --no-prompt --acceptLicense --force
+ if ($LASTEXITCODE -ne 0) { throw "upgrade.bat failed: $LASTEXITCODE" }
+ net start "OpenDJ Server"
+ if ($LASTEXITCODE -ne 0) { throw "net start (upgraded) 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 } }
+ # The pre-upgrade data must still be served
+ & "$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 after upgrade failed: $LASTEXITCODE" }
+ net stop "OpenDJ Server"
+ if ($LASTEXITCODE -ne 0) { throw "net stop (upgraded) failed: $LASTEXITCODE" }
+ - name: Auto-detect the legacy default directory on a fresh install
+ shell: pwsh
+ run: |
+ $msi = (Get-ChildItem -Recurse -Filter *.msi -Path opendj-packages/opendj-msi | Select-Object -First 1).FullName
+ # Clean up the previous scenario first.
+ $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/x `"$msi`" /quiet /qn /norestart /l*v uninstall1.log"
+ if ($p.ExitCode -ne 0) { Get-Content uninstall1.log -Tail 80; throw "msiexec /x failed: $($p.ExitCode)" }
+ if (Get-Service OpenDJ -ErrorAction SilentlyContinue) { throw "service not removed by uninstall" }
+ # An existing legacy default directory must be picked up when OPENDJ is not given.
+ New-Item -ItemType Directory -Force "C:\Program Files (x86)\OpenDJ" | Out-Null
+ $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/i `"$msi`" /quiet /qn /norestart /l*v install-autodetect.log"
+ if ($p.ExitCode -ne 0) { Get-Content install-autodetect.log -Tail 80; throw "msiexec /i (autodetect) failed: $($p.ExitCode)" }
+ if (-not (Test-Path "C:\Program Files (x86)\OpenDJ\setup.bat")) { Get-Content install-autodetect.log -Tail 80; throw "installer did not auto-detect the legacy default dir" }
+ if (Test-Path "C:\Program Files\OpenDJ") { throw "installer used the x64 default dir despite an existing legacy dir" }
+ Write-Host "Legacy default directory auto-detected OK"
+ $p = Start-Process msiexec -Wait -PassThru -ArgumentList "/x `"$msi`" /quiet /qn /norestart /l*v uninstall2.log"
+ if ($p.ExitCode -ne 0) { Get-Content uninstall2.log -Tail 80; throw "msiexec /x (cleanup) failed: $($p.ExitCode)" }
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index c06529245b..5b6e055401 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -33,19 +33,12 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
run: echo "$GITHUB_CONTEXT"
- - name: Install wine+rpm for distribution
+ - name: Install rpm for distribution
if: runner.os == 'Linux'
shell: bash
run: |
- sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
- sudo dpkg --add-architecture i386
- sudo mkdir -pm755 /etc/apt/keyrings && sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
- sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -c -s)/winehq-$(lsb_release -c -s).sources
sudo apt-get update
- sudo apt install --install-recommends winehq-stable || sudo apt install --install-recommends winehq-staging
- wine --version
- version="9.4.0"; sudo wget "https://dl.winehq.org/wine/wine-mono/$version/wine-mono-$version-x86.msi" -O /tmp/wine-mono.msi
- wine msiexec /i /tmp/wine-mono.msi
+ sudo apt-get install -y rpm
- uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -109,11 +102,22 @@ jobs:
with:
name: OpenDJ RPM Package
path: opendj-packages/opendj-rpm/opendj-rpm-standard/target/rpm/opendj/RPMS/noarch/*.rpm
+ # The MSI can only be built on Windows; reuse the one already built by the triggering
+ # Build run (windows-latest-11 artifact) instead of rebuilding it here.
+ - name: Download Windows build artifact (contains the MSI)
+ continue-on-error: true
+ uses: actions/download-artifact@v8
+ with:
+ name: windows-latest-11
+ run-id: ${{ github.event.workflow_run.id }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ path: windows-build
- name: Upload artifacts OpenDJ MSI Package
+ continue-on-error: true
uses: actions/upload-artifact@v7
with:
name: OpenDJ MSI Package
- path: opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi
+ path: windows-build/opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi
- name: Upload artifacts OpenDJ Docker Packages
uses: actions/upload-artifact@v7
with:
@@ -188,3 +192,4 @@ jobs:
git commit -a -m "upload ${{github.event.repository.name}} docs after deploy ${{ github.sha }}"
git push --force https://github.com/OpenIdentityPlatform/doc.openidentityplatform.org.git
fi
+
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 48d3cfb5b3..62750246f6 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -38,18 +38,11 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
run: echo "$GITHUB_CONTEXT"
- - name: Install wine+rpm for distribution
+ - name: Install rpm for distribution
shell: bash
run: |
- sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
- sudo dpkg --add-architecture i386
- sudo mkdir -pm755 /etc/apt/keyrings && sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
- sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -c -s)/winehq-$(lsb_release -c -s).sources
sudo apt-get update
- sudo apt install --install-recommends winehq-stable || sudo apt install --install-recommends winehq-staging
- wine --version
- version="9.4.0"; sudo wget "https://dl.winehq.org/wine/wine-mono/$version/wine-mono-$version-x86.msi" -O /tmp/wine-mono.msi
- wine msiexec /i /tmp/wine-mono.msi
+ sudo apt-get install -y rpm
- uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -89,8 +82,17 @@ jobs:
MAVEN_OPTS: -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.requestSentEnabled=true -Dmaven.wagon.http.retryHandler.count=10
if: ${{ env.MAVEN_USERNAME!='' && env.MAVEN_PASSWORD!='' }}
run: mvn --batch-mode -Darguments="-Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }}" -DsignTag=true -DtagNameFormat="${{ github.event.inputs.releaseVersion }}" -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -DdevelopmentVersion=${{ github.event.inputs.developmentVersion }} release:prepare release:perform --file pom.xml
+ # Hand the just-released server zip to the release-msi job (the MSI can only be
+ # built on Windows), so it does not have to rebuild opendj-server-legacy.
+ - name: Upload the server zip for the MSI job
+ continue-on-error: true
+ uses: actions/upload-artifact@v7
+ with:
+ name: release-server-zip
+ retention-days: 1
+ path: target/checkout/opendj-server-legacy/target/package/*.zip
- name: Release on GitHub
- uses: softprops/action-gh-release@v2
+ uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
with:
name: ${{ github.event.inputs.releaseVersion }}
tag_name: ${{ github.event.inputs.releaseVersion }}
@@ -103,7 +105,6 @@ jobs:
target/checkout/opendj-ldap-toolkit/target/*.zip
target/checkout/opendj-packages/opendj-deb/opendj-deb-standard/target/*.deb
target/checkout/opendj-packages/opendj-rpm/opendj-rpm-standard/target/rpm/opendj/RPMS/noarch/*.rpm
- target/checkout/opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi
target/checkout/opendj-packages/opendj-docker/target/Dockerfile.zip
target/checkout/opendj-packages/opendj-openshift-template/*.yaml
target/checkout/opendj-doc-generated-ref/target/*.zip
@@ -149,6 +150,65 @@ jobs:
git tag -f ${TAG_NAME}
git push --quiet --force origin ${TAG_NAME}
+ # The MSI can only be built on Windows. Reuses the server zip built by release-maven
+ # (installed into the local repo), so only the opendj-msi-standard module is built here.
+ # continue-on-error: an MSI failure must not break the release.
+ release-msi:
+ name: Windows MSI release
+ runs-on: 'windows-latest'
+ continue-on-error: true
+ needs:
+ - release-maven
+ steps:
+ - uses: actions/checkout@v6
+ with:
+ ref: ${{ github.event.inputs.releaseVersion }}
+ submodules: recursive
+ - name: Set up Java
+ uses: actions/setup-java@v5
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ - name: Cache Maven packages
+ uses: actions/cache@v5
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-m2-repository-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2-repository
+ - name: Setup WiX (.NET tool)
+ shell: bash
+ run: |
+ echo "DOTNET_ROLL_FORWARD=Major" >> "$GITHUB_ENV"
+ export DOTNET_ROLL_FORWARD=Major
+ dotnet tool install --global wix --version 5.0.2 || dotnet tool update --global wix --version 5.0.2
+ echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH"
+ export PATH="$HOME/.dotnet/tools:$PATH"
+ wix --version
+ wix extension add -g WixToolset.UI.wixext/5.0.2 || true
+ - name: Download the server zip built by release-maven
+ uses: actions/download-artifact@v8
+ with:
+ name: release-server-zip
+ path: server-zip
+ - name: Install the server zip into the local Maven repository
+ shell: bash
+ run: |
+ ZIP=$(ls server-zip/*.zip | head -1)
+ echo "Installing $ZIP as opendj-server-legacy:${{ github.event.inputs.releaseVersion }}:zip"
+ mvn --batch-mode install:install-file -Dfile="$ZIP" \
+ -DgroupId=org.openidentityplatform.opendj -DartifactId=opendj-server-legacy \
+ -Dversion=${{ github.event.inputs.releaseVersion }} -Dpackaging=zip
+ - name: Build the MSI (packaging only, no rebuild)
+ env:
+ MAVEN_OPTS: -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.requestSentEnabled=true -Dmaven.wagon.http.retryHandler.count=10
+ run: mvn --batch-mode --errors -DskipTests package -pl :opendj-msi-standard --file pom.xml
+ - name: Attach the MSI to the GitHub release
+ uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
+ with:
+ tag_name: ${{ github.event.inputs.releaseVersion }}
+ fail_on_unmatched_files: true
+ files: opendj-packages/opendj-msi/opendj-msi-standard/target/*.msi
+
release-docker:
name: Docker release
runs-on: 'ubuntu-latest'
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 f976103528..5770bfc24d 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
@@ -640,14 +640,16 @@ opendj 0:off 1:off 2:on 3:on 4:on 5:on 6:off
[#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.
+On Windows you can install OpenDJ directory server from the `.msi` package. The installer copies the server files to disk and registers the `OpenDJ Server` Windows service, but it does not configure or start a server (run `setup` first) 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.
+The installer refuses to install when it cannot detect a Java installation: install a JRE (for example link:https://adoptium.net[Eclipse Temurin, window=\_blank]), set `JAVA_HOME` to your Java installation, or make sure the `java` executable is on the `PATH`, before running it. If your default Java environment is not the one OpenDJ should use, additionally set `OPENDJ_JAVA_HOME` to the correct Java installation (or `OPENDJ_JAVA_BIN` to the absolute path of the `java` command) before you run `setup` or start the server.
. Install the package, either with the GUI or silently:
+
+The package is not code-signed, so Windows SmartScreen or User Account Control may warn about an unrecognized publisher; choose to run the installer anyway.
++
* 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):
@@ -658,7 +660,7 @@ The installer does not check for Java. If your default Java environment is not a
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).
+By default the x64 package installs under `C:\Program Files\OpenDJ`.
. 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:
+
@@ -668,12 +670,11 @@ By default the package installs under `C:\Program Files\OpenDJ` (the 32-bit inst
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:
+. Start the OpenDJ Windows service. The installer already registered it as `OpenDJ Server`; after configuring with `setup`, start it (it is not started automatically during installation):
+
[source, console]
----
-C:\path\to\opendj\bat> windows-service.bat --enableService
C:\> net start "OpenDJ Server"
----
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 17e0413635..3adc45d381 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
@@ -162,17 +162,9 @@ Removing the package does not remove your data or configuration. You must remove
[#uninstall-msi]
.To Uninstall the Windows MSI Package
====
-Remove OpenDJ directory server installed from the `.msi` package like any other Windows program.
+Remove OpenDJ directory server installed from the `.msi` package like any other Windows program. The uninstaller stops and removes the `OpenDJ Server` Windows service that the package registered.
-. 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:
+* 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"]
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 c514e71c82..d91b763711 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
@@ -250,25 +250,19 @@ $
====
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
-----
+. Stop the current OpenDJ server (if it runs as a Windows service, `net stop "OpenDJ Server"`).
. 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:
+. Install the newer package (GUI or silent). The installer detects the existing installation — the location recorded in the registry by a previous x64 package, or the default directory of the older 32-bit package (`C:\Program Files (x86)\OpenDJ`) — and installs into the same directory, so your configured instance data (`config`, `db`, `logs`) is kept and only the program files are replaced. If the older server was installed in a custom directory the installer did not detect, select that directory in the GUI or pass it explicitly:
+
[source, console, subs="attributes"]
----
C:\> msiexec /i opendj-{opendj-version}.msi /quiet OPENDJ="C:\path\to\opendj"
----
++
+The installer replaces a service registered by the older `windows-service.bat` command with the MSI-managed `OpenDJ Server` service automatically; no manual `--disableService`/`--enableService` is needed.
. 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:
+
@@ -278,14 +272,12 @@ C:\> msiexec /i opendj-{opendj-version}.msi /quiet OPENDJ="C:\path\to\opendj"
C:\path\to\opendj> upgrade.bat --no-prompt --acceptLicense
----
-. Start the upgraded OpenDJ server.
-
-. If you disabled the Windows service, enable it again:
+. Start the upgraded OpenDJ server:
+
[source, console]
----
-C:\path\to\opendj\bat> windows-service.bat --enableService
+C:\> net start "OpenDJ Server"
----
====
diff --git a/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml b/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml
index 807215df25..aa5ea6b003 100644
--- a/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml
+++ b/opendj-packages/opendj-msi/opendj-msi-standard/pom.xml
@@ -13,7 +13,7 @@
information: "Portions Copyright [year] [name of copyright owner]".
Copyright 2015-2016 ForgeRock AS.
- Portions Copyright 2018 Open Identity Platform Community
+ Portions Copyright 2018-2026 3A Systems, LLC
-->
4.0.0
@@ -29,265 +29,130 @@
OpenDJ MSI Standard Package
- This module generates an OpenDJ MSI package.
+ This module generates an OpenDJ MSI package using the WiX Toolset v5 .NET tool
+ (cross-platform; no wine). The `wix` tool must be on the PATH (CI installs it via
+ `dotnet tool install --global wix`).
${basedir}/resources/msi
${project.build.directory}/${product.name.lowercase}
+
+ ${project.build.directory}/msi-staging
+ ${project.build.directory}/msi-staging-lib
+ ${package.dir}/lib/opendj_service.exe
+ ${project.build.directory}/${product.name.lowercase}-${project.version}.msi
-
-
-
+
+
${project.groupId}.${project.artifactId}
+
org.codehaus.mojo
build-helper-maven-plugin
-
-
-
- org.openidentityplatform.commons
- maven-external-dependency-plugin
- false
-
-
- ${project.build.directory}/dependencies/
-
- false
- true
- false
-
-
- openidentityplatform.org
- wixtoolset
- 3.11.1
- zip
-
- https://github.com/wixtoolset/wix3/releases/download/wix3111rtm/wix311-binaries.zip
-
- false
-
-
- openidentityplatform.org
- winetricks
- LAST
- sh
-
- https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks
-
- false
-
-
-
-
-
- clean-external-dependencies
- clean
-
- clean-external
-
-
-
- resolve-install-external-dependencies
- process-resources
-
- resolve-external
- install-external
-
-
-
- deploy-external-dependencies
- deploy
-
- deploy-external
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
- unpack-wix
+ attach-msi-and-bundle
package
- unpack
+ attach-artifact
-
-
- openidentityplatform.org
- wixtoolset
- 3.11.1
- zip
-
-
-
- ${project.build.directory}/wix
-
-
-
-
- unpack-winetricks
- package
-
- copy
-
-
-
-
- openidentityplatform.org
- winetricks
- LAST
- sh
-
-
-
- ${project.build.directory}/winetricks
-
+
+ ${msi.file}msi
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+
org.apache.maven.plugins
maven-antrun-plugin
- build-msi-package-prepare
+ stage-msi-payload
package
run
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ------------------- ${exec.heat} ${param.heat} -------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ------------------- ${exec.candle} ${param.candle} -------------------
-
-
-
-
-
-
-
-
-
-
-
-
- ------------------- ${exec.light} ${param.light} -------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+
+ wix-build-msi
+ package
+
+ exec
+
+
+ wix
+ ${project.build.directory}
+
+ build
+ ${msi.resources}/package.wxs
+ -archx64
+ -extWixToolset.UI.wixext
+ -bindpath${msi.resources}
+ -dname=${product.name}
+ -dmajor=${parsedVersion.majorVersion}
+ -dminor=${parsedVersion.minorVersion}
+ -dpoint=${parsedVersion.incrementalVersion}
+ -dstagingRoot=${staging.root}
+ -dstagingLib=${staging.lib}
+ -dserviceExe=${service.exe}
+ -o${msi.file}
+
+
+
+
+
-
\ No newline at end of file
+
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 f77dda7a14..cfb0d85b79 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
@@ -15,45 +15,155 @@
! Copyright 2013-2016 ForgeRock AS.
! Portions Copyright 2018-2026 3A Systems, LLC
! -->
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
-
-
-
-
-
-
- NOT Installed
- 1
-
-
+
+
+
diff --git a/opendj-packages/opendj-msi/pom.xml b/opendj-packages/opendj-msi/pom.xml
index 748456f462..1611afa4bb 100644
--- a/opendj-packages/opendj-msi/pom.xml
+++ b/opendj-packages/opendj-msi/pom.xml
@@ -32,63 +32,10 @@
This module contains configuration and generic plugin call to build OpenDJ MSI packages.
-
-
- /usr/bin/wine
-
- unix
- /usr/bin/wine
-
-
- opendj-msi-standard
-
-
- /usr/bin/wine${project.build.directory}/wix/heat.exe
- /usr/bin/wine${project.build.directory}/wix/candle.exe
- /usr/bin/wine${project.build.directory}/wix/light.exe
-
-
-
- /usr/local/bin/wine
-
- unix
- /usr/local/bin/wine
-
-
- opendj-msi-standard
-
-
- /usr/local/bin/wine${project.build.directory}/wix/heat.exe
- /usr/local/bin/wine${project.build.directory}/wix/candle.exe
- /usr/local/bin/wine${project.build.directory}/wix/light.exe
-
-
-
- /opt/local/bin/wine
-
- unix
- /opt/local/bin/wine
-
-
- opendj-msi-standard
-
-
- /opt/local/bin/wine${project.build.directory}/wix/heat.exe
- /opt/local/bin/wine${project.build.directory}/wix/candle.exe
- /opt/local/bin/wine${project.build.directory}/wix/light.exe
-
-
-
- windows
- windows
-
- opendj-msi-standard
-
-
- ${project.build.directory}\wix\heat.exe
- ${project.build.directory}\wix\candle.exe
- ${project.build.directory}\wix\light.exe
-
-
-
+
+
+ opendj-msi-standard
+
diff --git a/opendj-packages/pom.xml b/opendj-packages/pom.xml
index d2148a7768..d6d05e72d9 100644
--- a/opendj-packages/pom.xml
+++ b/opendj-packages/pom.xml
@@ -55,7 +55,6 @@
opendj-deb
opendj-rpm
opendj-svr4
- opendj-msi
opendj-docker
@@ -69,7 +68,6 @@
opendj-svr4
- opendj-msi
opendj-docker
@@ -79,6 +77,8 @@
windows
+
opendj-msi
opendj-docker
diff --git a/opendj-server-legacy/lib/launcher_administrator.exe b/opendj-server-legacy/lib/launcher_administrator.exe
index 50b885811d..bfb8ae4fdf 100644
Binary files a/opendj-server-legacy/lib/launcher_administrator.exe and b/opendj-server-legacy/lib/launcher_administrator.exe differ
diff --git a/opendj-server-legacy/lib/opendj_service.exe b/opendj-server-legacy/lib/opendj_service.exe
index 0b458d8f72..4e20ac7941 100644
Binary files a/opendj-server-legacy/lib/opendj_service.exe and b/opendj-server-legacy/lib/opendj_service.exe differ
diff --git a/opendj-server-legacy/lib/winlauncher.exe b/opendj-server-legacy/lib/winlauncher.exe
index 04f210cb64..d1e3eaef75 100644
Binary files a/opendj-server-legacy/lib/winlauncher.exe and b/opendj-server-legacy/lib/winlauncher.exe differ
diff --git a/opendj-server-legacy/src/build-tools/windows/service.c b/opendj-server-legacy/src/build-tools/windows/service.c
index 1fd7e373d0..29fbb87381 100644
--- a/opendj-server-legacy/src/build-tools/windows/service.c
+++ b/opendj-server-legacy/src/build-tools/windows/service.c
@@ -826,6 +826,118 @@ ServiceReturnCode createServiceBinPath(char* serviceBinPath)
return returnValue;
} // createServiceBinPath
+// ----------------------------------------------------
+// Reads the next command line token starting at *p into out (at most
+// outSize - 1 characters). A token is either a run of non-blank
+// characters or a double-quoted string; quotes are treated as plain
+// delimiters without escape processing, so a trailing backslash-quote
+// sequence (as written by msiexec) leaves trailing backslashes on the
+// token, which normalizeInstanceDir strips. Returns the position right
+// after the token.
+// ----------------------------------------------------
+
+static const char* nextCmdToken(const char* p, char* out, int outSize)
+{
+ int i = 0;
+ while ((*p == ' ') || (*p == '\t'))
+ {
+ p++;
+ }
+ if (*p == '"')
+ {
+ p++;
+ while ((*p != '\0') && (*p != '"'))
+ {
+ if (i < (outSize - 1))
+ {
+ out[i++] = *p;
+ }
+ p++;
+ }
+ if (*p == '"')
+ {
+ p++;
+ }
+ }
+ else
+ {
+ while ((*p != '\0') && (*p != ' ') && (*p != '\t'))
+ {
+ if (i < (outSize - 1))
+ {
+ out[i++] = *p;
+ }
+ p++;
+ }
+ }
+ out[i] = '\0';
+ return p;
+} // nextCmdToken
+
+// ----------------------------------------------------
+// Strips trailing backslashes and "\." path segments from an instance
+// dir so that "C:\opendj", "C:\opendj\" and "C:\opendj\." all compare
+// equal.
+// ----------------------------------------------------
+
+static void normalizeInstanceDir(char* dir)
+{
+ size_t len = strlen(dir);
+ BOOL changed = TRUE;
+ while (changed && (len > 0))
+ {
+ changed = FALSE;
+ if (dir[len - 1] == '\\')
+ {
+ dir[--len] = '\0';
+ changed = TRUE;
+ }
+ else if ((len > 1) && (dir[len - 1] == '.') && (dir[len - 2] == '\\'))
+ {
+ dir[--len] = '\0';
+ changed = TRUE;
+ }
+ }
+} // normalizeInstanceDir
+
+// ----------------------------------------------------
+// Tells whether two service command lines refer to the same server
+// instance. The strings cannot be compared verbatim because every
+// writer quotes differently: this executable and the java tools write
+// '"\lib\opendj_service.exe" start ""' while the MSI
+// ServiceInstall writes the executable unquoted (when the path has no
+// spaces) and the instance dir with a trailing backslash. Instead the
+// executable path, the subcommand and the normalized instance dir are
+// compared token by token, case-insensitively.
+// ----------------------------------------------------
+
+static BOOL serviceCmdsMatch(const char* cmd1, const char* cmd2)
+{
+ char exe1[COMMAND_SIZE];
+ char exe2[COMMAND_SIZE];
+ char sub1[COMMAND_SIZE];
+ char sub2[COMMAND_SIZE];
+ char dir1[COMMAND_SIZE];
+ char dir2[COMMAND_SIZE];
+ const char* p1 = cmd1;
+ const char* p2 = cmd2;
+
+ p1 = nextCmdToken(p1, exe1, COMMAND_SIZE);
+ p1 = nextCmdToken(p1, sub1, COMMAND_SIZE);
+ nextCmdToken(p1, dir1, COMMAND_SIZE);
+
+ p2 = nextCmdToken(p2, exe2, COMMAND_SIZE);
+ p2 = nextCmdToken(p2, sub2, COMMAND_SIZE);
+ nextCmdToken(p2, dir2, COMMAND_SIZE);
+
+ normalizeInstanceDir(dir1);
+ normalizeInstanceDir(dir2);
+
+ return (_stricmp(exe1, exe2) == 0)
+ && (_stricmp(sub1, sub2) == 0)
+ && (_stricmp(dir1, dir2) == 0);
+} // serviceCmdsMatch
+
// ----------------------------------------------------
// Returns the service name that maps the command used to start the
// product. All commands are supposed to be unique because they have
@@ -866,7 +978,7 @@ ServiceReturnCode getServiceName(char* cmdToRun, char* serviceName)
ServiceDescriptor curService = serviceList[i];
if (curService.cmdToRun != NULL)
{
- if (_stricmp(cmdToRun, curService.cmdToRun) == 0)
+ if (serviceCmdsMatch(cmdToRun, curService.cmdToRun))
{
if (strlen(curService.serviceName) < MAX_SERVICE_NAME)
{