QPID-8504:[Broker-J] Resolve container attributes before encrypter creation

This closes #81
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/AbstractContainer.java b/broker-core/src/main/java/org/apache/qpid/server/model/AbstractContainer.java
index 76c0c28..13d2028 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/AbstractContainer.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/AbstractContainer.java
@@ -101,6 +101,10 @@
         {
             _confidentialConfigurationEncryptionProvider =
                     String.valueOf(attributes.get(CONFIDENTIAL_CONFIGURATION_ENCRYPTION_PROVIDER));
+
+            // resolve context attributes here
+            // as encrypter factory relies on context variables
+            onResolve();
             updateEncrypter(_confidentialConfigurationEncryptionProvider);
         }
 
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AbstractAESKeyFileEncrypterFactory.java b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AbstractAESKeyFileEncrypterFactory.java
index 5927a06..607e9fc 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AbstractAESKeyFileEncrypterFactory.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AbstractAESKeyFileEncrypterFactory.java
@@ -244,18 +244,18 @@
         }
     }
 
-    private boolean isPosixFileSystem(File file)
+    private static boolean isPosixFileSystem(File file)
     {
         return Files.getFileAttributeView(file.toPath(), PosixFileAttributeView.class) != null;
     }
 
-    private boolean isAclFileSystem(File file)
+    private static boolean isAclFileSystem(File file)
     {
         return Files.getFileAttributeView(file.toPath(), AclFileAttributeView.class) != null;
     }
 
 
-    private void createAndPopulateKeyFile(final File file)
+    static void createAndPopulateKeyFile(final File file)
     {
         try
         {
@@ -277,7 +277,7 @@
         }
     }
 
-    private void makeKeyFileReadOnly(File file) throws IOException
+    private static void makeKeyFileReadOnly(File file) throws IOException
     {
         if (isPosixFileSystem(file))
         {
@@ -317,7 +317,7 @@
         }
     }
 
-    private void createEmptyKeyFile(File file) throws IOException
+    private static void createEmptyKeyFile(File file) throws IOException
     {
         final Path parentFilePath = file.getAbsoluteFile().getParentFile().toPath();
 
diff --git a/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterTest.java b/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterTest.java
index d790ea1..da63577 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterTest.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterTest.java
@@ -247,6 +247,25 @@
     }
 
     @Test
+    public void testSetKeyLocationAsExpression() throws Exception
+    {
+        final Path workDir = Files.createTempDirectory("qpid_work_dir");
+        final File keyFile = new File(workDir.toFile(), "test.key");
+        AbstractAESKeyFileEncrypterFactory.createAndPopulateKeyFile(keyFile);
+        final Map<String, String> context = Collections.singletonMap(
+                AbstractAESKeyFileEncrypterFactory.ENCRYPTER_KEY_FILE,
+                "${qpid.work_dir}" + File.separator + keyFile.getName());
+        createBrokerAndAuthenticationProviderWithEncrypterPassword(AESGCMKeyFileEncrypterFactory.TYPE,
+                                                                   workDir,
+                                                                   context);
+        final String encryptedPassword = getEncryptedPasswordFromConfig();
+        final SecretKeySpec aesSecretKey = new SecretKeySpec(Files.readAllBytes(keyFile.toPath()), "AES");
+        final AESGCMKeyFileEncrypter encrypter = new AESGCMKeyFileEncrypter(aesSecretKey);
+        final String decryptedPassword = encrypter.decrypt(encryptedPassword);
+        assertEquals("Decrypted text doesnt match original", SECRET, decryptedPassword);
+    }
+
+    @Test
     public void testChangeOfEncryptionToAES() throws Exception
     {
         createBrokerAndAuthenticationProviderWithEncrypterPassword(AESGCMKeyFileEncrypterFactory.TYPE);
@@ -278,12 +297,26 @@
     private void createBrokerAndAuthenticationProviderWithEncrypterPassword(final Object encryptionType)
             throws Exception
     {
-        _workDir = Files.createTempDirectory("qpid_work_dir");
+        createBrokerAndAuthenticationProviderWithEncrypterPassword(encryptionType,
+                                                                   Files.createTempDirectory("qpid_work_dir"),
+                                                                   Collections.emptyMap());
+    }
+
+    private void createBrokerAndAuthenticationProviderWithEncrypterPassword(final Object encryptionType,
+                                                                            final Path workDir,
+                                                                            final Map<String, String> brokerContext)
+            throws Exception
+    {
+        _workDir = workDir;
+        final Map<String, String> context = new HashMap<>();
+        context.put("qpid.work_dir", workDir.toFile().getAbsolutePath());
+
         _configurationLocation = Files.createTempFile(_workDir, "config", ".json");
         final Map<String, Object> config = new HashMap<>();
         config.put(ConfiguredObject.NAME, getTestName());
         config.put(Broker.MODEL_VERSION, BrokerModel.MODEL_VERSION);
         config.put(Broker.CONFIDENTIAL_CONFIGURATION_ENCRYPTION_PROVIDER, encryptionType);
+        config.put(Broker.CONTEXT, brokerContext);
         new ObjectMapper().writeValue(_configurationLocation.toFile(), config);
 
         final Map<String, Object> attributes = new HashMap<>();
@@ -291,8 +324,7 @@
         attributes.put("preferenceStoreAttributes", "{\"type\": \"Noop\"}");
         attributes.put(SystemConfig.TYPE, JsonSystemConfigImpl.SYSTEM_CONFIG_TYPE);
         attributes.put(SystemConfig.STARTUP_LOGGED_TO_SYSTEM_OUT, Boolean.FALSE);
-        attributes.put(SystemConfig.CONTEXT,
-                       Collections.singletonMap("qpid.work_dir", _workDir.toFile().getAbsolutePath()));
+        attributes.put(SystemConfig.CONTEXT, context);
         final SettableFuture<SystemConfig<?>> configFuture = SettableFuture.create();
         _systemLauncher = new SystemLauncher(new SystemLauncherListener.DefaultSystemLauncherListener()
         {