You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/08/14 05:42:46 UTC

[17/54] incubator-brooklyn git commit: [BROOKLYN-162] Renaming package brooklyn.location

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java
deleted file mode 100644
index e5696c2..0000000
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * 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.jclouds.templates;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.jclouds.compute.domain.Hardware;
-import org.jclouds.compute.domain.Image;
-import org.jclouds.compute.domain.OsFamily;
-import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.domain.TemplateBuilder;
-import org.jclouds.compute.options.TemplateOptions;
-
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-
-public abstract class AbstractPortableTemplateBuilder<T extends AbstractPortableTemplateBuilder<?>> implements TemplateBuilder {
-
-    /** list of commands supplied by user, excluding options */
-    protected List<Function<TemplateBuilder,TemplateBuilder>> commands = new ArrayList<Function<TemplateBuilder,TemplateBuilder>>();
-    
-    private Hardware hardware;
-    private Image image;
-    private Template template;
-    private String locationId;
-    private String imageId;
-    private String hardwareId;
-    private OsFamily os;
-    private String osNameRegex;
-    private String osDescriptionRegex;
-    private String osVersionRegex;
-    private String osArchitectureRegex;
-    private String hypervisorRegex;
-    private Boolean is64bit;
-    private String imageNameRegex;
-    private String imageDescriptionRegex;
-    private String imageVersionRegex;
-    private Double minCores;
-    private Integer minRam;
-    private Double minDisk;
-    private Predicate<Image> imageCondition;
-    private Function<Iterable<? extends Image>, Image> imageChooserFunction;
-    /** this is the last options instance set by a call to options(TemplateOptions) */
-    private TemplateOptions options;
-    /** these are extra options that we want _added_, in order, on top of the last options set */
-    private List<TemplateOptions> additionalOptions = new ArrayList<TemplateOptions>();
-    
-    @Override
-    public T any() {
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.any(); }});
-        return (T)this;
-    }
-
-    @Override
-    public T fromHardware(final Hardware hardware) {
-        this.hardware = hardware;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.fromHardware(hardware); }});
-        return (T)this;
-    }
-
-    public Hardware getHardware() {
-        return hardware;
-    }
-
-    @Override
-    public T fromImage(final Image image) {
-        this.image = image;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.fromImage(image); }});
-        return (T)this;
-    }
-    
-    public Image getImage() {
-        return image;
-    }
-
-    @Override
-    public T fromTemplate(final Template template) {
-        this.template = template;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.fromTemplate(template); }});
-        return (T)this;
-    }
-    
-    public Template getTemplate() {
-        return template;
-    }
-
-    @Override
-    public T smallest() {
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.smallest(); }});
-        return (T)this;
-    }
-
-    @Override
-    public T fastest() {
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.fastest(); }});
-        return (T)this;
-    }
-
-    @Override
-    public T biggest() {
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.biggest(); }});
-        return (T)this;
-    }
-
-    @Override
-    public T locationId(final String locationId) {
-        this.locationId = locationId;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.locationId(locationId); }});
-        return (T)this;
-    }
-    
-    public String getLocationId() {
-        return locationId;
-    }
-
-    @Override
-    public T imageId(final String imageId) {
-        this.imageId = imageId;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.imageId(imageId); }});
-        return (T)this;
-    }
-    
-    public String getImageId() {
-        return imageId;
-    }
-
-    @Override
-    public T hardwareId(final String hardwareId) {
-        this.hardwareId = hardwareId;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.hardwareId(hardwareId); }});
-        return (T)this;
-    }
-    
-    public String getHardwareId() {
-        return hardwareId;
-    }
-
-    @Override
-    public T osNameMatches(final String osNameRegex) {
-        this.osNameRegex = osNameRegex;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.osNameMatches(osNameRegex); }});
-        return (T)this;
-    }
-    
-    public String getOsNameMatchesRegex() {
-        return osNameRegex;
-    }
-
-    @Override
-    public T osDescriptionMatches(final String osDescriptionRegex) {
-        this.osDescriptionRegex = osDescriptionRegex;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.osDescriptionMatches(osDescriptionRegex); }});
-        return (T)this;
-    }
-    
-    public String getOsDescriptionMatchesRegex() {
-        return osDescriptionRegex;
-    }
-
-    @Override
-    public T osVersionMatches(final String osVersionRegex) {
-        this.osVersionRegex = osVersionRegex;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.osVersionMatches(osVersionRegex); }});
-        return (T)this;
-    }
-    
-    public String getOsVersionMatchesRegex() {
-        return osVersionRegex;
-    }
-
-    @Override
-    public T osArchMatches(final String osArchitectureRegex) {
-        this.osArchitectureRegex = osArchitectureRegex;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.osArchMatches(osArchitectureRegex); }});
-        return (T)this;
-    }
-    
-    public String getOsArchitectureMatchesRegex() {
-        return osArchitectureRegex;
-    }
-
-    @Override
-    public T os64Bit(final boolean is64bit) {
-        this.is64bit = is64bit;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.os64Bit(is64bit); }});
-        return (T)this;
-    }
-    
-    public Boolean getIs64bit() {
-        return is64bit;
-    }
-
-    @Override
-    public T osFamily(final OsFamily os) {
-        this.os = os;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.osFamily(os); }});
-        return (T)this;
-    }
-    
-    public OsFamily getOsFamily() {
-        return os;
-    }
-
-    @Override
-    public T hypervisorMatches(final String hypervisorRegex) {
-        this.hypervisorRegex = hypervisorRegex;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.hypervisorMatches(hypervisorRegex); }});
-        return (T)this;
-    }
-    
-    public String getHypervisorMatchesRegex() {
-        return hypervisorRegex;
-    }
-
-    @Override
-    public T imageNameMatches(final String imageNameRegex) {
-        this.imageNameRegex = imageNameRegex;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.imageNameMatches(imageNameRegex); }});
-        return (T)this;
-    }
-    
-    public String getImageNameMatchesRegex() {
-        return imageNameRegex;
-    }
-
-    @Override
-    public T imageDescriptionMatches(final String imageDescriptionRegex) {
-        this.imageDescriptionRegex = imageDescriptionRegex;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.imageDescriptionMatches(imageDescriptionRegex); }});
-        return (T)this;
-    }
-    
-    public String getImageDescriptionMatchesRegex() {
-        return imageDescriptionRegex;
-    }
-
-    @Override
-    public T imageVersionMatches(final String imageVersionRegex) {
-        this.imageVersionRegex = imageVersionRegex;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.imageVersionMatches(imageVersionRegex); }});
-        return (T)this;
-    }
-    
-    public String getImageVersionMatchesRegex() {
-        return imageVersionRegex;
-    }
-    
-    @Override
-    public T imageMatches(final Predicate<Image> condition) {
-        this.imageCondition = condition;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.imageMatches(condition); }});
-        return (T)this;
-    }
-    
-    public Predicate<Image> getImageMatchesCondition() {
-        return imageCondition;
-    }
-
-    @Override
-    public T minCores(final double minCores) {
-        this.minCores = minCores;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.minCores(minCores); }});
-        return (T)this;
-    }
-    
-    public Double getMinCores() {
-        return minCores;
-    }
-
-    @Override
-    public T minRam(final int megabytes) {
-        this.minRam = megabytes;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.minRam(megabytes); }});
-        return (T)this;
-    }
-    
-    /** megabytes */
-    public Integer getMinRam() {
-        return minRam;
-    }
-    
-    @Override
-    public T minDisk(final double gigabytes) {
-        this.minDisk = gigabytes;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.minDisk(gigabytes); }});
-        return (T)this;
-    }
-
-    /** megabytes */
-    public Double getMinDisk() {
-        return minDisk;
-    }
-
-    public T imageChooser(final Function<Iterable<? extends Image>, Image> imageChooserFunction) {
-        this.imageChooserFunction = imageChooserFunction;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.imageChooser(imageChooserFunction); }});
-        return (T)this;
-    }
-    
-    public Function<Iterable<? extends Image>, Image> imageChooser() {
-        return imageChooserFunction;
-    }
-
-    /** clears everything set in this template, including any default from the compute service */
-    // not sure this is that useful, as the default is only applied if there are no changes
-    public T blank() {
-        hardware = null;
-        image = null;
-        template = null;
-        hypervisorRegex = null;
-        os = null;
-        locationId = null;
-        imageId = null;
-        hardwareId = null;
-        osNameRegex = null;
-        osDescriptionRegex = null;
-        osVersionRegex = null;
-        osArchitectureRegex = null;
-        is64bit = null;
-        imageNameRegex = null;
-        imageDescriptionRegex = null;
-        imageVersionRegex = null;
-        imageCondition = null;
-        minCores = null;
-        minRam = null;
-        options = null;
-        additionalOptions.clear();
-
-        // clear all fields, and commands
-        commands.clear();
-        // then add a command to clear osName + Version + 64bit
-        osFamily(null);
-        osVersionMatches(null);
-        // no way to turn off 64-bitness, but it won't usually be turned on
-//        os64bit(null);
-        // set _something_ to prevent the default from applying
-        minRam(1);
-
-        return (T)this;
-    }
-    
-    /** true if the templateBuilder spec is blank (ignoring customization options e.g. tags for the resulting instance) */
-    public boolean isBlank() {
-        if (commands.isEmpty()) return true;
-        //also "blank" if we've blanked it
-        if (commands.size()==1 && (minRam!=null && minRam==1)) return true;
-        return false;
-    }
-    
-    @Override
-    public T options(final TemplateOptions options) {
-        this.options = options;
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.options(options); }});
-        return (T)this;
-    }
-
-    /** sets customization options; may be null if not set. use addOptions(new TemplateOptions()) to set new ones. */
-    public TemplateOptions getOptions() {
-        return options;
-    }
-    
-    /** adds customization options; if options have already been set, this will additively set selected options
-     * (but not all, see addTemplateOptions for more info)
-     */
-    public T addOptions(final TemplateOptions options) {
-        this.additionalOptions.add(options);
-        commands.add(new Function<TemplateBuilder,TemplateBuilder>() { 
-            public TemplateBuilder apply(TemplateBuilder b) { return b.options(options); }});
-        return (T)this;
-    }
-
-    public List<TemplateOptions> getAdditionalOptions() {
-        return ImmutableList.copyOf(additionalOptions);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(
-                hypervisorRegex,
-                os,
-                locationId,
-                hardwareId,
-                imageId,
-                imageDescriptionRegex,
-                imageNameRegex,
-                imageVersionRegex,
-                // might not be implement hashCode, so ignore
-//                imageCondition,
-//                imageChooserFunction,
-                is64bit,
-                locationId,
-                osArchitectureRegex,
-                osDescriptionRegex,
-                osNameRegex,
-                osVersionRegex,
-                minCores,
-                minRam,
-                minDisk,
-                options,
-                additionalOptions,
-                // might not implement hashCode, so ignore
-//                template,
-                0);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        AbstractPortableTemplateBuilder other = (AbstractPortableTemplateBuilder) obj;
-        if (!Objects.equal(additionalOptions, other.additionalOptions)) return false;
-        if (!Objects.equal(commands, other.commands)) return false;
-        if (!Objects.equal(locationId, other.locationId)) return false;
-        if (!Objects.equal(hardware, other.hardware)) return false;
-        if (!Objects.equal(hardwareId, other.hardwareId)) return false;
-        if (!Objects.equal(image, other.image)) return false;
-        if (!Objects.equal(imageId, other.imageId)) return false;
-        if (!Objects.equal(imageDescriptionRegex, other.imageDescriptionRegex)) return false;
-        if (!Objects.equal(imageNameRegex, other.imageNameRegex)) return false;
-        if (!Objects.equal(imageVersionRegex, other.imageVersionRegex)) return false;
-        if (!Objects.equal(imageCondition, other.imageCondition)) return false;
-        if (!Objects.equal(imageChooserFunction, other.imageChooserFunction)) return false;
-        if (!Objects.equal(os, other.os)) return false;
-        if (!Objects.equal(osArchitectureRegex, other.osArchitectureRegex)) return false;
-        if (!Objects.equal(osDescriptionRegex, other.osDescriptionRegex)) return false;
-        if (!Objects.equal(osNameRegex, other.osNameRegex)) return false;
-        if (!Objects.equal(osVersionRegex, other.osVersionRegex)) return false;
-        if (!Objects.equal(is64bit, other.is64bit)) return false;
-        if (!Objects.equal(hypervisorRegex, other.hypervisorRegex)) return false;
-        if (!Objects.equal(minCores, other.minCores)) return false;
-        if (!Objects.equal(minRam, other.minRam)) return false;
-        if (!Objects.equal(minDisk, other.minDisk)) return false;
-        if (!Objects.equal(options, other.options)) return false;
-        if (!Objects.equal(template, other.template)) return false;
-        return true;
-    }
-    
-    @Override
-    public String toString() {
-        return getClass().getSimpleName()+"["+makeNonTrivialArgumentsString()+"]";
-    }
-
-    protected String makeNonTrivialArgumentsString() {
-        String s =
-                  (hardware != null ? "hardware=" + hardware + ", " : "")
-                + (image != null ? "image=" + image + ", " : "")
-                + (template != null ? "template=" + template + ", " : "")
-                + (hypervisorRegex != null ? "hypervisorRegex="
-                        + hypervisorRegex + ", " : "")
-                + (os != null ? "os=" + os + ", " : "")
-                + (locationId != null ? "locationId=" + locationId + ", " : "")
-                + (imageId != null ? "imageId=" + imageId + ", " : "")
-                + (hardwareId != null ? "hardwareId=" + hardwareId + ", " : "")
-                + (osNameRegex != null ? "osNameRegex=" + osNameRegex + ", "
-                        : "")
-                + (osDescriptionRegex != null ? "osDescriptionRegex="
-                        + osDescriptionRegex + ", " : "")
-                + (osVersionRegex != null ? "osVersionRegex=" + osVersionRegex
-                        + ", " : "")
-                + (osArchitectureRegex != null ? "osArchictectureRegex="
-                        + osArchitectureRegex + ", " : "")
-                + (is64bit != null ? "is64bit=" + is64bit + ", " : "")
-                + (imageNameRegex != null ? "imageNameRegex=" + imageNameRegex
-                        + ", " : "")
-                + (imageDescriptionRegex != null ? "imageDescriptionRegex="
-                        + imageDescriptionRegex + ", " : "")
-                + (imageVersionRegex != null ? "imageVersionRegex="
-                        + imageVersionRegex + ", " : "")
-                + (imageCondition != null ? "imageCondition=" + imageCondition
-                        + ", " : "")
-                + (imageChooserFunction != null ? "imageChooserFunction=" + imageChooserFunction
-                        + ", " : "")
-                + (minCores != null ? "minCores=" + minCores + ", " : "")
-                + (minRam != null ? "minRam=" + minRam + ", " : "")
-                + (minDisk != null ? "minDisk=" + minDisk + ", " : "");
-        if (s.endsWith(", ")) s = s.substring(0, s.length()-2);
-        return s;
-    }    
-
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java
deleted file mode 100644
index 3baa003..0000000
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.jclouds.templates;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-import org.jclouds.compute.ComputeService;
-import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.domain.TemplateBuilder;
-import org.jclouds.compute.domain.TemplateBuilderSpec;
-import org.jclouds.compute.options.TemplateOptions;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.primitives.Ints;
-
-
-public class PortableTemplateBuilder<T extends PortableTemplateBuilder<?>> extends AbstractPortableTemplateBuilder<T> {
-    
-    ComputeService svc;
-    List<TemplateOptions> additionalOptionalOptions = new ArrayList<TemplateOptions>();
-
-    @Override
-    public synchronized Template build() {
-        if (svc!=null) return newJcloudsTemplate(svc);
-        throw new IllegalStateException("Cannot build a portable template until a compute service is attached");
-    }
-    
-    public synchronized ComputeService attachComputeService(ComputeService svc) {
-        ComputeService old = this.svc;
-        this.svc = svc;
-        return old;
-    }
-
-    public TemplateBuilder newJcloudsTemplateBuilder(ComputeService svc) {
-        TemplateBuilder tb = svc.templateBuilder();
-        for (Function<TemplateBuilder,TemplateBuilder> c: commands) {
-            tb = c.apply(tb);
-        }
-        
-        tb.options(computeAggregatedOptions(true));
-        
-        return tb;
-    }
-
-    public Template newJcloudsTemplate(ComputeService svc) {
-        return newJcloudsTemplateBuilder(svc).build();
-    }
-
-    /** Adds template options which are used for building, but not for matching/filtering. 
-     * (eg tags added here will be set on any machine created by this template,
-     * but will not be required when matching this template to existing machines) */
-    @SuppressWarnings("unchecked")
-    public T addOptionalOptions(TemplateOptions options) {
-        additionalOptionalOptions.add(options);
-        return (T)this;
-    }
-    
-    protected TemplateOptions computeAggregatedOptions(boolean includeOptional) {
-        TemplateOptions result;
-        if (getOptions()!=null) result = getOptions().clone();
-        else result = new TemplateOptions();
-        if (includeOptional)
-            for (TemplateOptions moreOptions: getAdditionalOptionalOptions()) result = addTemplateOptions(result, moreOptions);
-        for (TemplateOptions moreOptions: getAdditionalOptions()) result = addTemplateOptions(result, moreOptions);
-        return result;
-    }
-    
-    public List<TemplateOptions> getAdditionalOptionalOptions() {
-        return ImmutableList.copyOf(additionalOptionalOptions);
-    }
-    
-    /** like TemplateOptions.copyTo but additive wrt arrays, collections, and maps,
-     * putting moreOptions in on top of / at the end of options.
-     * currently applies to inboundPorts, tags, and userMetadata. */
-    public static TemplateOptions addTemplateOptions(TemplateOptions options, TemplateOptions moreOptions) {
-        TemplateOptions result = options.clone();
-        moreOptions.copyTo(result);
-        
-        Set<String> tags = new LinkedHashSet<String>(options.getTags());
-        tags.addAll(moreOptions.getTags());
-        result.tags(tags);
-
-        Map<String,String> userMetadata = new LinkedHashMap<String,String>(options.getUserMetadata());
-        userMetadata.putAll(moreOptions.getUserMetadata());
-        result.userMetadata(userMetadata);
-        
-        Set<Integer> inboundPorts = new TreeSet<Integer>();
-        for (int port: options.getInboundPorts()) inboundPorts.add(port);
-        for (int port: moreOptions.getInboundPorts()) inboundPorts.add(port);
-        int[] inboundPortsArray = new int[inboundPorts.size()];
-        int i=0;
-        for (Iterator<Integer> portI=inboundPorts.iterator(); portI.hasNext();) {
-            inboundPortsArray[i++] = portI.next();
-        }
-        result.inboundPorts(inboundPortsArray);
-        
-        return result;
-    }
-
-    protected String makeNonTrivialArgumentsString() {
-        String s = super.makeNonTrivialArgumentsString();
-        TemplateOptions aggr = computeAggregatedOptions(false);
-        if (aggr.getInboundPorts().length>0) s = "ports="+Ints.asList(aggr.getInboundPorts())+(s!=null && s.length()>0 ? ", "+s : "");
-        if (!aggr.getUserMetadata().isEmpty()) s = "metadata="+aggr.getUserMetadata()+(s!=null && s.length()>0 ? ", "+s : "");
-        if (!aggr.getTags().isEmpty()) s = "tags="+aggr.getTags()+(s!=null && s.length()>0 ? ", "+s : "");
-        return s;
-    }
-    
-    @Override
-    public TemplateBuilder from(TemplateBuilderSpec spec) {
-        TemplateOptions options = new TemplateOptions();
-        addOptionalOptions(options);
-        TemplateBuilder result = spec.copyTo(this, options);
-        return result;
-    }
-
-    @Override
-    public TemplateBuilder from(String spec) {
-        return from(TemplateBuilderSpec.parse(spec));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtension.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtension.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtension.java
deleted file mode 100644
index 20bfb4c..0000000
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtension.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.jclouds.zone;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.List;
-import java.util.Set;
-
-import org.apache.brooklyn.api.management.ManagementContext;
-import org.jclouds.aws.ec2.AWSEC2Api;
-import org.jclouds.ec2.domain.AvailabilityZoneInfo;
-
-import brooklyn.location.Location;
-import brooklyn.location.cloud.AbstractAvailabilityZoneExtension;
-import brooklyn.location.cloud.AvailabilityZoneExtension;
-import brooklyn.location.jclouds.JcloudsLocation;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-
-public class AwsAvailabilityZoneExtension extends AbstractAvailabilityZoneExtension implements AvailabilityZoneExtension {
-
-    private final JcloudsLocation loc;
-    
-    public AwsAvailabilityZoneExtension(ManagementContext managementContext, JcloudsLocation loc) {
-        super(managementContext);
-        this.loc = checkNotNull(loc, "loc");
-        checkArgument(loc.getProvider().equals("aws-ec2"), "provider not aws-ec2 (%s)", loc.getProvider());
-    }
-
-    @Override
-    protected List<Location> doGetAllSubLocations() {
-        List<Location> result = Lists.newArrayList();
-        Set<AvailabilityZoneInfo> zones = getAvailabilityZones();
-        for (AvailabilityZoneInfo zone : zones) {
-            result.add(newSubLocation(loc, zone));
-        }
-        return result;
-    }
-    
-    @Override
-    protected boolean isNameMatch(Location loc, Predicate<? super String> namePredicate) {
-        return namePredicate.apply(((JcloudsLocation)loc).getRegion());
-    }
-    
-    protected Set<AvailabilityZoneInfo> getAvailabilityZones() {
-        String regionName = loc.getRegion();
-        AWSEC2Api ec2Client = loc.getComputeService().getContext().unwrapApi(AWSEC2Api.class);
-        return ec2Client.getAvailabilityZoneAndRegionApi().get().describeAvailabilityZonesInRegion(regionName);
-    }
-    
-    protected JcloudsLocation newSubLocation(Location parent, AvailabilityZoneInfo zone) {
-        return loc.newSubLocation(ImmutableMap.of(JcloudsLocation.CLOUD_REGION_ID, zone.getZone()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java b/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java
index f0d1140..bd02f60 100644
--- a/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java
+++ b/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java
@@ -28,8 +28,8 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.event.basic.Sensors;
-import brooklyn.location.Location;
-import brooklyn.location.basic.WinRmMachineLocation;
+import org.apache.brooklyn.location.Location;
+import org.apache.brooklyn.location.basic.WinRmMachineLocation;
 import brooklyn.policy.basic.AbstractPolicy;
 
 import com.google.common.annotations.Beta;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java b/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
index 2459187..4e2f68c 100644
--- a/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
+++ b/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
@@ -37,8 +37,8 @@ import brooklyn.entity.basic.AbstractEntity;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.event.basic.Sensors;
-import brooklyn.location.Location;
-import brooklyn.location.basic.SshMachineLocation;
+import org.apache.brooklyn.location.Location;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.internal.ssh.SshTool;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsSubnetSshMachineLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsSubnetSshMachineLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsSubnetSshMachineLocation.java
new file mode 100644
index 0000000..c21810d
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsSubnetSshMachineLocation.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import java.util.Map;
+
+import org.jclouds.compute.domain.NodeMetadata;
+
+import org.apache.brooklyn.location.basic.SupportsPortForwarding.RequiresPortForwarding;
+
+public abstract class AbstractJcloudsSubnetSshMachineLocation extends JcloudsSshMachineLocation implements RequiresPortForwarding {
+
+    public AbstractJcloudsSubnetSshMachineLocation() {
+    }
+
+    /** @deprecated since 0.6.0 use no-arg constructor, as per parent */
+    @Deprecated
+    public AbstractJcloudsSubnetSshMachineLocation(Map flags, JcloudsLocation parent, NodeMetadata node) {
+        super(flags, parent, node);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java
new file mode 100644
index 0000000..e73d5f7
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java
@@ -0,0 +1,99 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.options.TemplateOptions;
+
+import com.google.common.annotations.Beta;
+
+
+/**
+ * A default no-op implementation, which can be extended to override the appropriate methods.
+ * 
+ * Sub-classing will give the user some protection against future API changes - note that 
+ * {@link JcloudsLocationCustomizer} is marked {@link Beta}.
+ * 
+ * @author aled
+ */
+public class BasicJcloudsLocationCustomizer implements JcloudsLocationCustomizer {
+
+    @Override
+    public void customize(JcloudsLocation location, ComputeService computeService, TemplateBuilder templateBuilder) {
+        // no-op
+    }
+
+    @Override
+    public void customize(JcloudsLocation location, ComputeService computeService, Template template) {
+        // no-op
+    }
+
+    @Override
+    public void customize(JcloudsLocation location, ComputeService computeService, TemplateOptions templateOptions) {
+        // no-op
+    }
+
+    @Override
+    public void customize(JcloudsLocation location, ComputeService computeService, JcloudsMachineLocation machine) {
+        if (machine instanceof JcloudsSshMachineLocation) {
+            customize(location, computeService, (JcloudsSshMachineLocation)machine);
+        } else {
+            // no-op
+        }
+    }
+    
+    @Override
+    public void preRelease(JcloudsMachineLocation machine) {
+        if (machine instanceof JcloudsSshMachineLocation) {
+            preRelease((JcloudsSshMachineLocation)machine);
+        } else {
+            // no-op
+        }
+    }
+
+    @Override
+    public void postRelease(JcloudsMachineLocation machine) {
+        if (machine instanceof JcloudsSshMachineLocation) {
+            postRelease((JcloudsSshMachineLocation)machine);
+        } else {
+            // no-op
+        }
+    }
+    
+    @Override
+    @Deprecated
+    public void customize(JcloudsLocation location, ComputeService computeService, JcloudsSshMachineLocation machine) {
+        // no-op
+    }
+
+    @Override
+    @Deprecated
+    public void preRelease(JcloudsSshMachineLocation machine) {
+        // no-op
+    }
+
+    @Override
+    @Deprecated
+    public void postRelease(JcloudsSshMachineLocation machine) {
+        // no-op
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynImageChooser.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynImageChooser.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynImageChooser.java
new file mode 100644
index 0000000..62cf003
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynImageChooser.java
@@ -0,0 +1,367 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.collections.MutableList;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Ordering;
+import com.google.common.math.DoubleMath;
+
+@Beta
+/** NB: subclasses must implement {@link #clone()} */
+public class BrooklynImageChooser implements Cloneable {
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynImageChooser.class);
+    
+    protected ComputeService computeService;
+    protected String cloudProviderName;
+    
+    protected static int compare(double left, double right) {
+        return DoubleMath.fuzzyCompare(left, right, 0.00000001);
+    }
+    
+    protected static boolean imageNameContains(Image img, String pattern) {
+        if (img.getName()==null) return false;
+        return img.getName().contains(pattern);
+    }
+    
+    protected static boolean imageNameContainsCaseInsensitive(Image img, String pattern) {
+        if (img.getName()==null) return false;
+        return img.getName().toLowerCase().contains(pattern.toLowerCase());
+    }
+    
+    protected static boolean imageNameContainsWordCaseInsensitive(Image img, String pattern) {
+        if (img.getName()==null) return false;
+        return img.getName().toLowerCase().matches("(.*[^a-z])?"+pattern.toLowerCase()+"([^a-z].*)?");
+    }
+    
+    public double punishmentForOldOsVersions(Image img, OsFamily family, double minVersion) {
+        OperatingSystem os = img.getOperatingSystem();
+        if (os!=null && family.equals(os.getFamily())) {
+            String v = os.getVersion();
+            if (v!=null) {
+                try {
+                    double vd = Double.parseDouble(v);
+                    // punish older versions, with a -log function (so 0.5 version behind is -log(1.5)=-0.5 and 2 versions behind is -log(3)=-1.2  
+                    if (vd < minVersion) return -Math.log(1+(minVersion - vd));
+                } catch (Exception e) {
+                    /* ignore unparseable versions */
+                }
+            }
+        }
+        return 0;
+    }
+    
+    public List<String> blackListedImageIds() {
+        return Arrays.asList(
+                // bad natty image - causes 403 on attempts to apt-get; https://bugs.launchpad.net/ubuntu/+bug/987182
+                "us-east-1/ami-1cb30875",
+                // wrong login user advertised, causes "Error Invalid packet: indicated length 1349281121 too large"
+                // from sshj due to message coming back "Plea"(se log in as another user), according to https://github.com/jclouds/legacy-jclouds/issues/748
+                "us-east-1/ami-08faa660"
+            );
+    }
+
+    public List<String> whilelistedImageIds() {
+        return Arrays.asList(
+        // these are the ones we recommend in brooklyn.properties, but now autodetection should be more reliable
+//                "us-east-1/ami-d0f89fb9",
+//                "us-west-1/ami-fe002cbb",
+//                "us-west-2/ami-70f96e40",
+//                "eu-west-1/ami-ce7b6fba",
+//                "sa-east-1/ami-a3da00be",
+//                "ap-southeast-1/ami-64084736",
+//                "ap-southeast-2/ami-04ea7a3e",
+//                "ap-northeast-1/ami-fe6ceeff"
+            );
+    }
+    
+    public double score(Image img) {
+        double score = 0;
+
+        if (blackListedImageIds().contains(img.getId()))
+            score -= 50;
+
+        if (whilelistedImageIds().contains(img.getId()))
+            // NB: this should be less than deprecated punishment to catch deprecation of whitelisted items
+            score += 20;
+
+        score += punishmentForDeprecation(img);
+
+    
+        // prefer these guys, in stock brooklyn provisioning
+        score += punishmentForOldOsVersions(img, OsFamily.UBUNTU, 11);
+        score += punishmentForOldOsVersions(img, OsFamily.CENTOS, 6);
+
+        OperatingSystem os = img.getOperatingSystem();
+        if (os!=null) {
+            if (os.getFamily()!=null) {
+                // preference for these open, popular OS (but only wrt versions above) 
+                if (os.getFamily().equals(OsFamily.CENTOS)) score += 2;
+                else if (os.getFamily().equals(OsFamily.UBUNTU)) {
+                    score += 2;
+
+                    // prefer these LTS releases slightly above others (including above CentOS)
+                    // (but note in AWS Virginia, at least, version is empty for the 14.04 images for some reason, as of Aug 2014)
+                    if ("14.04".equals(os.getVersion())) score += 0.2;
+                    else if ("12.04".equals(os.getVersion())) score += 0.1;
+
+                    // NB some 13.10 images take 20m+ before they are sshable on AWS
+                    // with "vesafb: module verification error" showing in the AWS system log
+                }
+
+                // slight preference for these 
+                else if (os.getFamily().equals(OsFamily.RHEL)) score += 1;
+                else if (os.getFamily().equals(OsFamily.AMZN_LINUX)) score += 1;
+                else if (os.getFamily().equals(OsFamily.DEBIAN)) score += 1;
+
+                // prefer to take our chances with unknown / unlabelled linux than something explicitly windows
+                else if (os.getFamily().equals(OsFamily.WINDOWS)) score -= 1;
+                
+                if ("softlayer".equals(cloudProviderName)) {
+                    // on softlayer, prefer images where family is part of the image id
+                    // (this is the only way to identiy official images; but in other clouds
+                    // it can cause not-so-good images to get selected!)
+                    if (img.getId().toLowerCase().indexOf(os.getFamily().toString().toLowerCase()) >= 0)
+                        score += 0.5;
+                }
+            }
+            // prefer 64-bit
+            if (os.is64Bit()) score += 0.5;
+        }
+
+        // TODO prefer known providerIds
+
+        if (log.isTraceEnabled())
+            log.trace("initial score "+score+" for "+img);
+        
+        return score;
+    }
+
+    protected double punishmentForDeprecation(Image img) {
+        // google deprecation strategy
+        //        userMetadata={deprecatedState=DEPRECATED}}
+        String deprecated = img.getUserMetadata().get("deprecatedState");
+        if (deprecated!=null) {
+            if ("deprecated".equalsIgnoreCase(deprecated))
+                return -30;
+            log.warn("Unrecognised 'deprecatedState' value '"+deprecated+"' when scoring "+img+"; ignoring that metadata");
+        }
+        
+        // common strategies
+        if (imageNameContainsWordCaseInsensitive(img, "deprecated")) return -30;
+        if (imageNameContainsWordCaseInsensitive(img, "alpha")) return -10;
+        if (imageNameContainsWordCaseInsensitive(img, "beta")) return -5;
+        if (imageNameContainsWordCaseInsensitive(img, "testing")) return -5;
+        if (imageNameContainsWordCaseInsensitive(img, "rc")) return -3;
+
+        // no indication this is deprecated
+        return 0;
+    }
+
+    public BrooklynImageChooser clone() {
+        return new BrooklynImageChooser();
+    }
+    
+    protected void use(ComputeService service) {
+        if (this.computeService!=null && !this.computeService.equals(service))
+            throw new IllegalStateException("ImageChooser must be cloned to set a compute service");
+        this.computeService = service;
+        if (computeService!=null) {
+            cloudProviderName = computeService.getContext().unwrap().getId();
+        }
+    }
+    
+    public BrooklynImageChooser cloneFor(ComputeService service) {
+        BrooklynImageChooser result = clone();
+        result.use(service);
+        return result;
+    }
+    
+    public static class OrderingScoredWithoutDefaults extends Ordering<Image> implements ComputeServiceAwareChooser<OrderingScoredWithoutDefaults> {
+        private BrooklynImageChooser chooser;
+        public OrderingScoredWithoutDefaults(BrooklynImageChooser chooser) {
+            this.chooser = chooser;
+        }
+        public int compare(Image left, Image right) {
+            return BrooklynImageChooser.compare(chooser.score(left), chooser.score(right));
+        }
+        @Override
+        public OrderingScoredWithoutDefaults cloneFor(ComputeService service) {
+            return new OrderingScoredWithoutDefaults(chooser.cloneFor(service));
+        }        
+    }
+    
+    public Ordering<Image> orderingScoredWithoutDefaults() {
+        return new OrderingScoredWithoutDefaults(this);
+    }
+    
+    /** @deprecated since 0.7.0 kept in case persisted */
+    @Deprecated
+    public Ordering<Image> orderingScoredWithoutDefaultsDeprecated() {
+        return new Ordering<Image>() {
+            @Override
+            public int compare(Image left, Image right) {
+                return BrooklynImageChooser.compare(score(left), score(right));
+            }
+        };
+    }
+    
+    public static class OrderingWithDefaults extends Ordering<Image> implements ComputeServiceAwareChooser<OrderingWithDefaults> {
+        Ordering<Image> primaryOrdering;
+        public OrderingWithDefaults(final Ordering<Image> primaryOrdering) {
+            this.primaryOrdering = primaryOrdering;
+        }
+        @Override
+        public int compare(Image left, Image right) {
+            return ComparisonChain.start()
+                .compare(left, right, primaryOrdering)
+                // fall back to default strategy otherwise, except preferring *non*-null values
+                // TODO use AlphaNum string comparator
+                .compare(left.getName(), right.getName(), Ordering.<String> natural().nullsFirst())
+                .compare(left.getVersion(), right.getVersion(), Ordering.<String> natural().nullsFirst())
+                .compare(left.getDescription(), right.getDescription(), Ordering.<String> natural().nullsFirst())
+                .compare(left.getOperatingSystem().getName(), right.getOperatingSystem().getName(), Ordering.<String> natural().nullsFirst())
+                .compare(left.getOperatingSystem().getVersion(), right.getOperatingSystem().getVersion(), Ordering.<String> natural().nullsFirst())
+                .compare(left.getOperatingSystem().getDescription(), right.getOperatingSystem().getDescription(), Ordering.<String> natural().nullsFirst())
+                .compare(left.getOperatingSystem().getArch(), right.getOperatingSystem().getArch(), Ordering.<String> natural().nullsFirst()).result();
+        }    
+        @Override
+        public OrderingWithDefaults cloneFor(ComputeService service) {
+            if (primaryOrdering instanceof ComputeServiceAwareChooser) {
+                return new OrderingWithDefaults( BrooklynImageChooser.cloneFor(primaryOrdering, service) );
+            }
+            return this;
+        }        
+    }
+    
+    public static Ordering<Image> orderingWithDefaults(final Ordering<Image> primaryOrdering) {
+        return new OrderingWithDefaults(primaryOrdering);
+    }
+    
+    /** @deprecated since 0.7.0 kept in case persisted */
+    @Deprecated
+    public static Ordering<Image> orderingWithDefaultsDeprecated(final Ordering<Image> primaryOrdering) {
+        return new Ordering<Image>() {
+            @Override
+            public int compare(Image left, Image right) {
+                return ComparisonChain.start()
+                    .compare(left, right, primaryOrdering)
+                    // fall back to default strategy otherwise, except preferring *non*-null values
+                    // TODO use AlphaNum string comparator
+                    .compare(left.getName(), right.getName(), Ordering.<String> natural().nullsFirst())
+                    .compare(left.getVersion(), right.getVersion(), Ordering.<String> natural().nullsFirst())
+                    .compare(left.getDescription(), right.getDescription(), Ordering.<String> natural().nullsFirst())
+                    .compare(left.getOperatingSystem().getName(), right.getOperatingSystem().getName(), Ordering.<String> natural().nullsFirst())
+                    .compare(left.getOperatingSystem().getVersion(), right.getOperatingSystem().getVersion(), Ordering.<String> natural().nullsFirst())
+                    .compare(left.getOperatingSystem().getDescription(), right.getOperatingSystem().getDescription(), Ordering.<String> natural().nullsFirst())
+                    .compare(left.getOperatingSystem().getArch(), right.getOperatingSystem().getArch(), Ordering.<String> natural().nullsFirst()).result();
+            }
+        };
+    }
+    
+    public static class ImageChooserFromOrdering implements Function<Iterable<? extends Image>, Image>, ComputeServiceAwareChooser<ImageChooserFromOrdering> {
+        final Ordering<Image> ordering;
+        public ImageChooserFromOrdering(final Ordering<Image> ordering) { this.ordering = ordering; }
+        @Override
+        public Image apply(Iterable<? extends Image> input) {
+            List<? extends Image> maxImages = multiMax(ordering, input);
+            return maxImages.get(maxImages.size() - 1);
+        }
+        @Override
+        public ImageChooserFromOrdering cloneFor(ComputeService service) {
+            if (ordering instanceof ComputeServiceAwareChooser) {
+                return new ImageChooserFromOrdering( BrooklynImageChooser.cloneFor(ordering, service) );
+            }
+            return this;
+        }        
+    }
+
+    public static Function<Iterable<? extends Image>, Image> imageChooserFromOrdering(final Ordering<Image> ordering) {
+        return new ImageChooserFromOrdering(ordering);
+    }
+    
+    /** @deprecated since 0.7.0 kept in case persisted */
+    @Deprecated
+    public static Function<Iterable<? extends Image>, Image> imageChooserFromOrderingDeprecated(final Ordering<Image> ordering) {
+        return new Function<Iterable<? extends Image>, Image>() {
+            @Override
+            public Image apply(Iterable<? extends Image> input) {
+                List<? extends Image> maxImages = multiMax(ordering, input);
+                return maxImages.get(maxImages.size() - 1);
+            }
+        };
+    }
+    
+    protected interface ComputeServiceAwareChooser<T> {
+        public T cloneFor(ComputeService service);
+    }
+
+    /** Attempts to clone the given item for use with the given {@link ComputeService}, if
+     * the item is {@link ComputeServiceAwareChooser}; otherwise it returns the item unchanged */
+    @SuppressWarnings("unchecked")
+    public static <T> T cloneFor(T item, ComputeService service) {
+        if (item instanceof ComputeServiceAwareChooser) {
+            return ((ComputeServiceAwareChooser<T>)item).cloneFor(service);
+        }
+        return item;
+    }
+    
+    // from jclouds
+    static <T, E extends T> List<E> multiMax(Comparator<T> ordering, Iterable<E> iterable) {
+        Iterator<E> iterator = iterable.iterator();
+        List<E> maxes = MutableList.of(iterator.next());
+        E maxSoFar = maxes.get(0);
+        while (iterator.hasNext()) {
+           E current = iterator.next();
+           int comparison = ordering.compare(maxSoFar, current);
+           if (comparison == 0) {
+              maxes.add(current);
+           } else if (comparison < 0) {
+              maxes = MutableList.of(current);
+              maxSoFar = current;
+           }
+        }
+        return maxes;
+     }
+    
+    public Ordering<Image> ordering() {
+        return orderingWithDefaults(orderingScoredWithoutDefaults());
+    }
+
+    public Function<Iterable<? extends Image>,Image> chooser() {
+        return imageChooserFromOrdering(ordering());
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java
new file mode 100644
index 0000000..e857e93
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java
@@ -0,0 +1,219 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import static org.apache.brooklyn.location.jclouds.pool.MachinePoolPredicates.matching;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.management.Task;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.trait.Startable;
+import org.apache.brooklyn.location.MachineLocation;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+import org.apache.brooklyn.location.jclouds.pool.MachinePool;
+import org.apache.brooklyn.location.jclouds.pool.MachineSet;
+import org.apache.brooklyn.location.jclouds.pool.ReusableMachineTemplate;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.task.BasicExecutionContext;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * @deprecated since 0.6.0; never used in production setting, and thus of dubious value; best avoided as unlikely to be supported in future versions
+ */
+@Deprecated
+public class BrooklynMachinePool extends MachinePool {
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynMachinePool.class);
+    
+    protected final JcloudsLocation location;
+    final List<Task<?>> activeTasks = new ArrayList<Task<?>>();
+    final String providerLocationId;
+    
+    public BrooklynMachinePool(JcloudsLocation l) {
+        super(l.getComputeService());
+        providerLocationId = l.getRegion();
+        this.location = l;
+    }
+    
+    /** claims a machine with the indicated spec, creating if necessary */
+    public SshMachineLocation obtain(ReusableMachineTemplate t) {
+        MachineSet previous = unclaimed(matching(t));
+        
+        while (true) {
+            NodeMetadata m = claim(1, t).iterator().next();
+            // TODO ideally shouldn't have to rebind
+            SshMachineLocation result = null;
+            try {
+                result = toSshMachineLocation( m );
+            } catch (Exception e) {
+                if (previous.contains(m)) {
+                    log.debug("attempt to bind to previous existing machine "+m+" failed (will blacklist and retry another): "+e);
+                } else {
+                    log.warn("attempt to bind to machine "+m+" failed: "+e);
+                    throw Throwables.propagate(e);
+                }
+            }
+            if (result!=null) return result;
+            if (previous.contains(m)) {
+                log.debug("could not bind to previous existing machine "+m+"; blacklisting and trying a new one");
+                addToBlacklist(new MachineSet(m));
+            } else {
+                throw new IllegalStateException("cannot bind/connect to newly created machine; error in configuration");
+            }
+        }
+    }
+    
+    protected MachineSet filterForAllowedMachines(MachineSet input) {
+        MachineSet result = super.filterForAllowedMachines(input);
+        if (providerLocationId!=null) {
+            result = result.filtered(matching( new ReusableMachineTemplate().locationId(providerLocationId).strict(false) ));
+        }
+        return result;
+    }
+
+    /** returns an SshMachineLocation, if one can be created and accessed; returns null if it cannot be created */
+    protected SshMachineLocation toSshMachineLocation(NodeMetadata m) {
+        try {
+            JcloudsSshMachineLocation sshM = location.rebindMachine(m);
+            if (sshM.execCommands("check-reachable", Arrays.asList("whoami")) != 0) {
+                log.warn("cannot bind to machine "+m);
+                return null;
+            }
+            return sshM;
+        } catch (Exception e) {
+            throw Throwables.propagate(e);
+        }
+    }
+    
+    @Override
+    public MachineSet create(int count, ReusableMachineTemplate template) {
+        List<NodeMetadata> nodes = new ArrayList<NodeMetadata>();
+        for (int i=0; i<count; i++) {
+            // TODO this in parallel
+            JcloudsSshMachineLocation m;
+            try {
+                MachineLocation machineLocation = location.obtain(MutableMap.of("callerContext", ""+this+"("+template+")"), template);
+                // Class has been deprecated since 0.6.0, and prior to that, obtain would have returned a JcloudsSshMachineLocation
+                if (machineLocation instanceof JcloudsSshMachineLocation) {
+                    m = (JcloudsSshMachineLocation) machineLocation;
+                } else {
+                    throw new UnsupportedOperationException("Cannot create WinRmMachineLocation");
+                }
+            } catch (Exception e) {
+                throw Throwables.propagate(e);
+            }
+            nodes.add(m.getNode());
+        }
+        MachineSet result = new MachineSet(nodes);
+        registerNewNodes(result, template);
+        return result;
+    }
+
+    public boolean unclaim(SshMachineLocation location) {
+        init();
+        if (location instanceof JcloudsSshMachineLocation)
+            return unclaim(new MachineSet( ((JcloudsSshMachineLocation)location).getNode()) ) > 0;
+        return false;
+    }
+    public boolean destroy(SshMachineLocation location) {
+        init();
+        if (location instanceof JcloudsSshMachineLocation)
+            return destroy(new MachineSet( ((JcloudsSshMachineLocation)location).getNode()) ) > 0;
+        return false;
+    }
+
+    // TODO we need to remove stale tasks somewhere
+    protected <T> Task<T> addTask(Task<T> t) {
+        synchronized (activeTasks) { activeTasks.add(t); }
+        return t;
+    }
+    
+    public List<Task<?>> getActiveTasks() {
+        List<Task<?>> result;
+        synchronized (activeTasks) { result = ImmutableList.<Task<?>>copyOf(activeTasks); }
+        return result;
+    }
+
+    public void blockUntilTasksEnded() {
+        while (true) {
+            boolean allDone = true;
+            List<Task<?>> tt = getActiveTasks();
+            for (Task<?> t: tt) {
+                if (!t.isDone()) {
+                    allDone = false;
+                    if (log.isDebugEnabled()) log.debug("Pool "+this+", blocking for completion of: "+t);
+                    t.blockUntilEnded();
+                }
+            }
+            synchronized (activeTasks) {
+                List<Task> newTT = new ArrayList<Task>(getActiveTasks());
+                newTT.removeAll(tt);
+                if (allDone && tt.isEmpty()) {
+                    //task list has stabilized, and there are no active tasks; clear and exit
+                    if (log.isDebugEnabled()) log.debug("Pool "+this+", all known tasks have completed, clearing list");
+                    activeTasks.clear();
+                    break;
+                }
+                if (log.isDebugEnabled()) log.debug("Pool "+this+", all previously known tasks have completed, but there are new tasks ("+newTT+") checking them");
+            }
+        }
+    }
+
+    /** starts the given template; for use only within a task (e.g. application's start effector).
+     * returns a child task of the current task.
+     * <p>
+     * throws exception if not in a task. (you will have to claim, then invoke the effectors manually.) */
+    public Task<?> start(final ReusableMachineTemplate template, final List<? extends Startable> entities) {
+        BasicExecutionContext ctx = BasicExecutionContext.getCurrentExecutionContext();
+        if (ctx==null) throw new IllegalStateException("Pool.start is only permitted within a task (effector)");
+        final AtomicReference<Task<?>> t = new AtomicReference<Task<?>>();
+        synchronized (t) {
+            t.set(ctx.submit(new Runnable() {
+                public void run() {
+                    synchronized (t) {
+                        if (log.isDebugEnabled()) log.debug("Pool "+this+", task "+t.get()+" claiming a "+template);
+                        SshMachineLocation m = obtain(template);
+                        if (log.isDebugEnabled()) log.debug("Pool "+this+", task "+t.get()+" got "+m+"; starting "+entities);
+                        for (Startable entity: entities)
+                            addTask( ((Entity)entity).invoke(Startable.START, MutableMap.of("locations", Arrays.asList(m))) );
+                    }
+                }
+            }));
+        }
+        addTask(t.get());
+        return t.get();
+    }
+
+    /** @see #start(ReusableMachineTemplate, List) */
+    public Task<?> start(ReusableMachineTemplate template, Startable ...entities) {
+        return start(template, Arrays.asList(entities));
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java
new file mode 100644
index 0000000..a835e2c
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java
@@ -0,0 +1,28 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import org.jclouds.compute.ComputeService;
+
+import brooklyn.util.config.ConfigBag;
+
+public interface ComputeServiceRegistry {
+    
+    public ComputeService findComputeService(ConfigBag conf, boolean allowReuse);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java
new file mode 100644
index 0000000..f3f39bb
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java
@@ -0,0 +1,183 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import static brooklyn.util.JavaGroovyEquivalents.groovyTruth;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_AMI_QUERY;
+import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_CC_AMI_QUERY;
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.brooklyn.location.cloud.CloudLocationConfig;
+import org.jclouds.Constants;
+import org.jclouds.ContextBuilder;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.ec2.reference.EC2Constants;
+import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule;
+import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.Sanitizer;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.inject.Module;
+
+public class ComputeServiceRegistryImpl implements ComputeServiceRegistry, JcloudsLocationConfig {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(ComputeServiceRegistryImpl.class);
+
+    public static final ComputeServiceRegistryImpl INSTANCE = new ComputeServiceRegistryImpl();
+        
+    protected ComputeServiceRegistryImpl() {
+    }
+    
+    protected final Map<Map<?,?>,ComputeService> cachedComputeServices = new ConcurrentHashMap<Map<?,?>,ComputeService>();
+
+    protected final Object createComputeServicesMutex = new Object();
+
+    @Override
+    public ComputeService findComputeService(ConfigBag conf, boolean allowReuse) {
+        String provider = checkNotNull(conf.get(CLOUD_PROVIDER), "provider must not be null");
+        String identity = checkNotNull(conf.get(CloudLocationConfig.ACCESS_IDENTITY), "identity must not be null");
+        String credential = checkNotNull(conf.get(CloudLocationConfig.ACCESS_CREDENTIAL), "credential must not be null");
+        
+        Properties properties = new Properties();
+        properties.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, Boolean.toString(true));
+        properties.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, Boolean.toString(true));
+        properties.setProperty("jclouds.ssh.max-retries", conf.getStringKey("jclouds.ssh.max-retries") != null ? 
+                conf.getStringKey("jclouds.ssh.max-retries").toString() : "50");
+        // Enable aws-ec2 lazy image fetching, if given a specific imageId; otherwise customize for specific owners; or all as a last resort
+        // See https://issues.apache.org/jira/browse/WHIRR-416
+        if ("aws-ec2".equals(provider)) {
+            // TODO convert AWS-only flags to config keys
+            if (groovyTruth(conf.get(IMAGE_ID))) {
+                properties.setProperty(PROPERTY_EC2_AMI_QUERY, "");
+                properties.setProperty(PROPERTY_EC2_CC_AMI_QUERY, "");
+            } else if (groovyTruth(conf.getStringKey("imageOwner"))) {
+                properties.setProperty(PROPERTY_EC2_AMI_QUERY, "owner-id="+conf.getStringKey("imageOwner")+";state=available;image-type=machine");
+            } else if (groovyTruth(conf.getStringKey("anyOwner"))) {
+                // set `anyOwner: true` to override the default query (which is restricted to certain owners as per below), 
+                // allowing the AMI query to bind to any machine
+                // (note however, we sometimes pick defaults in JcloudsLocationFactory);
+                // (and be careful, this can give a LOT of data back, taking several minutes,
+                // and requiring extra memory allocated on the command-line)
+                properties.setProperty(PROPERTY_EC2_AMI_QUERY, "state=available;image-type=machine");
+                /*
+                 * by default the following filters are applied:
+                 * Filter.1.Name=owner-id&Filter.1.Value.1=137112412989&
+                 * Filter.1.Value.2=063491364108&
+                 * Filter.1.Value.3=099720109477&
+                 * Filter.1.Value.4=411009282317&
+                 * Filter.2.Name=state&Filter.2.Value.1=available&
+                 * Filter.3.Name=image-type&Filter.3.Value.1=machine&
+                 */
+            }
+            
+            // occasionally can get com.google.common.util.concurrent.UncheckedExecutionException: java.lang.RuntimeException: 
+            //     security group eu-central-1/jclouds#brooklyn-bxza-alex-eu-central-shoul-u2jy-nginx-ielm is not available after creating
+            // the default timeout was 500ms so let's raise it in case that helps
+            properties.setProperty(EC2Constants.PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT, ""+Duration.seconds(30).toMilliseconds());
+        }
+
+        // FIXME Deprecated mechanism, should have a ConfigKey for overrides
+        Map<String, Object> extra = Maps.filterKeys(conf.getAllConfig(), Predicates.containsPattern("^jclouds\\."));
+        if (extra.size() > 0) {
+            LOG.warn("Jclouds using deprecated property overrides: "+Sanitizer.sanitize(extra));
+        }
+        properties.putAll(extra);
+
+        String endpoint = conf.get(CloudLocationConfig.CLOUD_ENDPOINT);
+        if (!groovyTruth(endpoint)) endpoint = getDeprecatedProperty(conf, Constants.PROPERTY_ENDPOINT);
+        if (groovyTruth(endpoint)) properties.setProperty(Constants.PROPERTY_ENDPOINT, endpoint);
+
+        Map<?,?> cacheKey = MutableMap.builder()
+                .putAll(properties)
+                .put("provider", provider)
+                .put("identity", identity)
+                .put("credential", credential)
+                .putIfNotNull("endpoint", endpoint)
+                .build()
+                .asUnmodifiable();
+
+        if (allowReuse) {
+            ComputeService result = cachedComputeServices.get(cacheKey);
+            if (result!=null) {
+                LOG.trace("jclouds ComputeService cache hit for compute service, for "+Sanitizer.sanitize(properties));
+                return result;
+            }
+            LOG.debug("jclouds ComputeService cache miss for compute service, creating, for "+Sanitizer.sanitize(properties));
+        }
+
+        Iterable<Module> modules = getCommonModules();
+
+        // Synchronizing to avoid deadlock from sun.reflect.annotation.AnnotationType.
+        // See https://github.com/brooklyncentral/brooklyn/issues/974
+        ComputeServiceContext computeServiceContext;
+        synchronized (createComputeServicesMutex) {
+            computeServiceContext = ContextBuilder.newBuilder(provider)
+                    .modules(modules)
+                    .credentials(identity, credential)
+                    .overrides(properties)
+                    .build(ComputeServiceContext.class);
+        }
+        final ComputeService computeService = computeServiceContext.getComputeService();
+        if (allowReuse) {
+            synchronized (cachedComputeServices) {
+                ComputeService result = cachedComputeServices.get(cacheKey);
+                if (result != null) {
+                    LOG.debug("jclouds ComputeService cache recovery for compute service, for "+Sanitizer.sanitize(cacheKey));
+                    //keep the old one, discard the new one
+                    computeService.getContext().close();
+                    return result;
+                }
+                LOG.debug("jclouds ComputeService created "+computeService+", adding to cache, for "+Sanitizer.sanitize(properties));
+                cachedComputeServices.put(cacheKey, computeService);
+            }
+        }
+        return computeService;
+     }
+
+    /** returns the jclouds modules we typically install */ 
+    protected ImmutableSet<Module> getCommonModules() {
+        return ImmutableSet.<Module> of(
+                new SshjSshClientModule(), 
+                new SLF4JLoggingModule(),
+                new BouncyCastleCryptoModule());
+    }
+     
+    protected String getDeprecatedProperty(ConfigBag conf, String key) {
+        if (conf.containsKey(key)) {
+            LOG.warn("Jclouds using deprecated brooklyn-jclouds property "+key+": "+Sanitizer.sanitize(conf.getAllConfig()));
+            return (String) conf.getStringKey(key);
+        } else {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java
new file mode 100644
index 0000000..5aa026d
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java
@@ -0,0 +1,184 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.location.LocationRegistry;
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.brooklyn.location.LocationResolver;
+import org.apache.brooklyn.location.LocationSpec;
+import org.apache.brooklyn.location.NoMachinesAvailableException;
+import org.apache.brooklyn.location.basic.BasicLocationRegistry;
+import org.apache.brooklyn.location.basic.FixedListMachineProvisioningLocation;
+import org.apache.brooklyn.location.basic.LocationConfigKeys;
+import org.apache.brooklyn.location.basic.LocationConfigUtils;
+import org.apache.brooklyn.location.basic.LocationInternal;
+import org.apache.brooklyn.location.basic.LocationPropertiesFromBrooklynProperties;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.text.KeyValueParser;
+import brooklyn.util.text.Strings;
+import brooklyn.util.text.WildcardGlobs;
+import brooklyn.util.text.WildcardGlobs.PhraseTreatment;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ * Examples of valid specs:
+ *   <ul>
+ *     <li>byon:(hosts=myhost)
+ *     <li>byon:(hosts="myhost, myhost2")
+ *     <li>byon:(hosts="myhost, myhost2", name="my location name")
+ *   </ul>
+ * 
+ * @author aled
+ */
+@SuppressWarnings({"unchecked","rawtypes"})
+public class JcloudsByonLocationResolver implements LocationResolver {
+
+    public static final Logger log = LoggerFactory.getLogger(JcloudsByonLocationResolver.class);
+    
+    public static final String BYON = "jcloudsByon";
+
+    private static final Pattern PATTERN = Pattern.compile("("+BYON+"|"+BYON.toUpperCase()+")" + ":" + "\\((.*)\\)$");
+
+    private ManagementContext managementContext;
+
+    @Override
+    public void init(ManagementContext managementContext) {
+        this.managementContext = checkNotNull(managementContext, "managementContext");
+    }
+    
+    // TODO Remove some duplication from JcloudsResolver; needs more careful review
+    @Override
+    public FixedListMachineProvisioningLocation<JcloudsSshMachineLocation> 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 jcloudsByon(provider=\"aws-ec2\",region=\"us-east-1\",hosts=\"i-f2014593,i-d1234567\")");
+        }
+        
+        String argsPart = matcher.group(2);
+        Map<String, String> argsMap = KeyValueParser.parseMap(argsPart);
+        
+        // prefer args map over location flags
+        
+        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
+
+        String providerOrApi = argsMap.containsKey("provider") ? argsMap.get("provider") : (String)locationFlags.get("provider");
+
+        String regionName = argsMap.containsKey("region") ? argsMap.get("region") : (String)locationFlags.get("region");
+        
+        String endpoint = argsMap.containsKey("endpoint") ? argsMap.get("endpoint") : (String)locationFlags.get("endpoint");
+        
+        String name = argsMap.containsKey("name") ? argsMap.get("name") : (String)locationFlags.get("name");
+
+        String user = argsMap.containsKey("user") ? argsMap.get("user") : (String)locationFlags.get("user");
+
+        String privateKeyFile = argsMap.containsKey("privateKeyFile") ? argsMap.get("privateKeyFile") : (String)locationFlags.get("privateKeyFile");
+        
+        String hosts = argsMap.get("hosts");
+        
+        if (Strings.isEmpty(providerOrApi)) {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; provider must be defined");
+        }
+        if (hosts == null || hosts.isEmpty()) {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; at least one host must be defined");
+        }
+        if (argsMap.containsKey("name") && (Strings.isEmpty(name))) {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; if name supplied then value must be non-empty");
+        }
+
+        // For everything in brooklyn.properties, only use things with correct prefix (and remove that prefix).
+        // But for everything passed in via locationFlags, pass those as-is.
+        // TODO Should revisit the locationFlags: where are these actually used? Reason accepting properties without
+        //      full prefix is that the map's context is explicitly this location, rather than being generic properties.
+        Map allProperties = getAllProperties(registry, globalProperties);
+        Map jcloudsProperties = new JcloudsPropertiesFromBrooklynProperties().getJcloudsProperties(providerOrApi, regionName, namedLocation, allProperties);
+        jcloudsProperties.putAll(locationFlags);
+        jcloudsProperties.putAll(argsMap);
+        
+        String jcloudsSpec = "jclouds:"+providerOrApi + (regionName != null ? ":"+regionName : "") + (endpoint != null ? ":"+endpoint : "");
+        JcloudsLocation jcloudsLocation = (JcloudsLocation) registry.resolve(jcloudsSpec, jcloudsProperties);
+
+        List<String> hostIdentifiers = WildcardGlobs.getGlobsAfterBraceExpansion("{"+hosts+"}",
+                true /* numeric */, /* no quote support though */ PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR);
+        List<JcloudsSshMachineLocation> machines = Lists.newArrayList();
+        
+        for (String hostIdentifier : hostIdentifiers) {
+            Map<?, ?> machineFlags = MutableMap.builder()
+                    .put("id", hostIdentifier)
+                    .putIfNotNull("user", user)
+                    .putIfNotNull("privateKeyFile", privateKeyFile)
+                    .build();
+            try {
+                JcloudsSshMachineLocation machine = jcloudsLocation.rebindMachine(jcloudsLocation.config().getBag().putAll(machineFlags));
+                machines.add(machine);
+            } catch (NoMachinesAvailableException e) {
+                log.warn("Error rebinding to jclouds machine "+hostIdentifier+" in "+jcloudsLocation, e);
+                Exceptions.propagate(e);
+            }
+        }
+        
+        ConfigBag flags = ConfigBag.newInstance(jcloudsProperties);
+
+        flags.putStringKey("machines", machines);
+        flags.putIfNotNull(LocationConfigKeys.USER, user);
+        flags.putStringKeyIfNotNull("name", name);
+        
+        if (registry != null) 
+            LocationPropertiesFromBrooklynProperties.setLocalTempDir(registry.getProperties(), flags);
+
+        log.debug("Created Jclouds BYON location "+name+": "+machines);
+        
+        return managementContext.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+                .configure(flags.getAllConfig())
+                .configure(LocationConfigUtils.finalAndOriginalSpecs(spec, locationFlags, globalProperties, namedLocation)));
+    }
+    
+    private Map getAllProperties(LocationRegistry registry, Map<?,?> properties) {
+        Map<Object,Object> allProperties = Maps.newHashMap();
+        if (registry!=null) allProperties.putAll(registry.getProperties());
+        allProperties.putAll(properties);
+        return allProperties;
+    }
+    
+    @Override
+    public String getPrefix() {
+        return BYON;
+    }
+    
+    @Override
+    public boolean accepts(String spec, LocationRegistry registry) {
+        return BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true);
+    }
+}
\ No newline at end of file