From d2c435ce517886d65463159f323aba006de253ae Mon Sep 17 00:00:00 2001 From: Daniel Scalzi Date: Tue, 15 May 2018 01:05:10 -0400 Subject: [PATCH] Added Java Validations for Linux. Also abstracted and optimized the common functions for resolving Java between the three supported operating systems. Changes made to win32 and darwin validations will require testing to ensure everything is functional. --- app/assets/js/assetguard.js | 351 +++++++++++++++++++++++------------- 1 file changed, 223 insertions(+), 128 deletions(-) diff --git a/app/assets/js/assetguard.js b/app/assets/js/assetguard.js index 9fc73ab..3f098d5 100644 --- a/app/assets/js/assetguard.js +++ b/app/assets/js/assetguard.js @@ -702,8 +702,8 @@ class AssetGuard extends EventEmitter { * * @param {string} stderr The output to validate. * - * @returns {Promise.} A promise which resolves to true if the properties are valid. - * Otherwise false. + * @returns {Promise.} A promise which resolves to a meta object about the JVM. + * The validity is stored inside the `valid` property. */ static _validateJVMProperties(stderr){ const res = stderr @@ -712,14 +712,18 @@ class AssetGuard extends EventEmitter { const goal = 2 let checksum = 0 + const meta = {} + for(let i=0; i -1){ let arch = props[i].split('=')[1].trim() + arch = parseInt(arch) console.log(props[i].trim()) - if(parseInt(arch) === 64){ + if(arch === 64){ + meta.arch = arch ++checksum if(checksum === goal){ - return true + break } } } else if(props[i].indexOf('java.runtime.version') > -1){ @@ -727,15 +731,18 @@ class AssetGuard extends EventEmitter { console.log(props[i].trim()) const verOb = AssetGuard.parseJavaRuntimeVersion(verString) if(verOb.major === 8 && verOb.update > 52){ + meta.version = verOb ++checksum if(checksum === goal){ - return true + break } } } } + + meta.valid = checksum === goal - return checksum === goal + return meta } /** @@ -748,8 +755,8 @@ class AssetGuard extends EventEmitter { * * @param {string} binaryExecPath Path to the java executable we wish to validate. * - * @returns {Promise.} Resolves to false only if the test is successful and the result - * is less than 64. + * @returns {Promise.} A promise which resolves to a meta object about the JVM. + * The validity is stored inside the `valid` property. */ static _validateJavaBinary(binaryExecPath){ @@ -761,36 +768,16 @@ class AssetGuard extends EventEmitter { resolve(this._validateJVMProperties(stderr)) } catch (err){ // Output format might have changed, validation cannot be completed. - resolve(false) + resolve({valid: false}) } }) } else { - resolve(false) + resolve({valid: false}) } }) } - /*static _validateJavaBinaryDarwin(binaryPath){ - - return new Promise((resolve, reject) => { - if(fs.existsSync(binaryExecPath)){ - child_process.exec('export JAVA_HOME="' + binaryPath + '"; java -XshowSettings:properties', (err, stdout, stderr) => { - try { - // Output is stored in stderr? - resolve(this._validateJVMProperties(stderr)) - } catch (err){ - // Output format might have changed, validation cannot be completed. - resolve(false) - } - }) - } else { - resolve(false) - } - }) - - }*/ - /** * Checks for the presence of the environment variable JAVA_HOME. If it exits, we will check * to see if the value points to a path which exists. If the path exits, the path is returned. @@ -808,37 +795,6 @@ class AssetGuard extends EventEmitter { } } - /** - * Scans the data folder's runtime directory for suitable JRE candidates. - * - * @param {string} dataDir The base launcher directory. - * @returns {Promise.>} A set containing suitable JRE candidates found - * in the runtime directory. - */ - static _scanDataFolder(dataDir){ - return new Promise((resolve, reject) => { - const x64RuntimeDir = path.join(dataDir, 'runtime', 'x64') - fs.exists(x64RuntimeDir, (e) => { - let res = new Set() - if(e){ - fs.readdir(x64RuntimeDir, (err, files) => { - if(err){ - resolve(res) - console.log(err) - } else { - for(let i=0; i>} A promise which resolves to a set of the discovered + * root JVM folders. + */ + static _scanFileSystem(scanDir){ + return new Promise((resolve, reject) => { + + fs.exists(scanDir, (e) => { + + let res = new Set() + + if(e){ + fs.readdir(scanDir, (err, files) => { + if(err){ + resolve(res) + console.log(err) + } else { + let pathsDone = 0 + + for(let i=0; i { + + if(v){ + res.add(combinedPath) + } + + ++pathsDone + + if(pathsDone === files.length){ + resolve(res) + } + + }) + } + if(pathsDone === files.length){ + resolve(res) + } + } + }) + } else { + resolve(res) + } + }) + + }) + } + + /** + * + * @param {Set.} rootSet A set of JVM root strings to validate. + * @returns {Promise.} A promise which resolves to an array of meta objects + * for each valid JVM root directory. + */ + static async _validateJavaRootSet(rootSet){ + + const rootArr = Array.from(rootSet) + const validArr = [] + + for(let i=0; i { + // Note that Java 9+ uses semver and that will need to be accounted for in + // the future. + + if(a.version.major === b.version.major){ + + if(a.version.update === b.version.update){ + + if(a.version.build === b.version.build){ + + // Same version, give priority to JRE. + + if(a.execPath.toLowerCase().indexOf('jdk') > -1){ + return 1 + } else { + return b.execPath.toLowerCase().indexOf('jdk') > -1 ? 0 : -1 + } + + + } else { + return a.version.build > b.version.build ? 1 : -1 + } + + } else { + return a.version.update > b.version.update ? 1 : -1 + } + + } else { + return a.version.major > b.version.major ? 1 : -1 + } + + }) + + return retArr + } + /** * Attempts to find a valid x64 installation of Java on Windows machines. * Possible paths will be pulled from the registry and the JAVA_HOME environment @@ -966,78 +1059,59 @@ class AssetGuard extends EventEmitter { static async _win32JavaValidate(dataDir){ // Get possible paths from the registry. - const pathSet = await AssetGuard._scanRegistry() - - //console.log(Array.from(pathSet)) // DEBUGGING + let pathSet1 = await AssetGuard._scanRegistry() + if(pathSet1.length === 0){ + // Do a manual file system scan of program files. + pathSet1 = AssetGuard._scanFileSystem('C:\\Program Files\\Java') + } // Get possible paths from the data directory. - const pathSet2 = await AssetGuard._scanDataFolder(dataDir) + const pathSet2 = await AssetGuard._scanFileSystem(path.join(dataDir, 'runtime', 'x64')) - // Validate JAVA_HOME + // Merge the results. + const uberSet = new Set([...pathSet1, ...pathSet2]) + + // Validate JAVA_HOME. const jHome = AssetGuard._scanJavaHome() if(jHome != null && jHome.indexOf('(x86)') === -1){ - pathSet.add(jHome) + uberSet.add(jHome) } - const mergedSet = new Set([...pathSet, ...pathSet2]) + let pathArr = await AssetGuard._validateJavaRootSet(uberSet) + pathArr = AssetGuard._sortValidJavaArray(pathArr) - // Convert path set to an array for processing. - let pathArr = Array.from(mergedSet) - - //console.log(pathArr) // DEBUGGING - - // Sorts array. Higher version numbers preceed lower. JRE preceeds JDK. - pathArr = pathArr.sort((a, b) => { - // Note that Java 9+ uses semver and that will need to be accounted for in - // the future. - const aVer = parseInt(a.split('_')[1]) - const bVer = parseInt(b.split('_')[1]) - if(bVer === aVer){ - return a.indexOf('jdk') > -1 ? 1 : 0 - } else { - return bVer - aVer - } - }) - - //console.log(pathArr) // DEBUGGING - - // Validate that the binary is actually x64. - for(let i=0; i 0){ + return pathArr[0].execPath + } else { + return null } - // No suitable candidates found. - return null; - } /** - * See if JRE exists in the Internet Plug-Ins folder. + * Attempts to find a valid x64 installation of Java on MacOS. + * The system JVM directory is scanned for possible installations. + * The JAVA_HOME enviroment variable and internet plugins directory + * are also scanned and validated. * - * @returns {string} The path of the JRE if found, otherwise null. - */ - static _scanInternetPlugins(){ - // /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java - const pth = '/Library/Internet Plug-Ins/JavaAppletPlugin.plugin' - const res = fs.existsSync(AssetGuard.javaExecFromRoot(pth)) - return res ? pth : null - } - - /** - * WIP -> get a valid x64 Java path on macOS. + * Higher versions > Lower versions + * If versions are equal, JRE > JDK. + * + * @param {string} dataDir The base launcher directory. + * @returns {Promise.} A Promise which resolves to the executable path of a valid + * x64 Java installation. If none are found, null is returned. */ static async _darwinJavaValidate(dataDir){ - const pathSet = new Set() + const pathSet1 = await AssetGuard._scanFileSystem('/Library/Java/JavaVirtualMachines') + const pathSet2 = await AssetGuard._scanFileSystem(path.join(dataDir, 'runtime', 'x64')) + + const uberSet = new Set([...pathSet1, ...pathSet2]) // Check Internet Plugins folder. const iPPath = AssetGuard._scanInternetPlugins() if(iPPath != null){ - pathSet.add(iPPath) + uberSet.add(iPPath) } // Check the JAVA_HOME environment variable. @@ -1047,31 +1121,52 @@ class AssetGuard extends EventEmitter { if(jHome.contains('/Contents/Home')){ jHome = jHome.substring(0, jHome.indexOf('/Contents/Home')) } - pathSet.add(jHome) + uberSet.add(jHome) } - // Get possible paths from the data directory. - const pathSet2 = await AssetGuard._scanDataFolder(dataDir) + let pathArr = await AssetGuard._validateJavaRootSet(uberSet) + pathArr = AssetGuard._sortValidJavaArray(pathArr) - // TODO Sort by highest version. - - let pathArr = Array.from(pathSet2).concat(Array.from(pathSet)) - for(let i=0; i 0){ + return pathArr[0].execPath + } else { + return null } - - return null } /** - * WIP -> get a valid x64 Java path on linux. + * Attempts to find a valid x64 installation of Java on Linux. + * The system JVM directory is scanned for possible installations. + * The JAVA_HOME enviroment variable is also scanned and validated. + * + * Higher versions > Lower versions + * If versions are equal, JRE > JDK. + * + * @param {string} dataDir The base launcher directory. + * @returns {Promise.} A Promise which resolves to the executable path of a valid + * x64 Java installation. If none are found, null is returned. */ static async _linuxJavaValidate(dataDir){ - return null + + const pathSet1 = await AssetGuard._scanFileSystem('/usr/lib/jvm') + const pathSet2 = await AssetGuard._scanFileSystem(path.join(dataDir, 'runtime', 'x64')) + + const uberSet = new Set([...pathSet1, ...pathSet2]) + + // Validate JAVA_HOME + const jHome = AssetGuard._scanJavaHome() + if(jHome != null){ + uberSet.add(jHome) + } + + let pathArr = await AssetGuard._validateJavaRootSet(uberSet) + pathArr = AssetGuard._sortValidJavaArray(pathArr) + + if(pathArr.length > 0){ + return pathArr[0].execPath + } else { + return null + } } /**