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.
pull/1/head
Daniel Scalzi 2018-05-15 01:05:10 -04:00
parent 54e3861ba8
commit d2c435ce51
No known key found for this signature in database
GPG Key ID: 4B388B523BBBF147
1 changed files with 223 additions and 128 deletions

View File

@ -702,8 +702,8 @@ class AssetGuard extends EventEmitter {
* *
* @param {string} stderr The output to validate. * @param {string} stderr The output to validate.
* *
* @returns {Promise.<boolean>} A promise which resolves to true if the properties are valid. * @returns {Promise.<Object>} A promise which resolves to a meta object about the JVM.
* Otherwise false. * The validity is stored inside the `valid` property.
*/ */
static _validateJVMProperties(stderr){ static _validateJVMProperties(stderr){
const res = stderr const res = stderr
@ -712,14 +712,18 @@ class AssetGuard extends EventEmitter {
const goal = 2 const goal = 2
let checksum = 0 let checksum = 0
const meta = {}
for(let i=0; i<props.length; i++){ for(let i=0; i<props.length; i++){
if(props[i].indexOf('sun.arch.data.model') > -1){ if(props[i].indexOf('sun.arch.data.model') > -1){
let arch = props[i].split('=')[1].trim() let arch = props[i].split('=')[1].trim()
arch = parseInt(arch)
console.log(props[i].trim()) console.log(props[i].trim())
if(parseInt(arch) === 64){ if(arch === 64){
meta.arch = arch
++checksum ++checksum
if(checksum === goal){ if(checksum === goal){
return true break
} }
} }
} else if(props[i].indexOf('java.runtime.version') > -1){ } else if(props[i].indexOf('java.runtime.version') > -1){
@ -727,15 +731,18 @@ class AssetGuard extends EventEmitter {
console.log(props[i].trim()) console.log(props[i].trim())
const verOb = AssetGuard.parseJavaRuntimeVersion(verString) const verOb = AssetGuard.parseJavaRuntimeVersion(verString)
if(verOb.major === 8 && verOb.update > 52){ if(verOb.major === 8 && verOb.update > 52){
meta.version = verOb
++checksum ++checksum
if(checksum === goal){ 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. * @param {string} binaryExecPath Path to the java executable we wish to validate.
* *
* @returns {Promise.<boolean>} Resolves to false only if the test is successful and the result * @returns {Promise.<Object>} A promise which resolves to a meta object about the JVM.
* is less than 64. * The validity is stored inside the `valid` property.
*/ */
static _validateJavaBinary(binaryExecPath){ static _validateJavaBinary(binaryExecPath){
@ -761,36 +768,16 @@ class AssetGuard extends EventEmitter {
resolve(this._validateJVMProperties(stderr)) resolve(this._validateJVMProperties(stderr))
} catch (err){ } catch (err){
// Output format might have changed, validation cannot be completed. // Output format might have changed, validation cannot be completed.
resolve(false) resolve({valid: false})
} }
}) })
} else { } 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 * 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. * 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.<Set.<string>>} 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<files.length; i++){
res.add(path.join(x64RuntimeDir, files[i]))
}
resolve(res)
}
})
} else {
resolve(res)
}
})
})
}
/** /**
* Scans the registry for 64-bit Java entries. The paths of each entry are added to * Scans the registry for 64-bit Java entries. The paths of each entry are added to
* a set and returned. Currently, only Java 8 (1.8) is supported. * a set and returned. Currently, only Java 8 (1.8) is supported.
@ -949,6 +905,143 @@ class AssetGuard extends EventEmitter {
} }
/**
* See if JRE exists in the Internet Plug-Ins folder.
*
* @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
}
/**
* Scan a directory for root JVM folders.
*
* @param {string} scanDir The directory to scan.
* @returns {Promise.<Set.<string>>} 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<files.length; i++){
const combinedPath = path.join(scanDir, files[i])
const execPath = AssetGuard.javaExecFromRoot(combinedPath)
fs.exists(execPath, (v) => {
if(v){
res.add(combinedPath)
}
++pathsDone
if(pathsDone === files.length){
resolve(res)
}
})
}
if(pathsDone === files.length){
resolve(res)
}
}
})
} else {
resolve(res)
}
})
})
}
/**
*
* @param {Set.<string>} rootSet A set of JVM root strings to validate.
* @returns {Promise.<Object[]>} 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<rootArr.length; i++){
const execPath = AssetGuard.javaExecFromRoot(rootArr[i])
const metaOb = await AssetGuard._validateJavaBinary(execPath)
if(metaOb.valid){
metaOb.execPath = execPath
validArr.push(metaOb)
}
}
return validArr
}
/**
* Sort an array of JVM meta objects. Best candidates are placed before all others.
* Sorts based on version and gives priority to JREs over JDKs if versions match.
*
* @param {Object[]} validArr An array of JVM meta objects.
* @returns {Object[]} A sorted array of JVM meta objects.
*/
static _sortValidJavaArray(validArr){
const retArr = validArr.sort((a, b) => {
// 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. * 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 * 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){ static async _win32JavaValidate(dataDir){
// Get possible paths from the registry. // Get possible paths from the registry.
const pathSet = await AssetGuard._scanRegistry() let pathSet1 = await AssetGuard._scanRegistry()
if(pathSet1.length === 0){
//console.log(Array.from(pathSet)) // DEBUGGING // Do a manual file system scan of program files.
pathSet1 = AssetGuard._scanFileSystem('C:\\Program Files\\Java')
}
// Get possible paths from the data directory. // 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() const jHome = AssetGuard._scanJavaHome()
if(jHome != null && jHome.indexOf('(x86)') === -1){ 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. if(pathArr.length > 0){
let pathArr = Array.from(mergedSet) return pathArr[0].execPath
} else {
//console.log(pathArr) // DEBUGGING return null
// 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<pathArr.length; i++) {
const execPath = AssetGuard.javaExecFromRoot(pathArr[i])
let res = await AssetGuard._validateJavaBinary(execPath)
if(res){
return execPath
}
} }
// 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. * Higher versions > Lower versions
*/ * If versions are equal, JRE > JDK.
static _scanInternetPlugins(){ *
// /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java * @param {string} dataDir The base launcher directory.
const pth = '/Library/Internet Plug-Ins/JavaAppletPlugin.plugin' * @returns {Promise.<string>} A Promise which resolves to the executable path of a valid
const res = fs.existsSync(AssetGuard.javaExecFromRoot(pth)) * x64 Java installation. If none are found, null is returned.
return res ? pth : null
}
/**
* WIP -> get a valid x64 Java path on macOS.
*/ */
static async _darwinJavaValidate(dataDir){ 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. // Check Internet Plugins folder.
const iPPath = AssetGuard._scanInternetPlugins() const iPPath = AssetGuard._scanInternetPlugins()
if(iPPath != null){ if(iPPath != null){
pathSet.add(iPPath) uberSet.add(iPPath)
} }
// Check the JAVA_HOME environment variable. // Check the JAVA_HOME environment variable.
@ -1047,31 +1121,52 @@ class AssetGuard extends EventEmitter {
if(jHome.contains('/Contents/Home')){ if(jHome.contains('/Contents/Home')){
jHome = jHome.substring(0, jHome.indexOf('/Contents/Home')) jHome = jHome.substring(0, jHome.indexOf('/Contents/Home'))
} }
pathSet.add(jHome) uberSet.add(jHome)
} }
// Get possible paths from the data directory. let pathArr = await AssetGuard._validateJavaRootSet(uberSet)
const pathSet2 = await AssetGuard._scanDataFolder(dataDir) pathArr = AssetGuard._sortValidJavaArray(pathArr)
// TODO Sort by highest version. if(pathArr.length > 0){
return pathArr[0].execPath
let pathArr = Array.from(pathSet2).concat(Array.from(pathSet)) } else {
for(let i=0; i<pathArr.length; i++) { return null
const execPath = AssetGuard.javaExecFromRoot(pathArr[i])
let res = await AssetGuard._validateJavaBinary(execPath)
if(res){
return execPath
}
} }
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.<string>} 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){ 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
}
} }
/** /**