changeset 2590:3e1ccd4a2d51

Properly handle spaces in paths. Reviewed-by: stooke, neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-February/022176.html
author Severin Gehwolf <sgehwolf@redhat.com>
date Thu, 09 Feb 2017 12:28:11 +0100
parents a6ba41a449c8
children 987a599a8ca5
files common/core/src/test/java/com/redhat/thermostat/common/internal/CustomX509TrustManagerTest.java common/core/src/test/java/com/redhat/thermostat/common/internal/KeyStoreProviderTest.java common/core/src/test/java/com/redhat/thermostat/common/ssl/SSLContextFactoryTest.java config/src/main/java/com/redhat/thermostat/shared/config/internal/SSLConfigurationImpl.java config/src/test/java/com/redhat/thermostat/shared/config/internal/SSLConfigurationImplTest.java dev/perflog-analyzer/src/test/java/com/redhat/thermostat/dev/perf/logs/internal/LogAnalyzerImplTest.java dev/perflog-analyzer/src/test/java/com/redhat/thermostat/dev/perf/logs/internal/StatsParserBuilderTest.java dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/StoragePopulatorCommandTest.java dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/internal/config/PopulationConfigTest.java distribution/packaging/shared/bash-completion/thermostat-completion distribution/scripts/thermostat distribution/scripts/thermostat-command-channel distribution/scripts/thermostat-common distribution/scripts/thermostat-devsetup distribution/tools/verify-bash-completion.sh integration-tests/itest-run/pom.xml storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/DBStartupConfigurationTest.java validate-command/command/src/test/java/com/redhat/thermostat/validate/command/internal/ValidateCommandTest.java vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/BytemanControlCommandTest.java web/server/src/test/java/com/redhat/thermostat/web/server/auth/spi/PropertiesUserValidatorTest.java web/server/src/test/java/com/redhat/thermostat/web/server/auth/spi/PropertiesUsernameRolesLoginModuleTest.java web/server/src/test/java/com/redhat/thermostat/web/server/auth/spi/RolesAmenderTest.java
diffstat 22 files changed, 281 insertions(+), 101 deletions(-) [+]
line wrap: on
line diff
--- a/common/core/src/test/java/com/redhat/thermostat/common/internal/CustomX509TrustManagerTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/internal/CustomX509TrustManagerTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -42,6 +42,9 @@
 import static org.mockito.Mockito.mock;
 
 import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 
@@ -90,8 +93,8 @@
 
     @Test
     public void testLoadEmptyTrustStoreForOur() throws SslInitException {
-        File emptyKeyStore = new File(this.getClass()
-                .getResource("/empty.keystore").getFile());
+        File emptyKeyStore = new File(decodeFilePath(this.getClass()
+                .getResource("/empty.keystore")));
         X509TrustManager tm = new CustomX509TrustManager(null, emptyKeyStore,
                 null);
         assertEquals(0, tm.getAcceptedIssuers().length);
@@ -111,8 +114,8 @@
 
     @Test
     public void testLoadEmptyTrustStoreForOurDefaultAsUsual() throws Exception {
-        File emptyKeyStore = new File(this.getClass()
-                .getResource("/empty.keystore").getFile());
+        File emptyKeyStore = new File(decodeFilePath(this.getClass()
+                .getResource("/empty.keystore")));
         X509TrustManager tm = new CustomX509TrustManager(emptyKeyStore, null);
         // Default list should not be null
         assertNotNull(tm.getAcceptedIssuers());
@@ -125,8 +128,8 @@
     
     @Test
     public void canGetCustomCaCertFromOurTrustManager() throws SslInitException {
-        File ourKeyStore = new File(this.getClass()
-                .getResource("/test_ca.keystore").getFile());
+        File ourKeyStore = new File(decodeFilePath(this.getClass()
+                .getResource("/test_ca.keystore")));
         X509TrustManager tm = new CustomX509TrustManager((X509TrustManager)null, ourKeyStore, "testpassword");
         // keystore contains private key of itself + imported CA cert
         assertEquals(2, tm.getAcceptedIssuers().length);
@@ -138,5 +141,15 @@
                 .getIssuerX500Principal().getName());
     }
 
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
+
 }
 
--- a/common/core/src/test/java/com/redhat/thermostat/common/internal/KeyStoreProviderTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/internal/KeyStoreProviderTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -40,6 +40,9 @@
 import static org.junit.Assert.assertNull;
 
 import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
 
 import org.junit.Test;
 
@@ -67,9 +70,19 @@
     
     @Test
     public void existingFileWithCorrectPasswordWorks() throws SslInitException {
-        File keystore = new File(this.getClass()
-                .getResource("/test_ca.keystore").getFile());
+        File keystore = new File(decodeFilePath(this.getClass()
+                .getResource("/test_ca.keystore")));
         assertNotNull("Should have been able to retrieve and load keystore", KeyStoreProvider.getKeyStore(keystore, "testpassword"));
     }
+
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
 
--- a/common/core/src/test/java/com/redhat/thermostat/common/ssl/SSLContextFactoryTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/common/core/src/test/java/com/redhat/thermostat/common/ssl/SSLContextFactoryTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -45,6 +45,9 @@
 import static org.mockito.Mockito.when;
 
 import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 
@@ -82,8 +85,8 @@
     @Test
     public void verifySetsUpServerContextWithProperKeyMaterial()
             throws Exception {
-        File keystoreFile = new File(this.getClass()
-                .getResource("/cmdChanServer.keystore").getFile());
+        File keystoreFile = new File(decodeFilePath(this.getClass()
+                .getResource("/cmdChanServer.keystore")));
 
         SSLConfiguration sslConf = mock(SSLConfiguration.class);
         when(sslConf.getKeystoreFile()).thenReturn(
@@ -127,8 +130,8 @@
     @Test
     public void verifySetsUpClientContextWithProperTrustManager()
             throws Exception {
-        File keystoreFile = new File(this.getClass()
-                .getResource("/cmdChanServer.keystore").getFile());
+        File keystoreFile = new File(decodeFilePath(this.getClass()
+                .getResource("/cmdChanServer.keystore")));
 
         SSLConfiguration sslConf = mock(SSLConfiguration.class);
         when(sslConf.getKeystoreFile()).thenReturn(
@@ -213,6 +216,15 @@
             // pass
         }
     }
-    
+
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
 
--- a/config/src/main/java/com/redhat/thermostat/shared/config/internal/SSLConfigurationImpl.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/config/src/main/java/com/redhat/thermostat/shared/config/internal/SSLConfigurationImpl.java	Thu Feb 09 12:28:11 2017 +0100
@@ -115,6 +115,7 @@
         } catch (IOException | IllegalArgumentException e) {
             // Could not load ssl properties file. This is fine as it's
             // an optional config.
+            logger.log(Level.FINEST, "Caught exception loading properties. Diagnostics follow.", e);
         }
     }
 
--- a/config/src/test/java/com/redhat/thermostat/shared/config/internal/SSLConfigurationImplTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/config/src/test/java/com/redhat/thermostat/shared/config/internal/SSLConfigurationImplTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -45,6 +45,9 @@
 import static org.mockito.Mockito.verify;
 
 import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
 
 import com.redhat.thermostat.testutils.TestUtils;
 import org.junit.Before;
@@ -57,9 +60,9 @@
     private SSLConfigurationImpl sslConf;
 
     @Before
-    public void setUp() {
+    public void setUp() throws UnsupportedEncodingException {
         sslConf = new SSLConfigurationImpl(null);
-        File clientProps = new File(this.getClass().getResource("/client.properties").getFile());
+        File clientProps = new File(decodeFilePath(this.getClass().getResource("/client.properties")));
         sslConf.initProperties(clientProps);
     }
 
@@ -108,7 +111,7 @@
      */
     @Test
     public void canInitFromSystemHomeConfig() {
-        File systemEtc = new File(this.getClass().getResource("/system_th_home").getFile());
+        File systemEtc = new File(decodeFilePath(this.getClass().getResource("/system_th_home")));
         CommonPaths paths = mock(CommonPaths.class);
         when(paths.getSystemConfigurationDirectory()).thenReturn(systemEtc);
         File userEtc = new File("/thermostat/not-existing-foo");
@@ -142,10 +145,10 @@
      */
     @Test
     public void userHomeConfigOverridesSystem() {
-        File systemEtc = new File(this.getClass().getResource("/system_th_home").getFile());
+        File systemEtc = new File(decodeFilePath(this.getClass().getResource("/system_th_home")));
         CommonPaths paths = mock(CommonPaths.class);
         when(paths.getSystemConfigurationDirectory()).thenReturn(systemEtc);
-        File userEtc = new File(this.getClass().getResource("/user_th_home").getFile());
+        File userEtc = new File(decodeFilePath(this.getClass().getResource("/user_th_home")));
         when(paths.getUserConfigurationDirectory()).thenReturn(userEtc);
         
         // assert preconditions
@@ -181,7 +184,7 @@
         File systemEtc = new File("/thermostat/not-existing-foo");
         CommonPaths paths = mock(CommonPaths.class);
         when(paths.getSystemConfigurationDirectory()).thenReturn(systemEtc);
-        File userEtc = new File(this.getClass().getResource("/user_th_home").getFile());
+        File userEtc = new File(decodeFilePath(this.getClass().getResource("/user_th_home")));
         when(paths.getUserConfigurationDirectory()).thenReturn(userEtc);
         
         // assert preconditions
@@ -238,5 +241,15 @@
         assertFalse(config.enableForCmdChannel());
         assertFalse(config.disableHostnameVerification());
     }
+    
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
 
--- a/dev/perflog-analyzer/src/test/java/com/redhat/thermostat/dev/perf/logs/internal/LogAnalyzerImplTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/dev/perflog-analyzer/src/test/java/com/redhat/thermostat/dev/perf/logs/internal/LogAnalyzerImplTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -42,6 +42,9 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
 
 import org.junit.After;
 import org.junit.Before;
@@ -67,7 +70,7 @@
 
     @Test
     public void verifyBasicAnalysis() {
-        File logFile = new File(this.getClass().getResource("/perflogMultipleLogTags.log").getFile());
+        File logFile = new File(decodeFilePath(this.getClass().getResource("/perflogMultipleLogTags.log")));
         StatsConfig config = new StatsConfig(logFile, SortBy.AVG, Direction.DSC, false);
         LogAnalyzerImpl analyzer = new LogAnalyzerImpl(config);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -85,4 +88,14 @@
         assertTrue(moreOutput.contains(output));
         assertFalse(moreOutput.equals(output));
     }
+    
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
--- a/dev/perflog-analyzer/src/test/java/com/redhat/thermostat/dev/perf/logs/internal/StatsParserBuilderTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/dev/perflog-analyzer/src/test/java/com/redhat/thermostat/dev/perf/logs/internal/StatsParserBuilderTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -42,6 +42,9 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -122,7 +125,7 @@
     @Test
     public void canParseBasicLogFile() throws FileNotFoundException {
         StatsParser parser = StatsParserBuilder.build();
-        File perfLogFile = new File(this.getClass().getResource("/testPerfLogFile.log").getFile());
+        File perfLogFile = new File(decodeFilePath(this.getClass().getResource("/testPerfLogFile.log")));
         Scanner scanner = new Scanner(perfLogFile);
         ArrayList<LineStat> stats = new ArrayList<>();
         while (scanner.hasNextLine()) {
@@ -146,7 +149,7 @@
     @Test
     public void canParseLogFileWithMultipleLogTags() throws FileNotFoundException, IllegalFilterException, ParseException {
         StatsParser parser = StatsParserBuilder.build();
-        File perfLogFile = new File(this.getClass().getResource("/perflogMultipleLogTags.log").getFile());
+        File perfLogFile = new File(decodeFilePath(this.getClass().getResource("/perflogMultipleLogTags.log")));
         Scanner scanner = new Scanner(perfLogFile);
         LogFileStats stats = new LogFileStats(new SharedStatementState(), null);
         // add composite filters for queue stats.
@@ -236,4 +239,14 @@
         stat = parser.parse(logEntry);
         assertNull(stat);
     }
+    
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
--- a/dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/StoragePopulatorCommandTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/StoragePopulatorCommandTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -53,6 +53,9 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
 import java.util.Collections;
 import java.util.Map;
 
@@ -167,7 +170,7 @@
     @Test
     public void testCommandFailsWithInvalidPopulator() throws CommandException {
         final File invalidConfigFile = new File(
-                getClass().getResource("/invalid-config.json").getFile());
+                decodeFilePath(getClass().getResource("/invalid-config.json")));
         command = new StoragePopulatorCommand() {
             @Override
             File getConfigFile(Arguments args) {
@@ -183,8 +186,8 @@
 
     @Test
     public void testCommandPopulatesDatabase() throws CommandException {
-        final File validConfigFile = new File(
-                getClass().getResource("/valid-config.json").getFile());
+        final File validConfigFile = new File(decodeFilePath(
+                getClass().getResource("/valid-config.json")));
         command = new StoragePopulatorCommand() {
             @Override
             File getConfigFile(Arguments args) {
@@ -250,4 +253,14 @@
         threadDao = mock(ThreadDao.class);
         command.bindThreadDAO(threadDao);
     }
+
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
--- a/dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/internal/config/PopulationConfigTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/dev/storage-populator/command/src/test/java/com/redhat/thermostat/storage/populator/internal/config/PopulationConfigTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -42,6 +42,9 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.LinkedList;
@@ -66,7 +69,8 @@
 
     @Test
     public void canParseFromFile() throws IOException {
-        String json = new String(Files.readAllBytes(new File(getClass().getResource("/testconfig").getFile()).toPath()));
+        File testConfig = new File(decodeFilePath(getClass().getResource("/testconfig")));
+        String json = new String(Files.readAllBytes(testConfig.toPath()));
         PopulationConfig config = PopulationConfig.parseFromJsonString(json);
         ConfigItem item = config.getConfig("agent-config").getProperty("item");
         assertNotNull(item);
@@ -221,4 +225,14 @@
         PopulationConfig pc = PopulationConfig.createFromLists(records, rels);
         pc.getConfigsTopologicallySorted();
     }
+
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
--- a/distribution/packaging/shared/bash-completion/thermostat-completion	Fri Feb 10 15:52:51 2017 -0500
+++ b/distribution/packaging/shared/bash-completion/thermostat-completion	Thu Feb 09 12:28:11 2017 +0100
@@ -4,14 +4,14 @@
     local cur prev opts base
     local thermostat_install_dir thermostat_logging_opts
     thermostat_install_dir="${thermostat.home}"
-    thermostat_logging_opts="-J-Djava.util.logging.config.file=${thermostat_install_dir}/etc/bash-complete-logging.properties"
+    thermostat_logging_opts="-J-Djava.util.logging.config.file=\"${thermostat_install_dir}/etc/bash-complete-logging.properties\""
     COMPREPLY=()
     cur="${COMP_WORDS[COMP_CWORD]}"
     prev="${COMP_WORDS[COMP_CWORD-1]}"
 
     # Thermostat Options
     # All valid commands and options are prefixed with a single space
-    opts="$(${thermostat_install_dir}/bin/thermostat ${thermostat_logging_opts} help | grep '^ ' | cut -d " " -f 2 | tr '\n' ' ')"
+    opts="$( "${thermostat_install_dir}/bin/thermostat" "${thermostat_logging_opts}" help | grep '^ ' | cut -d " " -f 2 | tr '\n' ' ')"
 
     COMPREPLY=($(compgen -W "${opts}" -- ${cur}))  
     return 0
--- a/distribution/scripts/thermostat	Fri Feb 10 15:52:51 2017 -0500
+++ b/distribution/scripts/thermostat	Thu Feb 09 12:28:11 2017 +0100
@@ -126,7 +126,7 @@
     if [ x"${PID_FILE}" = "x" ]; then
         usage 
     else
-        "${JAVA}" ${THERMOSTAT_EXT_JAVA_OPTS} ${LOGGING_ARGS} "${JAVA_ARGS[@]}" \
+        "${JAVA}" ${THERMOSTAT_EXT_JAVA_OPTS} "${LOGGING_ARGS[@]}" "${JAVA_ARGS[@]}" \
                 -cp "${BOOT_CLASSPATH}" \
                 ${THERMOSTAT_MAIN} ${THERMOSTAT_EXT_OPTS} "${ARGS[@]}" &
         retval=$?
@@ -134,7 +134,7 @@
         retval=$(( ${retval} + $? ))
     fi
 else
-    "${JAVA}" ${THERMOSTAT_EXT_JAVA_OPTS} ${LOGGING_ARGS} "${JAVA_ARGS[@]}" \
+    "${JAVA}" ${THERMOSTAT_EXT_JAVA_OPTS} "${LOGGING_ARGS[@]}" "${JAVA_ARGS[@]}" \
             -cp "${BOOT_CLASSPATH}" \
             ${THERMOSTAT_MAIN} ${THERMOSTAT_EXT_OPTS} "${ARGS[@]}"
     retval=$?
--- a/distribution/scripts/thermostat-command-channel	Fri Feb 10 15:52:51 2017 -0500
+++ b/distribution/scripts/thermostat-command-channel	Thu Feb 09 12:28:11 2017 +0100
@@ -90,16 +90,16 @@
 	CONFIG_FILE_ARG="-DipcConfigFile=`cygpath -w ${CONFIG_FILE}`"
 	# Drop permissions, if root
 	if [ "$(id -u)" -eq 0 ]; then
-	  exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} -cp `cygpath -w -p ${IPC_CLASSPATH}` ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}"
+	  exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} "${LOGGING_ARGS[@]}" -cp "`cygpath -w -p ${IPC_CLASSPATH}`" ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}"
 	else
-	  exec ${JAVA} "${CONFIG_FILE_ARG}" ${LOGGING_ARGS} -cp "`cygpath -w -p ${IPC_CLASSPATH}`" ${DEBUG_OPTS} "${CMD_CHANNEL_CLASS}" "${HOSTNAME}" "${PORT}"
+	  exec ${JAVA} "${CONFIG_FILE_ARG}" "${LOGGING_ARGS[@]}" -cp "`cygpath -w -p ${IPC_CLASSPATH}`" ${DEBUG_OPTS} "${CMD_CHANNEL_CLASS}" "${HOSTNAME}" "${PORT}"
 	fi
 else
 	CONFIG_FILE_ARG="-DipcConfigFile=${CONFIG_FILE}"
 	# Drop permissions, if root
 	if [ "$(id -u)" -eq 0 ]; then
-	  exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} -cp ${IPC_CLASSPATH} ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}"
+	  exec /bin/su -s /bin/bash -c "${JAVA} ${CONFIG_FILE_ARG} "${LOGGING_ARGS[@]}" -cp "${IPC_CLASSPATH}" ${DEBUG_OPTS} ${CMD_CHANNEL_CLASS} ${HOSTNAME} ${PORT}" "${SCRIPT_OWNER}"
 	else
-	  exec ${JAVA} "${CONFIG_FILE_ARG}" ${LOGGING_ARGS} -cp ${IPC_CLASSPATH} ${DEBUG_OPTS} "${CMD_CHANNEL_CLASS}" "${HOSTNAME}" "${PORT}"
+	  exec ${JAVA} "${CONFIG_FILE_ARG}" "${LOGGING_ARGS[@]}" -cp "${IPC_CLASSPATH}" ${DEBUG_OPTS} "${CMD_CHANNEL_CLASS}" "${HOSTNAME}" "${PORT}"
 	fi
 fi
--- a/distribution/scripts/thermostat-common	Fri Feb 10 15:52:51 2017 -0500
+++ b/distribution/scripts/thermostat-common	Thu Feb 09 12:28:11 2017 +0100
@@ -111,10 +111,10 @@
 fi
 
 # Source system thermostat profile
-. ${THERMOSTAT_HOME}/etc/thermostatrc
+. "${THERMOSTAT_HOME}/etc/thermostatrc"
 # Source user thermostat profile (if any)
-if [[ -f ${USER_THERMOSTAT_HOME}/etc/thermostatrc ]]; then
-  . ${USER_THERMOSTAT_HOME}/etc/thermostatrc
+if [[ -f "${USER_THERMOSTAT_HOME}/etc/thermostatrc" ]]; then
+  . "${USER_THERMOSTAT_HOME}/etc/thermostatrc"
 fi
 
 # Verify that JAVA_HOME is a real JDK
@@ -130,20 +130,20 @@
 SYSTEM_LOG_CONFIG_FILE="${THERMOSTAT_HOME}/etc/logging.properties"
 if [ -f "${SYSTEM_LOG_CONFIG_FILE}" ] ; then
   if [ $CYGWIN_MODE -eq 1 ]; then
-    LOGGING_ARGS="-Djava.util.logging.config.file=`cygpath -w ${SYSTEM_LOG_CONFIG_FILE}`"
+    LOGGING_ARGS=( "-Djava.util.logging.config.file=`cygpath -w ${SYSTEM_LOG_CONFIG_FILE}`" )
   else
-    LOGGING_ARGS="-Djava.util.logging.config.file=${SYSTEM_LOG_CONFIG_FILE}"
+    LOGGING_ARGS=( "-Djava.util.logging.config.file=${SYSTEM_LOG_CONFIG_FILE}" )
   fi
 fi
 
 USER_LOG_CONFIG_FILE="${USER_THERMOSTAT_HOME}/etc/logging.properties"
 if [ -f "${USER_LOG_CONFIG_FILE}" ] ; then
   if [ $CYGWIN_MODE -eq 1 ]; then
-    LOGGING_ARGS="-Djava.util.logging.config.file=`cygpath -w ${USER_LOG_CONFIG_FILE}`"
+    LOGGING_ARGS=( "-Djava.util.logging.config.file=`cygpath -w ${USER_LOG_CONFIG_FILE}`" )
   else
-    LOGGING_ARGS="-Djava.util.logging.config.file=${USER_LOG_CONFIG_FILE}"
+    LOGGING_ARGS=( "-Djava.util.logging.config.file=${USER_LOG_CONFIG_FILE}" )
   fi
 fi
 
-LOGGING_ARGS="${LOGGING_ARGS} -Djline.log.jul=true"
+LOGGING_ARGS=( "${LOGGING_ARGS[@]}" "-Djline.log.jul=true" )
 
--- a/distribution/scripts/thermostat-devsetup	Fri Feb 10 15:52:51 2017 -0500
+++ b/distribution/scripts/thermostat-devsetup	Thu Feb 09 12:28:11 2017 +0100
@@ -46,9 +46,8 @@
 # Source thermostat-common from same directory as this script
 . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"/thermostat-common
 
-THERMOSTAT_SETUP="$THERMOSTAT_HOME/bin/thermostat setup -c"
 DEV_INPUT="$THERMOSTAT_HOME/etc/devsetup.input"
 
 # Call the setup script
-$THERMOSTAT_SETUP < $DEV_INPUT
+"$THERMOSTAT" setup -c < "$DEV_INPUT"
 exit $?
--- a/distribution/tools/verify-bash-completion.sh	Fri Feb 10 15:52:51 2017 -0500
+++ b/distribution/tools/verify-bash-completion.sh	Thu Feb 09 12:28:11 2017 +0100
@@ -41,7 +41,7 @@
 
 errors=0
 
-TARGET="$(dirname $0)/../target"
+TARGET="$(dirname "$0")/../target"
 
 # Join the supplied arguments into a single string using the specified separator
 # $1 : the separator
@@ -65,9 +65,10 @@
     echo "Exporting custom home..."
     export USER_THERMOSTAT_HOME
     echo "Checking that help works..."
-    ${TARGET}/image/bin/thermostat help >/dev/null
+    "${TARGET}/image/bin/thermostat" help >/dev/null
 
     completion_file="${TARGET}/packaging/bash-completion/thermostat-completion"
+    echo "'$completion_file'"
     source "${completion_file}"
 }
 
@@ -98,12 +99,12 @@
     expected=$2
     expected_pretty=$(echo $expected | __prettify)
     # clean up (cygwin doesn't seem to delete redirect files if no output)
-    rm -f ${TARGET}/completion.actual
+    rm -f "${TARGET}/completion.actual"
     # save completions and any other output separately and check both
-    __find_completion $input >${TARGET}/completion.actual 2>${TARGET}/completion.output
-    actual=$(<${TARGET}/completion.actual)
+    __find_completion $input > "${TARGET}/completion.actual" 2> "${TARGET}/completion.output"
+    actual=$(<"${TARGET}/completion.actual")
     actual_pretty=$(echo "$actual" | __prettify)
-    output=$(<${TARGET}/completion.output)
+    output=$(<"${TARGET}/completion.output")
     if [[ $actual == $expected && -z $output ]] ; then
         echo "[OK]   '$input' => '$actual_pretty'"
     elif [[ -z $output ]]; then
--- a/integration-tests/itest-run/pom.xml	Fri Feb 10 15:52:51 2017 -0500
+++ b/integration-tests/itest-run/pom.xml	Thu Feb 09 12:28:11 2017 +0100
@@ -86,7 +86,7 @@
         <configuration>
           <redirectTestOutputToFile>true</redirectTestOutputToFile>
           <argLine>
-            -Djava.security.auth.login.config=${thermostat.home}/etc/thermostat_jaas.conf
+            -Djava.security.auth.login.config="${thermostat.home}/etc/thermostat_jaas.conf"
             ${coverageAgent}
           </argLine>
           <skip>true</skip>
--- a/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/DBStartupConfigurationTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/storage/cli/src/test/java/com/redhat/thermostat/storage/cli/internal/DBStartupConfigurationTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -41,6 +41,9 @@
 import static org.junit.Assert.fail;
 
 import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
 
 import org.junit.After;
 import org.junit.Before;
@@ -71,7 +74,7 @@
 
     @Test
     public void canGetConfigFromPropertiesFile() throws Exception {
-        File dbProps = new File(this.getClass().getResource("/testDbConfig.properties").getFile());
+        File dbProps = new File(decodeFilePath(this.getClass().getResource("/testDbConfig.properties")));
         File canNotBeFoundFile = new File("");
         DBStartupConfiguration dbConfig = new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile);
         
@@ -87,7 +90,7 @@
     
     @Test
     public void canGetConfigFromPropertiesFile2() throws Exception {
-        File dbProps = new File(this.getClass().getResource("/testDbConfig2.properties").getFile());
+        File dbProps = new File(decodeFilePath(this.getClass().getResource("/testDbConfig2.properties")));
         File canNotBeFoundFile = new File("");
         DBStartupConfiguration dbConfig = new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile);
         
@@ -103,7 +106,7 @@
     
     @Test
     public void missingBindThrowsConfigException() throws Exception {
-        File dbProps = new File(this.getClass().getResource("/brokenDbConfig.properties").getFile());
+        File dbProps = new File(decodeFilePath(this.getClass().getResource("/brokenDbConfig.properties")));
         File canNotBeFoundFile = new File("");
         try {
             new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile);
@@ -115,7 +118,7 @@
     
     @Test
     public void missingPortThrowsConfigException() throws Exception {
-        File dbProps = new File(this.getClass().getResource("/brokenDbConfig2.properties").getFile());
+        File dbProps = new File(decodeFilePath(this.getClass().getResource("/brokenDbConfig2.properties")));
         File canNotBeFoundFile = new File("");
         try {
             new DBStartupConfiguration(dbProps, canNotBeFoundFile, dbPath, dbLogFile, dbPidFile);
@@ -124,5 +127,15 @@
             assertEquals("PORT property missing", e.getMessage());
         }
     }
+
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
 
--- a/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/internal/ValidateCommandTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/internal/ValidateCommandTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -44,6 +44,9 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -91,7 +94,7 @@
     
     @Test
     public void validateIncorrectFile() throws CommandException, MissingArgumentException {
-        fileName = PluginValidator.class.getResource("/incorrectPlugin.xml").getPath().toString();
+        fileName = decodeFilePath(PluginValidator.class.getResource("/incorrectPlugin.xml"));
 
         when(mockArgs.getNonOptionArguments()).thenReturn(Collections.singletonList(fileName));
 
@@ -105,7 +108,7 @@
 
     @Test
     public void validateCorrectFile() throws CommandException, MissingArgumentException {
-        fileName = PluginValidator.class.getResource("/correctPlugin.xml").getPath().toString();
+        fileName = decodeFilePath(PluginValidator.class.getResource("/correctPlugin.xml"));
 
         when(mockArgs.getNonOptionArguments()).thenReturn(Collections.singletonList(fileName));
 
@@ -188,5 +191,15 @@
         return builder.toString();
     }
 
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
+
 }
 
--- a/vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/BytemanControlCommandTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/vm-byteman/client-cli/src/test/java/com/redhat/thermostat/vm/byteman/client/cli/BytemanControlCommandTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -57,7 +57,10 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.net.InetSocketAddress;
+import java.net.URL;
+import java.net.URLDecoder;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -375,7 +378,7 @@
         when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(status);
         Arguments args = getBasicArgsWithAction(LOAD_ACTION);
         when(args.hasArgument(RULE_OPTION)).thenReturn(true);
-        String file = getClass().getResource("/testRule.btm").getFile();
+        String file = decodeFilePath(getClass().getResource("/testRule.btm"));
         when(args.getArgument(RULE_OPTION)).thenReturn(file);
         CommandContext ctx = ctxFactory.createContext(args);
         command.unbindVmBytemanDao(dao);
@@ -412,7 +415,7 @@
         when(dao.findBytemanStatus(eq(new VmId(SOME_VM_ID)))).thenReturn(null);
         Arguments args = getBasicArgsWithAction(LOAD_ACTION);
         when(args.hasArgument(RULE_OPTION)).thenReturn(true);
-        String file = getClass().getResource("/testRule.btm").getFile();
+        String file = decodeFilePath(getClass().getResource("/testRule.btm"));
         when(args.getArgument(RULE_OPTION)).thenReturn(file);
         CommandContext ctx = ctxFactory.createContext(args);
         command.unbindVmBytemanDao(dao);
@@ -502,4 +505,14 @@
         });
         return args;
     }
+
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/auth/spi/PropertiesUserValidatorTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/auth/spi/PropertiesUserValidatorTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -41,7 +41,9 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.io.UnsupportedEncodingException;
 import java.net.URL;
+import java.net.URLDecoder;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -57,8 +59,8 @@
 
     @Before
     public void setup() {
-        URL testFile = this.getClass().getResource("/test_users.properties");
-        validator = new PropertiesUserValidator(testFile.getFile());   
+        String testFile = decodeFilePath(this.getClass().getResource("/test_users.properties"));
+        validator = new PropertiesUserValidator(testFile);   
     }
     
     @After
@@ -145,5 +147,15 @@
         expectedSet.remove("user1");
         assertFalse(expectedSet.equals(actual));
     }
+
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
 
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/auth/spi/PropertiesUsernameRolesLoginModuleTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/auth/spi/PropertiesUsernameRolesLoginModuleTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -41,7 +41,9 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.io.UnsupportedEncodingException;
 import java.net.URL;
+import java.net.URLDecoder;
 import java.security.Principal;
 import java.util.HashMap;
 import java.util.Map;
@@ -85,10 +87,10 @@
     public void canInitialize() {
         LoginModule loginModule = new PropertiesUsernameRolesLoginModule();
         mockCallBack = new SimpleCallBackHandler("testUser", "testpassword".toCharArray());
-        URL userFile = this.getClass().getResource("/properties_module_test_users.properties");
-        URL rolesFile = this.getClass().getResource("/properties_module_test_roles.properties");
-        mockOptions.put("users.properties", userFile.getFile());
-        mockOptions.put("roles.properties", rolesFile.getFile());
+        String userFile = decodeFilePath(this.getClass().getResource("/properties_module_test_users.properties"));
+        String rolesFile = decodeFilePath(this.getClass().getResource("/properties_module_test_roles.properties"));
+        mockOptions.put("users.properties", userFile);
+        mockOptions.put("roles.properties", rolesFile);
         try {
             // this must not throw an exception
             loginModule.initialize(subject, mockCallBack, mockSharedState, mockOptions);
@@ -103,10 +105,10 @@
         LoginModule loginModule = new PropertiesUsernameRolesLoginModule();
         // testUser/testpassword not defined
         mockCallBack = new SimpleCallBackHandler("testUser", "testpassword".toCharArray());
-        URL userFile = this.getClass().getResource("/properties_module_test_users.properties");
-        URL rolesFile = this.getClass().getResource("/properties_module_test_roles.properties");
-        mockOptions.put("users.properties", userFile.getFile());
-        mockOptions.put("roles.properties", rolesFile.getFile());
+        String userFile = decodeFilePath(this.getClass().getResource("/properties_module_test_users.properties"));
+        String rolesFile = decodeFilePath(this.getClass().getResource("/properties_module_test_roles.properties"));
+        mockOptions.put("users.properties", userFile);
+        mockOptions.put("roles.properties", rolesFile);
         loginModule.initialize(subject, mockCallBack, mockSharedState, mockOptions);
         try {
             loginModule.login();
@@ -130,10 +132,10 @@
     public void canLoginWithValidCredentials() {
         LoginModule loginModule = new PropertiesUsernameRolesLoginModule();
         mockCallBack = new SimpleCallBackHandler("user1", "somepassword".toCharArray());
-        URL userFile = this.getClass().getResource("/properties_module_test_users.properties");
-        URL rolesFile = this.getClass().getResource("/properties_module_test_roles.properties");
-        mockOptions.put("users.properties", userFile.getFile());
-        mockOptions.put("roles.properties", rolesFile.getFile());
+        String userFile = decodeFilePath(this.getClass().getResource("/properties_module_test_users.properties"));
+        String rolesFile = decodeFilePath(this.getClass().getResource("/properties_module_test_roles.properties"));
+        mockOptions.put("users.properties", userFile);
+        mockOptions.put("roles.properties", rolesFile);
         loginModule.initialize(subject, mockCallBack, mockSharedState, mockOptions);
         try {
             boolean retval = loginModule.login();
@@ -158,10 +160,10 @@
     public void canCommitOnSuccessfulLogin() throws LoginException {
         LoginModule loginModule = new PropertiesUsernameRolesLoginModule();
         mockCallBack = new SimpleCallBackHandler("user1", "somepassword".toCharArray());
-        URL userFile = this.getClass().getResource("/properties_module_test_users.properties");
-        URL rolesFile = this.getClass().getResource("/properties_module_test_roles.properties");
-        mockOptions.put("users.properties", userFile.getFile());
-        mockOptions.put("roles.properties", rolesFile.getFile());
+        String userFile = decodeFilePath(this.getClass().getResource("/properties_module_test_users.properties"));
+        String rolesFile = decodeFilePath(this.getClass().getResource("/properties_module_test_roles.properties"));
+        mockOptions.put("users.properties", userFile);
+        mockOptions.put("roles.properties", rolesFile);
         loginModule.initialize(subject, mockCallBack, mockSharedState, mockOptions);
         assertTrue(loginModule.login());
         try {
@@ -185,10 +187,10 @@
     public void testRecursiveRoles() throws LoginException {
         LoginModule loginModule = new PropertiesUsernameRolesLoginModule();
         mockCallBack = new SimpleCallBackHandler("user2", "password".toCharArray());
-        URL userFile = this.getClass().getResource("/properties_module_test_users.properties");
-        URL rolesFile = this.getClass().getResource("/properties_module_test_roles.properties");
-        mockOptions.put("users.properties", userFile.getFile());
-        mockOptions.put("roles.properties", rolesFile.getFile());
+        String userFile = decodeFilePath(this.getClass().getResource("/properties_module_test_users.properties"));
+        String rolesFile = decodeFilePath(this.getClass().getResource("/properties_module_test_roles.properties"));
+        mockOptions.put("users.properties", userFile);
+        mockOptions.put("roles.properties", rolesFile);
         loginModule.initialize(subject, mockCallBack, mockSharedState, mockOptions);
         assertTrue(loginModule.login());
         try {
@@ -214,10 +216,10 @@
     public void testRecursiveRolesMultiple() throws LoginException {
         LoginModule loginModule = new PropertiesUsernameRolesLoginModule();
         mockCallBack = new SimpleCallBackHandler("user3", "password".toCharArray());
-        URL userFile = this.getClass().getResource("/properties_module_test_users.properties");
-        URL rolesFile = this.getClass().getResource("/properties_module_test_roles.properties");
-        mockOptions.put("users.properties", userFile.getFile());
-        mockOptions.put("roles.properties", rolesFile.getFile());
+        String userFile = decodeFilePath(this.getClass().getResource("/properties_module_test_users.properties"));
+        String rolesFile = decodeFilePath(this.getClass().getResource("/properties_module_test_roles.properties"));
+        mockOptions.put("users.properties", userFile);
+        mockOptions.put("roles.properties", rolesFile);
         loginModule.initialize(subject, mockCallBack, mockSharedState, mockOptions);
         assertTrue(loginModule.login());
         try {
@@ -267,10 +269,10 @@
         
         loginModule = new PropertiesUsernameRolesLoginModule();
         mockCallBack = new SimpleCallBackHandler("user1", "somepassword".toCharArray());
-        URL userFile = this.getClass().getResource("/properties_module_test_users.properties");
-        URL rolesFile = this.getClass().getResource("/properties_module_test_roles.properties");
-        mockOptions.put("users.properties", userFile.getFile());
-        mockOptions.put("roles.properties", rolesFile.getFile());
+        String userFile = decodeFilePath(this.getClass().getResource("/properties_module_test_users.properties"));
+        String rolesFile = decodeFilePath(this.getClass().getResource("/properties_module_test_roles.properties"));
+        mockOptions.put("users.properties", userFile);
+        mockOptions.put("roles.properties", rolesFile);
         loginModule.initialize(subject, mockCallBack, mockSharedState, mockOptions);
         assertFalse(loginModule.commit());
         assertEquals(0, subject.getPrincipals().size());
@@ -280,10 +282,10 @@
     public void testLoginCommitAbort() throws LoginException {
         LoginModule loginModule = new PropertiesUsernameRolesLoginModule();
         mockCallBack = new SimpleCallBackHandler("user1", "somepassword".toCharArray());
-        URL userFile = this.getClass().getResource("/properties_module_test_users.properties");
-        URL rolesFile = this.getClass().getResource("/properties_module_test_roles.properties");
-        mockOptions.put("users.properties", userFile.getFile());
-        mockOptions.put("roles.properties", rolesFile.getFile());
+        String userFile = decodeFilePath(this.getClass().getResource("/properties_module_test_users.properties"));
+        String rolesFile = decodeFilePath(this.getClass().getResource("/properties_module_test_roles.properties"));
+        mockOptions.put("users.properties", userFile);
+        mockOptions.put("roles.properties", rolesFile);
         loginModule.initialize(subject, mockCallBack, mockSharedState, mockOptions);
         assertTrue(loginModule.login());
         assertTrue(loginModule.commit());
@@ -300,10 +302,10 @@
     public void testLoginCommitLogout() throws LoginException {
         LoginModule loginModule = new PropertiesUsernameRolesLoginModule();
         mockCallBack = new SimpleCallBackHandler("user1", "somepassword".toCharArray());
-        URL userFile = this.getClass().getResource("/properties_module_test_users.properties");
-        URL rolesFile = this.getClass().getResource("/properties_module_test_roles.properties");
-        mockOptions.put("users.properties", userFile.getFile());
-        mockOptions.put("roles.properties", rolesFile.getFile());
+        String userFile = decodeFilePath(this.getClass().getResource("/properties_module_test_users.properties"));
+        String rolesFile = decodeFilePath(this.getClass().getResource("/properties_module_test_roles.properties"));
+        mockOptions.put("users.properties", userFile);
+        mockOptions.put("roles.properties", rolesFile);
         loginModule.initialize(subject, mockCallBack, mockSharedState, mockOptions);
         assertTrue(loginModule.login());
         assertTrue(loginModule.commit());
@@ -315,5 +317,15 @@
         // make sure principals are cleared
         assertEquals(0, principals.size());
     }
+
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }
 
--- a/web/server/src/test/java/com/redhat/thermostat/web/server/auth/spi/RolesAmenderTest.java	Fri Feb 10 15:52:51 2017 -0500
+++ b/web/server/src/test/java/com/redhat/thermostat/web/server/auth/spi/RolesAmenderTest.java	Thu Feb 09 12:28:11 2017 +0100
@@ -42,7 +42,9 @@
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 
+import java.io.UnsupportedEncodingException;
 import java.net.URL;
+import java.net.URLDecoder;
 import java.security.Principal;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -68,8 +70,8 @@
     
     @Before
     public void setup() {
-        URL testFile = this.getClass().getResource("/test_roles.properties");
-        validFile = testFile.getFile();
+        String testFile = decodeFilePath(this.getClass().getResource("/test_roles.properties"));
+        validFile = testFile;
         Set<Object> users = new HashSet<>();
         users.add("user1");
         users.add("user2");
@@ -202,7 +204,7 @@
     
     @Test
     public void parseFailsIfUserInRecursiveRole() {
-        String brokenFile = this.getClass().getResource("/broken_test_roles.properties").getFile();
+        String brokenFile = decodeFilePath(this.getClass().getResource("/broken_test_roles.properties"));
         try {
             rolesAmender = new RolesAmender(brokenFile, validUsers);
             fail("Should not parse");
@@ -214,7 +216,7 @@
     
     @Test
     public void parseFailsIfNotAnyUserMemberOfRecursiveRole() {
-        String brokenFile = this.getClass().getResource("/broken_test_roles2.properties").getFile();
+        String brokenFile = decodeFilePath(this.getClass().getResource("/broken_test_roles2.properties"));
         try {
             rolesAmender = new RolesAmender(brokenFile, validUsers);
             fail("Should not parse");
@@ -263,5 +265,15 @@
         roleMembers.add("testuser");
         assertEquals(1, info.getMemberUsers().size());
     }
+    
+    private static String decodeFilePath(URL url) {
+        try {
+            // Spaces are encoded as %20 in URLs. Use URLDecoder.decode() so
+            // as to handle cases like that.
+            return URLDecoder.decode(url.getFile(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError("UTF-8 not supported, huh?");
+        }
+    }
 }