You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2014/09/03 15:34:00 UTC

[5/7] git commit: Adds AbstractLocationResolver

Adds AbstractLocationResolver


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/c55f361b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/c55f361b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/c55f361b

Branch: refs/heads/master
Commit: c55f361b225494cce635bf09655ddcb4f43ab69a
Parents: 597f0d5
Author: Aled Sage <al...@gmail.com>
Authored: Fri Aug 1 15:12:42 2014 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Sep 3 14:32:28 2014 +0100

----------------------------------------------------------------------
 .../basic/AbstractLocationResolver.java         | 189 +++++++++++++++++++
 .../location/basic/BasicLocationRegistry.java   |   6 +-
 .../location/basic/ByonLocationResolver.java    | 116 ++++--------
 .../location/basic/HostLocationResolver.java    |  61 +++---
 .../basic/LocalhostLocationResolver.java        |  88 ++-------
 .../basic/SingleMachineLocationResolver.java    |  61 ++----
 .../basic/ByonLocationResolverTest.java         |  66 ++++---
 .../basic/LocalhostLocationResolverTest.java    |  21 ++-
 .../SingleMachineLocationResolverTest.java      |  18 +-
 9 files changed, 361 insertions(+), 265 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c55f361b/core/src/main/java/brooklyn/location/basic/AbstractLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/AbstractLocationResolver.java b/core/src/main/java/brooklyn/location/basic/AbstractLocationResolver.java
new file mode 100644
index 0000000..132edfb
--- /dev/null
+++ b/core/src/main/java/brooklyn/location/basic/AbstractLocationResolver.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.location.basic;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.location.Location;
+import brooklyn.location.LocationRegistry;
+import brooklyn.location.LocationResolver;
+import brooklyn.location.LocationSpec;
+import brooklyn.location.basic.AbstractLocationResolver.SpecParser.ParsedSpec;
+import brooklyn.management.ManagementContext;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.text.KeyValueParser;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Examples of valid specs:
+ *   <ul>
+ *     <li>byon(hosts=myhost)
+ *     <li>byon(hosts=myhost,myhost2)
+ *     <li>byon(hosts="myhost, myhost2")
+ *     <li>byon(hosts=myhost,myhost2, name=abc)
+ *     <li>byon(hosts="myhost, myhost2", name="my location name")
+ *   </ul>
+ * 
+ * @author aled
+ */
+@SuppressWarnings({"unchecked","rawtypes"})
+public abstract class AbstractLocationResolver implements LocationResolver {
+
+    public static final Logger log = LoggerFactory.getLogger(AbstractLocationResolver.class);
+    
+    protected volatile ManagementContext managementContext;
+
+    protected volatile SpecParser specParser;
+
+    protected abstract Class<? extends Location> getLocationType();
+    
+    protected abstract SpecParser getSpecParser();
+    
+    @Override
+    public void init(ManagementContext managementContext) {
+        this.managementContext = checkNotNull(managementContext, "managementContext");
+        this.specParser = getSpecParser();
+    }
+    
+    @Override
+    public boolean accepts(String spec, LocationRegistry registry) {
+        return BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true);
+    }
+
+    @Override
+    public Location newLocationFromString(Map locationFlags, String spec, brooklyn.location.LocationRegistry registry) {
+        ConfigBag config = extractConfig(locationFlags, spec, registry);
+        Map globalProperties = registry.getProperties();
+        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
+        
+        if (registry != null) {
+            LocationPropertiesFromBrooklynProperties.setLocalTempDir(globalProperties, config);
+        }
+
+        return managementContext.getLocationManager().createLocation(LocationSpec.create(getLocationType())
+            .configure(config.getAllConfig())
+            .configure(LocationConfigUtils.finalAndOriginalSpecs(spec, locationFlags, globalProperties, namedLocation)));
+    }
+
+    protected ConfigBag extractConfig(Map<?,?> locationFlags, String spec, brooklyn.location.LocationRegistry registry) {
+        Map globalProperties = registry.getProperties();
+        ParsedSpec parsedSpec = specParser.parse(spec);
+        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
+        
+        // prefer args map over location flags
+        Map<String, Object> filteredProperties = getFilteredLocationProperties(getPrefix(), namedLocation, globalProperties);
+        ConfigBag flags = ConfigBag.newInstance(parsedSpec.argsMap).putIfAbsent(locationFlags).putIfAbsent(filteredProperties);
+
+        return flags;
+    }
+    
+    protected Map<String, Object> getFilteredLocationProperties(String provider, String namedLocation, Map<String, ?> globalProperties) {
+        return new LocationPropertiesFromBrooklynProperties().getLocationProperties(getPrefix(), namedLocation, globalProperties);
+    }
+    
+    /**
+     * Parses a spec, by default of the general form "prefix:parts1:part2(arg1=val1,arg2=val2)"
+     */
+    protected static class SpecParser {
+        
+        protected static class ParsedSpec {
+            public final String spec;
+            public final List<String> partsList;
+            public final Map<String,String> argsMap;
+            
+            ParsedSpec(String spec, List<String> partsList, Map<String,String> argsMap) {
+                this.spec = spec;
+                this.partsList = ImmutableList.copyOf(partsList);
+                this.argsMap = Collections.unmodifiableMap(MutableMap.copyOf(argsMap));
+            }
+        }
+        
+        protected final String prefix;
+        protected final Pattern pattern;
+        private String exampleUsage;
+        
+        public SpecParser(String prefix) {
+            this.prefix = prefix;
+            pattern = Pattern.compile("("+prefix.toLowerCase()+"|"+prefix.toUpperCase()+")" + "(:)?" + "(\\((.*)\\))?$");
+        }
+        
+        public SpecParser(String prefix, Pattern pattern) {
+            this.prefix = prefix;
+            this.pattern = pattern;
+        }
+        
+        public SpecParser setExampleUsage(String exampleUsage) {
+            this.exampleUsage = exampleUsage;
+            return this;
+        }
+
+        protected String getUsage(String spec) {
+            if (exampleUsage == null) {
+                return "Spec should be in the form "+pattern;
+            } else {
+                return "for example, "+exampleUsage;
+            }
+        }
+        
+        protected void checkParsedSpec(ParsedSpec parsedSpec) {
+            // If someone tries "byon:(),byon:()" as a single spec, we get weird key-values!
+            for (String key : parsedSpec.argsMap.keySet()) {
+                if (key.contains(":") || key.contains("{") || key.contains("}") || key.contains("(") || key.contains(")")) {
+                    throw new IllegalArgumentException("Invalid byon spec: "+parsedSpec.spec+" (key="+key+")");
+                }
+            }
+            String name = parsedSpec.argsMap.get("name");
+            if (parsedSpec.argsMap.containsKey("name") && (name == null || name.isEmpty())) {
+                throw new IllegalArgumentException("Invalid location '"+parsedSpec.spec+"'; if name supplied then value must be non-empty");
+            }
+            String displayName = parsedSpec.argsMap.get("displayName");
+            if (parsedSpec.argsMap.containsKey("displayName") && (displayName == null || displayName.isEmpty())) {
+                throw new IllegalArgumentException("Invalid location '"+parsedSpec.spec+"'; if displayName supplied then value must be non-empty");
+            }
+        }
+        
+        public ParsedSpec parse(String spec) {
+            Matcher matcher = pattern.matcher(spec);
+            if (!matcher.matches()) {
+                throw new IllegalArgumentException("Invalid location '"+spec+"'; "+getUsage(spec));
+            }
+            
+            String argsPart = matcher.group(3);
+            if (argsPart != null && argsPart.startsWith("(") && argsPart.endsWith(")")) {
+                // TODO Hacky; hosts("1.1.1.1") returns argsPart=("1.1.1.1")
+                argsPart = argsPart.substring(1, argsPart.length()-1);
+            }
+            Map<String, String> argsMap = KeyValueParser.parseMap(argsPart);
+            ParsedSpec result = new ParsedSpec(spec, ImmutableList.<String>of(), argsMap);
+            checkParsedSpec(result);
+            return result;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c55f361b/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java b/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java
index 8bbba45..6fd63b4 100644
--- a/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java
+++ b/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java
@@ -271,8 +271,10 @@ public class BasicLocationRegistry implements LocationRegistry {
     }
 
     protected LocationResolver getSpecResolver(String spec) {
-        int colon = spec.indexOf(':');
-        String prefix = colon>=0 ? spec.substring(0, colon) : spec;
+        int colonIndex = spec.indexOf(':');
+        int bracketIndex = spec.indexOf("(");
+        int dividerIndex = (colonIndex < 0) ? bracketIndex : (bracketIndex < 0 ? colonIndex : Math.min(bracketIndex, colonIndex));
+        String prefix = dividerIndex >= 0 ? spec.substring(0, dividerIndex) : spec;
         LocationResolver resolver = resolvers.get(prefix);
        
         if (resolver == null)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c55f361b/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java b/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
index c5428ec..66ffc24 100644
--- a/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
+++ b/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
@@ -18,25 +18,17 @@
  */
 package brooklyn.location.basic;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.net.InetAddress;
 import java.util.List;
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.location.LocationRegistry;
-import brooklyn.location.LocationResolver;
+import brooklyn.location.Location;
 import brooklyn.location.LocationSpec;
-import brooklyn.management.ManagementContext;
 import brooklyn.util.JavaGroovyEquivalents;
-import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;
-import brooklyn.util.text.KeyValueParser;
 import brooklyn.util.text.WildcardGlobs;
 import brooklyn.util.text.WildcardGlobs.PhraseTreatment;
 
@@ -55,66 +47,52 @@ import com.google.common.collect.Lists;
  * 
  * @author aled
  */
-@SuppressWarnings({"unchecked","rawtypes"})
-public class ByonLocationResolver implements LocationResolver {
+@SuppressWarnings({"rawtypes"})
+public class ByonLocationResolver extends AbstractLocationResolver {
 
     public static final Logger log = LoggerFactory.getLogger(ByonLocationResolver.class);
     
     public static final String BYON = "byon";
 
-    private static final Pattern PATTERN = Pattern.compile("(?i)" + BYON + "(?::\\((.*)\\))?$");
-
-    private volatile ManagementContext managementContext;
+    @Override
+    public String getPrefix() {
+        return BYON;
+    }
 
     @Override
-    public void init(ManagementContext managementContext) {
-        this.managementContext = checkNotNull(managementContext, "managementContext");
+    protected Class<? extends Location> getLocationType() {
+        return FixedListMachineProvisioningLocation.class;
     }
-    
+
     @Override
-    public FixedListMachineProvisioningLocation<SshMachineLocation> newLocationFromString(Map locationFlags, String spec, brooklyn.location.LocationRegistry registry) {
-        Map globalProperties = registry.getProperties();
+    protected SpecParser getSpecParser() {
+        return new AbstractLocationResolver.SpecParser(getPrefix()).setExampleUsage("\"byon(hosts='addr1,addr2')\"");
+    }
 
-        Matcher matcher = PATTERN.matcher(spec);
-        if (!matcher.matches()) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; must specify something like byon(hosts=\"addr1,addr2\")");
-        }
-        
-        String argsPart = matcher.group(1);
-        Map<String, String> argsMap = KeyValueParser.parseMap(argsPart);
-        
-        // If someone tries "byon:(),byon:()" as a single spec, we get weird key-values!
-        for (String key : argsMap.keySet()) {
-            if (key.contains(":") || key.contains("{") || key.contains("}") || key.contains("(") || key.contains(")")) {
-                throw new IllegalArgumentException("Invalid byon spec: "+spec);
-            }
-        }
+    @Override
+    protected ConfigBag extractConfig(Map<?,?> locationFlags, String spec, brooklyn.location.LocationRegistry registry) {
+        ConfigBag config = super.extractConfig(locationFlags, spec, registry);
 
-        // prefer args map over location flags
-        
-        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
-        String name = argsMap.containsKey("name") ? argsMap.get("name") : (String)locationFlags.get("name");
-        
-        Object hosts = argsMap.containsKey("hosts") ? argsMap.get("hosts") : locationFlags.get("hosts");
-        
-        String user = argsMap.containsKey("user") ? argsMap.get("user") : (String)locationFlags.get("user");
-        
-        if (hosts == null || hosts.toString().isEmpty()) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; at least one host must be defined");
-        }
-        if (argsMap.containsKey("name") && (name == null || name.isEmpty())) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; if name supplied then value must be non-empty");
-        }
+        Object hosts = config.getStringKey("hosts");
+        config.remove("hosts");
+        String user = (String)config.getStringKey("user");
         
         List<String> hostAddresses;
         
         if (hosts instanceof String) {
-            hostAddresses = WildcardGlobs.getGlobsAfterBraceExpansion("{"+hosts+"}",
-                    true /* numeric */, /* no quote support though */ PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR);
+            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((List)hosts);
+            hostAddresses = ImmutableList.copyOf((Iterable<String>)hosts);
         } else {
-            throw new IllegalStateException("Illegal parameter for 'hosts'; must be a string or map (but got " + hosts + ")");
+            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<SshMachineLocation> machines = Lists.newArrayList();
@@ -132,38 +110,18 @@ public class ByonLocationResolver implements LocationResolver {
                 throw new IllegalArgumentException("Invalid host '"+hostHere+"' specified in '"+spec+"': "+e);
             }
             if (JavaGroovyEquivalents.groovyTruth(userHere)) {
-                machine = managementContext.getLocationManager().createLocation(MutableMap.of("user", userHere.trim(), "address", hostHere.trim()), SshMachineLocation.class);    
+                machine = managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+                        .configure("user", userHere.trim())
+                        .configure("address", hostHere.trim()));    
             } else {
-                machine = managementContext.getLocationManager().createLocation(MutableMap.of("address", hostHere.trim()), SshMachineLocation.class);
+                machine = managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+                        .configure("address", hostHere.trim()));    
             }
             machines.add(machine);
         }
         
-        Map<String, Object> filteredProperties = new LocationPropertiesFromBrooklynProperties().getLocationProperties("byon", namedLocation, globalProperties);
-        ConfigBag flags = ConfigBag.newInstance(argsMap).putIfAbsent(locationFlags).putIfAbsent(filteredProperties);
-
-        flags.putStringKey("machines", machines);
-        flags.putIfNotNull(LocationConfigKeys.USER, user);
-        flags.putStringKeyIfNotNull("name", name);
-        
-        if (registry != null) 
-            LocationPropertiesFromBrooklynProperties.setLocalTempDir(registry.getProperties(), flags);
-
-        log.debug("Created BYON location "+name+": "+machines);
+        config.putStringKey("machines", machines);
 
-        return managementContext.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
-                .configure(flags.getAllConfigRaw())
-                .configure(LocationConfigUtils.finalAndOriginalSpecs(spec, locationFlags, globalProperties, namedLocation)));
+        return config;
     }
-    
-    @Override
-    public String getPrefix() {
-        return BYON;
-    }
-    
-    @Override
-    public boolean accepts(String spec, LocationRegistry registry) {
-        return BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true);
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c55f361b/core/src/main/java/brooklyn/location/basic/HostLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/HostLocationResolver.java b/core/src/main/java/brooklyn/location/basic/HostLocationResolver.java
index 3744cb9..5f6783e 100644
--- a/core/src/main/java/brooklyn/location/basic/HostLocationResolver.java
+++ b/core/src/main/java/brooklyn/location/basic/HostLocationResolver.java
@@ -18,56 +18,53 @@
  */
 package brooklyn.location.basic;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import brooklyn.location.Location;
 import brooklyn.location.LocationRegistry;
-import brooklyn.location.LocationResolver;
 import brooklyn.location.LocationSpec;
-import brooklyn.management.ManagementContext;
-import brooklyn.util.collections.MutableMap;
+import brooklyn.location.basic.AbstractLocationResolver.SpecParser.ParsedSpec;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.text.KeyValueParser;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
 
-public class HostLocationResolver implements LocationResolver {
+public class HostLocationResolver extends AbstractLocationResolver {
     
     private static final String HOST = "host";
     
-    private static final Pattern PATTERN = Pattern.compile("(" + HOST + "|" + HOST.toUpperCase() + ")" + ":" + "\\((.+)\\)$");
-    
-    private volatile ManagementContext managementContext;
-
-    @Override
-    public void init(ManagementContext managementContext) {
-        this.managementContext = checkNotNull(managementContext, "managementContext");
-    }
-
     @SuppressWarnings("rawtypes")
     @Override
     public Location newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) {
-        Map globalProperties = registry.getProperties();
-        
-        Matcher matcher = PATTERN.matcher(spec);
-        if (!matcher.matches()) {
-            throw new IllegalArgumentException("Invalid location '" + spec + "'; must specify something like host:(\"1.1.1.1\")");
+        // Extract args from spec
+        ParsedSpec parsedSpec = specParser.parse(spec);
+        Map<String, String> argsMap = parsedSpec.argsMap;
+        if (argsMap.isEmpty()) {
+            throw new IllegalArgumentException("Invalid host spec (no host supplied): "+spec);
+        } else if (argsMap.size() == 1 && Iterables.get(argsMap.values(), 0) == null) {
+            // only given ip or hostname
+            argsMap = ImmutableMap.of("hosts", Iterables.get(argsMap.keySet(), 0));
+        } else if (!(argsMap.containsKey("host") || argsMap.containsKey("hosts"))) {
+            throw new IllegalArgumentException("Invalid host spec (no host supplied): "+spec);
         }
-        
-        String namedLocation = (String) locationFlags.get("named");
 
+        // Find generic applicable properties
+        Map globalProperties = registry.getProperties();
+        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
         Map<String, Object> filteredProperties = new LocationPropertiesFromBrooklynProperties().getLocationProperties(null, namedLocation, globalProperties);
-        MutableMap<String, Object> flags = MutableMap.<String, Object>builder().putAll(filteredProperties).putAll(locationFlags).removeAll("named").build();
+        ConfigBag flags = ConfigBag.newInstance(locationFlags).putIfAbsent(filteredProperties);
+        flags.remove(LocationInternal.NAMED_SPEC_NAME);
 
-        String args = matcher.group(2);
-        String target = "byon:(hosts=" + args + ")";
+        // Generate target spec
+        String target = "byon("+KeyValueParser.toLine(argsMap)+")";
         if (!managementContext.getLocationRegistry().canMaybeResolve(target)) {
             throw new IllegalArgumentException("Invalid target location '" + target + "'; must be resolvable location");
         }
         
         return managementContext.getLocationManager().createLocation(LocationSpec.create(SingleMachineProvisioningLocation.class)
                 .configure("location", target)
-                .configure("locationFlags", flags)
+                .configure("locationFlags", flags.getAllConfig())
                 .configure(LocationConfigUtils.finalAndOriginalSpecs(spec, locationFlags, globalProperties, namedLocation)));
     }
     
@@ -77,8 +74,12 @@ public class HostLocationResolver implements LocationResolver {
     }
     
     @Override
-    public boolean accepts(String spec, LocationRegistry registry) {
-        return BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true);
+    protected Class<? extends Location> getLocationType() {
+        return SingleMachineProvisioningLocation.class;
     }
 
+    @Override
+    protected SpecParser getSpecParser() {
+        return new SpecParser(getPrefix()).setExampleUsage("\"host(1.1.1.1)\" or \"host(host=1.1.1.1,name=myname)\"");
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c55f361b/core/src/main/java/brooklyn/location/basic/LocalhostLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/LocalhostLocationResolver.java b/core/src/main/java/brooklyn/location/basic/LocalhostLocationResolver.java
index 0769063..08963a4 100644
--- a/core/src/main/java/brooklyn/location/basic/LocalhostLocationResolver.java
+++ b/core/src/main/java/brooklyn/location/basic/LocalhostLocationResolver.java
@@ -18,53 +18,28 @@
  */
 package brooklyn.location.basic;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Collections;
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import brooklyn.location.Location;
-import brooklyn.location.LocationRegistry;
-import brooklyn.location.LocationResolver;
 import brooklyn.location.LocationResolver.EnableableLocationResolver;
-import brooklyn.location.LocationSpec;
-import brooklyn.management.ManagementContext;
 import brooklyn.util.config.ConfigBag;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.text.KeyValueParser;
 
 /**
  * Examples of valid specs:
  *   <ul>
  *     <li>localhost
- *     <li>localhost:()
- *     <li>localhost:(name=abc)
- *     <li>localhost:(name="abc")
+ *     <li>localhost()
+ *     <li>localhost(name=abc)
+ *     <li>localhost(name="abc")
  *   </ul>
  * 
  * @author alex, aled
  */
-public class LocalhostLocationResolver implements LocationResolver, EnableableLocationResolver {
+public class LocalhostLocationResolver extends AbstractLocationResolver implements EnableableLocationResolver {
 
-    private static final Logger log = LoggerFactory.getLogger(LocalhostLocationResolver.class);
-    
     public static final String LOCALHOST = "localhost";
-    
-    private static final Pattern PATTERN = Pattern.compile("("+LOCALHOST+"|"+LOCALHOST.toUpperCase()+")" + "(:\\((.*)\\))?$");
-    
-    private ManagementContext managementContext;
 
     @Override
-    public void init(ManagementContext managementContext) {
-        this.managementContext = checkNotNull(managementContext, "managementContext");
-    }
-    
-    @Override
     public String getPrefix() {
         return LOCALHOST;
     }
@@ -73,50 +48,25 @@ public class LocalhostLocationResolver implements LocationResolver, EnableableLo
     public boolean isEnabled() {
         return LocationConfigUtils.isEnabled(managementContext, "brooklyn.location.localhost");
     }
-    
-    @Override
-    public Location newLocationFromString(Map locationFlags, String spec, brooklyn.location.LocationRegistry registry) {
-        Map globalProperties = registry.getProperties();
-    
-        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
-        
-        Matcher matcher = PATTERN.matcher(spec);
-        if (!matcher.matches()) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; must specify something like localhost or localhost(name=abc)");
-        }
-        
-        String argsPart = matcher.group(3);
-        Map<String, String> argsMap = (argsPart != null) ? KeyValueParser.parseMap(argsPart) : Collections.<String,String>emptyMap();
-        
-        // If someone tries "localhost:(),localhost:()" as a single spec, we get weird key-values!
-        for (String key : argsMap.keySet()) {
-            if (key.contains(":") || key.contains("{") || key.contains("}") || key.contains("(") || key.contains(")")) {
-                throw new IllegalArgumentException("Invalid localhost spec: "+spec);
-            }
-        }
-        
-        String namePart = argsMap.get("name");
-        if (argsMap.containsKey("name") && (namePart == null || namePart.isEmpty())) {
-            throw new IllegalArgumentException("Invalid location '"+spec+"'; if name supplied then value must be non-empty");
-        }
 
-        Map<String, Object> filteredProperties = new LocalhostPropertiesFromBrooklynProperties().getLocationProperties("localhost", namedLocation, globalProperties);
-        // TODO filteredProperties stuff above should not be needed as named location items will already be passed in
-        ConfigBag flags = ConfigBag.newInstance(argsMap).putIfAbsent(locationFlags).putIfAbsent(filteredProperties);
-        
-        flags.putStringKey("name", Maybe.fromNullable(namePart).or("localhost"));
-        
-        if (registry != null) 
-            LocationPropertiesFromBrooklynProperties.setLocalTempDir(registry.getProperties(), flags);
-        
-        return managementContext.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class)
-            .configure(flags.getAllConfig())
-            .configure(LocationConfigUtils.finalAndOriginalSpecs(spec, locationFlags, globalProperties, namedLocation)));
+    @Override
+    protected Class<? extends Location> getLocationType() {
+        return LocalhostMachineProvisioningLocation.class;
     }
 
     @Override
-    public boolean accepts(String spec, LocationRegistry registry) {
-        return BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true);
+    protected SpecParser getSpecParser() {
+        return new AbstractLocationResolver.SpecParser(getPrefix()).setExampleUsage("\"localhost\" or \"localhost(displayName=abc)\"");
+    }
+    
+    protected Map<String, Object> getFilteredLocationProperties(String provider, String namedLocation, Map<String, ?> globalProperties) {
+        return new LocalhostPropertiesFromBrooklynProperties().getLocationProperties("localhost", namedLocation, globalProperties);
     }
 
+    @Override
+    protected ConfigBag extractConfig(Map<?,?> locationFlags, String spec, brooklyn.location.LocationRegistry registry) {
+        ConfigBag config = super.extractConfig(locationFlags, spec, registry);
+        config.putAsStringKeyIfAbsent("name", "localhost");
+        return config;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c55f361b/core/src/main/java/brooklyn/location/basic/SingleMachineLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/SingleMachineLocationResolver.java b/core/src/main/java/brooklyn/location/basic/SingleMachineLocationResolver.java
index 6d52b1e..5024cfc 100644
--- a/core/src/main/java/brooklyn/location/basic/SingleMachineLocationResolver.java
+++ b/core/src/main/java/brooklyn/location/basic/SingleMachineLocationResolver.java
@@ -18,76 +18,55 @@
  */
 package brooklyn.location.basic;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import brooklyn.location.Location;
 import brooklyn.location.LocationRegistry;
-import brooklyn.location.LocationResolver;
 import brooklyn.location.LocationSpec;
-import brooklyn.management.ManagementContext;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.text.KeyValueParser;
+import brooklyn.util.config.ConfigBag;
 
-public class SingleMachineLocationResolver implements LocationResolver {
+public class SingleMachineLocationResolver extends AbstractLocationResolver {
     
     private static final String SINGLE = "single";
     
-    private static final Pattern PATTERN = Pattern.compile("(" + SINGLE + "|" + SINGLE.toUpperCase() + ")" + ":" + "\\((.*)\\)$");
-    
-    private volatile ManagementContext managementContext;
-
+    @SuppressWarnings({ "rawtypes", "unchecked" })
     @Override
-    public void init(ManagementContext managementContext) {
-        this.managementContext = checkNotNull(managementContext, "managementContext");
-    }
-
-    @Override
-    public Location newLocationFromString(Map locationFlags, String spec, brooklyn.location.LocationRegistry registry) {
+    public Location newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) {
+        ConfigBag config = extractConfig(locationFlags, spec, registry);
         Map globalProperties = registry.getProperties();
+        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
         
-        Matcher matcher = PATTERN.matcher(spec);
-        if (!matcher.matches()) {
-            throw new IllegalArgumentException("Invalid location '" + spec + "'; must specify something like single(named:foo)");
+        if (registry != null) {
+            LocationPropertiesFromBrooklynProperties.setLocalTempDir(globalProperties, config);
         }
-        
-        String namedLocation = (String) locationFlags.get("named");
-        String args = matcher.group(2);
-        Map<String,?> locationArgs = KeyValueParser.parseMap(args);
 
-        Map<String, Object> filteredProperties = new LocationPropertiesFromBrooklynProperties().getLocationProperties(null, namedLocation, globalProperties);
-        MutableMap<String, Object> flags = MutableMap.<String, Object>builder()
-                .putAll(filteredProperties)
-                .putAll(locationFlags)
-                .removeAll("named")
-                .putAll(locationArgs).build();
-        
-        if (locationArgs.get("target") == null) {
+        if (config.getStringKey("target") == null) {
             throw new IllegalArgumentException("target must be specified in single-machine spec");
         }
-        String target = locationArgs.get("target").toString();
-        locationArgs.remove("target");
+        String target = config.getStringKey("target").toString();
+        config.remove("target");
         if (!managementContext.getLocationRegistry().canMaybeResolve(target)) {
             throw new IllegalArgumentException("Invalid target location '" + target + "'; must be resolvable location");
         }
         
         return managementContext.getLocationManager().createLocation(LocationSpec.create(SingleMachineProvisioningLocation.class)
                 .configure("location", target)
-                .configure("locationFlags", flags)
+                .configure("locationFlags", config.getAllConfig())
                 .configure(LocationConfigUtils.finalAndOriginalSpecs(spec, locationFlags, globalProperties, namedLocation)));
     }
-
+    
     @Override
     public String getPrefix() {
         return SINGLE;
     }
+    
+    @Override
+    protected Class<? extends Location> getLocationType() {
+        return SingleMachineProvisioningLocation.class;
+    }
 
     @Override
-    public boolean accepts(String spec, LocationRegistry registry) {
-        return BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true);
+    protected SpecParser getSpecParser() {
+        return new SpecParser(getPrefix()).setExampleUsage("\"single(target=jclouds:aws-ec2:us-east-1)\"");
     }
-    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c55f361b/core/src/test/java/brooklyn/location/basic/ByonLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/location/basic/ByonLocationResolverTest.java b/core/src/test/java/brooklyn/location/basic/ByonLocationResolverTest.java
index 802c50b..e0f1dda 100644
--- a/core/src/test/java/brooklyn/location/basic/ByonLocationResolverTest.java
+++ b/core/src/test/java/brooklyn/location/basic/ByonLocationResolverTest.java
@@ -46,8 +46,8 @@ import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.test.Asserts;
 import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.util.collections.MutableMap;
-import brooklyn.util.os.Os;
 import brooklyn.util.net.Networking;
+import brooklyn.util.os.Os;
 import brooklyn.util.text.StringPredicates;
 
 import com.google.common.base.Function;
@@ -88,7 +88,7 @@ public class ByonLocationResolverTest {
         brooklynProperties.put("brooklyn.location.byon.publicKeyData", "myPublicKeyData");
         brooklynProperties.put("brooklyn.location.byon.privateKeyPassphrase", "myprivateKeyPassphrase");
 
-        Map<String, Object> conf = resolve("byon:(hosts=\"1.1.1.1\")").getAllConfig(true);
+        Map<String, Object> conf = resolve("byon(hosts=\"1.1.1.1\")").getAllConfig(true);
         
         assertEquals(conf.get("privateKeyFile"), "myprivatekeyfile");
         assertEquals(conf.get("publicKeyFile"), "mypublickeyfile");
@@ -99,7 +99,7 @@ public class ByonLocationResolverTest {
 
     @Test
     public void testNamedByonLocation() throws Exception {
-        brooklynProperties.put("brooklyn.location.named.mynamed", "byon:(hosts=\"1.1.1.1\")");
+        brooklynProperties.put("brooklyn.location.named.mynamed", "byon(hosts=\"1.1.1.1\")");
         
         FixedListMachineProvisioningLocation<SshMachineLocation> loc = resolve("named:mynamed");
         assertEquals(loc.obtain().getAddress(), InetAddress.getByName("1.1.1.1"));
@@ -107,7 +107,7 @@ public class ByonLocationResolverTest {
 
     @Test
     public void testPropertiesInSpec() throws Exception {
-        FixedListMachineProvisioningLocation<SshMachineLocation> loc = resolve("byon:(privateKeyFile=myprivatekeyfile,hosts=\"1.1.1.1\")");
+        FixedListMachineProvisioningLocation<SshMachineLocation> loc = resolve("byon(privateKeyFile=myprivatekeyfile,hosts=\"1.1.1.1\")");
         SshMachineLocation machine = loc.obtain();
         
         assertEquals(machine.getAllConfig(true).get("privateKeyFile"), "myprivatekeyfile");
@@ -116,7 +116,7 @@ public class ByonLocationResolverTest {
 
     @Test
     public void testPropertyScopePrecedence() throws Exception {
-        brooklynProperties.put("brooklyn.location.named.mynamed", "byon:(hosts=\"1.1.1.1\")");
+        brooklynProperties.put("brooklyn.location.named.mynamed", "byon(hosts=\"1.1.1.1\")");
         
         // prefer those in "named" over everything else
         brooklynProperties.put("brooklyn.location.named.mynamed.privateKeyFile", "privateKeyFile-inNamed");
@@ -141,40 +141,46 @@ public class ByonLocationResolverTest {
     public void testThrowsOnInvalid() throws Exception {
         assertThrowsNoSuchElement("wrongprefix:(hosts=\"1.1.1.1\")");
         assertThrowsIllegalArgument("byon"); // no hosts
-        assertThrowsIllegalArgument("byon:()"); // no hosts
-        assertThrowsIllegalArgument("byon:(hosts=\"\")"); // empty hosts
-        assertThrowsIllegalArgument("byon:(hosts=\"1.1.1.1\""); // no closing bracket
-        assertThrowsIllegalArgument("byon:(hosts=\"1.1.1.1\", name)"); // no value for name
-        assertThrowsIllegalArgument("byon:(hosts=\"1.1.1.1\", name=)"); // no value for name
+        assertThrowsIllegalArgument("byon()"); // no hosts
+        assertThrowsIllegalArgument("byon(hosts=\"\")"); // empty hosts
+        assertThrowsIllegalArgument("byon(hosts=\"1.1.1.1\""); // no closing bracket
+        assertThrowsIllegalArgument("byon(hosts=\"1.1.1.1\", name)"); // no value for name
+        assertThrowsIllegalArgument("byon(hosts=\"1.1.1.1\", name=)"); // no value for name
     }
     
     @Test(expectedExceptions={IllegalArgumentException.class})
     public void testRegistryCommaResolutionInListNotAllowed() throws NoMachinesAvailableException {
         // disallowed since 0.7.0
         // fails because it interprets the entire string as a single byon spec, which does not parse
-        managementContext.getLocationRegistry().resolve(ImmutableList.of("byon:(hosts=\"192.168.0.1\",user=bob),byon:(hosts=\"192.168.0.2\",user=bob2)"));
+        managementContext.getLocationRegistry().resolve(ImmutableList.of("byon(hosts=\"192.168.0.1\",user=bob),byon(hosts=\"192.168.0.2\",user=bob2)"));
     }
 
     @Test
     public void testResolvesHosts() throws Exception {
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\")"), ImmutableSet.of("1.1.1.1"));
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\")"), ImmutableSet.of("1.1.1.1"));
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1,1.1.1.2\")"), ImmutableSet.of("1.1.1.1","1.1.1.2"));
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1, 1.1.1.2\")"), ImmutableSet.of("1.1.1.1","1.1.1.2"));
+    }
+
+    @Test
+    public void testWithOldStyleColon() throws Exception {
         assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1\")"), ImmutableSet.of("1.1.1.1"));
-        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1\")"), ImmutableSet.of("1.1.1.1"));
-        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1,1.1.1.2\")"), ImmutableSet.of("1.1.1.1","1.1.1.2"));
-        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1, 1.1.1.2\")"), ImmutableSet.of("1.1.1.1","1.1.1.2"));
+        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1\", name=myname)"), ImmutableSet.of("1.1.1.1"), "myname");
     }
 
     @Test
     public void testUsesDisplayName() throws Exception {
-        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1\", name=myname)"), ImmutableSet.of("1.1.1.1"), "myname");
-        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1\", name=\"myname\")"), ImmutableSet.of("1.1.1.1"), "myname");
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\", name=myname)"), ImmutableSet.of("1.1.1.1"), "myname");
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\", name=\"myname\")"), ImmutableSet.of("1.1.1.1"), "myname");
     }
 
     @Test
     public void testResolvesHostsGlobExpansion() throws Exception {
-        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.{1,2}\")"), ImmutableSet.of("1.1.1.1","1.1.1.2"));
-        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.{1.1,2.{1,2}}\")"), 
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.{1,2}\")"), ImmutableSet.of("1.1.1.1","1.1.1.2"));
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.{1.1,2.{1,2}}\")"), 
                 ImmutableSet.of("1.1.1.1","1.1.2.1","1.1.2.2"));
-        assertByonClusterEquals(resolve("byon:(hosts=\"1.1.{1,2}.{1,2}\")"), 
+        assertByonClusterEquals(resolve("byon(hosts=\"1.1.{1,2}.{1,2}\")"), 
                 ImmutableSet.of("1.1.1.1","1.1.1.2","1.1.2.1","1.1.2.2"));
     }
 
@@ -183,7 +189,7 @@ public class ByonLocationResolverTest {
         Asserts.assertFailsWith(new Runnable() {
             @Override public void run() {
                 FixedListMachineProvisioningLocation<SshMachineLocation> x = 
-                        resolve("byon:(hosts=\"1.1.1.{1,2}}\")");
+                        resolve("byon(hosts=\"1.1.1.{1,2}}\")");
                 log.error("got "+x+" but should have failed (your DNS is giving an IP for hostname '1.1.1.1}' (with the extra '}')");
             }
         }, new Predicate<Throwable>() {
@@ -203,21 +209,21 @@ public class ByonLocationResolverTest {
 
     @Test
     public void testResolvesUsernameAtHost() throws Exception {
-        assertByonClusterWithUsersEquals(resolve("byon:(hosts=\"myuser@1.1.1.1\")"), ImmutableSet.of(new UserHostTuple("myuser", "1.1.1.1")));
-        assertByonClusterWithUsersEquals(resolve("byon:(hosts=\"myuser@1.1.1.1,myuser2@1.1.1.1\")"), ImmutableSet.of(new UserHostTuple("myuser", "1.1.1.1"), new UserHostTuple("myuser2", "1.1.1.1")));
-        assertByonClusterWithUsersEquals(resolve("byon:(hosts=\"myuser@1.1.1.1,myuser2@1.1.1.2\")"), ImmutableSet.of(new UserHostTuple("myuser", "1.1.1.1"), new UserHostTuple("myuser2", "1.1.1.2")));
+        assertByonClusterWithUsersEquals(resolve("byon(hosts=\"myuser@1.1.1.1\")"), ImmutableSet.of(new UserHostTuple("myuser", "1.1.1.1")));
+        assertByonClusterWithUsersEquals(resolve("byon(hosts=\"myuser@1.1.1.1,myuser2@1.1.1.1\")"), ImmutableSet.of(new UserHostTuple("myuser", "1.1.1.1"), new UserHostTuple("myuser2", "1.1.1.1")));
+        assertByonClusterWithUsersEquals(resolve("byon(hosts=\"myuser@1.1.1.1,myuser2@1.1.1.2\")"), ImmutableSet.of(new UserHostTuple("myuser", "1.1.1.1"), new UserHostTuple("myuser2", "1.1.1.2")));
     }
 
     @Test
     public void testResolvesUserArg() throws Exception {
-        assertByonClusterWithUsersEquals(resolve("byon:(hosts=\"1.1.1.1\",user=bob)"), ImmutableSet.of(new UserHostTuple("bob", "1.1.1.1")));
-        assertByonClusterWithUsersEquals(resolve("byon:(user=\"bob\",hosts=\"myuser@1.1.1.1,1.1.1.1\")"), 
+        assertByonClusterWithUsersEquals(resolve("byon(hosts=\"1.1.1.1\",user=bob)"), ImmutableSet.of(new UserHostTuple("bob", "1.1.1.1")));
+        assertByonClusterWithUsersEquals(resolve("byon(user=\"bob\",hosts=\"myuser@1.1.1.1,1.1.1.1\")"), 
                 ImmutableSet.of(new UserHostTuple("myuser", "1.1.1.1"), new UserHostTuple("bob", "1.1.1.1")));
     }
 
     @Test
     public void testResolvesUserArg2() throws Exception {
-        String spec = "byon:(hosts=\"1.1.1.1\",user=bob)";
+        String spec = "byon(hosts=\"1.1.1.1\",user=bob)";
         FixedListMachineProvisioningLocation<SshMachineLocation> ll = resolve(spec);
         SshMachineLocation l = ll.obtain();
         Assert.assertEquals("bob", l.getUser());
@@ -226,7 +232,7 @@ public class ByonLocationResolverTest {
     @SuppressWarnings("unchecked")
     @Test
     public void testResolvesUserArg3() throws Exception {
-        String spec = "byon:(hosts=\"1.1.1.1\")";
+        String spec = "byon(hosts=\"1.1.1.1\")";
         managementContext.getLocationRegistry().getProperties().putAll(MutableMap.of(
                 "brooklyn.location.named.foo", spec,
                 "brooklyn.location.named.foo.user", "bob"));
@@ -242,7 +248,7 @@ public class ByonLocationResolverTest {
     @Test
     /** private key should be inherited, so confirm that happens correctly */
     public void testResolvesPrivateKeyArgInheritance() throws Exception {
-        String spec = "byon:(hosts=\"1.1.1.1\")";
+        String spec = "byon(hosts=\"1.1.1.1\")";
         managementContext.getLocationRegistry().getProperties().putAll(MutableMap.of(
                 "brooklyn.location.named.foo", spec,
                 "brooklyn.location.named.foo.user", "bob",
@@ -275,7 +281,7 @@ public class ByonLocationResolverTest {
         String localTempDir = Os.mergePaths(Os.tmp(), "testResolvesUsernameAtHost");
         brooklynProperties.put("brooklyn.location.byon.localTempDir", localTempDir);
 
-        FixedListMachineProvisioningLocation<SshMachineLocation> byon = resolve("byon:(hosts=\"1.1.1.1\")");
+        FixedListMachineProvisioningLocation<SshMachineLocation> byon = resolve("byon(hosts=\"1.1.1.1\")");
         SshMachineLocation machine = byon.obtain();
         assertEquals(machine.getConfig(SshMachineLocation.LOCAL_TEMP_DIR), localTempDir);
     }
@@ -283,7 +289,7 @@ public class ByonLocationResolverTest {
     @Test
     public void testMachinesObtainedInOrder() throws Exception {
         List<String> ips = ImmutableList.of("1.1.1.1", "1.1.1.6", "1.1.1.3", "1.1.1.4", "1.1.1.5");
-        String spec = "byon:(hosts=\""+Joiner.on(",").join(ips)+"\")";
+        String spec = "byon(hosts=\""+Joiner.on(",").join(ips)+"\")";
         
         MachineProvisioningLocation<SshMachineLocation> ll = resolve(spec);
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c55f361b/core/src/test/java/brooklyn/location/basic/LocalhostLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/location/basic/LocalhostLocationResolverTest.java b/core/src/test/java/brooklyn/location/basic/LocalhostLocationResolverTest.java
index e6fa5c3..5efeb60 100644
--- a/core/src/test/java/brooklyn/location/basic/LocalhostLocationResolverTest.java
+++ b/core/src/test/java/brooklyn/location/basic/LocalhostLocationResolverTest.java
@@ -137,9 +137,9 @@ public class LocalhostLocationResolverTest {
     @Test
     public void testThrowsOnInvalid() throws Exception {
         assertThrowsNoSuchElement("wrongprefix");
-        assertThrowsIllegalArgument("localhost:(name=abc"); // no closing bracket
-        assertThrowsIllegalArgument("localhost:(name)"); // no value for name
-        assertThrowsIllegalArgument("localhost:(name=)"); // no value for name
+        assertThrowsIllegalArgument("localhost(name=abc"); // no closing bracket
+        assertThrowsIllegalArgument("localhost(name)"); // no value for name
+        assertThrowsIllegalArgument("localhost(name=)"); // no value for name
     }
     
 
@@ -180,14 +180,14 @@ public class LocalhostLocationResolverTest {
     public void testRegistryCommaResolutionInListNotAllowed2() throws NoMachinesAvailableException {
         // disallowed since 0.7.0
         // fails because it interprets the entire string as a single spec, which does not parse
-        getLocationResolver().resolve(ImmutableList.of("localhost:(),localhost:()"));
+        getLocationResolver().resolve(ImmutableList.of("localhost(),localhost()"));
     }
 
     @Test(expectedExceptions={IllegalArgumentException.class})
     public void testRegistryCommaResolutionInListNotAllowed3() throws NoMachinesAvailableException {
         // disallowed since 0.7.0
         // fails because it interprets the entire string as a single spec, which does not parse
-        getLocationResolver().resolve(ImmutableList.of("localhost:(name=a),localhost:(name=b)"));
+        getLocationResolver().resolve(ImmutableList.of("localhost(name=a),localhost(name=b)"));
     }
 
     @Test(expectedExceptions={IllegalArgumentException.class})
@@ -197,6 +197,13 @@ public class LocalhostLocationResolverTest {
 
     @Test
     public void testResolvesExplicitName() throws Exception {
+        Location location = resolve("localhost(name=myname)");
+        assertTrue(location instanceof LocalhostMachineProvisioningLocation);
+        assertEquals(location.getDisplayName(), "myname");
+    }
+    
+    @Test
+    public void testWithOldStyleColon() throws Exception {
         Location location = resolve("localhost:(name=myname)");
         assertTrue(location instanceof LocalhostMachineProvisioningLocation);
         assertEquals(location.getDisplayName(), "myname");
@@ -204,7 +211,7 @@ public class LocalhostLocationResolverTest {
     
     @Test
     public void testResolvesPropertiesInSpec() throws Exception {
-        Location location = resolve("localhost:(privateKeyFile=myprivatekeyfile,name=myname)");
+        Location location = resolve("localhost(privateKeyFile=myprivatekeyfile,name=myname)");
         assertTrue(location instanceof LocalhostMachineProvisioningLocation);
         assertEquals(location.getDisplayName(), "myname");
         assertEquals(location.getAllConfig(true).get("privateKeyFile"), "myprivatekeyfile");
@@ -216,7 +223,7 @@ public class LocalhostLocationResolverTest {
         assertTrue(location instanceof LocalhostMachineProvisioningLocation);
         assertEquals(location.getDisplayName(), "localhost");
 
-        Location location2 = resolve("localhost:()");
+        Location location2 = resolve("localhost()");
         assertTrue(location2 instanceof LocalhostMachineProvisioningLocation);
         assertEquals(location2.getDisplayName(), "localhost");
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c55f361b/core/src/test/java/brooklyn/location/basic/SingleMachineLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/location/basic/SingleMachineLocationResolverTest.java b/core/src/test/java/brooklyn/location/basic/SingleMachineLocationResolverTest.java
index a137943..8edcfea 100644
--- a/core/src/test/java/brooklyn/location/basic/SingleMachineLocationResolverTest.java
+++ b/core/src/test/java/brooklyn/location/basic/SingleMachineLocationResolverTest.java
@@ -59,21 +59,25 @@ public class SingleMachineLocationResolverTest {
     
     @Test
     public void testThrowsOnInvalidTarget() throws Exception {
-        assertThrowsIllegalArgument("single:()");
-        assertThrowsIllegalArgument("single:(wrongprefix:(hosts=\"1.1.1.1\"))");
-        assertThrowsIllegalArgument("single:(foo:bar)");
+        assertThrowsIllegalArgument("single()");
+        assertThrowsIllegalArgument("single(wrongprefix:(hosts=\"1.1.1.1\"))");
+        assertThrowsIllegalArgument("single(foo:bar)");
     }
 
     @Test
     public void resolveHosts() {
+        resolve("single(target=localhost)");
+        resolve("single(target=named:foo)");
+        resolve("single(target=byon(hosts=\"1.1.1.1\"))");
+    }
+    @Test
+    public void resolveWithOldColonFormat() {
         resolve("single:(target=localhost)");
-        resolve("single:(target=named:foo)");
-        resolve("single:(target=byon:(hosts=\"1.1.1.1\"))");
     }
     
     @Test
     public void testNamedByonLocation() throws Exception {
-        brooklynProperties.put("brooklyn.location.named.mynamed", "single:(target=byon:(hosts=\"1.1.1.1\"))");
+        brooklynProperties.put("brooklyn.location.named.mynamed", "single(target=byon:(hosts=\"1.1.1.1\"))");
         
         SingleMachineProvisioningLocation<SshMachineLocation> loc = resolve("named:mynamed");
         assertEquals(loc.obtain(ImmutableMap.of()).getAddress(), InetAddress.getByName("1.1.1.1"));
@@ -81,7 +85,7 @@ public class SingleMachineLocationResolverTest {
 
     @Test
     public void testPropertyScopePrescedence() throws Exception {
-        brooklynProperties.put("brooklyn.location.named.mynamed", "single:(target=byon:(hosts=\"1.1.1.1\"))");
+        brooklynProperties.put("brooklyn.location.named.mynamed", "single(target=byon:(hosts=\"1.1.1.1\"))");
         
         // prefer those in "named" over everything else
         brooklynProperties.put("brooklyn.location.named.mynamed.privateKeyFile", "privateKeyFile-inNamed");