Mercurial > hg > release > icedtea-web-1.8
changeset 1503:1e7642497ca2
Implemented JVM recognition in rust launchers
* .hgignore: added rust-launcher/.idea
* .Makefile: (launcher.build) adapted to tmp handling - exporting rust_tests_tmp and creating it
* rust-launcher/src/hardcoded_paths.rs: not-using returns consistently
* rust-launcher/src/jvm_from_properties.rs: logic to parse properties file, and to read JDK value from here
* rust-launcher/src/jvm_from_properties_resolver.rs: logic to waterfall all usable properties files and try locate JDK here.
* rust-launcher/src/main.rs: now trying various JDKS: proeprties->JAVA_HOME->registry->default (from build)
* rust-launcher/src/os_access.rs: set of classes serving to bridge few details where win and Linux may differ
* rust-launcher/src/property.rs: class to read generic properties file
* rust-launcher/src/utils.rs: class for various utility methods. Including testing subclass
author | Jiri Vanek <jvanek@redhat.com> |
---|---|
date | Wed, 05 Sep 2018 18:18:40 +0200 |
parents | 2fa722d9abfd |
children | 047c7c709220 |
files | .hgignore ChangeLog Makefile.am rust-launcher/src/hardcoded_paths.rs rust-launcher/src/jvm_from_properties.rs rust-launcher/src/jvm_from_properties_resolver.rs rust-launcher/src/main.rs rust-launcher/src/os_access.rs rust-launcher/src/property.rs rust-launcher/src/utils.rs |
diffstat | 10 files changed, 949 insertions(+), 13 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Mon Aug 13 16:47:25 2018 +0200 +++ b/.hgignore Wed Sep 05 18:18:40 2018 +0200 @@ -14,4 +14,4 @@ netx-dist-tests-whitelist rust-launcher/target rust-launcher/Cargo.lock - +rust-launcher/.idea
--- a/ChangeLog Mon Aug 13 16:47:25 2018 +0200 +++ b/ChangeLog Wed Sep 05 18:18:40 2018 +0200 @@ -1,3 +1,18 @@ +2018-09-05 Jiri Vanek <jvanek@redhat.com> + + Implemented JVM recognition in rust launchers + * .hgignore: added rust-launcher/.idea + * .Makefile: (launcher.build) adapted to tmp handling - exporting rust_tests_tmp and creating it + * rust-launcher/src/hardcoded_paths.rs: not-using returns consistently + * rust-launcher/src/jvm_from_properties.rs: logic to parse properties file, and to read JDK value from here + * rust-launcher/src/jvm_from_properties_resolver.rs: logic to waterfall all usable properties files and try + locate JDK here. + * rust-launcher/src/main.rs: now trying various JDKS: proeprties->JAVA_HOME->registry->default (from build) + * rust-launcher/src/os_access.rs: set of classes serving to bridge few details where win and Linux may differ + * rust-launcher/src/property.rs: class to read generic properties file + * rust-launcher/src/utils.rs: class for various utility methods. Including testing subclass + + 2018-08-13 Jiri Vanek <jvanek@redhat.com> * LICENSE: new file. Added explicit license with details
--- a/Makefile.am Mon Aug 13 16:47:25 2018 +0200 +++ b/Makefile.am Wed Sep 05 18:18:40 2018 +0200 @@ -878,6 +878,8 @@ # there is curently harecoded sh, so it can somehow basically work # see the DESKTOP_SUFFIX for final tuning launcher.build/$(javaws) launcher.build/$(itweb_settings) launcher.build/$(policyeditor): rust-launcher/src/main.rs rust-launcher/Cargo.toml + export ITW_TMP_REPLACEMENT=$(TESTS_DIR)/rust_tests_tmp ; \ + mkdir -p $$ITW_TMP_REPLACEMENT; \ filename=`basename $@` ; \ type=$${filename%.*} ; \ srcs=$(TOP_SRC_DIR)/rust-launcher ; \
--- a/rust-launcher/src/hardcoded_paths.rs Mon Aug 13 16:47:25 2018 +0200 +++ b/rust-launcher/src/hardcoded_paths.rs Wed Sep 05 18:18:40 2018 +0200 @@ -12,33 +12,31 @@ pub fn get_jre() -> &'static str { - return JRE.unwrap_or("JRE-dev-unspecified") + JRE.unwrap_or("JRE-dev-unspecified") } pub fn get_java() -> &'static str { - return JAVA.unwrap_or("JAVA-dev-unspecified") + JAVA.unwrap_or("JAVA-dev-unspecified") } pub fn get_main() -> &'static str { - return MAIN_CLASS.unwrap_or("MAIN_CLASS-dev-unspecified") + MAIN_CLASS.unwrap_or("MAIN_CLASS-dev-unspecified") } pub fn get_name() -> &'static str { - return PROGRAM_NAME.unwrap_or("PROGRAM_NAME-dev-unspecified") + PROGRAM_NAME.unwrap_or("PROGRAM_NAME-dev-unspecified") } pub fn get_bin() -> &'static str { - return BIN_LOCATION.unwrap_or("BIN_LOCATION-dev-unspecified") + BIN_LOCATION.unwrap_or("BIN_LOCATION-dev-unspecified") } - /*new variables*/ /*tests*/ #[cfg(test)] mod tests { - #[test] fn variables_non_default() { assert_ne!(String::from(super::get_jre()).trim(), String::from("JRE-dev-unspecified"));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust-launcher/src/jvm_from_properties.rs Wed Sep 05 18:18:40 2018 +0200 @@ -0,0 +1,305 @@ +use property; + +use std; +use std::env; +use std::string::String; +use std::fs::File; + +static ICEDTEA_WEB: &'static str = "icedtea-web"; +pub static DEPLOYMENT_PROPERTIES: &'static str = "deployment.properties"; +pub static PROPERTY_NAME: &'static str = "deployment.jre.dir"; + +fn is_file(path: &std::path::PathBuf) -> bool { + let mdr = path.metadata(); + match mdr { + Ok(md) => md.is_file(), + Err(_e) => false + } +} + +fn get_home() -> Option<std::path::PathBuf> { + match env::home_dir() { + Some(p) => Some(p), + None => None + } +} + +fn get_config_dir() -> Option<std::path::PathBuf> { + match env::var("XDG_CONFIG_HOME") { + Ok(war) => { + Some(std::path::PathBuf::from(war)) + } + Err(_e) => { + match get_home() { + Some(mut p) => { + p.push(".config"); + Some(p) + } + None => None + } + } + } +} + +pub fn get_itw_config_dir() -> Option<std::path::PathBuf> { + match get_config_dir() { + Some(mut p) => { + p.push(ICEDTEA_WEB); + Some(p) + } + None => None + } +} + + +pub fn get_itw_legacy_config_dir() -> Option<std::path::PathBuf> { + match get_home() { + Some(mut p) => { + p.push(".icedtea"); + Some(p) + } + None => None + } +} + + +pub fn get_itw_config_file() -> Option<std::path::PathBuf> { + match get_itw_config_dir() { + Some(mut p) => { + p.push(DEPLOYMENT_PROPERTIES); + Some(p) + } + None => None + } +} + +pub fn get_itw_legacy_config_file() -> Option<std::path::PathBuf> { + match get_itw_legacy_config_dir() { + Some(mut p) => { + p.push(DEPLOYMENT_PROPERTIES); + Some(p) + } + None => None + } +} + + +pub fn get_itw_legacy_global_config_file() -> Option<std::path::PathBuf> { + let mut path = std::path::PathBuf::from("/etc/.java/.deploy"); + path.push(DEPLOYMENT_PROPERTIES); + Some(path) +} + +pub fn get_itw_global_config_file() -> Option<std::path::PathBuf> { + let mut path = std::path::PathBuf::from("/etc/.java/deployment"); + path.push(DEPLOYMENT_PROPERTIES); + Some(path) +} + + +pub fn check_file_for_property_jredir(file: File) -> Option<String> { + check_file_for_property(file, PROPERTY_NAME) +} + +fn check_file_for_property(file: File, key: &str) -> Option<String> { + let p = property::Property::load(file, key); + match p { + None => { None } + Some(property) => { + Some(property.value) + } + } +} + + +pub fn get_jre_from_file(file: Option<std::path::PathBuf>) -> Option<String> { + match file { + None => None, + Some(path) => { + get_jre_from_file_direct(path) + } + } +} + +fn get_jre_from_file_direct(path: std::path::PathBuf) -> Option<String> { + if !path.exists() { + None + } else if !is_file(&path) { + return None; + } else { + let fileresult = File::open(path); + match fileresult { + Err(_fe) => None, + Ok(file) => { + let result = check_file_for_property_jredir(file); + result + } + } + } +} + +pub fn verify_jdk_string(file: &String) -> bool { + verify_jdk_path(&std::path::PathBuf::from(file)) +} + +fn verify_jdk_path(ffile: &std::path::PathBuf) -> bool { + let mut file = ffile.clone(); + file.push("bin"); + file.push("java"); + if !file.exists() { + false + } else if !is_file(&file) { + false + } else { + true + } +} + +/*tests*/ +#[cfg(test)] +mod tests { + use std; + use std::fs::File; + use utils::tests_utils as tu; + + + #[test] + fn is_not_file_() { + let r = super::is_file(&std::path::PathBuf::from("/definitely/not/existing/file")); + assert_eq!(false, r); + } + + #[test] + fn is_file_() { + let dir = tu::create_tmp_file(); + let r = super::is_file(&dir); + tu::debuggable_remove_file(&dir); + assert_eq!(true, r); + } + + #[test] + fn check_file_for_property_jredir_not_found() { + let path = tu::create_tmp_file(); + let f = File::open(&path); + let prop = super::check_file_for_property_jredir(f.expect("file was not opened")); + tu::debuggable_remove_file(&path); + assert_eq!(None, prop); + } + + #[test] + fn check_file_for_property_jredir() { + let path = tu::create_tmp_propfile_with_content(); + let f = File::open(&path); + let prop = super::check_file_for_property_jredir(f.expect("file was not opened")); + tu::debuggable_remove_file(&path); + assert_eq!("/some/jre", prop.expect("property was supposed to be loaded")); + } + + + #[test] + fn check_file_for_property_not_found() { + let path = tu::create_tmp_propfile_with_content(); + let f = File::open(&path); + let k = "not_existing_key"; + let prop = super::check_file_for_property(f.expect("file was not opened"), k); + tu::debuggable_remove_file(&path); + assert_eq!(None, prop); + } + + #[test] + fn check_file_for_property_item_exists() { + let path = tu::create_tmp_propfile_with_content(); + let f = File::open(&path); + let k = "key2"; + let prop = super::check_file_for_property(f.expect("file was not opened"), k); + tu::debuggable_remove_file(&path); + assert_eq!("val2", prop.expect("property was supposed to be loaded")); + } + + #[test] + fn get_jre_from_file_exists() { + let path = tu::create_tmp_propfile_with_content(); + let prop = super::get_jre_from_file(Some(path.clone())); + tu::debuggable_remove_file(&path); + assert_eq!("/some/jre", prop.expect("property was supposed to be loaded")); + } + + #[test] + fn get_jre_from_file_not_found() { + let path = tu::create_tmp_file(); + let prop = super::get_jre_from_file(Some(path.clone())); + tu::debuggable_remove_file(&path); + assert_eq!(None, prop); + } + + + #[test] + fn get_jre_from_file_notexists() { + let path = tu::create_tmp_file(); + tu::debuggable_remove_file(&path); + let prop = super::get_jre_from_file(Some(path)); + assert_eq!(None, prop); + } + + #[test] + fn get_jre_from_file_none() { + let prop = super::get_jre_from_file(None); + assert_eq!(None, prop); + } + + #[test] + fn verify_jdk_string_verify_jdk_path_jdk_ok() { + let master_dir = tu::fake_jre(true); + let vs = super::verify_jdk_string(&master_dir.display().to_string()); + let vp = super::verify_jdk_path(&master_dir); + tu::debuggable_remove_dir(&master_dir); + assert_eq!(true, vs); + assert_eq!(true, vp); + } + + #[test] + fn verify_jdk_string_verify_jdk_path_jdk_bad() { + let master_dir = tu::fake_jre(false); + let vs = super::verify_jdk_string(&master_dir.display().to_string()); + let vp = super::verify_jdk_path(&master_dir); + tu::debuggable_remove_dir(&master_dir); + assert_eq!(false, vs); + assert_eq!(false, vp); + } + + #[test] + fn check_config_files_paths() { + let p1 = super::get_itw_config_dir(); + let p2 = super::get_itw_legacy_config_dir(); + let p3 = super::get_itw_config_file(); + let p4 = super::get_itw_legacy_config_file(); + let p5 = super::get_itw_legacy_global_config_file(); + let p6 = super::get_itw_global_config_file(); + assert_ne!(None, p1); + assert_ne!(None, p2); + assert_ne!(None, p3); + assert_ne!(None, p4); + assert_ne!(None, p5); + assert_ne!(None, p6); + println!("{}", p1.clone().expect("unwrap failed").display()); + println!("{}", p2.clone().expect("unwrap failed").display()); + println!("{}", p3.clone().expect("unwrap failed").display()); + println!("{}", p4.clone().expect("unwrap failed").display()); + println!("{}", p5.clone().expect("unwrap failed").display()); + println!("{}", p6.clone().expect("unwrap failed").display()); + assert_eq!(true, p1.clone().expect("unwrap failed").display().to_string().contains("icedtea-web")); + assert_eq!(true, p2.clone().expect("unwrap failed").display().to_string().contains(".icedtea")); + assert_eq!(true, p3.clone().expect("unwrap failed").display().to_string().contains("icedtea-web")); + assert_eq!(true, p3.clone().expect("unwrap failed").display().to_string().ends_with("deployment.properties")); + assert_eq!(true, p4.clone().expect("unwrap failed").display().to_string().contains(".icedtea")); + assert_eq!(true, p4.clone().expect("unwrap failed").display().to_string().ends_with("deployment.properties")); + assert_eq!(true, p5.clone().expect("unwrap failed").display().to_string().contains("etc")); + assert_eq!(true, p5.clone().expect("unwrap failed").display().to_string().contains(".java")); + assert_eq!(true, p5.clone().expect("unwrap failed").display().to_string().contains(".deploy")); + assert_eq!(true, p5.clone().expect("unwrap failed").display().to_string().ends_with("deployment.properties")); + assert_eq!(true, p5.clone().expect("unwrap failed").display().to_string().contains("etc")); + assert_eq!(true, p5.clone().expect("unwrap failed").display().to_string().contains(".java")); + assert_eq!(true, p5.clone().expect("unwrap failed").display().to_string().contains("deployment")); + assert_eq!(true, p6.clone().expect("unwrap failed").display().to_string().ends_with("deployment.properties")); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust-launcher/src/jvm_from_properties_resolver.rs Wed Sep 05 18:18:40 2018 +0200 @@ -0,0 +1,217 @@ +use hardcoded_paths; +use jvm_from_properties; +use os_access; + +use std; +use std::string::String; +use std::fmt::Write; + +pub fn try_jdk_from_properties(logger: &os_access::Os) -> Option<String> { + let array: [Option<std::path::PathBuf>; 4] = [ + jvm_from_properties::get_itw_config_file(), + jvm_from_properties::get_itw_legacy_config_file(), + jvm_from_properties::get_itw_legacy_global_config_file(), + jvm_from_properties::get_itw_global_config_file() + ]; + try_jdk_from_properties_files(logger, &array) +} + +fn try_jdk_from_properties_files(logger: &os_access::Os, array: &[Option<std::path::PathBuf>]) -> Option<String> { + for jdk in array { + let mut info1 = String::new(); + write!(&mut info1, "{} ", "itw-rust-debug: checking jre in:").expect("unwrap failed"); + write!(&mut info1, "{}", jdk.clone().unwrap_or(std::path::PathBuf::from("None")).display()).expect("unwrap failed"); + logger.log(&info1); + match jvm_from_properties::get_jre_from_file(jdk.clone()) { + Some(path) => { + let mut info2 = String::new(); + write!(&mut info2, "{} ", "itw-rust-debug: located").expect("unwrap failed"); + write!(&mut info2, "{}", path).expect("unwrap failed"); + write!(&mut info2, " in file {}", jdk.clone().expect("file should be already verified").display()).expect("unwrap failed"); + logger.log(&info2); + if jvm_from_properties::verify_jdk_string(&path) { + return Some(path); + } else { + //the only output out of verbose mode + let mut res = String::new(); + write!(&mut res, "{}", "Your custom JRE ").expect("unwrap failed"); + write!(&mut res, "{}", path).expect("unwrap failed"); + write!(&mut res, "{}", " read from ").expect("unwrap failed"); + write!(&mut res, "{}", jdk.clone().expect("jre path should be loaded").display()).expect("unwrap failed"); + write!(&mut res, "{}", " under key ").expect("unwrap failed"); + write!(&mut res, "{}", jvm_from_properties::PROPERTY_NAME).expect("unwrap failed"); + write!(&mut res, "{}", " is not valid. Trying other config files, then using default (").expect("unwrap failed"); + write!(&mut res, "{}", hardcoded_paths::get_java()).expect("unwrap failed"); + write!(&mut res, "{}", ", ").expect("unwrap failed"); + write!(&mut res, "{}", hardcoded_paths::get_jre()).expect("unwrap failed"); + write!(&mut res, "{}", ", registry or JAVA_HOME) in attempt to start. Please fix this.").expect("unwrap failed"); + logger.info(&res); + } + } + None => { + logger.log("itw-rust-debug: property not located or file inaccessible"); + } + } + } + None +} + +/*tests*/ +/*To print the diagnostic output use `cargo test -- --nocapture +`*/ +#[cfg(test)] +mod tests { + use std; + use os_access; + use std::cell::RefCell; + use utils::tests_utils as tu; + //if you wont to investigate files used for testing + // use cargo test -- --nocapture to see files which needs delete + static DELETE_TEST_FILES: bool = true; + + pub struct TestLogger { + vec: RefCell<Vec<String>>, + } + + impl TestLogger { + fn get_log(&self) -> String { + let joined = self.vec.borrow_mut().join("; "); + joined + } + } + + impl os_access::Os for TestLogger { + fn log(&self, s: &str) { + let ss = String::from(s); + self.vec.borrow_mut().push(ss); + } + + fn info(&self, s: &str) { + let ss = String::from(s); + self.vec.borrow_mut().push(ss); + } + + fn get_registry_jdk(&self) -> Option<std::path::PathBuf> { + None + } + } + + #[test] + fn try_jdk_from_properties_files_4nothing() { + let array: [Option<std::path::PathBuf>; 4] = [ + None, + None, + None, + None + ]; + let os = TestLogger { vec: RefCell::new(Vec::new()) }; + let r = super::try_jdk_from_properties_files(&os, &array); + println!("{}", &os.get_log()); + assert_eq!(None, r); + } + + #[test] + fn try_jdk_from_properties_files_4nonexisting() { + let array: [Option<std::path::PathBuf>; 4] = [ + Some(std::path::PathBuf::from("Nonexisting file 1")), + Some(std::path::PathBuf::from("Nonexisting file 2")), + Some(std::path::PathBuf::from("Nonexisting file 3")), + Some(std::path::PathBuf::from("Nonexisting file 4")), + ]; + let os = TestLogger { vec: RefCell::new(Vec::new()) }; + let r = super::try_jdk_from_properties_files(&os, &array); + println!("{}", &os.get_log()); + assert_eq!(None, r); + } + + fn clean_fake_files(array: &[Option<std::path::PathBuf>]) { + for jdk in array { + match jdk.clone() { + Some(path) => { + if DELETE_TEST_FILES { + tu::debuggable_remove_file(&path); + } else { + println!("file {} intentionally not deleted!", path.display()); + } + } + None => {} + } + } + } + + #[test] + fn try_jdk_from_properties_files_4empty() { + let array: [Option<std::path::PathBuf>; 4] = [ + Some(std::path::PathBuf::from(tu::create_tmp_file())), + Some(std::path::PathBuf::from(tu::create_tmp_file())), + Some(std::path::PathBuf::from(tu::create_tmp_file())), + Some(std::path::PathBuf::from(tu::create_tmp_file())), + ]; + let os = TestLogger { vec: RefCell::new(Vec::new()) }; + let r = super::try_jdk_from_properties_files(&os, &array); + println!("{}", &os.get_log()); + clean_fake_files(&array); + assert_eq!(None, r); + } + + #[test] + fn try_jdk_from_properties_files_invalid_jdk() { + let array: [Option<std::path::PathBuf>; 4] = [ + Some(std::path::PathBuf::from(tu::create_tmp_propfile_with_custom_jre_content("non/existing/jre1"))), + Some(std::path::PathBuf::from(tu::create_tmp_propfile_with_custom_jre_content("non/existing/jre2"))), + Some(std::path::PathBuf::from(tu::create_tmp_propfile_with_custom_jre_content("non/existing/jre3"))), + Some(std::path::PathBuf::from(tu::create_tmp_propfile_with_custom_jre_content("non/existing/jre4"))), + ]; + let os = TestLogger { vec: RefCell::new(Vec::new()) }; + let r = super::try_jdk_from_properties_files(&os, &array); + println!("{}", &os.get_log()); + clean_fake_files(&array); + assert_eq!(None, r); + assert_eq!(true, os.get_log().contains("is not valid")); + assert_eq!(true, os.get_log().contains("non/existing/jre1")); + assert_eq!(true, os.get_log().contains("non/existing/jre2")); + assert_eq!(true, os.get_log().contains("non/existing/jre3")); + assert_eq!(true, os.get_log().contains("non/existing/jre4")); + } + + #[test] + fn try_jdk_from_properties_files_none_and_valid() { + let master_dir = tu::fake_jre(true); + let array: [Option<std::path::PathBuf>; 4] = [ + Some(std::path::PathBuf::from(tu::create_tmp_file())), + Some(std::path::PathBuf::from(tu::create_tmp_propfile_with_custom_jre_content(&master_dir.display().to_string()))), + Some(std::path::PathBuf::from(tu::create_tmp_propfile_with_custom_jre_content("non/existing/jre3"))), + Some(std::path::PathBuf::from(tu::create_tmp_propfile_with_custom_jre_content("non/existing/jre4"))), + ]; + let os = TestLogger { vec: RefCell::new(Vec::new()) }; + let r = super::try_jdk_from_properties_files(&os, &array); + println!("{}", &os.get_log()); + clean_fake_files(&array); + assert_ne!(None, r); + assert_ne!(true, os.get_log().contains("is not valid")); + assert_ne!(true, os.get_log().contains("non/existing/jre3")); + assert_ne!(true, os.get_log().contains("non/existing/jre4")); + assert_eq!(master_dir.display().to_string(), r.expect("r should be full")); + } + + #[test] + fn try_jdk_from_properties_files_none_and_more_valid() { + let master_dir1 = tu::fake_jre(true); + let master_dir2 = tu::fake_jre(true); + let array: [Option<std::path::PathBuf>; 4] = [ + Some(std::path::PathBuf::from(tu::create_tmp_file())), + Some(std::path::PathBuf::from(tu::create_tmp_propfile_with_custom_jre_content(&master_dir1.display().to_string()))), + Some(std::path::PathBuf::from(tu::create_tmp_file())), + Some(std::path::PathBuf::from(tu::create_tmp_propfile_with_custom_jre_content(&master_dir2.display().to_string()))), + ]; + let os = TestLogger { vec: RefCell::new(Vec::new()) }; + let r = super::try_jdk_from_properties_files(&os, &array); + println!("{}", &os.get_log()); + clean_fake_files(&array); + assert_ne!(None, r); + assert_ne!(true, os.get_log().contains("is not valid")); + assert_eq!(master_dir1.display().to_string(), r.expect("also this r should be full")); + } +} + +
--- a/rust-launcher/src/main.rs Mon Aug 13 16:47:25 2018 +0200 +++ b/rust-launcher/src/main.rs Wed Sep 05 18:18:40 2018 +0200 @@ -1,9 +1,58 @@ mod hardcoded_paths; +mod jvm_from_properties; +mod os_access; +mod jvm_from_properties_resolver; +mod utils; +mod property; + +use std::string::String; +use std::fmt::Write; +use os_access::Os; +use std::env; + fn main() { - println!("{}",hardcoded_paths::get_jre()); - println!("{}",hardcoded_paths::get_java()); - println!("{}",hardcoded_paths::get_main()); - println!("{}",hardcoded_paths::get_name()); - println!("{}",hardcoded_paths::get_bin()); + //TODO verbose will be populated by -verbose in arguments and augmented by deployment properties + let os = os_access::Linux::new(true); + let java_dir: std::path::PathBuf; + let mut info1 = String::new(); + write!(&mut info1, "{}", "itw-rust-debug: trying jdk over properties (").expect("unwrap failed"); + write!(&mut info1, "{}", jvm_from_properties::PROPERTY_NAME).expect("unwrap failed"); + write!(&mut info1, "{}", ")").expect("unwrap failed"); + os.log(&info1); + match jvm_from_properties_resolver::try_jdk_from_properties(&os) { + Some(path) => { + java_dir = std::path::PathBuf::from(path); + os.log("itw-rust-debug: found and using"); + } + None => { + os.log("itw-rust-debug: nothing"); + os.log("itw-rust-debug: trying jdk JAVA_HOME"); + match env::var("JAVA_HOME") { + Ok(war) => { + java_dir = std::path::PathBuf::from(war); + os.log("itw-rust-debug: found and using"); + } + Err(_e) => { + os.log("itw-rust-debug: nothing"); + os.log("itw-rust-debug: trying jdk from registry"); + match os.get_registry_jdk() { + Some(path) => { + java_dir = path; + os.log("itw-rust-debug: found and using"); + } + None => { + os.log("itw-rust-debug: nothing"); + os.log("itw-rust-debug: failing down to hardcoded"); + java_dir = std::path::PathBuf::from(hardcoded_paths::get_jre()); + } + } + } + } + } + } + let mut info2 = String::new(); + write!(&mut info2, "{}", "itw-rust-debug: selected jre: ").expect("unwrap failed"); + write!(&mut info2, "{}", java_dir.display()).expect("unwrap failed"); + os.log(&info2); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust-launcher/src/os_access.rs Wed Sep 05 18:18:40 2018 +0200 @@ -0,0 +1,34 @@ +use std; + +pub trait Os { + //logging "api" can change + fn log(&self, s: &str); + fn info(&self, s: &str); + fn get_registry_jdk(&self) -> Option<std::path::PathBuf>; +} + +pub struct Linux { + verbose: bool, +} + +impl Linux { + pub fn new(debug: bool) -> Linux { + Linux { verbose: debug } + } +} + +impl Os for Linux { + fn log(&self, s: &str) { + if self.verbose { + println!("{}", s); + } + } + + fn info(&self, s: &str) { + println!("{}", s); + } + + fn get_registry_jdk(&self) -> Option<std::path::PathBuf> { + None + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust-launcher/src/property.rs Wed Sep 05 18:18:40 2018 +0200 @@ -0,0 +1,190 @@ +use std::fmt; +use std::io::{BufReader, BufRead}; +use std::fs::File; + +pub struct Property { + pub key: String, + pub value: String +} + +impl fmt::Debug for Property { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Property {{ k: {}, v: {} }}", self.key, self.value) + } +} + +impl PartialEq for Property { + fn eq(&self, other: &Property) -> bool { + self.key == other.key && self.value == other.value + } +} + +impl Property { + pub fn load(file: File, key: &str) -> Option<Property> { + let r = check_file_for_property(file, key); + match r { + None => { None } + Some(value) => { + Some(Property { key: key.to_string(), value: value }) + } + } + } +} + +/* + *checked_split is not safe. If you are splitting out of bounds, you get thread panic + */ +fn checked_split(s: String, i: usize) -> Property { + let key = &s[..i]; + let val = &s[(i + 1)..]; + Property { key: String::from(key.trim()), value: String::from(val.trim()) } +} + +fn split_property(string: &String) -> Option<Property> { + let trimmed = string.trim().to_string(); + if trimmed.starts_with("#") { + None + } else { + let eq_char = match trimmed.find("=") { + Some(i) => i, + None => usize::max_value() + }; + let doubledot_char = match trimmed.find(":") { + Some(i) => i, + None => usize::max_value() + }; + if eq_char == doubledot_char && doubledot_char == usize::max_value() { + None + } else if eq_char <= doubledot_char { + Some(checked_split(trimmed, eq_char)) + } else { + Some(checked_split(trimmed, doubledot_char)) + } + } +} + +fn check_file_for_property(file: File, key: &str) -> Option<String> { + let bf = BufReader::new(file); + for lineresult in bf.lines() { + match lineresult { + Err(_le) => { + return None; + } + Ok(line) => { + let kv = split_property(&line); + match kv { + None => {} + Some(kvv) => { + if kvv.key.eq(key) { + return Some(kvv.value); + } + } + } + } + } + } + None +} + +/*tests*/ +#[cfg(test)] +mod tests { + use utils::tests_utils as tu; + use std::fs::File; + + #[test] + fn check_property() { + let p1 = super::Property { key: String::from("k1"), value: String::from("v1") }; + let p2 = super::Property { key: String::from("k1"), value: String::from("v1") }; + let p3 = super::Property { key: String::from("k2"), value: String::from("v1") }; + let p4 = super::Property { key: String::from("k1"), value: String::from("v2") }; + let p5 = super::Property { key: String::from("k2"), value: String::from("v2") }; + assert_eq!(p1, p2); + assert_ne!(p1, p3); + assert_ne!(p1, p4); + assert_ne!(p1, p5); + } + + #[test] + fn checked_split() { + let p1 = super::checked_split("aXb".to_string(), 1); + assert_eq!("a".to_string(), p1.key); + assert_eq!("b".to_string(), p1.value); + let p2 = super::checked_split("aXb".to_string(), 0); + assert_eq!("".to_string(), p2.key); + assert_eq!("Xb".to_string(), p2.value); + let p3 = super::checked_split("aXb".to_string(), 2); + assert_eq!("aX".to_string(), p3.key); + assert_eq!("".to_string(), p3.value); + } + + #[test] + fn split_property_nodelimiter() { + let p = super::split_property(&"aXb".to_string()); + assert_eq!(None, p); + } + + #[test] + fn split_property_colon_delimiter() { + let p = super::split_property(&"a:b".to_string()).expect("should be some!"); + assert_eq!("a".to_string(), p.key); + assert_eq!("b".to_string(), p.value); + } + + #[test] + fn split_property_equals_delimiter() { + let p = super::split_property(&"a=b".to_string()).expect("should be some!"); + assert_eq!("a".to_string(), p.key); + assert_eq!("b".to_string(), p.value); + } + + #[test] + fn split_property_mixed_delimiter() { + let p1 = super::split_property(&"a=:b".to_string()).expect("should be some!"); + assert_eq!("a".to_string(), p1.key); + assert_eq!(":b".to_string(), p1.value); + let p2 = super::split_property(&"a:=b".to_string()).expect("should be some!"); + assert_eq!("a".to_string(), p2.key); + assert_eq!("=b".to_string(), p2.value); + } + + #[test] + fn split_property_trimming() { + let p1 = super::split_property(&" a = ".to_string()).expect("should be some!"); + assert_eq!("a".to_string(), p1.key); + assert_eq!("".to_string(), p1.value); + let p2 = super::split_property(&"a : b".to_string()).expect("should be some!"); + assert_eq!("a".to_string(), p2.key); + assert_eq!("b".to_string(), p2.value); + let p3 = super::split_property(&" a :b ".to_string()).expect("should be some!"); + assert_eq!("a".to_string(), p3.key); + assert_eq!("b".to_string(), p3.value) + } + + #[test] + fn split_property_reals() { + let p1 = super::split_property(&"java.property = some:terrible:jdk".to_string()).expect("should be some!"); + assert_eq!("java.property".to_string(), p1.key); + assert_eq!("some:terrible:jdk".to_string(), p1.value); + } + + #[test] + fn check_load_not_found() { + let path = tu::create_tmp_propfile_with_content(); + let f = File::open(&path); + let k = "not_existing_key"; + let prop = super::Property::load(f.expect("file was not opened"), k); + tu::debuggable_remove_file(&path); + assert_eq!(None, prop); + } + + #[test] + fn check_load_item_exists() { + let path = tu::create_tmp_propfile_with_content(); + let f = File::open(&path); + let k = "key2"; + let prop = super::Property::load(f.expect("file was not opened"), k); + tu::debuggable_remove_file(&path); + assert_eq!("val2", prop.expect("property was supposed to be loaded").value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust-launcher/src/utils.rs Wed Sep 05 18:18:40 2018 +0200 @@ -0,0 +1,126 @@ +#[cfg(test)] +pub mod tests_utils { + use std; + use std::fs::File; + use std::time::{SystemTime, UNIX_EPOCH}; + use std::fs::OpenOptions; + use std::fmt::Write as fmt_write; + use std::io::Write; + use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; + use jvm_from_properties; + + // rand is in separate crate, so using atomic increment instead + static TMP_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; + // use cargo test -- --nocapture to see files which needs delete + static CLEAN_TMP_FILES: bool = true; + + pub fn debuggable_remove_file(file: &std::path::PathBuf) { + if CLEAN_TMP_FILES { + std::fs::remove_file(file).expect("remove of tmp failed"); + } else { + println!("file {} intentionally not deleted!", file.display()); + } + } + + + pub fn debuggable_remove_dir(dir: &std::path::PathBuf) { + if CLEAN_TMP_FILES { + std::fs::remove_dir_all(dir).expect("remove of tmp dir failed"); + } else { + println!("directory {} intentionally not deleted!", dir.display()); + } + } + + pub fn create_tmp_name() -> String { + // base name on time + let now = SystemTime::now(); + let since_the_epoch = now.duration_since(UNIX_EPOCH).expect("Time went backwards"); + let in_ms = since_the_epoch.as_secs() * 1000 + since_the_epoch.subsec_nanos() as u64 / 1_000_000; + //each [test] run via cargo test run (by default) in its own thread, so two files of same name may be handled over time + //thus adding also atomic counter + let id = TMP_COUNTER.fetch_add(1, Ordering::AcqRel); + //create nice name + let mut owned_string: String = "itw-".to_owned(); + owned_string.push_str(&in_ms.to_string()); + owned_string.push_str("-"); + owned_string.push_str(&id.to_string()); + owned_string.push_str("-rusttmp"); + owned_string + } + + pub fn create_scratch_dir() -> std::path::PathBuf { + let exepath = std::env::current_exe().expect("Current exe path not accessible"); + let mut project_dir = exepath.parent().expect("Cannot get parent"); + while !project_dir.to_str().expect("Cannot get path name").ends_with("rust-launcher") { + project_dir = project_dir.parent().expect("Cannot get parent"); + } + let mut scratch_dir = std::path::PathBuf::new(); + scratch_dir.push(project_dir); + scratch_dir.push("target"); + scratch_dir.push("scratch"); + if !scratch_dir.exists() { + std::fs::create_dir_all(scratch_dir.as_path()).expect("Cannot create scratch dir"); + } + scratch_dir + } + + const CUSTOM_TMP_DIR: Option<&'static str> = option_env!("ITW_TMP_REPLACEMENT"); + + pub fn prepare_tmp_dir() -> std::path::PathBuf { + if CUSTOM_TMP_DIR.is_some() { + let dir = std::path::PathBuf::from(CUSTOM_TMP_DIR.expect("is_some failed for CUSTOM_TMP_DIR")); + assert_eq!(true, dir.exists()); + dir + } else { + create_scratch_dir() + } + } + + pub fn create_tmp_file() -> std::path::PathBuf { + //let mut dir = CUSTOM_TMP_DIR.unwrap_or(env::temp_dir()); + let mut dir = prepare_tmp_dir(); + let s = create_tmp_name(); + dir.push(s); + let f = File::create(&dir).expect("File created"); + f.sync_all().expect("data were written"); + dir + } + + pub fn create_tmp_propfile_with_content() -> std::path::PathBuf { + create_tmp_propfile_with_custom_jre_content("/some/jre") + } + + pub fn create_tmp_propfile_with_custom_jre_content(jre_path: &str) -> std::path::PathBuf { + let dir = create_tmp_file(); + let mut f = OpenOptions::new() + .write(true) + .open(&dir).expect("just created file failed to open"); + let mut res = String::new(); + write!(&mut res, "{}", "key1=val1\n").expect("unwrap failed"); + write!(&mut res, "{}", "key2=val2\n").expect("unwrap failed"); + write!(&mut res, "{}", jvm_from_properties::PROPERTY_NAME).expect("unwrap failed"); + write!(&mut res, "{}", "=").expect("unwrap failed"); + write!(&mut res, "{}", jre_path).expect("unwrap failed"); + write!(&mut res, "{}", "\n").expect("unwrap failed"); + write!(&mut res, "{}", "key2=val3\n").expect("unwrap failed"); + write!(&mut res, "{}", "key4=val4\n").expect("unwrap failed"); + write!(&mut res, "{}", ", ").expect("unwrap failed"); + f.write_all(res.as_bytes()).expect("writing of tmp file failed"); + f.sync_all().expect("data were written"); + dir + } + + pub fn fake_jre(valid: bool) -> std::path::PathBuf { + let mut fake_jre = create_tmp_file(); + debuggable_remove_file(&fake_jre); + let master_dir = fake_jre.clone(); + std::fs::create_dir(&fake_jre).expect("dir creation failed"); + fake_jre.push("bin"); + std::fs::create_dir(&fake_jre).expect("dir creation failed"); + fake_jre.push("java"); + if valid { + File::create(&fake_jre).expect("File created"); + } + master_dir + } +}