This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git


The following commit(s) were added to refs/heads/master by this push:
     new 8f8eb59c08 allow byon/fixed-list config to be dynamic
8f8eb59c08 is described below

commit 8f8eb59c0845dd098df3e9cba95fc6d2322455a2
Author: Alex Heneveld <[email protected]>
AuthorDate: Mon Jul 15 11:42:17 2024 +0100

    allow byon/fixed-list config to be dynamic
---
 .../camp/brooklyn/ByonLocationsYamlTest.java       |  70 +++-
 .../location/byon/ByonLocationResolver.java        | 354 ++++++++++++---------
 2 files changed, 281 insertions(+), 143 deletions(-)

diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
index d5cec29916..a57670015e 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
@@ -21,6 +21,8 @@ package org.apache.brooklyn.camp.brooklyn;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNull;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -38,7 +40,7 @@ import 
org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
 import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
 import org.apache.brooklyn.test.Asserts;
-import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.net.UserAndHostAndPort;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -97,6 +99,72 @@ public class ByonLocationsYamlTest extends AbstractYamlTest {
         assertEquals(machine.getPrivateAddresses(), 
ImmutableSet.of("10.0.0.1"));
     }
 
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testByonMachineResolvesDependentConfigForHost() throws 
Exception {
+        String yaml = Joiner.on("\n").join(
+                "location:",
+                "  byon:",
+                "    hosts:",
+                "    - $brooklyn:config(\"ip_address\")",
+                "services:",
+                "- type: org.apache.brooklyn.entity.stock.BasicApplication",
+                "  brooklyn.config:",
+                "    ip_address: 1.2.3.4"
+                );
+
+        Entity app = createStartWaitAndLogApplication(yaml);
+        FixedListMachineProvisioningLocation<SshMachineLocation> loc = 
(FixedListMachineProvisioningLocation<SshMachineLocation>) 
Iterables.get(app.getLocations(), 0);
+
+        Set<SshMachineLocation> machines = loc.getAvailable();
+        SshMachineLocation machine = Iterables.getOnlyElement(machines);
+        assertMachine(machine, UserAndHostAndPort.fromParts(machine.getUser(), 
"1.2.3.4",  22), Collections.emptyMap());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testByonMachineResolvesDependentConfigForHosts() throws 
Exception {
+        String yaml = Joiner.on("\n").join(
+                "location:",
+                "  byon:",
+                "    hosts: $brooklyn:config(\"ip_addresses\")",
+                "services:",
+                "- type: org.apache.brooklyn.entity.stock.BasicApplication",
+                "  brooklyn.config:",
+                "    ip_addresses: [ 1.2.3.4, { ssh: 1.2.3.5, user: Beth } ]"
+        );
+
+        Entity app = createStartWaitAndLogApplication(yaml);
+        FixedListMachineProvisioningLocation<SshMachineLocation> loc = 
(FixedListMachineProvisioningLocation<SshMachineLocation>) 
Iterables.get(app.getLocations(), 0);
+
+        List<SshMachineLocation> machines = 
MutableList.copyOf(loc.getAvailable());
+        Asserts.assertSize(machines, 2);
+        assertMachine(machines.get(0), 
UserAndHostAndPort.fromParts(machines.get(0).getUser(), "1.2.3.4",  22), 
Collections.emptyMap());
+        assertMachine(machines.get(1), UserAndHostAndPort.fromParts("Beth", 
"1.2.3.5",  22), Collections.emptyMap());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testByonMachineResolvesDependentConfigForUser() throws 
Exception {
+        String yaml = Joiner.on("\n").join(
+                "location:",
+                "  byon:",
+                "    user: $brooklyn:config(\"uzer\")",
+                "    hosts: [ 1.2.3.4 ]",
+                "services:",
+                "- type: org.apache.brooklyn.entity.stock.BasicApplication",
+                "  brooklyn.config:",
+                "    uzer: Beth"
+        );
+
+        Entity app = createStartWaitAndLogApplication(yaml);
+        FixedListMachineProvisioningLocation<SshMachineLocation> loc = 
(FixedListMachineProvisioningLocation<SshMachineLocation>) 
Iterables.get(app.getLocations(), 0);
+
+        Set<SshMachineLocation> machines = loc.getAvailable();
+        SshMachineLocation machine = Iterables.getOnlyElement(machines);
+        assertMachine(machine, UserAndHostAndPort.fromParts("Beth", "1.2.3.4", 
 22), Collections.emptyMap());
+    }
+
     @Test
     @SuppressWarnings("unchecked")
     public void testByonWindowsMachine() throws Exception {
diff --git 
a/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java
 
b/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java
index 40a998fb6b..ba361e38b6 100644
--- 
a/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java
+++ 
b/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java
@@ -24,23 +24,32 @@ import java.net.InetAddress;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationRegistry;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.config.Sanitizer;
+import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.location.AbstractLocationResolver;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.mgmt.internal.LocalLocationManager;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.ClassLoaderUtils;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.task.DeferredSupplier;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.net.UserAndHostAndPort;
 import org.apache.brooklyn.util.text.WildcardGlobs;
 import org.apache.brooklyn.util.text.WildcardGlobs.PhraseTreatment;
+import org.apache.commons.lang3.tuple.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -98,171 +107,232 @@ public class ByonLocationResolver extends 
AbstractLocationResolver {
     @Override
     protected ConfigBag extractConfig(Map<?,?> locationFlags, String spec, 
LocationRegistry registry) {
         ConfigBag config = super.extractConfig(locationFlags, spec, registry);
+        new MachineSpecsExtractor(spec, config).setConfigKey(this);
+        return config;
+    }
 
-        Object hosts = config.getStringKey("hosts");
-        config.remove("hosts");
-        String user = (String) config.getStringKey("user");
-        Integer port = TypeCoercions.coerce(config.getStringKey("port"), 
Integer.class);
-        Class<? extends MachineLocation> locationClass = 
getLocationClass(config.get(OS_FAMILY));
-
-        MutableMap<String, Object> defaultProps = MutableMap.of();
-        defaultProps.addIfNotNull("user", user);
-        defaultProps.addIfNotNull("port", port);
-
-        List<?> hostAddresses;
-        
-        if (hosts instanceof String) {
-            if (((String) hosts).isEmpty()) {
-                hostAddresses = ImmutableList.of();
-            } else {
-                hostAddresses = 
WildcardGlobs.getGlobsAfterBraceExpansion("{"+hosts+"}",
-                        true /* numeric */, /* no quote support though */ 
PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR);
-            }
-        } else if (hosts instanceof Iterable) {
-            hostAddresses = ImmutableList.copyOf((Iterable<?>)hosts);
-        } else {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; 
at least one host must be defined");
-        }
-        if (hostAddresses.isEmpty()) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; 
at least one host must be defined");
+    protected static class MachineSpecsExtractor implements 
DeferredSupplier<List<LocationSpec<? extends MachineLocation>>> {
+        String spec;
+        ConfigBag config;
+
+        public MachineSpecsExtractor(String spec, ConfigBag config) {
+            this.spec = spec;
+            this.config = config;
         }
-        
-        List<LocationSpec<? extends MachineLocation>> machineSpecs = 
Lists.newArrayList();
-        for (Object host : hostAddresses) {
-            LocationSpec<? extends MachineLocation> machineSpec;
-            if (host instanceof String) {
-                machineSpec = parseMachine((String)host, locationClass, 
defaultProps, spec);
-            } else if (host instanceof Map) {
-                machineSpec = parseMachine((Map<String, ?>)host, 
locationClass, defaultProps, spec);
-            } else {
-                throw new IllegalArgumentException("Expected machine to be 
String or Map, but was "+host.getClass().getName()+" ("+host+")");
+
+        /**
+         * the return value could theoretically change, which could cause 
unusual behaviour, but it is created unmanaged, so it shouldn't cause leaks;
+         * and any other unusual behaviour the caller shouldn't be too 
surprised about; we don't expect it to change often in any case
+         */
+        @Override
+        public List<LocationSpec<? extends MachineLocation>> get() {
+            Entity entity = BrooklynTaskTags.getContextEntity(Tasks.current());
+            if (entity == null) {
+                throw new IllegalStateException("BYON dynamic config can only 
be resolved in the context of an entity task");
             }
-            
machineSpec.configureIfNotNull(LocalLocationManager.CREATE_UNMANAGED, 
config.get(LocalLocationManager.CREATE_UNMANAGED));
-            machineSpecs.add(machineSpec);
+            return extractConfigMachineSpecs(entity, ((EntityInternal) 
entity).getManagementContext(), spec, config, false);
         }
-        
-        config.put(FixedListMachineProvisioningLocation.MACHINE_SPECS, 
machineSpecs);
 
-        return config;
-    }
-    
-    protected LocationSpec<? extends MachineLocation> parseMachine(Map<String, 
?> vals, Class<? extends MachineLocation> locationClass, Map<String, ?> 
defaults, String specForErrMsg) {
-        Map<String, Object> valSanitized = Sanitizer.sanitize(vals);
-        Map<String, Object> machineConfig = MutableMap.copyOf(vals);
-        
-        String osFamily = (String) machineConfig.remove(OS_FAMILY.getName());
-        String ssh = (String) machineConfig.remove("ssh");
-        String winrm = (String) machineConfig.remove("winrm");
-        Map<Integer, String> tcpPortMappings = (Map<Integer, String>) 
machineConfig.get("tcpPortMappings");
-        
-        checkArgument(ssh != null ^ winrm != null, "Must specify exactly one 
of 'ssh' or 'winrm' for machine: %s", valSanitized);
-        
-        UserAndHostAndPort userAndHostAndPort;
-        String host;
-        int port;
-        if (ssh != null) {
-            userAndHostAndPort = parseUserAndHostAndPort(ssh, 22);
-        } else {
-            // TODO set to null and rely on the MachineLocation. If not then 
make a dependency to WinRmMachineLocation and use its config key name.
-            userAndHostAndPort = parseUserAndHostAndPort(winrm, 
vals.get("winrm.useHttps") != null && (Boolean)vals.get("winrm.useHttps") ? 
5986 : 5985);
-        }
-        
-        // If there is a tcpPortMapping defined for the connection-port, then 
use that for ssh/winrm machine
-        port = userAndHostAndPort.getHostAndPort().getPort();
-        if (tcpPortMappings != null && tcpPortMappings.containsKey(port)) {
-            String override = tcpPortMappings.get(port);
-            HostAndPort hostAndPortOverride = HostAndPort.fromString(override);
-            if (!hostAndPortOverride.hasPort()) {
-                throw new IllegalArgumentException("Invalid portMapping 
('"+override+"') for port "+port+" in "+specForErrMsg);
+        public void setConfigKey(ByonLocationResolver resolver) {
+            ConfigBag configCopy = ConfigBag.newInstanceCopying(config);
+            try {
+                List<LocationSpec<? extends MachineLocation>> 
machineSpecsResolvableEarly = extractConfigMachineSpecs(resolver, 
resolver.managementContext, spec, config, true);
+                config.put(FixedListMachineProvisioningLocation.MACHINE_SPECS, 
machineSpecsResolvableEarly);
+
+            } catch (UsesDeferredSupplier e) {
+                // dynamic - update the config the location gets
+                config.put((ConfigKey) 
FixedListMachineProvisioningLocation.MACHINE_SPECS, this);
+                // but restore our local copy to the original one
+                config = configCopy;
             }
-            port = hostAndPortOverride.getPort();
-            host = hostAndPortOverride.getHost().trim();
-        } else {
-            host = userAndHostAndPort.getHostAndPort().getHost().trim();
-        }
-        
-        machineConfig.put("address", host);
-        try {
-            InetAddress.getByName(host);
-        } catch (Exception e) {
-            throw new IllegalArgumentException("Invalid host '"+host+"' 
specified in '"+specForErrMsg+"': "+e);
         }
 
-        if (userAndHostAndPort.getUser() != null) {
-            checkArgument(!vals.containsKey("user"), "Must not specify user 
twice for machine: %s", valSanitized);
-            machineConfig.put("user", userAndHostAndPort.getUser());
-        }
-        if (userAndHostAndPort.getHostAndPort().hasPort()) {
-            checkArgument(!vals.containsKey("port"), "Must not specify port 
twice for machine: %s", valSanitized);
-            machineConfig.put("port", port);
+        protected static class UsesDeferredSupplier extends RuntimeException {}
+
+        protected <T> T resolveOrThrow(boolean dynamicAllowed, String key, 
Class<T> type) {
+            Object v = config.getStringKey(key);
+            v = resolveOrThrow(dynamicAllowed, v);
+            return TypeCoercions.coerce(v, type);
         }
-        for (Map.Entry<String, ?> entry : defaults.entrySet()) {
-            if (!machineConfig.containsKey(entry.getKey())) {
-                machineConfig.put(entry.getKey(), entry.getValue());
+        protected Object resolveOrThrow(boolean dynamicAllowed, Object v) {
+            if (v instanceof DeferredSupplier) {
+                if (dynamicAllowed) throw new UsesDeferredSupplier();
+                v = ((DeferredSupplier)v).get();
             }
+            return v;
         }
-        
-        Class<? extends MachineLocation> locationClassHere = locationClass;
-        if (osFamily != null) {
-            locationClassHere = getLocationClass(osFamily);
+
+        // if dynamicAllowed, returns null if anything is can't be resolved;
+        // if not dynamicAllowed, throws if anything can't be resolved.
+        protected List<LocationSpec<? extends MachineLocation>> 
extractConfigMachineSpecs(Object context, ManagementContext mgmt, String spec, 
ConfigBag config, boolean dynamicAllowed) {
+            Object hosts = resolveOrThrow(dynamicAllowed, "hosts", 
Object.class);
+
+            Class<? extends MachineLocation> locationClass;
+            MutableMap<String, Object> defaultProps = MutableMap.of();
+            config.remove("hosts");
+            String user = resolveOrThrow(dynamicAllowed, "user", String.class);
+            Integer port = resolveOrThrow(dynamicAllowed, "port", 
Integer.class);
+            resolveOrThrow(dynamicAllowed, OS_FAMILY.getName(), Object.class);
+            locationClass = getLocationClass(mgmt, context, 
config.get(OS_FAMILY));
+
+            defaultProps.addIfNotNull("user", user);
+            defaultProps.addIfNotNull("port", port);
+
+            List<?> hostAddresses;
+
+            if (hosts instanceof String) {
+                if (((String) hosts).isEmpty()) {
+                    hostAddresses = ImmutableList.of();
+                } else {
+                    hostAddresses = 
WildcardGlobs.getGlobsAfterBraceExpansion("{" + hosts + "}",
+                            true /* numeric */, /* no quote support though */ 
PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR);
+                }
+            } else if (hosts instanceof Iterable) {
+                hostAddresses = ImmutableList.copyOf((Iterable<?>) hosts);
+            } else {
+                throw new IllegalArgumentException("Invalid location '" + spec 
+ "'; at least one host must be defined");
+            }
+            if (hostAddresses.isEmpty()) {
+                throw new IllegalArgumentException("Invalid location '" + spec 
+ "'; at least one host must be defined");
+            }
+
+            List<LocationSpec<? extends MachineLocation>> machineSpecs = 
Lists.newArrayList();
+            for (Object host : hostAddresses) {
+                host = resolveOrThrow(dynamicAllowed, host);
+
+                LocationSpec<? extends MachineLocation> machineSpec;
+                if (host instanceof String) {
+                    machineSpec = parseMachine((String) host, locationClass, 
defaultProps, spec);
+                } else if (host instanceof Map) {
+                    machineSpec = parseMachine(mgmt, context, (Map<String, ?>) 
host, locationClass, defaultProps, spec);
+                } else {
+                    throw new IllegalArgumentException("Expected machine to be 
String or Map, but was " + host.getClass().getName() + " (" + host + ")");
+                }
+                
machineSpec.configureIfNotNull(LocalLocationManager.CREATE_UNMANAGED, 
config.get(LocalLocationManager.CREATE_UNMANAGED));
+                machineSpecs.add(machineSpec);
+            }
+            return machineSpecs;
         }
 
-        return LocationSpec.create(locationClassHere).configure(machineConfig);
-    }
+        private LocationSpec<? extends MachineLocation> 
parseMachine(ManagementContext mgmt, Object context, Map<String, ?> vals, 
Class<? extends MachineLocation> locationClass, Map<String, ?> defaults, String 
specForErrMsg) {
+            Map<String, Object> valSanitized = Sanitizer.sanitize(vals);
+            Map<String, Object> machineConfig = MutableMap.copyOf(vals);
 
-    private Class<? extends MachineLocation> getLocationClass(String osFamily) 
{
-        try {
+            String osFamily = (String) 
machineConfig.remove(OS_FAMILY.getName());
+            String ssh = (String) machineConfig.remove("ssh");
+            String winrm = (String) machineConfig.remove("winrm");
+            Map<Integer, String> tcpPortMappings = (Map<Integer, String>) 
machineConfig.get("tcpPortMappings");
+
+            checkArgument(ssh != null ^ winrm != null, "Must specify exactly 
one of 'ssh' or 'winrm' for machine: %s", valSanitized);
+
+            UserAndHostAndPort userAndHostAndPort;
+            String host;
+            int port;
+            if (ssh != null) {
+                userAndHostAndPort = parseUserAndHostAndPort(ssh, 22);
+            } else {
+                // TODO set to null and rely on the MachineLocation. If not 
then make a dependency to WinRmMachineLocation and use its config key name.
+                userAndHostAndPort = parseUserAndHostAndPort(winrm, 
vals.get("winrm.useHttps") != null && (Boolean) vals.get("winrm.useHttps") ? 
5986 : 5985);
+            }
+
+            // If there is a tcpPortMapping defined for the connection-port, 
then use that for ssh/winrm machine
+            port = userAndHostAndPort.getHostAndPort().getPort();
+            if (tcpPortMappings != null && tcpPortMappings.containsKey(port)) {
+                String override = tcpPortMappings.get(port);
+                HostAndPort hostAndPortOverride = 
HostAndPort.fromString(override);
+                if (!hostAndPortOverride.hasPort()) {
+                    throw new IllegalArgumentException("Invalid portMapping 
('" + override + "') for port " + port + " in " + specForErrMsg);
+                }
+                port = hostAndPortOverride.getPort();
+                host = hostAndPortOverride.getHost().trim();
+            } else {
+                host = userAndHostAndPort.getHostAndPort().getHost().trim();
+            }
+
+            machineConfig.put("address", host);
+            try {
+                InetAddress.getByName(host);
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Invalid host '" + host + 
"' specified in '" + specForErrMsg + "': " + e);
+            }
+
+            if (userAndHostAndPort.getUser() != null) {
+                checkArgument(!vals.containsKey("user"), "Must not specify 
user twice for machine: %s", valSanitized);
+                machineConfig.put("user", userAndHostAndPort.getUser());
+            }
+            if (userAndHostAndPort.getHostAndPort().hasPort()) {
+                checkArgument(!vals.containsKey("port"), "Must not specify 
port twice for machine: %s", valSanitized);
+                machineConfig.put("port", port);
+            }
+            for (Map.Entry<String, ?> entry : defaults.entrySet()) {
+                if (!machineConfig.containsKey(entry.getKey())) {
+                    machineConfig.put(entry.getKey(), entry.getValue());
+                }
+            }
+
+            Class<? extends MachineLocation> locationClassHere = locationClass;
             if (osFamily != null) {
-                String className = 
OS_TO_MACHINE_LOCATION_TYPE.get(osFamily.toLowerCase(Locale.ENGLISH));
-                return new ClassLoaderUtils(this, 
managementContext).loadClass(className).asSubclass(MachineLocation.class);
+                locationClassHere = getLocationClass(mgmt, context, osFamily);
             }
-        } catch (ClassNotFoundException ex) {}
-        return null;
-    }
 
-    protected LocationSpec<? extends MachineLocation> parseMachine(String val, 
Class<? extends MachineLocation> locationClass, Map<String, ?> defaults, String 
specForErrMsg) {
-        Map<String, Object> machineConfig = Maps.newLinkedHashMap();
-        
-        UserAndHostAndPort userAndHostAndPort = parseUserAndHostAndPort(val);
-        
-        String host = userAndHostAndPort.getHostAndPort().getHost().trim();
-        machineConfig.put("address", host);
-        try {
-            InetAddress.getByName(host.trim());
-        } catch (Exception e) {
-            throw new IllegalArgumentException("Invalid host '"+host+"' 
specified in '"+specForErrMsg+"': "+e);
+            return 
LocationSpec.create(locationClassHere).configure(machineConfig);
         }
-        
-        if (userAndHostAndPort.getUser() != null) {
-            machineConfig.put("user", userAndHostAndPort.getUser());
-        }
-        if (userAndHostAndPort.getHostAndPort().hasPort()) {
-            machineConfig.put("port", 
userAndHostAndPort.getHostAndPort().getPort());
+
+        private Class<? extends MachineLocation> 
getLocationClass(ManagementContext mgmt, Object context, String osFamily) {
+            try {
+                if (osFamily != null) {
+                    String className = 
OS_TO_MACHINE_LOCATION_TYPE.get(osFamily.toLowerCase(Locale.ENGLISH));
+                    return new ClassLoaderUtils(context, 
mgmt).loadClass(className).asSubclass(MachineLocation.class);
+                }
+            } catch (ClassNotFoundException ex) {
+            }
+            return null;
         }
-        for (Map.Entry<String, ?> entry : defaults.entrySet()) {
-            if (!machineConfig.containsKey(entry.getKey())) {
-                machineConfig.put(entry.getKey(), entry.getValue());
+
+        private LocationSpec<? extends MachineLocation> parseMachine(String 
val, Class<? extends MachineLocation> locationClass, Map<String, ?> defaults, 
String specForErrMsg) {
+            Map<String, Object> machineConfig = Maps.newLinkedHashMap();
+
+            UserAndHostAndPort userAndHostAndPort = 
parseUserAndHostAndPort(val);
+
+            String host = userAndHostAndPort.getHostAndPort().getHost().trim();
+            machineConfig.put("address", host);
+            try {
+                InetAddress.getByName(host.trim());
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Invalid host '" + host + 
"' specified in '" + specForErrMsg + "': " + e);
+            }
+
+            if (userAndHostAndPort.getUser() != null) {
+                machineConfig.put("user", userAndHostAndPort.getUser());
+            }
+            if (userAndHostAndPort.getHostAndPort().hasPort()) {
+                machineConfig.put("port", 
userAndHostAndPort.getHostAndPort().getPort());
             }
+            for (Map.Entry<String, ?> entry : defaults.entrySet()) {
+                if (!machineConfig.containsKey(entry.getKey())) {
+                    machineConfig.put(entry.getKey(), entry.getValue());
+                }
+            }
+
+            return LocationSpec.create(locationClass).configure(machineConfig);
         }
 
-        return LocationSpec.create(locationClass).configure(machineConfig);
-    }
-    
-    private UserAndHostAndPort parseUserAndHostAndPort(String val) {
-        String userPart = null;
-        String hostPart = val;
-        if (val.contains("@")) {
-            userPart = val.substring(0, val.indexOf("@"));
-            hostPart = val.substring(val.indexOf("@")+1);
+        private UserAndHostAndPort parseUserAndHostAndPort(String val) {
+            String userPart = null;
+            String hostPart = val;
+            if (val.contains("@")) {
+                userPart = val.substring(0, val.indexOf("@"));
+                hostPart = val.substring(val.indexOf("@") + 1);
+            }
+            return UserAndHostAndPort.fromParts(userPart, 
HostAndPort.fromString(hostPart));
         }
-        return UserAndHostAndPort.fromParts(userPart, 
HostAndPort.fromString(hostPart));
-    }
-    
-    private UserAndHostAndPort parseUserAndHostAndPort(String val, int 
defaultPort) {
-        UserAndHostAndPort result = parseUserAndHostAndPort(val);
-        if (!result.getHostAndPort().hasPort()) {
-            result = UserAndHostAndPort.fromParts(result.getUser(), 
result.getHostAndPort().getHost(), defaultPort);
+
+        private UserAndHostAndPort parseUserAndHostAndPort(String val, int 
defaultPort) {
+            UserAndHostAndPort result = parseUserAndHostAndPort(val);
+            if (!result.getHostAndPort().hasPort()) {
+                result = UserAndHostAndPort.fromParts(result.getUser(), 
result.getHostAndPort().getHost(), defaultPort);
+            }
+            return result;
         }
-        return result;
     }
 }

Reply via email to