MyJavaManager.psm1
# # Script module for module 'MyJavaManager' # #Requires -Version 5.1 #using namespace System.Management.Automation #region Classes class PathFormat { static [string] NormalizePath([string] $Path) { <# .Description Format a given string to a normalized path. #> $Path_ = $Path $Path_ = [System.IO.Path]::GetFullPath($Path_) $Path_ = $Path_.TrimEnd([System.IO.Path]::DirectorySeparatorChar) return $Path_ } } class InventoryEntry { [string] $Name [string] $Path InventoryEntry([string] $Name, [string] $Path) { $this.Name = $Name $this.Path = [PathFormat]::NormalizePath($Path) } } class Inventory { [string] $Path = (Join-Path -Path $HOME -ChildPath "MyJavaManager.json") [string] $Encoding = "UTF8" [InventoryEntry[]] $JavaEntries [InventoryEntry] GetJavaEntry([string] $Name) { <# .Description Get a Java entry from the inventory. #> return $this.JavaEntries | Where-Object -Property Name -EQ $Name } [bool] JavaEntryExists([string] $Name) { <# .Description Test if a Java entry exists in the inventory. #> return $this.GetJavaEntry($Name).Count -gt 0 } [void] AddJavaEntry([string] $Name, [string] $Path) { <# .Description Add a Java entry to the inventory. #> $this.JavaEntries += New-Object -TypeName InventoryEntry -ArgumentList $Name, $Path Write-Debug -Message "Java entry `"$Name`" has been added to the inventory." } [void] RemoveJavaEntry([string] $Name) { <# .Description Remove a Java entry from the inventory. #> $this.JavaEntries = $this.JavaEntries | Where-Object -Property Name -NE $Name Write-Debug -Message "Java entry `"$Name`" has been removed from the inventory." } [bool] FileExists() { <# .Description Test if the inventory file exists. #> return Test-Path -Path $this.Path -PathType Leaf } [void] ReadFile() { <# .Description Read Java entries from inventory file. #> Write-Debug -Message "Start to read the inventory file." # Get content of the inventory file $GetContentParams = @{ Path = $this.Path Encoding = $this.Encoding } try { $Content = Get-Content @GetContentParams -ErrorAction Stop } catch { throw "Cannot get content of the inventory file: {0}" -f $_.Exception.Message } # Convert from JSON $Items = $Content | ConvertFrom-Json # Re-initialize JavaEntries array $this.JavaEntries = @() # Add JavaEntry objects to JavaEntries array foreach ($Item in $Items) { $this.AddJavaEntry($Item.Name, $Item.Path) } Write-Debug -Message "The inventory file has been read." } [void] SaveFile() { <# .Description Save inventory in file. #> Write-Debug -Message "Start to save the inventory file." # Set content of the inventory file $SetContentParams = @{ Path = $this.Path Value = ConvertTo-Json -InputObject @($this.JavaEntries) Encoding = $this.Encoding } try { Set-Content @SetContentParams -ErrorAction Stop } catch { throw "Cannot set content of the inventory file: {0}" -f $_.Exception.Message } Write-Debug -Message "The inventory file has been saved." } } class AdoptiumApi { # API documentation: https://api.adoptium.net/q/swagger-ui/ static [int[]] GetAvailableFeatureVersions() { <# .Description Get the available feature versions of Java. #> $RestMethodParams = @{ Method = "Get" Uri = "https://api.adoptium.net/v3/info/available_releases" } $RestMethodResult = Invoke-RestMethod @RestMethodParams return $RestMethodResult.available_releases } static [object] GetLatestAsset([int] $FeatureVersion) { <# .Description Get the latest assets of a Java feature version. #> $RestMethodParams = @{ Method = "Get" Uri = "https://api.adoptium.net/v3/assets/latest/$FeatureVersion/hotspot" } $RestMethodResult = Invoke-RestMethod @RestMethodParams return $RestMethodResult | Where-Object -FilterScript { $_.binary.architecture -eq "x64" -and $_.binary.heap_size -eq "normal" -and $_.binary.image_type -eq "jdk" -and $_.binary.jvm_impl -eq "hotspot" -and $_.binary.os -eq "windows" -and $_.binary.project -eq "jdk" } | Select-Object -First 1 } } class AdoptiumPackage { [string] $Name [string] $Uri [string] $Checksum AdoptiumPackage([int] $FeatureVersion) { $Asset = [AdoptiumApi]::GetLatestAsset($FeatureVersion) $this.Name = $Asset.release_name $this.Uri = $Asset.binary.package.link $this.Checksum = $Asset.binary.package.checksum } [System.IO.FileSystemInfo] Download($DestinationDirectory) { <# .Description Download and expand a Java package from Adoptium to a destination directory. #> $TempGuid = New-Guid $TempDirectory = Join-Path -Path $env:TEMP -ChildPath "java_$TempGuid" $DownloadDestinationArchive = Join-Path -Path $TempDirectory -ChildPath "package.zip" try { # Create temporary directory New-Item -Path $TempDirectory -ItemType Directory | Out-Null Write-Debug -Message "Temporary directory created `"$TempDirectory`"." # Download archive $WebClient = New-Object -TypeName System.Net.WebClient $WebClient.DownloadFile($this.Uri, $DownloadDestinationArchive) Write-Debug -Message ("Java archive `"$DownloadDestinationArchive`" downloaded from `"{0}`"." -f $this.Uri) # Validate checksum if ((Get-FileHash -Path $DownloadDestinationArchive).Hash -eq $this.Checksum) { Write-Debug -Message "Downloaded file `"$DownloadDestinationArchive`" checksum matches to the public one." } else { throw "Downloaded file `"$DownloadDestinationArchive`" checksum does not match to the public one." } # Expand archive Expand-Archive -Path $DownloadDestinationArchive -DestinationPath $TempDirectory Write-Debug -Message "Java archive `"$DownloadDestinationArchive`" extracted to `"$TempDirectory`"." # Move to destination directory $SourcePath = Join-Path -Path $TempDirectory -ChildPath $this.Name $DestinationPath = Join-Path -Path $DestinationDirectory -ChildPath $this.Name if (Test-Path -Path $DestinationPath) { throw "Package `"{0}`" already exists in destination directory `"{1}`"." -f $this.Name, $DestinationDirectory } $Item = Move-Item -Path $SourcePath -Destination $DestinationPath -Force -PassThru Write-Debug -Message "Java package `"$SourcePath`" moved to `"$DestinationPath`"." } catch { throw "Cannot download Java package: {0}" -f $_.Exception.Message } finally { Remove-Item -Path $TempDirectory -Recurse -Force Write-Debug -Message "Temporary directory removed `"$TempDirectory`"." } return $Item } } #endregion Classes #region Private functions function Export-BatchPathsToEnvPath { <# .SYNOPSIS Export paths including Batch variables to Path environment variable. .DESCRIPTION This function uses a Microsoft.Win32.Registry method to write the value of the Path environment variable to the Windows Registry. The motivation is to keep the batch variables in the value. This is only available for the Machine and User target scopes. .PARAMETER Paths Array of paths to export to the Path environment variable. .PARAMETER Target Target scope of the environment variable. .INPUTS None. You cannot pipe objects to Export-BatchPathsToEnvPath. .OUTPUTS System.Void. None. .EXAMPLE PS> Export-BatchPathsToEnvPath -Value $ArrayOfPaths -Target Machine Set Path environment variables with the given paths. .LINK - Batch "Windows Environment Variables" documentation: https://ss64.com/nt/syntax-variables.html - PowerShell "working with registry entries" documentation: https://docs.microsoft.com/en-us/powershell/scripting/samples/working-with-registry-entries #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Mandatory = $true, HelpMessage = "Array of paths to set in the Path environment variable" )] [string[]] $Paths, [Parameter( Mandatory = $true, HelpMessage = "Target scope of the environment variable" )] [ValidateSet("Machine", "User")] [string] $Target ) process { $Value = $Paths -join [System.IO.Path]::PathSeparator if ($PSCmdlet.ShouldProcess("Path environment variable in '$Target' scope", "Set value to '$Value'")) { $KeyName = switch ($Target) { "Machine" { "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path" } "User" { "HKEY_CURRENT_USER\Environment" } } [Microsoft.Win32.Registry]::SetValue($KeyName, "Path", $Value, [Microsoft.Win32.RegistryValueKind]::ExpandString) Write-Debug -Message "New value of the Path environement variable in $Target scope: `"$Value`"." } } } function Import-BatchPathsFromEnvPath { <# .SYNOPSIS Imports paths including Batch variables from Path environment variable. .DESCRIPTION This function uses an WshShell Object to read the value of the Path environment variable from the Windows Registry. The motivation is to keep the batch variables in the value. This is only available for the Machine and User target scopes. .PARAMETER Target Target scope of the environment variable. .INPUTS None. You cannot pipe objects to Import-BatchPathsFromEnvPath. .OUTPUTS System.String[]. Array of Batch paths. .EXAMPLE PS> Import-BatchPathsFromEnvPath -Target Machine C:\windows %SystemRoot%\system32 %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\ Returns an array which contains every path currently set in the Path environment variable. .LINK - Batch "Windows Environment Variables" documentation: https://ss64.com/nt/syntax-variables.html - PowerShell "working with registry entries" documentation: https://docs.microsoft.com/en-us/powershell/scripting/samples/working-with-registry-entries #> [OutputType([string[]])] [CmdletBinding()] param ( [Parameter( Mandatory = $true, HelpMessage = "Target scope of the environment variable" )] [ValidateSet("Machine", "User")] [string] $Target ) process { $WScriptShell = New-Object -ComObject WScript.Shell $Value = switch ($Target) { "Machine" { $WScriptShell.RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path") } "User" { $WScriptShell.RegRead("HKEY_CURRENT_USER\Environment\Path") } } Write-Debug -Message "Current value of the Path environement variable in $Target scope: `"$Value`"." $Value -split [System.IO.Path]::PathSeparator | Where-Object -FilterScript { $_ } | Select-Object -Unique } } function Send-WMSettingChangeMessage { <# .SYNOPSIS Send WM_SETTINGCHANGE message to apply environment changes. .DESCRIPTION Send WM_SETTINGCHANGE message to all top-level windows in the system to apply environment changes. .INPUTS None. You cannot pipe objects to Send-WMSettingChangeMessage. .OUTPUTS System.Void. None. .EXAMPLE PS> Send-WMSettingChangeMessage Send message to all windows in the system to apply environment changes. .LINK - "WM_SETTINGCHANGE message" documentation: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-settingchange #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param() begin { # Import SendMessageTimeout from Win32 if (-not ("Win32.NativeMethods" -as [Type])) { Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @' [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr SendMessageTimeout( IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult); '@ } } process { # Set SendMessageTimeout arguments to send WM_SETTINGCHANGE message [IntPtr] $HWnd = 0xffff [uint] $Msg = 0x1a # WM_SETTINGCHANGE flag [UIntPtr] $LpdwResult = [UIntPtr]::Zero if ($PSCmdlet.ShouldProcess("Send WM_SETTINGCHANGE message to all top-level windows")) { $ReturnedValue = [Win32.NativeMethods]::SendMessageTimeout( $HWnd, $Msg, [UIntPtr]::Zero, "Environment", 2, 5000, [ref] $LpdwResult ) if ($ReturnedValue -ne 0) { # The function succeeded Write-Debug -Message "Environment changes have been successfully applied to all top-level windows." } else { # The function failed or timed out Write-Debug -Message "Environment changes have not been applied to all top-level windows." } } } } function Update-EnvPath { <# .SYNOPSIS Update the Path environment variable. .DESCRIPTION Update the Path environment variable in the current session. .INPUTS None. You cannot pipe objects to Update-EnvPath. .OUTPUTS System.Void. None. .EXAMPLE PS> Update-EnvPath Update the Path environment variable. #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param () begin { $Separator = [System.IO.Path]::PathSeparator } process { if ($PSCmdlet.ShouldProcess("Update the Path environment variable in the current session")) { $Paths = "Machine", "User" | ForEach-Object -Process { [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::"$_") -split $Separator | Where-Object -FilterScript { $_ } | Select-Object -Unique } $env:Path = $Paths -join $Separator Write-Debug "Path environment variable has been updated in the current session." } } } function Use-InteractiveSelectionMenu { <# .SYNOPSIS Use interactive selection menu. .DESCRIPTION Use interactive selection menu with given items inside the PowerShell console. .PARAMETER Items List of items to add in the menu. .PARAMETER Header Menu header. .INPUTS None. You cannot pipe objects to Use-InteractiveSelectionMenu. .OUTPUTS System.String. The selected item. .EXAMPLE PS> Use-InteractiveSelectionMenu -Items @("item1", "item2", "item3") Select item: > item1 item2 item3 Show interactive menu to select an item. .EXAMPLE PS> Use-InteractiveSelectionMenu -Items @("coffee", "tea") -Header "Pick your hot beverages" Pick your hot beverages > coffee tea Show interactive menu to select an item. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "", Scope = "Function", Target = "*")] [OutputType([string])] [CmdletBinding()] param ( [Parameter( Mandatory = $true, HelpMessage = "List of items to add in the menu" )] [array] $Items, [Parameter( HelpMessage = "Menu header" )] [string] $Header = "Select item:" ) process { $FirstPrint = $true $Position = 0 $Entered = $false $Escaped = $false if ($Items.Length -gt 0) { try { [System.Console]::CursorVisible = $false Write-Host $Header -ForegroundColor Gray while (-not $Entered -and -not $Escaped) { if (-not $FirstPrint) { $Host.UI.RawUI.FlushInputBuffer() $Key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") switch ($Key.VirtualKeyCode) { <# Key mapping 38 = UP ARROW 75 = K #> { $_ -in 38, 75 -and $Position -gt 0 } { $Position-- } <# Key mapping 40 = DOWN ARROW 74 = J #> { $_ -in 40, 74 -and $Position -lt $Items.Length - 1 } { $Position++ } <# Key mapping 33 = PAGE UP 36 = HOME #> { $_ -in 33, 36 } { $Position = 0 } <# Key mapping 34 = PAGE DOWN 35 = END #> { $_ -in 34, 35 } { $Position = $Items.Length - 1 } <# Key mapping 13 = ENTER #> 13 { $Entered = $true } <# Key mapping 27 = ESC #> 27 { $Escaped = $true } } $StartPosition = [System.Console]::CursorTop - $Items.Length [System.Console]::SetCursorPosition(0, $StartPosition) } else { $FirstPrint = $false } # Print to console for ($i = 0; $i -lt $Items.Length; $i++) { if ($i -eq $Position) { Write-Host ">" $Items[$i] -ForegroundColor Yellow } else { Write-Host " " $Items[$i] } } } } finally { [System.Console]::SetCursorPosition(0, $StartPosition + $Items.Length) [System.Console]::CursorVisible = $true } } if ($Entered) { $Items[$Position] } else { $null } } } function Get-JavaVersion { <# .SYNOPSIS Get Java version. .DESCRIPTION Get version of a Java application. .PARAMETER Path Path to the Java home directory. .INPUTS None. You cannot pipe objects to Get-JavaVersion. .OUTPUTS System.String. Version of the Java application. .EXAMPLE PS> Get-JavaVersion -Path C:\path\to\java_home_directory jdk-11.0.13.0 Get version of the Java application. #> [OutputType([string])] [CmdletBinding()] param ( [Parameter( Mandatory = $true, HelpMessage = "Path to the Java home directory" )] [string] $Path ) process { $JavaApplicatonPath = Join-Path -Path $Path -ChildPath "bin\java.exe" $JavaCompilerPath = Join-Path -Path $Path -ChildPath "bin\javac.exe" try { $Version = (Get-Command -Name $JavaApplicatonPath -ErrorAction Stop).Version.ToString() Write-Debug -Message "Version of Java application `"$JavaApplicatonPath`" is `"$Version`"." } catch { throw "Cannot get the version of Java application." } if (Get-Command -Name $JavaCompilerPath -ErrorAction SilentlyContinue) { $Type = "jdk" Write-Debug -Message "Java compiler `"$JavaCompilerPath`" exists. Assuming it is JDK (Java Development Kit)." } else { $Type = "jre" Write-Debug -Message "Java compiler `"$JavaCompilerPath`" does not exist. Assuming it is JRE (Java Runtime Environment)." } "{0}-{1}" -f $Type, $Version } } function Set-EnvJavaHome { <# .SYNOPSIS Set JAVA_HOME environment variable. .DESCRIPTION Set JAVA_HOME environment variable in a target scope. .PARAMETER Path Path to the Java home directory. .PARAMETER Target Target scope of the environment variable. .INPUTS None. You cannot pipe objects to Set-EnvJavaHome. .OUTPUTS System.Void. None. .EXAMPLE PS> Set-EnvJavaHome -Path C:\path\to\java_home_directory -Target User Set JAVA_HOME environment variable with given path in user scope. .NOTES BUG REG_EXPAND_SZ environment variables not properly expanded for shells (related Github issue: https://github.com/microsoft/terminal/issues/9741) #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Mandatory = $true, HelpMessage = "Path to the Java home directory" )] [string] $Path, [Parameter( Mandatory = $true, HelpMessage = "Target scope of the environment variable" )] [ValidateSet("Machine", "User", "Process")] [string] $Target ) process { $NormalizedPath = [PathFormat]::NormalizePath($Path) $PreviousValue = $env:JAVA_HOME if ($PSCmdlet.ShouldProcess("JAVA_HOME environment variable in '$Target' scope", "Set value to '$NormalizedPath'")) { if ($Target -ne "Process") { try { [System.Environment]::SetEnvironmentVariable("JAVA_HOME", $NormalizedPath, [System.EnvironmentVariableTarget]::"$Target") Write-Debug -Message "JAVA_HOME environment variable in $Target scope has been set to `"$NormalizedPath`"." } catch { throw "Cannot set JAVA_HOME environment variable in $Target scope: {0}" -f $_.Exception.Message } } $env:JAVA_HOME = $NormalizedPath } if ($Target -ne "Process") { $BatchPaths = Import-BatchPathsFromEnvPath -Target $Target if ($BatchPaths -notcontains "%JAVA_HOME%\bin") { if ($PSCmdlet.ShouldProcess("Path environment variable in '$Target' scope", "Add path '%JAVA_HOME%\bin'")) { $BatchPaths += "%JAVA_HOME%\bin" try { Export-BatchPathsToEnvPath -Paths $BatchPaths -Target $Target Write-Debug -Message "`"%JAVA_HOME%\bin`" has been added to the Path environment variable in $Target scope." } catch { throw "Cannot add `"%JAVA_HOME%\bin`" to Path environment variable in $Target scope: {0}" -f $_.Exception.Message } Send-WMSettingChangeMessage Update-EnvPathProcess } } else { Write-Debug -Message "`"%JAVA_HOME%\bin`" is already present in the Path environment variable in $Target scope." } } if ($PreviousValue) { $env:Path = $env:Path.Replace($PreviousValue, $NormalizedPath) Write-Debug -Message "JAVA_HOME path from the Path environment variable in Process scope has been updated to `"$NormalizedPath`"." } else { if ($Target -ne "Process") { $env:Path = $env:Path.Replace("%JAVA_HOME%", $NormalizedPath) Write-Debug -Message "`"%JAVA_HOME%`" from the Path environment variable in Process scope has been replaced by `"$NormalizedPath`"." } else { $PathToAppend = "{0}$NormalizedPath\bin" -f [System.IO.Path]::PathSeparator $env:Path += $PathToAppend Write-Debug -Message "`"$PathToAppend`" has been appended to the Path environment variable in Process scope." } } } } function Test-JavaHomeDirectory { <# .SYNOPSIS Test Java home directory. .DESCRIPTION Test if a given directory is a Java home directory. .PARAMETER Path Path to the directory. .INPUTS None. You cannot pipe objects to Test-JavaHomeDirectory. .OUTPUTS System.Boolean. True if directory is a Java home directory. .EXAMPLE PS> Test-JavaHomeDirectory -Path C:\path\to\java_home_directory $true Test Java home directory. #> [OutputType([bool])] [CmdletBinding()] param ( [Parameter( Mandatory = $true, HelpMessage = "Path to the directory" )] [string] $Path ) process { $JavaApplicatonPath = Join-Path -Path $Path -ChildPath "bin\java.exe" $IsDirectory = Test-Path -Path $Path -PathType Container Write-Debug -Message "Is `"$Path`" a directory? $IsDirectory." $JavaApplicatonExists = Test-Path -Path $JavaApplicatonPath -PathType Leaf Write-Debug -Message "Does `"$JavaApplicatonPath`" exist? $JavaApplicatonExists." $IsDirectory -and $JavaApplicatonExists } } #endregion Private functions #region Public functions function Add-JavaEntry { <# .SYNOPSIS Add a Java entry. .DESCRIPTION Add a Java entry to the inventory file. .PARAMETER Path Path to the Java home directory to add as entry to the inventory file. .PARAMETER CustomName Custom name to set for the Java entry to add to the inventory file. .INPUTS None. You cannot pipe objects to Add-JavaEntry. .OUTPUTS System.Void. None. .EXAMPLE PS> Add-JavaEntry -Path C:\path\to\java_home_directory Add java entry to the inventory with default name (version name). .EXAMPLE PS> Add-JavaEntry -Path C:\path\to\java_home_directory -CustomName "MyJava" Add java entry to the inventory with custom name. #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Mandatory = $true, Position = 0, HelpMessage = "Path to the Java home directory to add as entry to the inventory file" )] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter( HelpMessage = "Custom name to set for the Java entry to add to the inventory file" )] [ValidateNotNullOrEmpty()] [Alias("Name")] [string] $CustomName ) begin { $InformationPreference = "Continue" $ErrorActionPreference = "Stop" } process { if (-not (Test-JavaHomeDirectory -Path $Path)) { Write-Error -Message "Path `"$Path`" is not a Java home directory." } $Version = Get-JavaVersion -Path $Path Write-Debug -Message "Java version is `"$Version`"." $Name = if ($CustomName) { $CustomName } else { $Version } Write-Debug -Message "Java entry will be set with name `"$Name`"." $Inventory = New-Object -TypeName Inventory if ($Inventory.FileExists()) { $Inventory.ReadFile() } if ($Inventory.JavaEntryExists($Name)) { Write-Error -Message "Java entry `"$Name`" already exists." } if ($PSCmdlet.ShouldProcess($Inventory.Path, "Add Java entry '$Name' to inventory file")) { $Inventory.AddJavaEntry($Name, $Path) $Inventory.SaveFile() Write-Information -MessageData "Java entry `"$Name`" added." } } } function Get-JavaEntry { <# .SYNOPSIS Get Java entries. .DESCRIPTION Get all the Java entries from the inventory file. .INPUTS None. You cannot pipe objects to Get-JavaEntry. .OUTPUTS System.Object. Array of Java entry objects. .EXAMPLE PS> Get-JavaEntry Name Path ---- ---- jdk-8.0.3120.7 C:\Users\user\.java_packages\jdk8u312-b07 jdk-11.0.13.0 C:\Users\user\.java_packages\jdk-11.0.13+8 jdk-17.0.1.0 C:\Users\user\.java_packages\jdk-17.0.1+12 my_jdk C:\Users\user\.java_packages\jdk-17.0.1+12 Get all the Java entries from the inventory file. #> [OutputType([object[]])] [CmdletBinding()] param () begin { $ErrorActionPreference = "Stop" } process { $Inventory = New-Object -TypeName Inventory if (-not $Inventory.FileExists()) { Write-Error -Message "The inventory file does not exist." } $Inventory.ReadFile() if (-not $Inventory.JavaEntries) { Write-Error -Message "There is no Java entry in the inventory." } $Inventory.JavaEntries } } function Invoke-DownloadJavaPackage { <# .SYNOPSIS Download a Java package. .DESCRIPTION Download and install a Java package from Adoptium and add an associated entry to the inventory file. .PARAMETER Version Version of Java to download. .INPUTS None. You cannot pipe objects to Invoke-DownloadJavaPackage. .OUTPUTS System.Void. None. .EXAMPLE PS> Invoke-DownloadJavaPackage If in interactive PowerShell session, select a version of Java using menu. Else, pick the latest available version of Java. Download and install the Java package in the .java_package folder. .EXAMPLE PS> Invoke-DownloadJavaPackage -Version 11 Download and install Java 11 in the .java_package folder. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "", Scope = "Function", Target = "*")] [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Position = 0, HelpMessage = "Version of Java to download" )] [int] $Version ) begin { $InformationPreference = "Continue" $ErrorActionPreference = "Stop" $AvailableVersions = [AdoptiumApi]::GetAvailableFeatureVersions() $DestinationDirectory = Join-Path -Path $HOME -ChildPath ".java_packages" } process { if ($Version) { if ($Version -notin $AvailableVersions) { Write-Error -Message ("Java version $Version is not available. Available versions: {0}." -f ($AvailableVersions -join ", ")) } } else { if ([Environment]::UserInteractive) { $Version = Use-InteractiveSelectionMenu -Items $AvailableVersions -Header "Select Java version:" if (-not $Version) { break } } else { $Version = ($AvailableVersions | Measure-Object -Maximum).Maximum } } $Package = New-Object -TypeName AdoptiumPackage -ArgumentList $Version if ($PSCmdlet.ShouldProcess($DestinationDirectory, ("Download and install Java package '{0}' to directory" -f $Package.Name))) { if (-not (Test-Path -Path $DestinationDirectory -PathType Container)) { New-Item -Path $DestinationDirectory -ItemType Directory | Out-Null } Write-Information -MessageData ("Downloading Java package `"{0}`"..." -f $Package.Name) $PackageObject = $Package.Download($DestinationDirectory) $PackagePath = $PackageObject.FullName Write-Information -MessageData "Java package installed in `"$PackagePath`"." try { Add-JavaEntry -Path $PackagePath } catch { Write-Error -Message ("Cannot add Java entry to the inventory file: {0}" -f $_.Exception.Message) } } } } function Remove-JavaEntry { <# .SYNOPSIS Remove a Java entry. .DESCRIPTION Remove a Java entry to the inventory file. .PARAMETER Name Name of the Java entry to remove from the inventory file. .INPUTS None. You cannot pipe objects to Remove-JavaEntry. .OUTPUTS System.Void. None. .EXAMPLE PS> Remove-JavaEntry -Name undesired_java Remove a Java entry with defined name from the inventory file. #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Position = 0, HelpMessage = "Name of the Java entry to remove from the inventory file" )] [string] $Name ) begin { $InformationPreference = "Continue" $ErrorActionPreference = "Stop" } process { $Inventory = New-Object -TypeName Inventory if (-not $Inventory.FileExists()) { Write-Error -Message "The inventory file does not exist." } $Inventory.ReadFile() if (-not $Inventory.JavaEntries) { Write-Error -Message "There is no Java entry in the inventory." } $JavaEntryNames = @() $Inventory.JavaEntries | ForEach-Object -Process { $JavaEntryNames += $_.Name } if (-not $Name) { if ([Environment]::UserInteractive) { $Name = Use-InteractiveSelectionMenu -Items $JavaEntryNames -Header "Select Java entry:" if (-not $Name) { break } } else { Write-Error -Message "Parameter `"Name`" cannot be an empty string." } } if (-not $Inventory.JavaEntryExists($Name)) { Write-Error -Message "Java entry with name `"$Name`" does not exist." } if ($PSCmdlet.ShouldProcess($Inventory.Path, "Remove Java entry '$Name' from the inventory file")) { $Inventory.RemoveJavaEntry($Name) $Inventory.SaveFile() Write-Information -MessageData "Java entry `"$Name`" removed." } } } function Switch-JavaVersion { <# .SYNOPSIS Switch Java version. .DESCRIPTION Switch the version of Java which is being used in a specific environment target scope. .PARAMETER Name Name of the Java entry from the inventory file to use. .PARAMETER Path Path to the Java home directory to use. .PARAMETER Target Target scope of the environment variable. .INPUTS None. You cannot pipe objects to Switch-JavaVersion. .OUTPUTS System.Void. None. .EXAMPLE PS> Switch-JavaVersion -Name MyJava Select a Java version which is defined in the inventory file and use it in default scope (user scope). .EXAMPLE PS> Switch-JavaVersion (Interactive menu) Select a Java version which is defined in the inventory file using an interactive menu and use it in default scope (user scope). .EXAMPLE PS> Switch-JavaVersion -Name MyJava -Target Process Select a Java version which is defined in the inventory file and use it in process scope. .EXAMPLE PS> Switch-JavaVersion -Path C:\path\to\java_home_directory Use the Java version from the given path to a Java home directory in default scope (user scope). #> [OutputType([void])] [CmdletBinding( SupportsShouldProcess = $true, DefaultParameterSetName = "UseInventory" )] param ( [Parameter( ParameterSetName = "UseInventory", Position = 0, HelpMessage = "Name of the Java entry from the inventory file to use" )] [string] $Name, [Parameter( ParameterSetName = "UsePath", Mandatory = $true, HelpMessage = "Path to the Java home directory to use" )] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter( HelpMessage = "Target scope of the environment variable" )] [ValidateSet("Machine", "User", "Process")] [string] $Target = "User" ) begin { $InformationPreference = "Continue" $ErrorActionPreference = "Stop" } process { if ($PSCmdlet.ParameterSetName -eq "UseInventory") { $Inventory = New-Object -TypeName Inventory if (-not $Inventory.FileExists()) { Write-Error -Message "The inventory file does not exist." } $Inventory.ReadFile() if (-not $Inventory.JavaEntries) { Write-Error -Message "There is no Java entry in the inventory." } $JavaEntryNames = @() $Inventory.JavaEntries | ForEach-Object -Process { $JavaEntryNames += $_.Name } if (-not $Name) { if ([Environment]::UserInteractive) { $Name = Use-InteractiveSelectionMenu -Items $JavaEntryNames -Header "Select Java entry:" if (-not $Name) { break } } else { Write-Error -Message "Parameter `"Name`" cannot be an empty string." } } if (-not $Inventory.JavaEntryExists($Name)) { Write-Error -Message "Java entry with name `"$Name`" does not exist." } $Path = $Inventory.GetJavaEntry($Name).Path } if (-not (Test-JavaHomeDirectory -Path $Path)) { Write-Error -Message "Path `"$Path`" is not a Java home directory." } $Version = Get-JavaVersion -Path $Path if ($PSCmdlet.ShouldProcess("Java home directory in '$Target' scope", "Use $Path")) { Set-EnvJavaHome -Path $Path -Target $Target Write-Information -MessageData "Java version `"$Version`" is now in use." } } } #endregion Public functions |