Skip to main content
Glama
locator.py7.66 kB
"""JDT.LS and Java installation locator.""" import logging import os import platform import re import subprocess from dataclasses import dataclass from pathlib import Path from jons_mcp_java.constants import JAVA_MIN_VERSION from jons_mcp_java.exceptions import ( JavaNotFoundError, JavaVersionError, JdtlsNotFoundError, UnsupportedPlatformError, ) logger = logging.getLogger(__name__) @dataclass class JdtlsInstallation: """Information about a JDT.LS installation.""" jdtls_home: Path launcher_jar: Path config_dir: Path java_executable: Path def locate_jdtls() -> JdtlsInstallation: """ Locate JDT.LS installation. Search order: 1. JDTLS_HOME environment variable 2. Homebrew via `brew --prefix jdtls` 3. Common Homebrew paths (Apple Silicon and Intel) 4. Manual installation (~/.local/share/jdtls) """ # Check environment variable first if jdtls_home := os.environ.get("JDTLS_HOME"): logger.debug(f"Using JDTLS_HOME from environment: {jdtls_home}") return _validate_jdtls_installation(Path(jdtls_home)) # Try Homebrew (most reliable) brew_path = _get_homebrew_jdtls_path() if brew_path: logger.debug(f"Found JDT.LS via Homebrew: {brew_path}") return _validate_jdtls_installation(brew_path) # Fallback paths fallback_paths = [ Path("/opt/homebrew/opt/jdtls"), # Apple Silicon Path("/usr/local/opt/jdtls"), # Intel Mac Path.home() / ".local/share/jdtls", # Manual install ] for path in fallback_paths: if path.exists(): logger.debug(f"Found JDT.LS at fallback path: {path}") return _validate_jdtls_installation(path) raise JdtlsNotFoundError("JDT.LS not found. Install via: brew install jdtls") def _get_homebrew_jdtls_path() -> Path | None: """Get JDT.LS path via Homebrew.""" try: result = subprocess.run( ["brew", "--prefix", "jdtls"], capture_output=True, text=True, timeout=5, ) if result.returncode == 0: return Path(result.stdout.strip()) except (subprocess.TimeoutExpired, FileNotFoundError): pass return None def _validate_jdtls_installation(jdtls_home: Path) -> JdtlsInstallation: """Validate JDT.LS installation and return full configuration.""" # Find launcher JAR launcher_jar = _find_launcher_jar(jdtls_home) # Get platform-specific config directory config_dir = get_config_dir(jdtls_home) # Locate Java java_executable = locate_java() return JdtlsInstallation( jdtls_home=jdtls_home, launcher_jar=launcher_jar, config_dir=config_dir, java_executable=java_executable, ) def _find_launcher_jar(jdtls_home: Path) -> Path: """Find the org.eclipse.equinox.launcher JAR.""" # Check both direct and libexec paths (Homebrew layout) for base in [jdtls_home, jdtls_home / "libexec"]: plugins_dir = base / "plugins" if plugins_dir.exists(): # Find launcher JAR launchers = list(plugins_dir.glob("org.eclipse.equinox.launcher_*.jar")) if launchers: return launchers[0] raise JdtlsNotFoundError(f"Launcher JAR not found in {jdtls_home}") def get_config_dir(jdtls_home: Path) -> Path: """Get platform-specific config directory.""" system = platform.system() machine = platform.machine() if system == "Darwin": # macOS if machine == "arm64": config_name = "config_mac_arm" else: config_name = "config_mac" elif system == "Linux": if "arm" in machine.lower() or "aarch64" in machine.lower(): config_name = "config_linux_arm" else: config_name = "config_linux" else: raise UnsupportedPlatformError(f"Platform {system} not supported") # Check both direct and libexec paths (Homebrew layout) for base in [jdtls_home, jdtls_home / "libexec"]: config_path = base / config_name if config_path.exists(): return config_path raise JdtlsNotFoundError(f"Config directory {config_name} not found in {jdtls_home}") def locate_java() -> Path: """Locate Java 21+ installation with version validation.""" java_path = _find_java_executable() _validate_java_version(java_path) return java_path def _find_java_executable() -> Path: """ Find Java executable. Search order: 1. JAVA_HOME environment variable 2. Homebrew openjdk@21 3. System java """ # Check JAVA_HOME if java_home := os.environ.get("JAVA_HOME"): java_path = Path(java_home) / "bin" / "java" if java_path.exists(): logger.debug(f"Using JAVA_HOME: {java_home}") return java_path # Try Homebrew openjdk@21 brew_java = _get_homebrew_java_path() if brew_java and brew_java.exists(): logger.debug(f"Found Java via Homebrew: {brew_java}") return brew_java # Try system java system_java = _find_system_java() if system_java: logger.debug(f"Found system Java: {system_java}") return system_java raise JavaNotFoundError("Java not found. Install via: brew install openjdk@21") def _get_homebrew_java_path() -> Path | None: """Get Java path via Homebrew.""" # Try openjdk@21 first, then generic openjdk for formula in ["openjdk@21", "openjdk"]: try: result = subprocess.run( ["brew", "--prefix", formula], capture_output=True, text=True, timeout=5, ) if result.returncode == 0: prefix = Path(result.stdout.strip()) java_path = prefix / "bin" / "java" if java_path.exists(): return java_path # Some formulas put it under libexec java_path = prefix / "libexec" / "openjdk.jdk" / "Contents" / "Home" / "bin" / "java" if java_path.exists(): return java_path except (subprocess.TimeoutExpired, FileNotFoundError): pass return None def _find_system_java() -> Path | None: """Find system Java using 'which'.""" try: result = subprocess.run( ["which", "java"], capture_output=True, text=True, timeout=5, ) if result.returncode == 0: return Path(result.stdout.strip()) except (subprocess.TimeoutExpired, FileNotFoundError): pass return None def _validate_java_version(java_path: Path) -> None: """Verify Java version is 21 or higher.""" try: result = subprocess.run( [str(java_path), "-version"], capture_output=True, text=True, timeout=10, ) except (subprocess.TimeoutExpired, FileNotFoundError) as e: raise JavaVersionError(f"Could not run Java: {e}") # Java version is on stderr version_output = result.stderr # Parse version (e.g., "openjdk version \"21.0.1\"") match = re.search(r'version "(\d+)', version_output) if not match: raise JavaVersionError(f"Could not parse Java version from: {version_output}") major_version = int(match.group(1)) if major_version < JAVA_MIN_VERSION: raise JavaVersionError( f"Java {JAVA_MIN_VERSION}+ required, found Java {major_version}. " f"Install via: brew install openjdk@21" ) logger.debug(f"Java version {major_version} validated")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jonmmease/jons-mcp-java'

If you have feedback or need assistance with the MCP directory API, please join our Discord server