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
+    }
+}