Project

General

Profile

hotel_gui_win.patch

Teodor Gorghe, 08/07/2026 10:16 AM

Download (105 KB)

View differences:

new/bootstrap_conversion.cmd 2026-08-07 14:04:48 +0000
4 4
:: Determine script directory
5 5
set scriptdir=%~dp0
6 6

  
7
:: Pass all arguments to PowerShell script
8
pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "%scriptdir%bootstrap_conversion.ps1" %*
7
:: Pass all arguments to PowerShell script, run_powershell.cmd picks the PowerShell which is installed
8
call "%scriptdir%run_powershell.cmd" "%scriptdir%bootstrap_conversion.ps1" %*
9
exit /b %ERRORLEVEL%
new/build.xml 2026-08-07 14:12:06 +0000
110 110
      <os family="unix" />
111 111
   </condition>
112 112

  
113
   <!-- The PowerShell to run the .ps1 scripts with: PowerShell Core when it is installed, otherwise the
114
        Windows PowerShell which is preinstalled with Windows.  A .cmd wrapper is not used here, ant
115
        cannot exec a batch file (CreateProcess only runs a real executable), which is also why the .ps1
116
        scripts are passed to the interpreter instead of being exec'd directly.  Should neither spelling
117
        of the variable resolve, this falls back to powershell, the one always present on Windows.
118

  
119
        Both env.PATH and env.Path are looked at: ant names these properties after the environment
120
        variables as the OS reports them, and Windows spells that one "Path".  Checking only the
121
        uppercase name would silently build on the Windows PowerShell on a machine which does have
122
        PowerShell Core installed. -->
123
   <property environment="env" />
124
   <available file="pwsh.exe" filepath="${env.PATH}" property="havePowerShellCore" />
125
   <available file="pwsh.exe" filepath="${env.Path}" property="havePowerShellCore" />
126
   <condition property="ps.executable" value="pwsh" else="powershell">
127
      <isset property="havePowerShellCore" />
128
   </condition>
129

  
113 130
   <condition property="escaped.quotes" value="&quot;&quot;&quot;" else="&quot;">
114 131
      <isset property="isWindows"  />
115 132
   </condition>
......
139 156

  
140 157
   <!-- path used when running application related tasks -->
141 158
   <path id="app.classpath">
142
      <fileset dir="${fwd.lib.home}"    includes="*.jar"/>
143
      <fileset dir="${deploy.home}/lib" includes="*.jar"/>
159
      <pathelement location="${fwd.lib.home}/p2j.jar"/>
160
      <pathelement location="${fwd.lib.home}/fwdspi.jar"/>
161
      <pathelement location="${fwd.lib.home}/fwdaopltw.jar"/>
162
      <pathelement location="${deploy.home}/lib/${appname}.jar"/>
144 163
   </path>
145 164

  
146 165
   <!-- path used when running database import tasks -->
147 166
   <path id="import.classpath">
148
      <fileset dir="${fwd.lib.home}"    includes="*.jar"/>
149
      <fileset dir="${deploy.home}/lib" includes="*.jar"/>
150
      <fileset dir="${build.home}/lib"  includes="*.jar"/>
167
      <pathelement location="${fwd.lib.home}/p2j.jar"/>
168
      <pathelement location="${fwd.lib.home}/fwdspi.jar"/>
169
      <pathelement location="${fwd.lib.home}/fwdaopltw.jar"/>
170
      <pathelement location="${deploy.home}/lib/${appname}.jar"/>
171
      <pathelement location="${build.home}/lib/${appname}.jar"/>
151 172
   </path>
152
   
173

  
174
   <target name="show.classpath" description="Print the classpaths used by the java tasks.">
175
      <echo message="fwd.lib.home      = ${fwd.lib.home}"/>
176
      <echo message="deploy lib        = ${deploy.home}/lib"/>
177
      <echo message="compile.classpath = ${toString:compile.classpath}"/>
178
      <echo message="app.classpath     = ${toString:app.classpath}"/>
179
      <echo message="import.classpath  = ${toString:import.classpath}"/>
180
   </target>
181

  
153 182
   <tstamp>
154 183
      <format property="DSTAMP" pattern="yyyyMMdd" />
155 184
   </tstamp>
......
172 201
   <target name="init-standard-df" depends="init-ant-contrib"
173 202
           description="Determine correct standard.df based on p2j build">
174 203

  
175
      <!-- Define script path -->
204
      <!-- Define script path. Windows cannot execute the bash script, it uses the .ps1 counterpart. -->
205
      <condition property="standard.selector.name"
206
                 value="select_standard.ps1" else="select_standard.sh">
207
         <isset property="isWindows"/>
208
      </condition>
176 209
      <property name="standard.selector.script"
177
                location="${data.rel}/select_standard.sh"/>
210
                location="${data.rel}/${standard.selector.name}"/>
178 211

  
179 212
      <!-- Check if script exists -->
180 213
      <available file="${standard.selector.script}"
......
187 220
            <echo message="Using selector script: ${standard.selector.script}"/>
188 221
            <exec executable="${standard.selector.script}"
189 222
                  outputproperty="standard.selector.result"
190
                  failonerror="true">
223
                  failonerror="true"
224
                  osfamily="unix">
225
                <arg value="${data.rel}"/>
226
                <arg value="${fwd.lib.home}/p2j.jar"/>
227
            </exec>
228

  
229
            <!-- CreateProcess cannot run a .ps1 either, the interpreter is launched explicitly -->
230
            <exec executable="${ps.executable}"
231
                  outputproperty="standard.selector.result"
232
                  failonerror="true"
233
                  osfamily="windows">
234
                <arg value="-NoProfile"/>
235
                <arg value="-ExecutionPolicy"/>
236
                <arg value="Bypass"/>
237
                <arg value="-File"/>
238
                <arg value="${standard.selector.script}"/>
191 239
                <arg value="${data.rel}"/>
192 240
                <arg value="${fwd.lib.home}/p2j.jar"/>
193 241
            </exec>
......
209 257
      <condition property="standard.source"
210 258
                 value="${data.rel}/standard_post9950.df"
211 259
                 else="${data.rel}/standard_pre9950.df">
212
          <equals arg1="${standard.selector.result}" arg2="post9950"/>
260
          <!-- trim, the PowerShell selector terminates its output with a CRLF -->
261
          <equals arg1="${standard.selector.result}" arg2="post9950" trim="true"/>
213 262
      </condition>
214 263

  
215 264
      <!-- Perform copy only if not skip -->
216 265
      <if>
217 266
         <not>
218
            <equals arg1="${standard.selector.result}" arg2="skip"/>
267
            <equals arg1="${standard.selector.result}" arg2="skip" trim="true"/>
219 268
         </not>
220 269
         <then>
221 270
            <echo message="Copying ${standard.source} to ${data.rel}/standard.df"/>
......
633 682
         <arg value="deploy_appcds.sh"/>
634 683
         <arg value="${deploy.home}/lib/p2j.jar"/>
635 684
      </exec>
636
      <exec executable="pwsh" dir="${deploy.home}/server" failonerror="true" osfamily="windows">
685
      <exec executable="${ps.executable}" dir="${deploy.home}/server" failonerror="true" osfamily="windows">
637 686
         <arg value="-ExecutionPolicy"/>
638 687
         <arg value="Bypass"/>
639 688
         <arg value="-File"/>
......
697 746
         <arg value="-no"/>
698 747
      </exec>
699 748

  
700
      <exec executable="pwsh" dir="." failonerror="true" osfamily="windows">
749
      <exec executable="${ps.executable}" dir="." failonerror="true" osfamily="windows">
701 750
         <arg value="-ExecutionPolicy"/>
702 751
         <arg value="Bypass"/>
703 752
         <arg value="-File"/>
......
987 1036
         <arg value="find ${app.4gl.src} -type f -name '*.w' ${e4gl-noclean} -exec grep -l ${e4gl.pattern} {} \; -exec rm -f {} +" />
988 1037
      </exec>
989 1038

  
990
      <exec executable="powershell" failonerror="true" osfamily="windows">
1039
      <exec executable="${ps.executable}" failonerror="true" osfamily="windows">
991 1040
         <arg value="-Command"/>
992 1041
         <arg value="Get-ChildItem -Path '${app.4gl.src}' -Filter '*.w' -Recurse | Select-String -Pattern ${e4gl-ps1.pattern} | ForEach-Object { Remove-Item $_.Path -Force }"/>
993 1042
      </exec>
new/build_db.xml 2026-08-07 14:04:48 +0000
14 14
   <condition property="java.locale.providers.param" else="-Djava.locale.providers=SPI,JRE" value="-Djava.locale.providers=SPI,CLDR,COMPAT">
15 15
       <javaversion atleast="11"/>
16 16
   </condition>
17
   <!-- Run with -Dclasspath.debug=true to have the forked JVMs report every class they load together
18
        with the jar it came from.  The output is captured by the <record> log of the target.  This is
19
        the only way to see the jars which the Class-Path manifest of p2j.jar contributes, those are
20
        resolved by the JVM and never appear in the classpath itself. -->
21
   <condition property="classpath.log.param" value="-verbose:class" else="-Dignorethis">
22
      <isset property="classpath.debug"/>
23
   </condition>
17 24
   <if>
18 25
      <and>
19 26
         <isset property="db.sql.shared" />
......
47 54
           description="Creates an empty H2 database instance using the DDL generated during conversion."
48 55
           if="${db.h2}">
49 56
      <record name="create_db_h2_${db.name}_${LOG_STAMP}.log" action="start"/>
57
      <echo message="app.classpath = ${toString:app.classpath}"/>
50 58
      <java classname="com.goldencode.p2j.persist.deploy.ScriptRunner"
51 59
            fork="true"
52 60
            failonerror="true"
......
54 62
         <jvmarg value="-Xmx1g"/>
55 63
         <jvmarg value="${java.locale.providers.param}"/>
56 64
         <jvmarg value="${java.ext.dir.param}"/>
65
         <jvmarg value="${classpath.log.param}"/>
57 66
         <jvmarg value="-Dfile.encoding=UTF-8"/>
58 67
         <jvmarg value="-Djava.util.logging.config.file=${fwd.home}/cfg/logging.properties"/>
59 68
         <arg value ="${sql.url.h2}"/>
......
72 81
           description="Import data (.d files) into H2 database."
73 82
           if="${db.h2}">
74 83
      <record name="import_db_h2_${db.name}_${LOG_STAMP}.log" action="start"/>
84
      <echo message="import.classpath = ${toString:import.classpath}"/>
75 85
      <java classname="com.goldencode.p2j.pattern.PatternEngine"
76 86
            fork="true"
77 87
            failonerror="true"
......
79 89
         <jvmarg value="-Xmx1g"/>
80 90
         <jvmarg value="${java.locale.providers.param}"/>
81 91
         <jvmarg value="${java.ext.dir.param}"/>
92
         <jvmarg value="${classpath.log.param}"/>
82 93
         <jvmarg value="-Dfile.encoding=UTF-8"/>
83 94
         <jvmarg value="-Djava.system.class.loader=com.goldencode.asm.AsmClassLoader"/>
84 95
         <arg value ="-d"/>
new/data/select_standard.ps1 2026-08-07 14:04:48 +0000
1
#
2
# The Windows counterpart of select_standard.sh, kept in sync with it: Windows cannot execute the bash
3
# script, and the policy rules below decide which standard.df the build uses, so the two MUST agree.
4
#
5
param (
6
   [Parameter(Position = 0, Mandatory = $true)] [string]$DataRel,
7
   [Parameter(Position = 1, Mandatory = $true)] [string]$JarPath
8
)
9

  
10
Set-StrictMode -Version Latest
11
$ErrorActionPreference = 'Stop'
12

  
13
# Get version line.  Only stdout is captured, exactly as the "$(java -jar "$jar_path")" of
14
# select_standard.sh does; whatever java puts on stderr is left to flow into the build log.
15
#
16
# The preference is relaxed for the call: Windows PowerShell turns the stderr of a native command
17
# into an error record, which the $ErrorActionPreference = 'Stop' above would then make terminating,
18
# and a JVM which merely reports "Picked up JAVA_TOOL_OPTIONS" would fail the build.  PowerShell 7
19
# does not do this.
20
$previousPreference = $ErrorActionPreference
21
$ErrorActionPreference = 'Continue'
22
try {
23
   $output = (& java -jar $JarPath | Out-String).Trim()
24
   $javaExit = $LASTEXITCODE
25
}
26
finally {
27
   $ErrorActionPreference = $previousPreference
28
}
29

  
30
# the counterpart of the "set -e" of select_standard.sh, which aborts on a failing java
31
if ($javaExit -ne 0)
32
{
33
   [Console]::Error.WriteLine("ERROR: java -jar $JarPath failed with exit code $javaExit")
34
   exit 1
35
}
36

  
37
# Expect format: FWD v4.0.0_p2j_<branch>_<revision>
38
if ($output -notmatch '_p2j_([^_]+)_([0-9]+)')
39
{
40
   [Console]::Error.WriteLine("ERROR: Unable to parse p2j output: $output")
41
   exit 1
42
}
43

  
44
$branch   = $Matches[1]
45
$revision = [int]$Matches[2]
46

  
47
# Default to using post9950 update. Add policies for using older standard.df
48
$usePre = $false
49

  
50
# ---- Policy Rules ----
51
if ($branch -eq "trunk" -and $revision -lt 16415)
52
{
53
   $usePre = $true
54
}
55

  
56
# Policy example. Any of these should exist only until the branch is rebased to trunk_16415
57

  
58
#if ($branch -eq "9986c")
59
#{
60
#   $usePre = $true
61
#}
62
# ----------------------
63

  
64
if ($usePre)
65
{
66
   Write-Output "pre9950"
67
   exit 0
68
}
69

  
70
# Fallback handling uses the in-place standard.df (post9950). Tell ant 'skip'
71
if (!(Test-Path -LiteralPath (Join-Path $DataRel 'standard_pre9950.df') -PathType Leaf) -and
72
     (Test-Path -LiteralPath (Join-Path $DataRel 'standard.df') -PathType Leaf))
73
{
74
   Write-Output "skip"
75
   exit 0
76
}
77

  
78
Write-Output "post9950"
new/data/select_standard.sh 2026-08-07 14:04:48 +0000
21 21

  
22 22
# ---- Policy Rules ----
23 23
if [[ "$branch" == "trunk" && "$revision" -lt 16415 ]]; then
24
   use_post=false
24
   use_pre=true
25 25
fi
26 26

  
27 27
# Policy example. Any of these should exist only until the branch is rebased to trunk_16415
new/deploy/client/client.cmd 2026-08-07 14:04:48 +0000
1 1
:: Standard FWD client startup script - Wrapper to client.ps1
2 2
@echo off
3 3
setlocal
4
pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0client.ps1" %*
5
endlocal
4

  
5
:: PowerShell Core when it is installed, otherwise the Windows PowerShell which is preinstalled with
6
:: Windows.  %%~$PATH:I expands to the full path of the file when it is found on the PATH, and to an
7
:: empty string when it is not.
8
set "psexe="
9
for %%I in (pwsh.exe) do if not "%%~$PATH:I" == "" set "psexe=%%~$PATH:I"
10
if not defined psexe set "psexe=powershell.exe"
11

  
12
"%psexe%" -NoProfile -ExecutionPolicy Bypass -File "%~dp0client.ps1" %*
13
exit /b %ERRORLEVEL%
new/deploy/client/client.ps1 2026-08-07 14:04:48 +0000
148 148
}
149 149

  
150 150
if ($debug) {
151
   $daddress = Test-Path "/.dockerenv" ? "0.0.0.0:"+$dport : $dport
151
   # an if expression, the ?: ternary operator needs PowerShell 7
152
   $daddress = if (Test-Path "/.dockerenv") { "0.0.0.0:" + $dport } else { $dport }
152 153
   $dtxt = "-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address="+$daddress+",server=y,suspend="+$suspend
153 154
} else {
154 155
   $dtxt = ""
new/deploy/deploy_helpers.ps1 2026-08-07 14:04:48 +0000
7 7
      [string]$JavaExecutable = "java"  # default to 'java' unless overridden
8 8
   )
9 9

  
10
   $output = & $JavaExecutable -version 2>&1 |
10
   # java writes its version banner to stderr, and Windows PowerShell turns the stderr of a native
11
   # command into an error record, which the $ErrorActionPreference = 'Stop' of the callers then makes
12
   # terminating ("NativeCommandError").  PowerShell 7 does not do this.  Relax the preference for the
13
   # call only, and stringify what comes back: with the streams merged the lines can arrive as error
14
   # records rather than as strings.
15
   $previousPreference = $ErrorActionPreference
16
   $ErrorActionPreference = 'Continue'
17
   try {
18
      $versionLines = & $JavaExecutable -version 2>&1 | ForEach-Object { "$_" }
19
   }
20
   finally {
21
      $ErrorActionPreference = $previousPreference
22
   }
23

  
24
   $output = $versionLines |
11 25
      Where-Object { $_ -match 'version' } |
12 26
      ForEach-Object {
13 27
         if ($_ -match '"([\d._]+)"') {
14 28
            $version = $matches[1]
15 29
            if ($version -like '1.*') {
16 30
               $parts = $version -split '\.'
17
               return [int]("${parts[0]}${parts[1]}")
31
               # "${parts[0]}" does not index, it names a variable called 'parts[0]'
32
               return [int]("$($parts[0])$($parts[1])")
18 33
            } else {
19 34
               return [int]($version -split '[._]')[0] * 10
20 35
            }
new/deploy/server/deploy_appcds.ps1 2026-08-07 14:04:48 +0000
122 122
        continue
123 123
    }
124 124

  
125
    Write-Host "JVM classpath for $clientType: $clientCp"
126
    Write-Host "JVM args for $clientType: $clientArgsStr"
125
    Write-Host "JVM classpath for ${clientType}: $clientCp"
126
    Write-Host "JVM args for ${clientType}: $clientArgsStr"
127 127

  
128 128
    # The archive is dumped below with our own -Xshare:dump and a fresh
129 129
    # -XX:SharedArchiveFile pointing at the .jsa we're generating. Strip the
new/deploy/server/prepare_dir.ps1 2026-08-07 14:04:48 +0000
1
# prepare_dir.ps1
2

  
3
# Parse parameters
4
param (
5
   [switch]$o,
6
   [string]$f
7
)
8

  
9
Set-PSDebug -Trace 0
10
Set-StrictMode -Version Latest
11
$ErrorActionPreference = 'Stop'
12

  
13
# Ensure the script is running with administrative privileges
14
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
15
{
16
   Write-Host "This script requires administrative privileges. Restarting with elevation..."
17
   Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`"" -Verb RunAs
18
   exit
19
}
20

  
21
# Helper Functions
22
function Show-Usage {
23
   Write-Host "Usage: prepare_dir.ps1 [-o] [-f <json_cfg_file>]"
24
}
25

  
26
function Get-Val($Key, $Default, $File) {
27
   if (Test-Path $File) {
28
      $json = Get-Content $File | ConvertFrom-Json
29
      $val = $json | Select-Object -ExpandProperty $Key -ErrorAction SilentlyContinue
30
      return $(if ($null -eq $val) { $Default } else { $val })
31
   }
32
   $Default
33
}
34

  
35
function Split-String($InputString) { return $InputString -split ',' }
36

  
37
# Defaults
38
$infile = "prepare_dir.json"
39
$keyboard = "US"
40
$pljava = "FALSE"
41
$def_p2j_entry = "start.p"
42
$def_propath = ".;common;"
43
$def_search_path = ".;common;"
44
$defdbname = "hotel"
45

  
46
if ($args -contains "-?" -or $args -contains "/?")
47
{
48
   Show-Usage
49
   exit 1
50
}
51

  
52
if ($f) { $infile = $f }
53
if (!(Test-Path $infile)) { Write-Error "Input file not found: $infile"; Show-Usage; exit 1 }
54

  
55
# Read JSON values
56
$cfg = @{
57
   spawner_path     = Get-Val "spawner_path" "/opt/spawner/spawn" $infile
58
   server_xml       = Get-Val "server_xml_file" "server.xml" $infile
59
   directory_xml    = Get-Val "directory_xml_file" "directory.xml" $infile
60
   client_start     = Get-Val "client_start_dir" "./deploy/client" $infile
61
   dateFormat       = Get-Val "dateFormat" "mdy" $infile
62
   numberGroupSep   = Get-Val "numberGroupSep" "," $infile
63
   numberDecimalSep = Get-Val "numberDecimalSep" "." $infile
64
   p2j_entry        = Get-Val "p2j_entry" $def_p2j_entry $infile
65
   pkgroot          = Get-Val "pkgroot" "com.goldencode.hotel" $infile
66
   propath          = Get-Val "propath" $def_propath $infile
67
   search_path      = Get-Val "search_path" $def_search_path $infile
68
   path_separator   = Get-Val "path_separator" ([IO.Path]::PathSeparator) $infile
69
   file_separator   = Get-Val "file_separator" ([IO.Path]::DirectorySeparatorChar) $infile
70
   case_sensitive   = Get-Val "case_sensitive" "TRUE" $infile
71
   os_user          = Get-Val "os_user" $env:USERNAME $infile
72
   kbd_layout       = Get-Val "kbd_layout" $keyboard $infile
73
   server_log       = Get-Val "server_log" "../logs" $infile
74
   client_log       = Get-Val "client_log" "../logs" $infile
75
   embedded_host    = Get-Val "embedded_host" "localhost" $infile
76
   admin_port       = Get-Val "admin_port" "7443" $infile
77
   dbnames          = Get-Val "dbnames" $defdbname $infile
78
   client_lib_path  = Get-Val "client_lib_path" "..\lib" $infile
79
}
80

  
81
$db_array = @(Split-String $cfg.dbnames)
82
$defdatabase = $db_array[0]
83

  
84
# Determine if we can now create the directory file
85
if (Test-Path -LiteralPath $cfg.directory_xml -PathType Leaf) {
86
    $dirfile = (Get-Item -LiteralPath $cfg.directory_xml).FullName
87
}
88
else {
89
    $parent = if ([string]::IsNullOrWhiteSpace((Split-Path $cfg.directory_xml -Parent))) {
90
        $PWD.Path  # Use current directory if no parent in path
91
    } else {
92
        (Get-Item (Split-Path $cfg.directory_xml -Parent)).FullName
93
    }
94
    $dirfile = Join-Path $parent (Split-Path $cfg.directory_xml -Leaf)
95
}
96
if ((Test-Path $dirfile) -and -not $o.IsPresent) {
97
   Write-Error "Output file exists: $dirfile, and '-o' not given."
98
   Show-Usage
99
   exit 1
100
}
101

  
102
# Templates processing
103
(Get-Content "server.xml.template") -replace '{directory_xml_file}', $cfg.directory_xml | Set-Content $cfg.server_xml
104

  
105
$directoryTemplate = Get-Content "directory.xml.template"
106
foreach ($k in $cfg.Keys) {
107
   $directoryTemplate = $directoryTemplate -replace "\{$k\}", $cfg[$k]
108
}
109
$directoryTemplate = $directoryTemplate -replace '{dbname}', $defdatabase
110
$directoryTemplate | Set-Content "directory_tmp.xml"
111

  
112
# DB info and Java merge
113
$dbdialect = @{
114
   h2       = "com.goldencode.p2j.persist.dialect.P2JH2Dialect"
115
   postgres = "com.goldencode.p2j.persist.dialect.P2JPostgreSQLDialect"
116
}
117
$dbdriver = @{
118
   h2       = "org.h2.Driver"
119
   postgres = "org.postgresql.Driver"
120
}
121

  
122
$fwd_lib = $env:FWD_LIB
123
if (-not $fwd_lib) { $fwd_lib = "../../p2j" }
124
$p2j_jar = if (Test-Path "$fwd_lib/build/lib/p2j.jar") {
125
   "$fwd_lib/build/lib/p2j.jar"
126
} elseif (Test-Path "$fwd_lib/lib/p2j.jar") {
127
   "$fwd_lib/lib/p2j.jar"
128
} else {
129
   "../../p2j/lib/p2j.jar"
130
}
131

  
132
foreach ($db in $db_array) {
133
   $dbtype = Get-Val "$db.dbtype" "h2" $infile
134
   if ($dbtype -ne "none") {
135
      $jdbc_url = if ($dbtype -eq "h2") {
136
         $pljava = "TRUE"
137
         "h2:$(Get-Val "$db.dbpath" "../db" $infile)/$db;DB_CLOSE_DELAY=-1;MV_STORE=FALSE;RTRIM=TRUE"
138
      } else {
139
         "postgresql://$(Get-Val "$db.dbhost" "localhost" $infile):$(Get-Val "$db.dbport" "5432" $infile)/$db"
140
      }
141

  
142
      $dbfile = "directory_db.xml.$db"
143
      (Get-Content "directory_db.xml.template") `
144
         -replace '{dbhost}', (Get-Val "$db.dbhost" "localhost" $infile) `
145
         -replace '{dbport}', (Get-Val "$db.dbport" "5432" $infile) `
146
         -replace '{dbname}', $db `
147
         -replace '{dbuser}', (Get-Val "$db.dbuser" "fwd_user" $infile) `
148
         -replace '{dbuserpass}', (Get-Val "$db.dbuserpass" "user" $infile) `
149
         -replace '{dbadmin}', (Get-Val "$db.dbadmin" "fwd_admin" $infile) `
150
         -replace '{dbadminpass}', (Get-Val "$db.dbadminpass" "admin" $infile) `
151
         -replace '{dbdialect}', $dbdialect[$dbtype] `
152
         -replace '{dbdriver}', $dbdriver[$dbtype] `
153
         -replace '{jdbc_url}', $jdbc_url `
154
         -replace '{pljava}', $pljava `
155
         -replace '{collation}', (Get-Val "$db.collation" "en_US@iso88591_fwd_basic" $infile) |
156
         Set-Content $dbfile
157

  
158
      # Java DirectoryCopy
159
      java -Xmx256m -classpath $p2j_jar com.goldencode.p2j.directory.DirectoryCopy copy $dbfile /server/standard/database/ directory_tmp.xml /server/standard/database/
160
      Remove-Item $dbfile
161
   } else {
162
      Copy-Item "directory_nodb.xml.template" "directory_nodb.xml"
163
   }
164
}
165

  
166
Move-Item -Force "directory_tmp.xml" $dirfile
167

  
168
exit 0
1
#
2
# prepare_dir.ps1 - the Windows counterpart of prepare_dir.sh.
3
#
4
# Renders server.xml and directory.xml on the deployment machine from prepare_dir.json, which
5
# json_template.ps1 -d (or json_template.sh -d) produces at build time.
6
#
7
# Run this from the directory holding the templates, deploy/server, exactly as prepare_dir.sh is.
8
#
9
param (
10
   [switch]$o,
11
   [string]$f
12
)
13

  
14
Set-PSDebug -Trace 0
15
Set-StrictMode -Version Latest
16
$ErrorActionPreference = 'Stop'
17

  
18
function Show-Usage
19
{
20
   Write-Host "Usage: prepare_dir.ps1 [-o] [-f <json_cfg_file>]"
21
   Write-Host "Where:"
22
   Write-Host "   o = Overwrite the specified directory.xml, if it already exists."
23
   Write-Host "   f = Use <json_cfg_file> instead of prepare_dir.json as input."
24
}
25

  
26
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
27

  
28
# Property lookup which tolerates a missing key under Set-StrictMode; $cfg.$Key would throw.
29
function Get-Node
30
{
31
   param (
32
      $Object,
33
      [string]$Key
34
   )
35

  
36
   if ($null -eq $Object) { return $null }
37

  
38
   $prop = $Object.PSObject.Properties[$Key]
39
   if ($null -eq $prop) { return $null }
40

  
41
   return $prop.Value
42
}
43

  
44
function Get-Val
45
{
46
   param (
47
      [string]$Key,
48
      [string]$Default = ""
49
   )
50

  
51
   $val = Get-Node $script:cfg $Key
52
   if ($null -eq $val -or "$val" -eq "") { return $Default }
53

  
54
   return "$val"
55
}
56

  
57
# The per-database values live in a sub-object named after the database, so the lookup is two
58
# levels.  This used to pass "<db>.<key>" as a single property name to Select-Object, which never
59
# resolves against nested json and silently handed back every default: with
60
# prepare_dir_h2_docker.json that turned dbpath into "../db" instead of "/opt/hotel/db", and did the
61
# same to the collation, the users and their passwords.
62
function Get-SubVal
63
{
64
   param (
65
      [string]$SubName,
66
      [string]$Key,
67
      [string]$Default = ""
68
   )
69

  
70
   $node = Get-Node $script:cfg $SubName
71
   $val = Get-Node $node $Key
72
   if ($null -eq $val -or "$val" -eq "") { return $Default }
73

  
74
   return "$val"
75
}
76

  
77
# Replaces every {key} of a template with its value and writes the result.  A literal String.Replace
78
# and not the -replace operator: -replace is a regular expression on both sides, so a value holding
79
# a $ or a \ would be mangled - a database password is the obvious way to hit that.
80
function Expand-Template
81
{
82
   param (
83
      [string]$Template,
84
      [hashtable]$Values,
85
      [string]$Destination
86
   )
87

  
88
   if (!(Test-Path -LiteralPath $Template -PathType Leaf))
89
   {
90
      throw "Could not create $Destination, the template $Template was not found."
91
   }
92

  
93
   $content = [System.IO.File]::ReadAllText((Resolve-Path -LiteralPath $Template).Path)
94

  
95
   foreach ($key in $Values.Keys)
96
   {
97
      $content = $content.Replace("{$key}", [string]$Values[$key])
98
   }
99

  
100
   [System.IO.File]::WriteAllText($Destination, $content, $utf8NoBom)
101
}
102

  
103
if ($args -contains "-?" -or $args -contains "/?" -or $args -contains "-h")
104
{
105
   Show-Usage
106
   exit 1
107
}
108

  
109
$infile = if ($f) { $f } else { "prepare_dir.json" }
110
if (!(Test-Path -LiteralPath $infile -PathType Leaf))
111
{
112
   Write-Error "Input file not found: $infile"
113
   Show-Usage
114
   exit 1
115
}
116

  
117
$here = (Get-Location).Path
118

  
119
# System.IO and every native command resolve a relative path against the PROCESS working
120
# directory, which PowerShell's own location does not track; keep the two in step.
121
[Environment]::CurrentDirectory = $here
122
$cfg = [System.IO.File]::ReadAllText((Resolve-Path -LiteralPath $infile).Path) | ConvertFrom-Json
123

  
124
# Nothing below carries an application specific default any more: pkgroot used to fall back to
125
# "com.goldencode.hotel" and dbnames to "hotel", so a deployment whose json was missing either one
126
# was configured for the sample application instead of failing.
127
$pkgroot = Get-Val "pkgroot"
128
$dbnames = Get-Val "dbnames"
129
if ([string]::IsNullOrEmpty($pkgroot))
130
{
131
   Write-Error "No 'pkgroot' in $infile."
132
   exit 1
133
}
134
if ([string]::IsNullOrEmpty($dbnames))
135
{
136
   Write-Error "No 'dbnames' in $infile."
137
   exit 1
138
}
139

  
140
# dbnames is a comma separated string in prepare_template.json but a real json array in the
141
# prepare_dir_*_docker.json files; accept either.  Splitting an array on ',' joined its elements
142
# with a space and yielded a single bogus name.
143
$dbnamesNode = Get-Node $cfg "dbnames"
144
if ($dbnamesNode -is [System.Array])
145
{
146
   $db_array = @($dbnamesNode | ForEach-Object { "$_".Trim() } | Where-Object { $_ -ne "" })
147
}
148
else
149
{
150
   $db_array = @($dbnames -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" })
151
}
152
$defdatabase = $db_array[0]
153

  
154
$directory_xml = Get-Val "directory_xml_file" "directory.xml"
155
$server_xml = Get-Val "server_xml_file" "server.xml"
156

  
157
# The spawner default follows this host, it used to be the Linux "/opt/spawner/spawn".
158
$spawner_path = Get-Val "spawner_path" (Join-Path $env:ProgramData 'FWD\spawner\spawn.exe')
159
$client_start_dir = Get-Val "client_start_dir" (Join-Path (Split-Path $here -Parent) 'client')
160

  
161
$client_lib_path = Get-SubVal "appcds" "client_lib_path" "..\lib"
162
$webclient_appcds_archive = Get-SubVal "appcds" "webclient_appcds_archive" ""
163
$webclient_appcds_jvmargs = (Get-SubVal "appcds" "webclient_appcds_jvmargs" "").Replace(
164
                               "{archive}", $webclient_appcds_archive)
165
$webclient_memory = Get-Val "webclient_memory" "128m"
166

  
167
# The libPath node is emitted only when an AppCDS block is configured, as json_template does.
168
if ($null -ne (Get-Node $cfg "appcds"))
169
{
170
   $libpath_rendered = @"
171
          <node class="string" name="libPath">
172
            <node-attribute name="value" value="$client_lib_path"/>
173
          </node>
174
"@
175
}
176
else
177
{
178
   $libpath_rendered = ""
179
}
180

  
181
# Where the directory.xml ends up; a bare file name has no parent, which Join-Path rejects.
182
$dirParent = Split-Path -Path $directory_xml -Parent
183
if ([string]::IsNullOrWhiteSpace($dirParent)) { $dirParent = "." }
184
if (!(Test-Path -LiteralPath $dirParent -PathType Container))
185
{
186
   Write-Error "The directory holding $directory_xml does not exist: $dirParent"
187
   exit 1
188
}
189
$dirfile = Join-Path (Resolve-Path -LiteralPath $dirParent).Path (Split-Path -Path $directory_xml -Leaf)
190

  
191
if ((Test-Path -LiteralPath $dirfile) -and -not $o.IsPresent)
192
{
193
   Write-Error "Output file exists: $dirfile, and '-o' not given."
194
   Show-Usage
195
   exit 1
196
}
197

  
198
# prepare_dir.sh reaches for sudo here, and only here, when the destination is not writable.  There
199
# is no in-place equivalent on Windows, and self-elevating the whole script would restart it in
200
# system32 where none of the relative template paths below resolve, so the condition is reported
201
# instead and the operator re-runs from an elevated prompt.
202
$probe = Join-Path (Split-Path $dirfile -Parent) ".prepare_dir.probe"
203
try
204
{
205
   [System.IO.File]::WriteAllText($probe, "")
206
   Remove-Item -LiteralPath $probe -Force
207
}
208
catch
209
{
210
   Write-Error ("Cannot write to $(Split-Path $dirfile -Parent). Re-run this script from an " +
211
                "elevated prompt, or grant yourself write access to that directory.")
212
   exit 1
213
}
214

  
215
# ---------------------------------------------------------------------------------------------------
216
# server.xml and directory.xml
217
# ---------------------------------------------------------------------------------------------------
218
Expand-Template 'server.xml.template' @{ directory_xml_file = $directory_xml } $server_xml
219

  
220
# Every marker of directory.xml.template is listed. client_start_dir, libPath,
221
# webclient_appcds_jvmargs and webclient_memory used to be absent, so they survived into the
222
# generated directory.xml verbatim. admin_console_pw is deliberately left alone, json_template does
223
# not fill it either.
224
Expand-Template 'directory.xml.template' @{
225
   spawner_path             = $spawner_path
226
   client_start_dir         = $client_start_dir
227
   libPath                  = $libpath_rendered
228
   dateFormat               = (Get-Val "dateFormat" "mdy")
229
   numberGroupSep           = (Get-Val "numberGroupSep" ",")
230
   numberDecimalSep         = (Get-Val "numberDecimalSep" ".")
231
   p2j_entry                = (Get-Val "p2j_entry" "start.p")
232
   pkgroot                  = $pkgroot
233
   propath                  = (Get-Val "propath" ".")
234
   search_path              = (Get-Val "search_path" ".")
235
   path_separator           = (Get-Val "path_separator" ";")
236
   file_separator           = (Get-Val "file_separator" "\")
237
   case_sensitive           = (Get-Val "case_sensitive" "FALSE")
238
   os_user                  = (Get-Val "os_user" $env:USERNAME)
239
   kbd_layout               = (Get-Val "kbd_layout" "US")
240
   dbname                   = $defdatabase
241
   server_log               = (Get-Val "server_log" "../logs")
242
   client_log               = (Get-Val "client_log" "../logs")
243
   embedded_host            = (Get-Val "embedded_host" "localhost")
244
   admin_port               = (Get-Val "admin_port" "7443")
245
   webclient_appcds_jvmargs = $webclient_appcds_jvmargs
246
   webclient_memory         = $webclient_memory
247
} 'directory_tmp.xml'
248

  
249
# ---------------------------------------------------------------------------------------------------
250
# Merge each database into the directory
251
# ---------------------------------------------------------------------------------------------------
252
$dbdialect = @{
253
   h2       = "com.goldencode.p2j.persist.dialect.P2JH2Dialect"
254
   postgres = "com.goldencode.p2j.persist.dialect.P2JPostgreSQLDialect"
255
}
256
$dbdriver = @{
257
   h2       = "org.h2.Driver"
258
   postgres = "org.postgresql.Driver"
259
}
260

  
261
$fwd_lib = if ($env:FWD_LIB) { $env:FWD_LIB } else { "../../p2j" }
262
$p2j_jar = if (Test-Path -LiteralPath "$fwd_lib/build/lib/p2j.jar") { "$fwd_lib/build/lib/p2j.jar" }
263
           elseif (Test-Path -LiteralPath "$fwd_lib/lib/p2j.jar")   { "$fwd_lib/lib/p2j.jar" }
264
           else                                                     { "../../p2j/lib/p2j.jar" }
265

  
266
foreach ($db in $db_array)
267
{
268
   $dbtype = Get-SubVal $db "dbtype" "h2"
269

  
270
   if ($dbtype -eq "none")
271
   {
272
      Copy-Item -LiteralPath 'directory_nodb.xml.template' -Destination 'directory_nodb.xml' -Force
273
      continue
274
   }
275

  
276
   if (-not $dbdialect.ContainsKey($dbtype))
277
   {
278
      throw "Unsupported database type '$dbtype' for '$db'."
279
   }
280

  
281
   $dbhost = Get-SubVal $db "dbhost" "localhost"
282
   $dbport = Get-SubVal $db "dbport" "5432"
283
   $dbpath = Get-SubVal $db "dbpath" "../db"
284
   $pljava = if ($dbtype -eq "h2") { "TRUE" } else { "FALSE" }
285

  
286
   $jdbc_url = if ($dbtype -eq "h2")
287
               {
288
                  "h2:$dbpath/$db;DB_CLOSE_DELAY=-1;MV_STORE=FALSE;RTRIM=TRUE"
289
               }
290
               else
291
               {
292
                  "postgresql://${dbhost}:${dbport}/$db"
293
               }
294

  
295
   $dbfile = "directory_db.xml.$db"
296
   Expand-Template 'directory_db.xml.template' @{
297
      dbhost        = $dbhost
298
      dbport        = $dbport
299
      dbname        = $db
300
      dbuser        = (Get-SubVal $db "dbuser" "fwd_user")
301
      dbuserpass    = (Get-SubVal $db "dbuserpass" "user")
302
      dbadmin       = (Get-SubVal $db "dbadmin" "fwd_admin")
303
      dbadminpass   = (Get-SubVal $db "dbadminpass" "admin")
304
      dbdialect     = $dbdialect[$dbtype]
305
      dbdriver      = $dbdriver[$dbtype]
306
      max_c3p0_pool = (Get-SubVal $db "max_c3p0_pool" "20")
307
      dbpath        = $dbpath
308
      jdbc_url      = $jdbc_url
309
      pljava        = $pljava
310
      collation     = (Get-SubVal $db "collation" "en_US@iso88591_fwd_basic")
311
   } $dbfile
312

  
313
   & java -Xmx256m -classpath $p2j_jar com.goldencode.p2j.directory.DirectoryCopy `
314
          copy $dbfile /server/standard/database/ directory_tmp.xml /server/standard/database/
315
   if ($LASTEXITCODE -ne 0)
316
   {
317
      throw "DirectoryCopy failed for $db with exit code $LASTEXITCODE."
318
   }
319

  
320
   Remove-Item -LiteralPath $dbfile -Force
321
}
322

  
323
Move-Item -LiteralPath 'directory_tmp.xml' -Destination $dirfile -Force
324
Write-Host "Wrote $dirfile"
325

  
326
exit 0
new/deploy/server/server.cmd 2026-08-07 14:04:48 +0000
1 1
:: Standard FWD server startup script - Wrapper to server.ps1
2 2
@echo off
3 3
setlocal
4
pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0server.ps1" %*
5
endlocal
4

  
5
:: PowerShell Core when it is installed, otherwise the Windows PowerShell which is preinstalled with
6
:: Windows.  %%~$PATH:I expands to the full path of the file when it is found on the PATH, and to an
7
:: empty string when it is not.
8
set "psexe="
9
for %%I in (pwsh.exe) do if not "%%~$PATH:I" == "" set "psexe=%%~$PATH:I"
10
if not defined psexe set "psexe=powershell.exe"
11

  
12
"%psexe%" -NoProfile -ExecutionPolicy Bypass -File "%~dp0server.ps1" %*
13
exit /b %ERRORLEVEL%
new/deploy/server/server.ps1 2026-08-07 14:04:48 +0000
126 126
      return
127 127
   }
128 128

  
129
   # Resolve the directory.xml path relative to the config's directory.
129
   # Resolve the directory.xml path relative to the config's directory.  Split-Path returns an empty
130
   # string when the config was found as a bare name in the current directory, which is what happens
131
   # when the server is started from within its own directory; Join-Path rejects that.
130 132
   $dirXmlPath = $dirXmlName
131 133
   if (-not [System.IO.Path]::IsPathRooted($dirXmlPath)) {
132
      $dirXmlPath = Join-Path (Split-Path $cfgPath -Parent) $dirXmlName
134
      $cfgDir = Split-Path $cfgPath -Parent
135
      if ([string]::IsNullOrWhiteSpace($cfgDir)) {
136
         $cfgDir = $PWD.Path  # Use current directory if no parent in path
137
      }
138
      $dirXmlPath = Join-Path $cfgDir $dirXmlName
133 139
   }
134 140

  
135 141
   # Use the same p2j.jar the server resolved (via Get-Classpath) instead of
......
321 327
      '^--gc$' { $psOptions.EnableGC = $true }
322 328
      '^--jmx_port=' { $psOptions.JmxPort = [int]($arg -split '=')[1] }
323 329
      '^--clear_logs' {
324
         $logDirs = $arg -match '=' ? ($arg -split '=')[1] : $default_log_dirs
330
         # an if expression, the ?: ternary operator needs PowerShell 7
331
         $logDirs = if ($arg -match '=') { ($arg -split '=')[1] } else { $default_log_dirs }
325 332
         Clear-Logs $logDirs
326 333
      }
327 334
      default {
new/deploy/server/start_server.cmd 2026-08-07 14:04:48 +0000
4 4
:: Determine script directory
5 5
set scriptdir=%~dp0
6 6

  
7
:: PowerShell Core when it is installed, otherwise the Windows PowerShell which is preinstalled
8
set "psexe="
9
for %%I in (pwsh.exe) do if not "%%~$PATH:I" == "" set "psexe=%%~$PATH:I"
10
if not defined psexe set "psexe=powershell.exe"
11

  
7 12
:: Pass all arguments to PowerShell script
8
pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "%scriptdir%start_server.ps1" %*
13
"%psexe%" -NoProfile -ExecutionPolicy Bypass -File "%scriptdir%start_server.ps1" %*
14
exit /b %ERRORLEVEL%
new/deploy/server/stop_server.cmd 2026-08-07 14:04:48 +0000
4 4
:: Determine script directory
5 5
set scriptdir=%~dp0
6 6

  
7
:: PowerShell Core when it is installed, otherwise the Windows PowerShell which is preinstalled
8
set "psexe="
9
for %%I in (pwsh.exe) do if not "%%~$PATH:I" == "" set "psexe=%%~$PATH:I"
10
if not defined psexe set "psexe=powershell.exe"
11

  
7 12
:: Pass all arguments to PowerShell script
8
pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "%scriptdir%stop_server.ps1" %*
13
"%psexe%" -NoProfile -ExecutionPolicy Bypass -File "%scriptdir%stop_server.ps1" %*
14
exit /b %ERRORLEVEL%
new/hotel.input.windows 2026-08-07 14:04:48 +0000
1 1
hotel
2

  
3

  
2
hotel_gui
3
*.[fhi]
4
*.[pPwW]
4 5
com.goldencode.hotel
5
.;common\;
6
.:common:
7
.;common;
6 8
ADM2.2
7 9
hotel
10
hotel
8 11
h2
12
localhost
13
../db
9 14
fwd_admin
10 15
admin
11 16
fwd_user
12 17
user
13

  
14

  
15

  
16
com.goldencode.hotel.Start
18
en_US@iso88591_fwd_basic
19
20
20
no
21
mdy
22
,
23
.
24
start.p
25
localhost
26
7443
27
directory.xml
28
yes
new/install_spawner.cmd 2026-08-07 14:04:48 +0000
4 4
:: Determine script directory
5 5
set scriptdir=%~dp0
6 6

  
7
:: Pass all arguments to PowerShell script
8
pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "%scriptdir%install_spawner.ps1" %*
7
:: Pass all arguments to PowerShell script, run_powershell.cmd picks the PowerShell which is installed
8
call "%scriptdir%run_powershell.cmd" "%scriptdir%install_spawner.ps1" %*
9
exit /b %ERRORLEVEL%
new/install_spawner.ps1 2026-08-07 14:04:48 +0000
14 14
# Enable strict mode for error handling
15 15
Set-StrictMode -Version Latest
16 16

  
17
# Ensure the script is running with administrative privileges
18
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
19
{
20
   Write-Host "This script requires administrative privileges. Restarting with elevation..."
21
   Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`"" -Verb RunAs
22
   exit
23
}
24

  
25 17
# Important helper to be included for compatibility with bash sourcing of appname
26 18
function Source-AppName
27 19
{
......
45 37

  
46 38
function Show-Usage {
47 39
   $usage = @(
48
      "Usage: $script_name [-a] [-d] [-s<path to place spawner>] [-p<path to FWD>] [-f<path to slf4j-impl.jar>] [-n<path to native spawner>] [-c<path to srv-certs>] [-t<path to postbuild.sh tool>]",
49
      "                [-n <native spawner>] [-c <srv-certs.store path>] [-t <postbuild.sh tool path>]",
40
      "Usage: $script_name [-a] [-d] [-s <path to place spawner>] [-p <path to FWD>] [-f <path to slf4j-impl.jar>] [-n <path to native spawner>] [-c <path to srv-certs>] [-t <path to postbuild.ps1 tool>]",
50 41
      "a = Copy only $appname libraries (prevents co-mingling of application and FWD libraries).",
51 42
      "d = Dry run. Just show commands.",
52 43
      "s = Destination directory to place spawner.exe (def=$fspawn)",
53 44
      "p = Location of FWD libraries p2j.jar and fwd-slf4j.jar (if no -f specified). `"FWD_LIB`" will be used as a fallback if default is not valid. (def=$p2j_lib)",
54 45
      "f = Location of file to be placed in slf4j-impl.jar destination (def=fwd-slf4j.jar in -p location or FWD_LIB)",
55
      "n = Location of built native spawner (def=$spawn_prog), unless -p specified, which will change the default location)",
46
      "n = Location of built native spawner (def=$spawn_prog, unless -p specified, which will change the default location)",
56 47
      "c = Location of srv-certs.store file (def=$srv_certs)",
57
      "t = Location of postbuild.sh tool (def=$postbuild), unless -p specified, which will change the default location)"
48
      "t = Location of postbuild.ps1 tool (def=$postbuild, unless -p specified, which will change the default location)"
58 49
   )
59 50
   $usage | ForEach-Object { Write-Host $_ }
60 51
}
61 52

  
53
# Quotes a value for the PowerShell command line assembled for the privileged step below.  A single
54
# quote is its own escape, so doubling it is all that is needed.
55
function ConvertTo-PSLiteral
56
{
57
   param (
58
      [string]$Value
59
   )
60

  
61
   return "'" + ($Value -replace "'", "''") + "'"
62
}
63

  
64
<#
65
   The counterpart of the "$mysudo" of install_spawner.sh.
66

  
67
   Windows has no sudo, a single command cannot be elevated in place, so the commands which need the
68
   rights are grouped into one child process and that process is started elevated.  Everything else
69
   in this script stays unelevated, exactly as on Linux: elevating the whole script would also run
70
   the jar copies below as an administrator and leave a deployment the developer cannot overwrite on
71
   the next build.
72

  
73
   The child is a separate process even when no elevation is needed, because an "exit" of a script
74
   called in the current session would end this one as well and skip the library copying below.
75

  
76
   An elevated process cannot share this console: UAC always gives it a window of its own, and
77
   -Verb RunAs cannot be combined with -RedirectStandardOutput.  Its output is therefore written to a
78
   file and replayed here, otherwise the window closes as soon as the work is done and nothing can be
79
   read.  The command is handed over with -EncodedCommand, which takes base64 and so needs no
80
   quoting of its own.
81
#>
82
function Invoke-Privileged
83
{
84
   param (
85
      [string]$Command,
86
      [switch]$Elevate
87
   )
88

  
89
   # the interpreter running this script, not a hardcoded "pwsh": PowerShell 7 is not necessarily
90
   # installed, install_spawner.cmd launches the Windows PowerShell 5.1 instead
91
   $psHost = (Get-Process -Id $PID).Path
92

  
93
   if (-not $Elevate)
94
   {
95
      & $psHost -NoProfile -ExecutionPolicy Bypass -Command "& { $Command }; exit `$LASTEXITCODE"
96
      return $LASTEXITCODE
97
   }
98

  
99
   Write-Host "Elevation is required for $fspawn, confirm the prompt to continue..."
100

  
101
   $log = Join-Path ([System.IO.Path]::GetTempPath()) "install_spawner_$PID.log"
102

  
103
   # the braces matter, a redirection binds to the last statement only and the command is a list
104
   $wrapped = "& { $Command } *> $(ConvertTo-PSLiteral $log); exit `$LASTEXITCODE"
105
   $encoded = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($wrapped))
106

  
107
   try
108
   {
109
      $proc = Start-Process -FilePath $psHost `
110
                            -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass',
111
                                            '-EncodedCommand', $encoded) `
112
                            -Verb RunAs -WorkingDirectory $cwd -Wait -PassThru
113
   }
114
   catch
115
   {
116
      # declining the UAC prompt lands here, as does a machine with no interactive desktop
117
      Write-Host "ERROR: the elevated step could not be started: $($_.Exception.Message)"
118
      return 1
119
   }
120

  
121
   if (Test-Path -LiteralPath $log)
122
   {
123
      Get-Content -LiteralPath $log
124
      Remove-Item -LiteralPath $log -Force -ErrorAction SilentlyContinue
125
   }
126

  
127
   return $proc.ExitCode
128
}
129

  
62 130
# Defaults
63 131
$script_name = $MyInvocation.MyCommand.Name
64 132
$wholePath = $MyInvocation.MyCommand.Path
......
74 142
   exit 1
75 143
}
76 144

  
77
# Default paths
78
$pwd = Get-Location
79
$fspawn = "/opt/spawner"
80
$p2j_lib = "$pwd/p2j/build/lib"
81
$deploy_home = "$pwd/deploy"
145
# Default paths.  Note that $pwd is not used to hold the current directory, it is the name of an
146
# automatic PowerShell variable.
147
$cwd = (Get-Location).Path
148
# The Windows counterparts of the /opt/spawner and /opt/fwd-deploy/spawner of install_spawner.sh.
149
$fspawn = Join-Path $env:ProgramData 'FWD\spawner'
150
$fwd_deploy = Join-Path $env:ProgramData 'FWD\fwd-deploy\spawner'
151
$p2j_lib = "$cwd/p2j/build/lib"
152
$deploy_home = "$cwd/deploy"
82 153
$srv_certs = "$deploy_home/server/srv-certs.store"
83
$build_home = "$pwd/build"
84
$p2j_root = "$pwd/p2j"
154
$build_home = "$cwd/build"
155
$p2j_root = "$cwd/p2j"
85 156
$postbuild = "$p2j_root/src/native/postbuild.ps1"
86 157
$spawn_prog = "$p2j_root/build/native/spawn.exe"
87 158
$p2j_passed = $false
......
114 185
}
115 186

  
116 187
# Set defaults when no options passed and spawn/postbuild.ps1 are not found (or bail)
117
if (-not $p2j_passed -and -not $spawn_passed -and !(Test-Path $spawn_prog) -and (Test-Path "/opt/fwd-deploy/spawner/spawn")) {
118
   $spawn_prog = "/opt/fwd-deploy/spawner/spawn.exe"
188
if (-not $p2j_passed -and -not $spawn_passed -and !(Test-Path $spawn_prog) -and (Test-Path "$fwd_deploy/spawn.exe")) {
189
   $spawn_prog = "$fwd_deploy/spawn.exe"
119 190
}
120 191
if (!(Test-Path $spawn_prog)) {
121 192
   Write-Error "Error: $spawn_prog not found. Exiting."
122 193
   exit 1
123 194
}
124
if (-not $p2j_passed -and -not $postbuild_passed -and !(Test-Path $postbuild) -and (Test-Path "/opt/fwd-deploy/spawner/postbuild.sh")) {
125
   $postbuild = "/opt/fwd-deploy/spawner/postbuild.ps1"
195
if (-not $p2j_passed -and -not $postbuild_passed -and !(Test-Path $postbuild) -and (Test-Path "$fwd_deploy/postbuild.ps1")) {
196
   $postbuild = "$fwd_deploy/postbuild.ps1"
126 197
}
127 198
if (!(Test-Path $postbuild)) {
128 199
   Write-Error "Error: $postbuild not found. Exiting."
......
140 211
         $spawn_prog = "$p2j_root/build/native/spawn.exe"
141 212
      }
142 213
      if (-not $postbuild_passed) {
143
         $postbuild = "$p2j_root/src/native/postbuild.sh"
214
         $postbuild = "$p2j_root/src/native/postbuild.ps1"
144 215
      }
145 216
   }
146 217
}
......
150 221
   $slf4j_impl = "$p2j_lib/fwd-slf4j.jar"
151 222
}
152 223

  
153
# Run postbuild
154
$cmdString = "& pwsh -File $postbuild -SpawnDir $fspawn -SpawnProg $spawn_prog -CertFile $srv_certs -FwdLib $p2j_jar"
155
if ($d) { 
156
   Write-Host $cmdString
224
# Elevation is needed only when the destination is outside the user's own profile and this session is
225
# not elevated already, the same test as the
226
#    [[ "$fspawn"* != "$HOME"* ]] && [[ $(whoami) != "root" ]]
227
# of install_spawner.sh.  Installing under -s $env:USERPROFILE\... raises no prompt at all.
228
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
229
           ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
230
$underHome = $fspawn.StartsWith($env:USERPROFILE, [System.StringComparison]::OrdinalIgnoreCase)
231
$needElevation = -not $isAdmin -and -not $underHome
232

  
233
# The privileged step: the commands install_spawner.sh runs under sudo, grouped so that at most one
234
# UAC prompt is raised.  The order differs from install_spawner.sh in one respect, the directory ACL
235
# is applied before postbuild instead of after it: postbuild.ps1 hardens the folder itself and
236
# honours its own -StrictAcl, so it has to have the last word.  Against an older postbuild.ps1 which
237
# leaves the folder alone, the grant below is still what gives the ordinary users their access.
238
$commands = @()
239

  
240
# Create the destination for the spawner, if not already there. postbuild bails out on a missing one,
241
# creating it is the job of this script, as in install_spawner.sh.
242
if (!(Test-Path $fspawn)) {
243
   $commands += "New-Item -Path $(ConvertTo-PSLiteral $fspawn) -ItemType Directory -Force | Out-Null"
244
}
245

  
246
# The equivalent of the "chmod gou+rx $fspawn" of install_spawner.sh, S-1-5-32-545 is the well known
247
# SID of the Users group (the group names are localized, the SIDs are not).
248
$commands += "& icacls $(ConvertTo-PSLiteral $fspawn) /grant '*S-1-5-32-545:(RX)' | Out-Null"
249

  
250
# Position the spawner files
251
$commands += ("& $(ConvertTo-PSLiteral $postbuild)" +
252
              " -SpawnDir $(ConvertTo-PSLiteral $fspawn)" +
253
              " -SpawnProg $(ConvertTo-PSLiteral $spawn_prog)" +
254
              " -CertFile $(ConvertTo-PSLiteral $srv_certs)" +
255
              " -FwdLib $(ConvertTo-PSLiteral $p2j_jar)" +
256
              " -Slf4jImpl $(ConvertTo-PSLiteral $slf4j_impl)")
257

  
258
$privileged = $commands -join '; '
259

  
260
if ($d) {
261
   Write-Host $privileged
157 262
} else {
158
   & pwsh -File $postbuild -SpawnDir $fspawn -SpawnProg $spawn_prog -CertFile $srv_certs -FwdLib $p2j_jar
263
   $rc = Invoke-Privileged -Command $privileged -Elevate:$needElevation
264
   if ($rc -ne 0) {
265
      Write-Host "WARNING! The spawner installation failed with exit code $rc."
266
   }
159 267
}
160 268

  
161 269
# Determine the configured JDK
162 270
#jver is 18 for java 1.8, 15 for java 1.5, 110 for java 11, 170 for java 17 etc.
163
$jver = & java -version 2>&1 | Select-String 'version' | ForEach-Object {
271
# java writes its version banner to stderr, which Windows PowerShell reports as an error record of the
272
# native command; keep the preference relaxed for the call and stringify what the merged streams give.
273
$previousPreference = $ErrorActionPreference
274
$ErrorActionPreference = 'Continue'
275
try {
276
   $javaVersionLines = & java -version 2>&1 | ForEach-Object { "$_" }
277
}
278
finally {
279
   $ErrorActionPreference = $previousPreference
280
}
281
$jver = $javaVersionLines | Select-String 'version' | ForEach-Object {
164 282
   if ($_ -match '"(\d+\.\d+|\d+)"') {
165 283
      $v = $matches[1]
166 284
      $parts = $v -split '\.'
167
      if ($parts.Length -eq 1) { return "$parts[0]0" }
285
      if ($parts.Length -eq 1) { return "$($parts[0])0" }
168 286
      return "$($parts[0])$($parts[1])"
169 287
   }
170 288
}
......
172 290

  
173 291
# Copy the application jar and the collation jar specifically to the deployment location
174 292
if (-not (Test-Path $spidir)) {
175
   if ($d) { Write-Host "New-Item -Path $spidir -ItemType Directory" } else { New-Item -Path $spidir -ItemType Directory | Out-Null }
293
   if ($d) { Write-Host "New-Item -Path $spidir -ItemType Directory -Force" } else { New-Item -Path $spidir -ItemType Directory -Force | Out-Null }
176 294
}
177 295
$appJar = "$build_home/lib/$appname.jar"
178 296
$fwdSpiJar = "$p2j_lib/fwdspi.jar"
179 297
if ($d) {
180 298
   Write-Host "Copy-Item $appJar $deploy_home/lib -Force"
181
   Write-Host "$fwdSpiJar $spidir -Force"
299
   Write-Host "Copy-Item $fwdSpiJar $spidir -Force"
182 300
} else {
183 301
   Copy-Item $appJar "$deploy_home/lib" -Force
184 302
   Copy-Item $fwdSpiJar $spidir -Force
185 303
}
186 304
# Copy the rest of the library files (except aspectjtools.jar and fwdspi.jar) to the deployment location, unless -a is included.
187 305
if (-not $a) {
188
   Get-ChildItem $p2j_lib -Include *.jar,*.zip -Recurse |
306
   Get-ChildItem $p2j_lib -Include *.jar,*.zip -Recurse -File |
189 307
   Where-Object { $_.Name -notmatch 'aspectjtools\.jar|fwdspi\.jar' } |
190 308
   ForEach-Object {
191 309
      if ($d) {
192
         Write-Host "Copy-Item $_.FullName $deploy_home/lib -Force"
310
         Write-Host "Copy-Item $($_.FullName) $deploy_home/lib -Force"
193 311
      } else {
194 312
         Copy-Item $_.FullName "$deploy_home/lib" -Force
195 313
      }
new/json_template.cmd 2026-08-07 11:54:43 +0000
1
@echo off
2
::
3
:: Wrapper for json_template.ps1, the counterpart of json_template.sh.
4
::
5
:: Run this from the project root, the script resolves its templates relative to the current
6
:: directory exactly as the shell version does.
7
::
8
set scriptdir=%~dp0
9

  
10
call "%scriptdir%run_powershell.cmd" "%scriptdir%json_template.ps1" %*
11
exit /b %ERRORLEVEL%
new/json_template.ps1 2026-08-07 12:11:51 +0000
1
#
2
# The Windows counterpart of json_template.sh, kept in sync with it: takes input from
3
# prepare_template.json and performs configuration of build.properties, zfile_set.txt, p2j.cfg.xml,
4
# deploy/server/server.xml and the directory.xml file (which can be located anywhere, as it is
5
# specified in the json file).
6
#
7
# Substitution is done with a literal String.Replace and not with the -replace operator: -replace is
8
# a regular expression on both sides, so a value holding a $ or a \ would be mangled - a database
9
# password is the obvious way to hit that.
10
#
11
param (
12
   [switch]$a,
13
   [switch]$d,
14
   [switch]$o,
15
   [switch]$n,
16
   [string]$f
17
)
18

  
19
Set-PSDebug -Trace 0
20
Set-StrictMode -Version Latest
21
$ErrorActionPreference = 'Stop'
22

  
23
function Show-Usage
24
{
25
   Write-Host "Usage: json_template.ps1 [-a] [-d] [-o] [-f <json_file>]"
26
   Write-Host "Takes input from prepare_template.json and performs configuration of build.properties,"
27
   Write-Host "p2j.cfg.xml, deploy/server/server.xml, and the directory.xml file."
28
   Write-Host "Where:"
29
   Write-Host "   a = Only performing Analytics"
30
   Write-Host "   d = Generate deploy/server/prepare_dir.json output file to be used by prepare_dir.ps1"
31
   Write-Host "   o = Overwrite the specified directory.xml, if it already exists. Otherwise, leave it alone"
32
   Write-Host "   f = Use <json_file> instead of prepare_template.json as input."
33
}
34

  
35
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
36

  
37
# Property lookup which tolerates a missing key under Set-StrictMode; $cfg.$Key would throw.
38
function Get-Node
39
{
40
   param (
41
      $Object,
42
      [string]$Key
43
   )
44

  
45
   if ($null -eq $Object) { return $null }
46

  
47
   $prop = $Object.PSObject.Properties[$Key]
48
   if ($null -eq $prop) { return $null }
49

  
50
   return $prop.Value
51
}
52

  
53
function Get-Val
54
{
55
   param (
56
      [string]$Key,
57
      [string]$Default = ""
58
   )
59

  
60
   $val = Get-Node $script:cfg $Key
61
   if ($null -eq $val -or "$val" -eq "") { return $Default }
62

  
63
   return "$val"
64
}
65

  
66
# The per-database values live in a sub-object named after the database, so the lookup is two
67
# levels.  Note prepare_dir.ps1 used to pass "<db>.<key>" as a single property name, which never
68
# resolved and silently handed back every default.
69
function Get-SubVal
70
{
71
   param (
72
      [string]$SubName,
73
      [string]$Key,
74
      [string]$Default = ""
75
   )
76

  
77
   $node = Get-Node $script:cfg $SubName
78
   $val = Get-Node $node $Key
79
   if ($null -eq $val -or "$val" -eq "") { return $Default }
80

  
81
   return "$val"
82
}
83

  
84
# Replaces every {key} of a template with its value and writes the result.
85
function Expand-Template
86
{
87
   param (
88
      [string]$Template,
89
      [hashtable]$Values,
90
      [string]$Destination
91
   )
92

  
93
   if (!(Test-Path -LiteralPath $Template -PathType Leaf))
94
   {
95
      throw "Could not create $Destination, the template $Template was not found."
96
   }
97

  
98
   $content = [System.IO.File]::ReadAllText($Template)
99

  
100
   foreach ($key in $Values.Keys)
101
   {
102
      $content = $content.Replace("{$key}", [string]$Values[$key])
103
   }
104

  
105
   [System.IO.File]::WriteAllText($Destination, $content, $utf8NoBom)
106
}
107

  
108
# Splits a PROPATH answer.  Both spellings are accepted so the same answer file works either way:
109
# a ';' separated list is split on ';' (which also keeps a "C:\..." drive letter intact), anything
110
# else is split on ':'.
111
function Split-PropathValue
112
{
113
   param (
114
      [string]$Value
115
   )
116

  
117
   $sep = if ($Value.Contains(';')) { ';' } else { ':' }
118

  
119
   return @($Value -split [regex]::Escape($sep) | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" })
120
}
121

  
122
if ($args -contains "-?" -or $args -contains "/?" -or $args -contains "-h")
123
{
124
   Show-Usage
125
   exit 1
126
}
127

  
128
$analytics = $a.IsPresent
129
$overwrite = $o.IsPresent
130
$json_file = if ($f) { $f } else { "prepare_template.json" }
131
$prepare_dir = if ($d) { "deploy/server/prepare_dir.json" } else { "" }
132

  
133
if (!(Test-Path -LiteralPath $json_file -PathType Leaf))
134
{
135
   Write-Error "Input file not found: $json_file"
136
   Show-Usage
137
   exit 1
138
}
139

  
140
$root = (Get-Location).Path
141

  
142
# System.IO and every native command resolve a relative path against the PROCESS working directory,
143
# which PowerShell's own location does not track: Push-Location moves the latter and leaves the
144
# former where the script started.  The two are kept in step here and around the Push-Location
145
# further down, otherwise the templates under deploy/server are looked for in the project root and
146
# the files written there land in the root as well.
147
[Environment]::CurrentDirectory = $root
148

  
149
$cfg = [System.IO.File]::ReadAllText($json_file) | ConvertFrom-Json
150

  
151
# ---------------------------------------------------------------------------------------------------
152
# Values taken straight from the json
153
# ---------------------------------------------------------------------------------------------------
154
$appname = Get-Val "appname"
155
$projname = Get-Val "projname" $appname
156
$include_spec = Get-Val "include_spec" "*.[fhi]"
157
$program_spec = Get-Val "program_spec" "*.[pPwW]"
158
$pkgroot = if ($analytics) { "com.goldencode.$appname" } else { Get-Val "pkgroot" "com.goldencode.$appname" }
159
$propath_bld = Get-Val "propath_bld" "."
160
$propath = Get-Val "propath" "."
161
$admversion = Get-Val "admversion" "ADM2.2"
162
$dbnames = Get-Val "dbnames"
163
$embedded_host = Get-Val "embedded_host" "localhost"
164
$admin_port = Get-Val "admin_port" "7443"
165

  
166
if ($analytics)
167
{
168
   $win_proj = "no"
169
   $dateFormat = "mdy"
170
   $numberGroupSep = ","
171
   $numberDecimalSep = "."
... This diff was truncated because it exceeds the maximum size that can be displayed.