diff --git a/src/main/java/org/utplsql/cli/DataSourceProvider.java b/src/main/java/org/utplsql/cli/DataSourceProvider.java index 7fee23e..ccccfd5 100644 --- a/src/main/java/org/utplsql/cli/DataSourceProvider.java +++ b/src/main/java/org/utplsql/cli/DataSourceProvider.java @@ -1,11 +1,14 @@ package org.utplsql.cli; import com.zaxxer.hikari.HikariDataSource; +import org.utplsql.api.EnvironmentVariableUtil; import org.utplsql.cli.datasource.TestedDataSourceProvider; import javax.sql.DataSource; import java.io.File; +import java.lang.reflect.InvocationTargetException; import java.sql.SQLException; +import java.util.List; /** Helper class to give you a ready-to-use datasource * @@ -13,14 +16,62 @@ */ public class DataSourceProvider { + private static String oracleHome = null; + private static String tnsAdmin = null; + static { - String oracleHome = System.getenv("ORACLE_HOME"); - if (oracleHome != null && System.getProperty("oracle.net.tns_admin") == null) { - System.setProperty("oracle.net.tns_admin", - String.join(File.separator, oracleHome, "NETWORK", "ADMIN")); + String tnsAdmin = loadOracleHomeAndTnsAdmin(); + if (tnsAdmin != null && System.getProperty("oracle.net.tns_admin") == null) { + System.setProperty("oracle.net.tns_admin", tnsAdmin); } } + private static String loadOracleHomeAndTnsAdmin() { + tnsAdmin = EnvironmentVariableUtil.getEnvValue("TNS_ADMIN"); + if (directoryExists(tnsAdmin)) { + return tnsAdmin; + } + + oracleHome = EnvironmentVariableUtil.getEnvValue("ORACLE_HOME"); + if (oracleHome != null) { + tnsAdmin = String.join(File.separator, oracleHome, "NETWORK", "ADMIN"); + + if (directoryExists(tnsAdmin)) { + return tnsAdmin; + } + } + + try { + final int root = WinRegistry.HKEY_LOCAL_MACHINE; + final String key = "SOFTWARE\\Oracle"; + + List oracleKeys = WinRegistry.readStringSubKeys(root, key); + for (String oracleKey : oracleKeys) { + String temp = key + "\\" + oracleKey; + + // check for HKEY_LOCAL_MACHINE\SOFTWARE\Oracle\\TNS_ADMIN + tnsAdmin = WinRegistry.readString(root, temp, "TNS_ADMIN"); + if (directoryExists(tnsAdmin)) { + return tnsAdmin; + } + + // check for HKEY_LOCAL_MACHINE\SOFTWARE\Oracle\\ORACLE_HOME + oracleHome = WinRegistry.readString(root, temp, "ORACLE_HOME"); + tnsAdmin = String.join(File.separator, oracleHome, "NETWORK", "ADMIN"); + if (directoryExists(tnsAdmin)) { + return tnsAdmin; + } + } + } catch (ExceptionInInitializerError | IllegalAccessException | InvocationTargetException e) { + } + + return null; + } + + private static boolean directoryExists(String path) { + return path != null && new File(path).exists(); + } + public static DataSource getDataSource(ConnectionInfo info, int maxConnections ) throws SQLException { requireOjdbc(); @@ -33,6 +84,14 @@ public static DataSource getDataSource(ConnectionInfo info, int maxConnections ) return pds; } + public static String getOracleHome() { + return oracleHome; + } + + public static String getTnsAdmin() { + return tnsAdmin; + } + private static void requireOjdbc() { if ( !OracleLibraryChecker.checkOjdbcExists() ) { diff --git a/src/main/java/org/utplsql/cli/RunCommand.java b/src/main/java/org/utplsql/cli/RunCommand.java index cbfca36..7744ad8 100644 --- a/src/main/java/org/utplsql/cli/RunCommand.java +++ b/src/main/java/org/utplsql/cli/RunCommand.java @@ -260,7 +260,8 @@ private void outputMainInformation() { formatter.appendLine(CliVersionInfo.getInfo()); formatter.appendLine(JavaApiVersionInfo.getInfo()); formatter.appendLine("Java-Version: " + System.getProperty("java.version")); - formatter.appendLine("ORACLE_HOME: " + EnvironmentVariableUtil.getEnvValue("ORACLE_HOME")); + formatter.appendLine("ORACLE_HOME: " + DataSourceProvider.getOracleHome()); + formatter.appendLine("TNS_ADMIN: " + DataSourceProvider.getTnsAdmin()); formatter.appendLine("NLS_LANG: " + EnvironmentVariableUtil.getEnvValue("NLS_LANG")); formatter.appendLine(""); formatter.appendLine("Thanks for testing!"); diff --git a/src/main/java/org/utplsql/cli/WinRegistry.java b/src/main/java/org/utplsql/cli/WinRegistry.java new file mode 100644 index 0000000..d3e4d02 --- /dev/null +++ b/src/main/java/org/utplsql/cli/WinRegistry.java @@ -0,0 +1,384 @@ +package org.utplsql.cli; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.ArrayList; +import java.util.List; +import java.util.prefs.Preferences; + +public class WinRegistry { + public static final int HKEY_CURRENT_USER = 0x80000001; + public static final int HKEY_LOCAL_MACHINE = 0x80000002; + public static final int REG_SUCCESS = 0; + public static final int REG_NOTFOUND = 2; + public static final int REG_ACCESSDENIED = 5; + + private static final int KEY_ALL_ACCESS = 0xf003f; + private static final int KEY_READ = 0x20019; + private static final Preferences userRoot = Preferences.userRoot(); + private static final Preferences systemRoot = Preferences.systemRoot(); + private static final Class userClass = userRoot.getClass(); + private static final Method regOpenKey; + private static final Method regCloseKey; + private static final Method regQueryValueEx; + private static final Method regEnumValue; + private static final Method regQueryInfoKey; + private static final Method regEnumKeyEx; + private static final Method regCreateKeyEx; + private static final Method regSetValueEx; + private static final Method regDeleteKey; + private static final Method regDeleteValue; + + static { + try { + regOpenKey = userClass.getDeclaredMethod("WindowsRegOpenKey", + int.class, byte[].class, int.class); + regOpenKey.setAccessible(true); + regCloseKey = userClass.getDeclaredMethod("WindowsRegCloseKey", + int.class); + regCloseKey.setAccessible(true); + regQueryValueEx = userClass.getDeclaredMethod("WindowsRegQueryValueEx", + int.class, byte[].class); + regQueryValueEx.setAccessible(true); + regEnumValue = userClass.getDeclaredMethod("WindowsRegEnumValue", + int.class, int.class, int.class); + regEnumValue.setAccessible(true); + regQueryInfoKey = userClass.getDeclaredMethod("WindowsRegQueryInfoKey1", + int.class); + regQueryInfoKey.setAccessible(true); + regEnumKeyEx = userClass.getDeclaredMethod( + "WindowsRegEnumKeyEx", int.class, int.class, + int.class); + regEnumKeyEx.setAccessible(true); + regCreateKeyEx = userClass.getDeclaredMethod( + "WindowsRegCreateKeyEx", int.class, + byte[].class); + regCreateKeyEx.setAccessible(true); + regSetValueEx = userClass.getDeclaredMethod( + "WindowsRegSetValueEx", int.class, + byte[].class, byte[].class); + regSetValueEx.setAccessible(true); + regDeleteValue = userClass.getDeclaredMethod( + "WindowsRegDeleteValue", int.class, + byte[].class); + regDeleteValue.setAccessible(true); + regDeleteKey = userClass.getDeclaredMethod( + "WindowsRegDeleteKey", int.class, + byte[].class); + regDeleteKey.setAccessible(true); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + private WinRegistry() { } + + /** + * Read a value from key and value name + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @param valueName + * @return the value + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static String readString(int hkey, String key, String valueName) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + return readString(systemRoot, hkey, key, valueName); + } + else if (hkey == HKEY_CURRENT_USER) { + return readString(userRoot, hkey, key, valueName); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Read value(s) and value name(s) form given key + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @return the value name(s) plus the value(s) + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static Map readStringValues(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + return readStringValues(systemRoot, hkey, key); + } + else if (hkey == HKEY_CURRENT_USER) { + return readStringValues(userRoot, hkey, key); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Read the value name(s) from a given key + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @return the value name(s) + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static List readStringSubKeys(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + return readStringSubKeys(systemRoot, hkey, key); + } + else if (hkey == HKEY_CURRENT_USER) { + return readStringSubKeys(userRoot, hkey, key); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Create a key + * @param hkey HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE + * @param key + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void createKey(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int [] ret; + if (hkey == HKEY_LOCAL_MACHINE) { + ret = createKey(systemRoot, hkey, key); + regCloseKey.invoke(systemRoot, ret[0]); + } + else if (hkey == HKEY_CURRENT_USER) { + ret = createKey(userRoot, hkey, key); + regCloseKey.invoke(userRoot, ret[0]); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + if (ret[1] != REG_SUCCESS) { + throw new IllegalArgumentException("rc=" + ret[1] + " key=" + key); + } + } + + /** + * Write a value in a given key/value name + * @param hkey + * @param key + * @param valueName + * @param value + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void writeStringValue + (int hkey, String key, String valueName, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + if (hkey == HKEY_LOCAL_MACHINE) { + writeStringValue(systemRoot, hkey, key, valueName, value); + } + else if (hkey == HKEY_CURRENT_USER) { + writeStringValue(userRoot, hkey, key, valueName, value); + } + else { + throw new IllegalArgumentException("hkey=" + hkey); + } + } + + /** + * Delete a given key + * @param hkey + * @param key + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void deleteKey(int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int rc = -1; + if (hkey == HKEY_LOCAL_MACHINE) { + rc = deleteKey(systemRoot, hkey, key); + } + else if (hkey == HKEY_CURRENT_USER) { + rc = deleteKey(userRoot, hkey, key); + } + if (rc != REG_SUCCESS) { + throw new IllegalArgumentException("rc=" + rc + " key=" + key); + } + } + + /** + * delete a value from a given key/value name + * @param hkey + * @param key + * @param value + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public static void deleteValue(int hkey, String key, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int rc = -1; + if (hkey == HKEY_LOCAL_MACHINE) { + rc = deleteValue(systemRoot, hkey, key, value); + } + else if (hkey == HKEY_CURRENT_USER) { + rc = deleteValue(userRoot, hkey, key, value); + } + if (rc != REG_SUCCESS) { + throw new IllegalArgumentException("rc=" + rc + " key=" + key + " value=" + value); + } + } + + // ===================== + + private static int deleteValue + (Preferences root, int hkey, String key, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + hkey, toCstr(key), KEY_ALL_ACCESS}); + if (handles[1] != REG_SUCCESS) { + return handles[1]; // can be REG_NOTFOUND, REG_ACCESSDENIED + } + int rc = (Integer) regDeleteValue.invoke(root, + new Object[]{ + handles[0], toCstr(value) + }); + regCloseKey.invoke(root, handles[0]); + return rc; + } + + private static int deleteKey(Preferences root, int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int rc = (Integer) regDeleteKey.invoke(root, + new Object[]{hkey, toCstr(key)}); + return rc; // can REG_NOTFOUND, REG_ACCESSDENIED, REG_SUCCESS + } + + private static String readString(Preferences root, int hkey, String key, String value) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + hkey, toCstr(key), KEY_READ}); + if (handles[1] != REG_SUCCESS) { + return null; + } + byte[] valb = (byte[]) regQueryValueEx.invoke(root, new Object[] { + handles[0], toCstr(value) }); + regCloseKey.invoke(root, handles[0]); + return (valb != null ? new String(valb).trim() : null); + } + + private static Map readStringValues + (Preferences root, int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + HashMap results = new HashMap(); + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + hkey, toCstr(key), KEY_READ}); + if (handles[1] != REG_SUCCESS) { + return null; + } + int[] info = (int[]) regQueryInfoKey.invoke(root, + new Object[] {handles[0]}); + + int count = info[0]; // count + int maxlen = info[3]; // value length max + for(int index=0; index readStringSubKeys + (Preferences root, int hkey, String key) + throws IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + List results = new ArrayList(); + int[] handles = (int[]) regOpenKey.invoke(root, new Object[] { + hkey, toCstr(key), KEY_READ + }); + if (handles[1] != REG_SUCCESS) { + return null; + } + int[] info = (int[]) regQueryInfoKey.invoke(root, + new Object[] {handles[0]}); + + int count = info[0]; // Fix: info[2] was being used here with wrong results. Suggested by davenpcj, confirmed by Petrucio + int maxlen = info[3]; // value length max + for(int index=0; index