Skip to content

Instantly share code, notes, and snippets.

@PeyaPeyaPeyang
Last active January 6, 2023 18:28
Show Gist options
  • Save PeyaPeyaPeyang/227cfdfa84ac9e6b076e9fd59b1e8422 to your computer and use it in GitHub Desktop.
Save PeyaPeyaPeyang/227cfdfa84ac9e6b076e9fd59b1e8422 to your computer and use it in GitHub Desktop.
JDK Switcher for Windows

JDK Switcher for Windows

The JDK switcher I made in an hour due to sdkman unavailability in pure Windows.

Usage

  • Create a folder with named "sdk" and create "sdks" folder in the folder.
  • Append paths of both folder to %PATH% (system)
  • Download main.py and sdk.bat and put them in the "sdk" folder.
  • Open main.py and change the "SDK_HOME" variable to the path of the "sdks" folder.

Create a vendor

This project manages the JDK with a group called "vendor".
So, you have to create a vendor before installing JDKs.

The command is interactive, so you can just type sdk create and follow the instructions.

jdk create vendor

Install a new JDK

  • Download the JDK put it in your working directory.
  • Run the command below.
jdk create version

This project automatically detects the JDK version and vendor.

@echo off
REM Run python script with full arguments
python %~dp0\main.py %*
MIT License
Copyright (c) 2023 Peyang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
import sys
import glob
import json
from os import path
import os
import shutil
import subprocess
SDK_PATH = "C:\\path\\to\\sdk"
VENDOR_INFO_FILE = "vendor.json"
VERSIONS_INFO_FILE = "versions.json"
class VendorInfo:
def __init__(self, name, versions, parent, path):
self.name = name
self.versions = versions
self.parent = parent
self.path = path
class VersionInfo:
def __init__(self, java_major, java_minor, vendor_original, path, vendor):
self.java_major = java_major
self.java_minor = java_minor
self.vendor_original = vendor_original
self.path = path
self.vendor = vendor
def load_versions_info(vendor_path, vendor_name):
versions = []
for version_path in glob.glob(path.join(SDK_PATH, vendor_path, "**", VERSIONS_INFO_FILE)):
if not path.exists(version_path):
print("No version info file found for vendor: " + vendor_name)
continue
with open(version_path, "r", encoding="utf-8") as f:
vendor_info = json.load(f)
versions.append(VersionInfo(
vendor_info["java_major"],
vendor_info["java_minor"],
vendor_info["vendor_original"],
path.dirname(version_path),
vendor_name
))
return versions
def load_vendor_info(vendor_root):
vendor_info_file = path.join(SDK_PATH, vendor_root + "/" + VENDOR_INFO_FILE)
if not path.exists(vendor_info_file):
return None
with open(vendor_info_file) as f:
data = json.load(f)
versions = load_versions_info(vendor_root, data["name"])
return VendorInfo(
data["name"],
versions,
data["parent"],
vendor_root
)
def save_vendor_info(vendor):
vendor_path = path.join(SDK_PATH, vendor.name.lower())
os.makedirs(vendor_path, exist_ok=True)
with open(path.join(vendor_path, VENDOR_INFO_FILE), "w") as f:
json.dump({
"name": vendor.name,
"parent": vendor.parent
}, f)
def save_version_info(version_info):
version_path = path.join(version_info.path, VERSIONS_INFO_FILE)
print("Saving version info to: " + version_path)
with open(version_path, "w") as f:
json.dump({
"java_major": version_info.java_major,
"java_minor": version_info.java_minor,
"vendor_original": version_info.vendor_original,
"vendor": version_info.vendor
}, f)
def vendor_list():
directories = glob.glob(SDK_PATH + "/*")
vendors = []
for directory in directories:
vendor = load_vendor_info(directory)
if vendor is not None:
vendors.append(vendor)
return vendors
def create_vendor(vendors):
print("Welcome to the vendor creation wizard!")
print("Please enter the following information:")
vendor_name = input("Vendor name: ")
if vendor_name.lower() is "current":
print("Vendor name cannot be 'current'")
return
if [v for v in vendors if v.name == vendor_name]:
print("Vendor already exists: " + vendor_name)
return
vendor_parent = input("Vendor parent: ")
if vendor_parent == "":
vendor_parent = None
elif not [v for v in vendors if v.name == vendor_parent]:
print("Vendor not found: " + vendor_parent)
return
print("Creating vendor: ")
print("Name: " + vendor_name)
if vendor_parent is None:
print("Parent: None")
else:
print("Parent: " + vendor_parent)
confirm = input("Confirm? [y/N] ")
if not confirm.startswith("y"):
print("Aborting...")
return
vendor = VendorInfo(vendor_name, [], vendor_parent, path.join(SDK_PATH, vendor_name))
save_vendor_info(vendor)
def decompress_to_temp(file_name):
temp_dir = path.join(SDK_PATH, "jdk-temp", file_name[:-4])
os.makedirs(temp_dir, exist_ok=True)
shutil.unpack_archive(file_name, temp_dir)
return temp_dir
def is_jdk_dir(dir):
return path.exists(path.join(dir, "bin", "javac.exe")) and path.exists(path.join(dir, "bin", "java.exe")) \
and path.exists(path.join(dir, "lib")) and path.exists(path.join(dir, "include"))
def patch_java(jdk_dir):
# for /r %x in ( *.pack ) do .\bin\unpack200 -r "%x" "%~dx %~px%~nx.jar"
subprocess.run(["cmd", "/c", "for /r %x in ( *.pack ) do .\\bin\\unpack200 -r ", "%x", "%~dx%~px%~nx.jar"],
cwd=jdk_dir,
stdout=subprocess.PIPE)
# above to python
def detect_java_version(jdk_dir):
java_file = path.join(jdk_dir, "bin", "java.exe")
process = subprocess.Popen([java_file, "-version"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = process.communicate()
if process.returncode != 0:
print("Failed to detect vendor version!")
print(output)
return None
version_string = None
vendor_version = None
for line in error.decode("utf-8").splitlines():
if line.startswith("java version "):
version_string = line[14:]
version_string = version_string.split('"')[0]
elif line.startswith("openjdk version "):
version_string = line[17:]
version_string = version_string.split('"')[0]
if line.startswith("Java(TM) SE Runtime Environment") or line.startswith("OpenJDK Runtime Environment"):
vendor_version = line
process.kill()
return {
"version_string": version_string,
"vendor_version": vendor_version
}
def parse_java_version(version_string):
parts = version_string.split(".")
if len(parts) < 2:
return None
java_major = int(parts[0])
if java_major == 1:
java_major = int(parts[1])
java_minor = parts[2].split("_")
if len(java_minor) < 2:
java_minor = parts[2]
else:
java_minor = java_minor[1]
return {
"java_major": java_major,
"java_minor": java_minor
}
def create_version(vendors):
print("Welcome to the version creation wizard!")
print("Please enter the following information:")
vendor_name = input("Vendor name: ")
vendor = [v for v in vendors if v.name.lower() == vendor_name.lower()]
if not vendor:
print("Vendor not found: " + vendor_name)
return
vendor = vendor[0]
print("Please enter the path to the JDK zip file:")
zip_path = input("Path: ")
if not path.exists(zip_path):
print("File not found: " + zip_path)
return
print("Decompressing...")
temp_dir = decompress_to_temp(zip_path)
print("Searching for JDK directory...")
jdk_dir = None
for dir in glob.glob(path.join(temp_dir, "*")):
if is_jdk_dir(dir):
jdk_dir = dir
break
if is_jdk_dir(temp_dir):
jdk_dir = temp_dir
if jdk_dir is None:
print("No JDK found!")
shutil.rmtree(temp_dir)
return
print("Found JDK directory: " + jdk_dir)
print("Patching JDK...")
patch_java(jdk_dir)
print("Detecting version...")
version_info = detect_java_version(jdk_dir)
if version_info is None:
print("Failed to detect version!")
shutil.rmtree(temp_dir)
return
version = parse_java_version(version_info["version_string"])
if version is None:
print("Failed to parse version!")
shutil.rmtree(temp_dir)
return
print("Creating version: ")
print("Vendor: " + vendor_name)
print("Version: " + version_info["version_string"])
print("Vendor version: " + version_info["vendor_version"])
print("Java major: " + str(version["java_major"]))
print("Java minor: " + str(version["java_minor"]))
java_dir = path.join(vendor.path, str(version["java_major"]) + "." + str(version["java_minor"]))
print("Java directory: " + java_dir)
confirm = input("Confirm? [y/N] ")
if not confirm.startswith("y"):
print("Aborting...")
shutil.rmtree(temp_dir)
return
print("Copying files...")
shutil.copytree(jdk_dir, java_dir)
shutil.rmtree(temp_dir)
print("Done!")
save_version_info(VersionInfo(
version["java_major"],
version["java_minor"],
version_info["vendor_version"],
java_dir,
vendor.name
))
def change_version(vendor_name, version_str):
vendors = vendor_list()
vendor = [v for v in vendors if v.name.lower() == vendor_name.lower()]
if len(vendor) == 0:
print("Vendor not found: " + vendor_name)
return
vendor = vendor[0]
versions = vendor.versions
version = [v for v in versions if str(v.java_major) + "." + v.java_minor == version_str or str(v.java_major) == version_str]
if len(version) == 0:
print("Version not found: " + version_str)
return
version = version[0]
print("Changing version to: ")
print("Vendor: " + vendor.name)
print("Version: " + version.vendor_original)
print("Java: " + str(version.java_major) + "." + str(version.java_minor))
confirm = input("Confirm? [y/N] ")
if not confirm.startswith("y"):
print("Aborting...")
return
print("Changing version...")
current = path.join(SDK_PATH, "current")
if path.exists(current):
shutil.rmtree(current)
shutil.copytree(version.path, current, copy_function=os.link)
print("Done!")
def main():
args = sys.argv[1:]
if len(args) == 0:
print("Usage: sdk <command> <platform> [path]")
return
command = args[0]
vendors = vendor_list()
if command == "list":
if len(args) == 1:
print("Available vendors:")
for vendor in vendors:
parent = vendor.parent
if parent is None:
print("- " + vendor.name)
else:
print("- " + vendor.name + " (Parent: " + parent + ")")
elif len(args) == 2:
print_version(args, vendors)
elif command == "create":
if len(args) == 1:
print("Usage: sdk create <vendor|version>")
elif len(args) >= 2:
if args[1] == "vendor":
create_vendor(vendors)
elif args[1] == "version":
create_version(vendors)
else:
print("Unknown command: " + " ".join(args[1:]) + ".")
elif command in ["use", "change", "switch", "set", "select", "sel", "ch", "sw"]:
if len(args) < 3:
print("Usage: sdk change <vendor> <version>")
if len(args) == 2:
print_version(args, vendors)
elif len(args) == 3:
change_version(args[1], args[2])
else:
print("Unknown command: " + " ".join(args[1:]) + ".")
else:
print("Unknown command: " + " ".join(args) + ".")
print("Available commands: list, create, change, use, switch, set, select, sel, ch, sw")
def print_version(args, vendors):
vendor = args[1]
vendor = [v for v in vendors if v.name.lower() == vendor.lower()]
if len(vendor) != 0:
vendor = vendor[0]
print("Available versions for vendor: " + vendor.name)
for version in vendor.versions:
print("- " + str(version.java_major) + "." + version.java_minor + " (" + version.vendor_original + ")")
else:
print("Vendor not found: " + args[1])
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment