| | | 1 | | <# |
| | | 2 | | .SYNOPSIS |
| | | 3 | | Imports a PowerShell object from a serialized XML representation. |
| | | 4 | | |
| | | 5 | | .DESCRIPTION |
| | | 6 | | Import-KrSharedState deserializes a PowerShell object that was previously |
| | | 7 | | created by Export-KrSharedState. The serialized content can come from a |
| | | 8 | | string, a byte array, or a file. |
| | | 9 | | |
| | | 10 | | Access is synchronized through a shared lock so that callers using the |
| | | 11 | | same lock deserialize shared state in a thread-safe manner within the |
| | | 12 | | current process. |
| | | 13 | | |
| | | 14 | | .PARAMETER InputString |
| | | 15 | | The serialized XML content as a string. |
| | | 16 | | If the string is null or empty, Import-KrSharedState returns null. |
| | | 17 | | |
| | | 18 | | .PARAMETER InputBytes |
| | | 19 | | The serialized XML content as a byte array. |
| | | 20 | | If the byte array is null or empty, Import-KrSharedState returns null. |
| | | 21 | | |
| | | 22 | | .PARAMETER Path |
| | | 23 | | The file path containing the serialized XML content. |
| | | 24 | | If the path is null, empty, or whitespace, Import-KrSharedState returns null. |
| | | 25 | | If the file does not exist, an error is thrown. |
| | | 26 | | |
| | | 27 | | .PARAMETER Lock |
| | | 28 | | The semaphore used to synchronize access to shared state. If not provided, |
| | | 29 | | the default shared-state lock is used. |
| | | 30 | | |
| | | 31 | | .PARAMETER TimeoutMilliseconds |
| | | 32 | | The maximum time to wait for the lock. Use -1 to wait indefinitely. |
| | | 33 | | |
| | | 34 | | .PARAMETER Encoding |
| | | 35 | | The text encoding used when converting bytes to text or reading from a file. |
| | | 36 | | |
| | | 37 | | .EXAMPLE |
| | | 38 | | $state = Import-KrSharedState -InputString $xml |
| | | 39 | | |
| | | 40 | | .EXAMPLE |
| | | 41 | | $state = Import-KrSharedState -InputBytes $bytes |
| | | 42 | | |
| | | 43 | | .EXAMPLE |
| | | 44 | | $state = Import-KrSharedState -Path '.\state.xml' |
| | | 45 | | |
| | | 46 | | .EXAMPLE |
| | | 47 | | $lock = Get-KrLock 'sharedstate:cache' |
| | | 48 | | $state = Import-KrSharedState -InputString $xml -Lock $lock |
| | | 49 | | #> |
| | | 50 | | function Import-KrSharedState { |
| | | 51 | | [KestrunRuntimeApi('Everywhere')] |
| | | 52 | | [CmdletBinding(DefaultParameterSetName = 'FromString')] |
| | | 53 | | param( |
| | | 54 | | [Parameter(ParameterSetName = 'FromString', Mandatory, Position = 0)] |
| | | 55 | | [AllowEmptyString()] |
| | | 56 | | [string]$InputString, |
| | | 57 | | |
| | | 58 | | [Parameter(ParameterSetName = 'FromBytes', Mandatory, Position = 0)] |
| | | 59 | | [byte[]]$InputBytes, |
| | | 60 | | |
| | | 61 | | [Parameter(ParameterSetName = 'FromFile', Mandatory, Position = 0)] |
| | | 62 | | [string]$Path, |
| | | 63 | | |
| | | 64 | | [Parameter()] |
| | | 65 | | [System.Threading.SemaphoreSlim]$Lock, |
| | | 66 | | |
| | | 67 | | [Parameter()] |
| | | 68 | | [int]$TimeoutMilliseconds = 30000, |
| | | 69 | | |
| | | 70 | | [Parameter()] |
| | | 71 | | [System.Text.Encoding]$Encoding = [System.Text.Encoding]::UTF8 |
| | | 72 | | ) |
| | | 73 | | |
| | 1 | 74 | | $stateLock = $null |
| | 1 | 75 | | $lockTaken = $false |
| | | 76 | | |
| | | 77 | | try { |
| | | 78 | | # Resolve lock |
| | 2 | 79 | | $stateLock = ($Lock)? $Lock : [Kestrun.Utilities.KestrunLockRegistry]::Default |
| | | 80 | | |
| | | 81 | | # Acquire |
| | 1 | 82 | | if ($TimeoutMilliseconds -lt 0) { |
| | 0 | 83 | | $stateLock.Wait() |
| | 0 | 84 | | $lockTaken = $true |
| | | 85 | | } else { |
| | 1 | 86 | | $lockTaken = $stateLock.Wait($TimeoutMilliseconds) |
| | 1 | 87 | | if (-not $lockTaken) { |
| | 1 | 88 | | throw 'Timeout waiting for shared state lock.' |
| | | 89 | | } |
| | | 90 | | } |
| | | 91 | | |
| | 1 | 92 | | switch ($PSCmdlet.ParameterSetName) { |
| | | 93 | | 'FromString' { |
| | 1 | 94 | | if ([string]::IsNullOrEmpty($InputString)) { |
| | 0 | 95 | | return $null |
| | | 96 | | } |
| | | 97 | | |
| | 1 | 98 | | return [System.Management.Automation.PSSerializer]::Deserialize($InputString) |
| | | 99 | | } |
| | | 100 | | |
| | | 101 | | 'FromBytes' { |
| | 1 | 102 | | if ($null -eq $InputBytes -or $InputBytes.Length -eq 0) { |
| | 0 | 103 | | return $null |
| | | 104 | | } |
| | | 105 | | |
| | 1 | 106 | | $xml = $Encoding.GetString($InputBytes) |
| | 1 | 107 | | return [System.Management.Automation.PSSerializer]::Deserialize($xml) |
| | | 108 | | } |
| | | 109 | | |
| | | 110 | | 'FromFile' { |
| | 1 | 111 | | if ([string]::IsNullOrWhiteSpace($Path)) { |
| | 0 | 112 | | return $null |
| | | 113 | | } |
| | | 114 | | |
| | 1 | 115 | | $fullPath = [System.IO.Path]::GetFullPath($Path) |
| | | 116 | | |
| | 2 | 117 | | if (-not (Test-Path -LiteralPath $fullPath)) { |
| | 1 | 118 | | throw "File not found: $fullPath" |
| | | 119 | | } |
| | | 120 | | |
| | 1 | 121 | | $xml = [System.IO.File]::ReadAllText($fullPath, $Encoding) |
| | 1 | 122 | | return [System.Management.Automation.PSSerializer]::Deserialize($xml) |
| | | 123 | | } |
| | | 124 | | } |
| | | 125 | | } finally { |
| | 1 | 126 | | if ($stateLock -and $lockTaken) { |
| | | 127 | | try { |
| | 1 | 128 | | $null = $stateLock.Release() |
| | | 129 | | } catch { |
| | 0 | 130 | | Write-KrLog -Level Verbose -Message 'Failed to release shared state lock' -ErrorRecord $_ |
| | | 131 | | } |
| | | 132 | | } |
| | | 133 | | } |
| | | 134 | | } |