You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/08/19 13:10:16 UTC
[58/72] [abbrv] incubator-brooklyn git commit: BROOKLYN-162 - jclouds
last few package prefixes needed,
and tidy in core and elsewhere related (or observed in the process)
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocation.java
new file mode 100644
index 0000000..14e43e5
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocation.java
@@ -0,0 +1,476 @@
+/*
+ * 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.byon;
+
+import static org.apache.brooklyn.util.GroovyJavaMethods.truth;
+
+import java.io.Closeable;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.location.MachineLocationCustomizer;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.api.mgmt.LocationManager;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.reflect.TypeToken;
+
+import org.apache.brooklyn.location.cloud.CloudLocationConfig;
+import org.apache.brooklyn.location.core.AbstractLocation;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.CollectionFunctionals;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.WildcardGlobs;
+import org.apache.brooklyn.util.text.WildcardGlobs.PhraseTreatment;
+
+/**
+ * A provisioner of {@link MachineLocation}s which takes a list of machines it can connect to.
+ * The collection of initial machines should be supplied in the 'machines' flag in the constructor,
+ * for example a list of machines which can be SSH'd to.
+ *
+ * This can be extended to have a mechanism to make more machines to be available
+ * (override provisionMore and canProvisionMore).
+ */
+public class FixedListMachineProvisioningLocation<T extends MachineLocation> extends AbstractLocation
+implements MachineProvisioningLocation<T>, Closeable {
+
+ // TODO Synchronization looks very wrong for accessing machines/inUse
+ // e.g. removeChild doesn't synchronize when doing machines.remove(...),
+ // and getMachines() returns the real sets risking
+ // ConcurrentModificationException in the caller if it iterates over them etc.
+
+ private static final Logger log = LoggerFactory.getLogger(FixedListMachineProvisioningLocation.class);
+
+ public static final ConfigKey<Function<Iterable<? extends MachineLocation>, MachineLocation>> MACHINE_CHOOSER =
+ ConfigKeys.newConfigKey(
+ new TypeToken<Function<Iterable<? extends MachineLocation>, MachineLocation>>() {},
+ "byon.machineChooser",
+ "For choosing which of the possible machines is chosen and returned by obtain()",
+ CollectionFunctionals.<MachineLocation>firstElement());
+
+ public static final ConfigKey<Collection<MachineLocationCustomizer>> MACHINE_LOCATION_CUSTOMIZERS = CloudLocationConfig.MACHINE_LOCATION_CUSTOMIZERS;
+
+ private final Object lock = new Object();
+
+ @SetFromFlag
+ protected Set<T> machines;
+
+ @SetFromFlag
+ protected Set<T> inUse;
+
+ @SetFromFlag
+ protected Set<T> pendingRemoval;
+
+ @SetFromFlag
+ protected Map<T, Map<String, Object>> origConfigs;
+
+ public FixedListMachineProvisioningLocation() {
+ this(Maps.newLinkedHashMap());
+ }
+ public FixedListMachineProvisioningLocation(Map properties) {
+ super(properties);
+
+ if (isLegacyConstruction()) {
+ init();
+ }
+ }
+
+ @Override
+ public void init() {
+ super.init();
+
+ Set<T> machinesCopy = MutableSet.of();
+ for (T location: machines) {
+ if (location==null) {
+ log.warn(""+this+" initialized with null location, removing (may be due to rebind with reference to an unmanaged location)");
+ } else {
+ Location parent = location.getParent();
+ if (parent == null) {
+ addChild(location);
+ }
+ machinesCopy.add(location);
+ }
+ }
+ if (!machinesCopy.equals(machines)) {
+ machines = machinesCopy;
+ }
+ }
+
+ @Override
+ public String toVerboseString() {
+ return Objects.toStringHelper(this).omitNullValues()
+ .add("id", getId()).add("name", getDisplayName())
+ .add("machinesAvailable", getAvailable()).add("machinesInUse", getInUse())
+ .toString();
+ }
+
+ @Override
+ public AbstractLocation configure(Map<?,?> properties) {
+ if (machines == null) machines = Sets.newLinkedHashSet();
+ if (inUse == null) inUse = Sets.newLinkedHashSet();
+ if (pendingRemoval == null) pendingRemoval = Sets.newLinkedHashSet();
+ if (origConfigs == null) origConfigs = Maps.newLinkedHashMap();
+ return super.configure(properties);
+ }
+
+ @SuppressWarnings("unchecked")
+ public FixedListMachineProvisioningLocation<T> newSubLocation(Map<?,?> newFlags) {
+ // TODO shouldn't have to copy config bag as it should be inherited (but currently it is not used inherited everywhere; just most places)
+ return getManagementContext().getLocationManager().createLocation(LocationSpec.create(getClass())
+ .parent(this)
+ .configure(config().getLocalBag().getAllConfig()) // FIXME Should this just be inherited?
+ .configure(newFlags));
+ }
+
+ @Override
+ public void close() {
+ for (T machine : machines) {
+ if (machine instanceof Closeable) Streams.closeQuietly((Closeable)machine);
+ }
+ }
+
+ public void addMachine(T machine) {
+ synchronized (lock) {
+ if (machines.contains(machine)) {
+ throw new IllegalArgumentException("Cannot add "+machine+" to "+toString()+", because already contained");
+ }
+
+ Location existingParent = ((Location)machine).getParent();
+ if (existingParent == null) {
+ addChild(machine);
+ }
+
+ machines.add(machine);
+ }
+ }
+
+ public void removeMachine(T machine) {
+ synchronized (lock) {
+ if (inUse.contains(machine)) {
+ pendingRemoval.add(machine);
+ } else {
+ machines.remove(machine);
+ pendingRemoval.remove(machine);
+ if (this.equals(machine.getParent())) {
+ removeChild((Location)machine);
+ }
+ }
+ }
+ }
+
+ protected Set<T> getMachines() {
+ return machines;
+ }
+
+ public Set<T> getAvailable() {
+ Set<T> a = Sets.newLinkedHashSet(machines);
+ a.removeAll(inUse);
+ return a;
+ }
+
+ public Set<T> getInUse() {
+ return Sets.newLinkedHashSet(inUse);
+ }
+
+ public Set<T> getAllMachines() {
+ return ImmutableSet.copyOf(machines);
+ }
+
+ @Override
+ public void addChild(Location child) {
+ super.addChild(child);
+ machines.add((T)child);
+ }
+
+ @Override
+ public boolean removeChild(Location child) {
+ if (inUse.contains(child)) {
+ throw new IllegalStateException("Child location "+child+" is in use; cannot remove from "+this);
+ }
+ machines.remove(child);
+ return super.removeChild(child);
+ }
+
+ protected boolean canProvisionMore() {
+ return false;
+ }
+
+ protected void provisionMore(int size) {
+ provisionMore(size, ImmutableMap.of());
+ }
+
+ protected void provisionMore(int size, Map<?,?> flags) {
+ throw new IllegalStateException("more not permitted");
+ }
+
+ public T obtain() throws NoMachinesAvailableException {
+ return obtain(Maps.<String,Object>newLinkedHashMap());
+ }
+
+ @Override
+ public T obtain(Map<?,?> flags) throws NoMachinesAvailableException {
+ T machine;
+ T desiredMachine = (T) flags.get("desiredMachine");
+ ConfigBag allflags = ConfigBag.newInstanceExtending(config().getBag()).putAll(flags);
+ Function<Iterable<? extends MachineLocation>, MachineLocation> chooser = allflags.get(MACHINE_CHOOSER);
+
+ synchronized (lock) {
+ Set<T> a = getAvailable();
+ if (a.isEmpty()) {
+ if (canProvisionMore()) {
+ provisionMore(1, allflags.getAllConfig());
+ a = getAvailable();
+ }
+ if (a.isEmpty())
+ throw new NoMachinesAvailableException("No machines available in "+toString());
+ }
+ if (desiredMachine != null) {
+ if (a.contains(desiredMachine)) {
+ machine = desiredMachine;
+ } else {
+ throw new IllegalStateException("Desired machine "+desiredMachine+" not available in "+toString()+"; "+
+ (inUse.contains(desiredMachine) ? "machine in use" : "machine unknown"));
+ }
+ } else {
+ machine = (T) chooser.apply(a);
+ if (!a.contains(machine)) {
+ throw new IllegalStateException("Machine chooser attempted to choose '"+machine+"' from outside the available set, in "+this);
+ }
+ }
+ inUse.add(machine);
+ updateMachineConfig(machine, flags);
+ }
+
+ for (MachineLocationCustomizer customizer : getMachineCustomizers(allflags)) {
+ customizer.customize(machine);
+ }
+
+ return machine;
+ }
+
+ @Override
+ public void release(T machine) {
+ ConfigBag machineConfig = ((ConfigurationSupportInternal)machine.config()).getBag();
+ for (MachineLocationCustomizer customizer : getMachineCustomizers(machineConfig)) {
+ customizer.preRelease(machine);
+ }
+
+ synchronized (lock) {
+ if (inUse.contains(machine) == false)
+ throw new IllegalStateException("Request to release machine "+machine+", but this machine is not currently allocated");
+ restoreMachineConfig(machine);
+ inUse.remove(machine);
+
+ if (pendingRemoval.contains(machine)) {
+ removeMachine(machine);
+ }
+ }
+ }
+
+ @Override
+ public Map<String,Object> getProvisioningFlags(Collection<String> tags) {
+ return Maps.<String,Object>newLinkedHashMap();
+ }
+
+ protected void updateMachineConfig(T machine, Map<?, ?> flags) {
+ if (origConfigs == null) {
+ // For backwards compatibility, where peristed state did not have this.
+ origConfigs = Maps.newLinkedHashMap();
+ }
+ Map<String, Object> strFlags = ConfigBag.newInstance(flags).getAllConfig();
+ Map<String, Object> origConfig = ((ConfigurationSupportInternal)machine.config()).getLocalBag().getAllConfig();
+ origConfigs.put(machine, origConfig);
+ requestPersist();
+
+ ((ConfigurationSupportInternal)machine.config()).addToLocalBag(strFlags);
+ }
+
+ protected void restoreMachineConfig(MachineLocation machine) {
+ if (origConfigs == null) {
+ // For backwards compatibility, where peristed state did not have this.
+ origConfigs = Maps.newLinkedHashMap();
+ }
+ Map<String, Object> origConfig = origConfigs.remove(machine);
+ if (origConfig == null) return;
+ requestPersist();
+
+ Set<String> currentKeys = ((ConfigurationSupportInternal)machine.config()).getLocalBag().getAllConfig().keySet();
+ Set<String> newKeys = Sets.difference(currentKeys, origConfig.entrySet());
+ for (String key : newKeys) {
+ ((ConfigurationSupportInternal)machine.config()).removeFromLocalBag(key);
+ }
+ ((ConfigurationSupportInternal)machine.config()).addToLocalBag(origConfig);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <K> K getConfigPreferringOverridden(ConfigKey<K> key, Map<?,?> overrides) {
+ K result = (K) overrides.get(key);
+ if (result == null) result = (K) overrides.get(key.getName());
+ if (result == null) result = getConfig(key);
+ return result;
+ }
+
+ protected Collection<MachineLocationCustomizer> getMachineCustomizers(ConfigBag setup) {
+ Collection<MachineLocationCustomizer> customizers = setup.get(MACHINE_LOCATION_CUSTOMIZERS);
+ return (customizers == null ? ImmutableList.<MachineLocationCustomizer>of() : customizers);
+ }
+
+ /**
+ * Facilitates fluent/programmatic style for constructing a fixed pool of machines.
+ * <pre>
+ * {@code
+ * new FixedListMachineProvisioningLocation.Builder()
+ * .user("alex")
+ * .keyFile("/Users/alex/.ssh/id_rsa")
+ * .addAddress("10.0.0.1")
+ * .addAddress("10.0.0.2")
+ * .addAddress("10.0.0.3")
+ * .addAddressMultipleTimes("me@127.0.0.1", 5)
+ * .build();
+ * }
+ * </pre>
+ */
+ public static class Builder {
+ LocationManager lm;
+ String user;
+ String privateKeyPassphrase;
+ String privateKeyFile;
+ String privateKeyData;
+ File localTempDir;
+ List machines = Lists.newArrayList();
+
+ public Builder(LocationManager lm) {
+ this.lm = lm;
+ }
+ public Builder user(String user) {
+ this.user = user;
+ return this;
+ }
+ public Builder keyPassphrase(String keyPassphrase) {
+ this.privateKeyPassphrase = keyPassphrase;
+ return this;
+ }
+ public Builder keyFile(String keyFile) {
+ this.privateKeyFile = keyFile;
+ return this;
+ }
+ public Builder keyData(String keyData) {
+ this.privateKeyData = keyData;
+ return this;
+ }
+ public Builder localTempDir(File val) {
+ this.localTempDir = val;
+ return this;
+ }
+ /** adds the locations; user and keyfile set in the builder are _not_ applied to the machine
+ * (use add(String address) for that)
+ */
+ public Builder add(SshMachineLocation location) {
+ machines.add(location);
+ return this;
+ }
+ public Builder addAddress(String address) {
+ return addAddresses(address);
+ }
+ public Builder addAddressMultipleTimes(String address, int n) {
+ for (int i=0; i<n; i++)
+ addAddresses(address);
+ return this;
+ }
+ public Builder addAddresses(String address1, String ...others) {
+ List<String> addrs = new ArrayList<String>();
+ addrs.addAll(WildcardGlobs.getGlobsAfterBraceExpansion("{"+address1+"}",
+ true /* numeric */, /* no quote support though */ PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR));
+ for (String address: others)
+ addrs.addAll(WildcardGlobs.getGlobsAfterBraceExpansion("{"+address+"}",
+ true /* numeric */, /* no quote support though */ PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR));
+ for (String addr: addrs)
+ add(createMachine(addr));
+ return this;
+ }
+ protected SshMachineLocation createMachine(String addr) {
+ if (lm==null)
+ return new SshMachineLocation(makeConfig(addr));
+ else
+ return lm.createLocation(makeConfig(addr), SshMachineLocation.class);
+ }
+ private Map makeConfig(String address) {
+ String user = this.user;
+ if (address.contains("@")) {
+ user = address.substring(0, address.indexOf("@"));
+ address = address.substring(address.indexOf("@")+1);
+ }
+ Map config = MutableMap.of("address", address);
+ if (truth(user)) {
+ config.put("user", user);
+ config.put("sshconfig.user", user);
+ }
+ if (truth(privateKeyPassphrase)) config.put("sshconfig.privateKeyPassphrase", privateKeyPassphrase);
+ if (truth(privateKeyFile)) config.put("sshconfig.privateKeyFile", privateKeyFile);
+ if (truth(privateKeyData)) config.put("sshconfig.privateKey", privateKeyData);
+ if (truth(localTempDir)) config.put("localTempDir", localTempDir);
+ return config;
+ }
+ @SuppressWarnings("unchecked")
+ public FixedListMachineProvisioningLocation<SshMachineLocation> build() {
+ if (lm==null)
+ return new FixedListMachineProvisioningLocation<SshMachineLocation>(MutableMap.builder()
+ .putIfNotNull("machines", machines)
+ .putIfNotNull("user", user)
+ .putIfNotNull("privateKeyPassphrase", privateKeyPassphrase)
+ .putIfNotNull("privateKeyFile", privateKeyFile)
+ .putIfNotNull("privateKeyData", privateKeyData)
+ .putIfNotNull("localTempDir", localTempDir)
+ .build());
+ else
+ return lm.createLocation(MutableMap.builder()
+ .putIfNotNull("machines", machines)
+ .putIfNotNull("user", user)
+ .putIfNotNull("privateKeyPassphrase", privateKeyPassphrase)
+ .putIfNotNull("privateKeyFile", privateKeyFile)
+ .putIfNotNull("privateKeyData", privateKeyData)
+ .putIfNotNull("localTempDir", localTempDir)
+ .build(),
+ FixedListMachineProvisioningLocation.class);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/byon/HostLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/byon/HostLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/byon/HostLocationResolver.java
new file mode 100644
index 0000000..55263e2
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/byon/HostLocationResolver.java
@@ -0,0 +1,93 @@
+/*
+ * 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.byon;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationRegistry;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.location.core.AbstractLocationResolver;
+import org.apache.brooklyn.location.core.LocationConfigUtils;
+import org.apache.brooklyn.location.core.LocationPropertiesFromBrooklynProperties;
+import org.apache.brooklyn.location.core.internal.LocationInternal;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.guava.Maybe.Absent;
+import org.apache.brooklyn.util.text.KeyValueParser;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+public class HostLocationResolver extends AbstractLocationResolver {
+
+ private static final String HOST = "host";
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Location newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) {
+ // 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);
+ }
+
+ // 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);
+ ConfigBag flags = ConfigBag.newInstance(locationFlags).putIfAbsent(filteredProperties);
+ flags.remove(LocationInternal.NAMED_SPEC_NAME);
+
+ // Generate target spec
+ String target = "byon("+KeyValueParser.toLine(argsMap)+")";
+ Maybe<Location> testResolve = managementContext.getLocationRegistry().resolve(target, false, null);
+ if (!testResolve.isPresent()) {
+ throw new IllegalArgumentException("Invalid target location '" + target + "' for location '"+HOST+"': "+
+ Exceptions.collapseText( ((Absent<?>)testResolve).getException() ), ((Absent<?>)testResolve).getException());
+ }
+
+ return managementContext.getLocationManager().createLocation(LocationSpec.create(SingleMachineProvisioningLocation.class)
+ .configure("location", target)
+ .configure("locationFlags", flags.getAllConfig())
+ .configure(LocationConfigUtils.finalAndOriginalSpecs(spec, locationFlags, globalProperties, namedLocation)));
+ }
+
+ @Override
+ public String getPrefix() {
+ return HOST;
+ }
+
+ @Override
+ 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/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/byon/SingleMachineLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/byon/SingleMachineLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/byon/SingleMachineLocationResolver.java
new file mode 100644
index 0000000..6265f5c
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/byon/SingleMachineLocationResolver.java
@@ -0,0 +1,81 @@
+/*
+ * 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.byon;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationRegistry;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.location.core.AbstractLocationResolver;
+import org.apache.brooklyn.location.core.LocationConfigUtils;
+import org.apache.brooklyn.location.core.LocationPropertiesFromBrooklynProperties;
+import org.apache.brooklyn.location.core.internal.LocationInternal;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.guava.Maybe.Absent;
+
+public class SingleMachineLocationResolver extends AbstractLocationResolver {
+
+ private static final String SINGLE = "single";
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ 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());
+
+ if (registry != null) {
+ LocationPropertiesFromBrooklynProperties.setLocalTempDir(globalProperties, config);
+ }
+
+ if (config.getStringKey("target") == null) {
+ throw new IllegalArgumentException("target must be specified in single-machine spec");
+ }
+ String target = config.getStringKey("target").toString();
+ config.remove("target");
+ Maybe<Location> testResolve = managementContext.getLocationRegistry().resolve(target, false, null);
+ if (!testResolve.isPresent()) {
+ throw new IllegalArgumentException("Invalid target location '" + target + "' for location '"+SINGLE+"': "+
+ Exceptions.collapseText( ((Absent<?>)testResolve).getException() ));
+ }
+
+ return managementContext.getLocationManager().createLocation(LocationSpec.create(SingleMachineProvisioningLocation.class)
+ .configure("location", target)
+ .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
+ 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/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/byon/SingleMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/byon/SingleMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/byon/SingleMachineProvisioningLocation.java
new file mode 100644
index 0000000..3338da5
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/byon/SingleMachineProvisioningLocation.java
@@ -0,0 +1,91 @@
+/*
+ * 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.byon;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+
+public class SingleMachineProvisioningLocation<T extends MachineLocation> extends FixedListMachineProvisioningLocation<T> {
+ private static final long serialVersionUID = -4216528515792151062L;
+
+ private static final Logger log = LoggerFactory.getLogger(SingleMachineProvisioningLocation.class);
+
+ @SetFromFlag(nullable=false)
+ private String location;
+
+ @SetFromFlag(nullable=false)
+ private Map<?,?> locationFlags;
+
+ private T singleLocation;
+ private int referenceCount;
+ private MachineProvisioningLocation<T> provisioningLocation;
+
+
+ public SingleMachineProvisioningLocation() {
+ }
+
+ @SuppressWarnings("rawtypes")
+ public SingleMachineProvisioningLocation(String location, Map locationFlags) {
+ this.locationFlags = locationFlags;
+ this.location = location;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public synchronized T obtain(Map flags) throws NoMachinesAvailableException {
+ log.info("Flags {} passed to newLocationFromString will be ignored, using {}", flags, locationFlags);
+ return obtain();
+ }
+
+ @Override
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public synchronized T obtain() throws NoMachinesAvailableException {
+ if (singleLocation == null) {
+ if (provisioningLocation == null) {
+ provisioningLocation = (MachineProvisioningLocation) getManagementContext().getLocationRegistry().resolve(
+ location, locationFlags);
+ }
+ singleLocation = provisioningLocation.obtain(ImmutableMap.of());
+ inUse.add(singleLocation);
+ }
+ referenceCount++;
+ return singleLocation;
+ }
+
+ @Override
+ public synchronized void release(T machine) {
+ if (!machine.equals(singleLocation)) {
+ throw new IllegalArgumentException("Invalid machine " + machine + " passed to release, expecting: " + singleLocation);
+ }
+ if (--referenceCount == 0) {
+ provisioningLocation.release(machine);
+ singleLocation = null;
+ }
+ inUse.remove(machine);
+ };
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/cloud/AbstractCloudMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/cloud/AbstractCloudMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/cloud/AbstractCloudMachineProvisioningLocation.java
index d09cadc..6a7dc73 100644
--- a/core/src/main/java/org/apache/brooklyn/location/cloud/AbstractCloudMachineProvisioningLocation.java
+++ b/core/src/main/java/org/apache/brooklyn/location/cloud/AbstractCloudMachineProvisioningLocation.java
@@ -24,7 +24,7 @@ import java.util.Map;
import org.apache.brooklyn.api.location.LocationSpec;
import org.apache.brooklyn.api.location.MachineLocation;
import org.apache.brooklyn.api.location.MachineProvisioningLocation;
-import org.apache.brooklyn.location.basic.AbstractLocation;
+import org.apache.brooklyn.location.core.AbstractLocation;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.core.internal.ssh.SshTool;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/cloud/AvailabilityZoneExtension.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/cloud/AvailabilityZoneExtension.java b/core/src/main/java/org/apache/brooklyn/location/cloud/AvailabilityZoneExtension.java
index 312cb83..c0143ae 100644
--- a/core/src/main/java/org/apache/brooklyn/location/cloud/AvailabilityZoneExtension.java
+++ b/core/src/main/java/org/apache/brooklyn/location/cloud/AvailabilityZoneExtension.java
@@ -22,7 +22,7 @@ import java.util.List;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.entity.group.DynamicCluster;
-import org.apache.brooklyn.location.basic.MultiLocation;
+import org.apache.brooklyn.location.core.MultiLocation;
import com.google.common.annotations.Beta;
import com.google.common.base.Predicate;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/cloud/CloudLocationConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/cloud/CloudLocationConfig.java b/core/src/main/java/org/apache/brooklyn/location/cloud/CloudLocationConfig.java
index 2bb2d7e..935a96c 100644
--- a/core/src/main/java/org/apache/brooklyn/location/cloud/CloudLocationConfig.java
+++ b/core/src/main/java/org/apache/brooklyn/location/cloud/CloudLocationConfig.java
@@ -27,7 +27,7 @@ import org.apache.brooklyn.api.location.MachineLocationCustomizer;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.BasicConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
-import org.apache.brooklyn.location.basic.LocationConfigKeys;
+import org.apache.brooklyn.location.core.LocationConfigKeys;
import org.apache.brooklyn.util.core.flags.SetFromFlag;
public interface CloudLocationConfig {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/core/AbstractLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/core/AbstractLocation.java b/core/src/main/java/org/apache/brooklyn/location/core/AbstractLocation.java
new file mode 100644
index 0000000..026cf87
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/core/AbstractLocation.java
@@ -0,0 +1,709 @@
+/*
+ * 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.core;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.brooklyn.util.GroovyJavaMethods.elvis;
+import static org.apache.brooklyn.util.JavaGroovyEquivalents.groovyTruth;
+
+import java.io.Closeable;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
+import org.apache.brooklyn.api.mgmt.rebind.mementos.LocationMemento;
+import org.apache.brooklyn.api.objs.Configurable;
+import org.apache.brooklyn.config.ConfigInheritance;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.internal.BrooklynFeatureEnablement;
+import org.apache.brooklyn.core.internal.storage.BrooklynStorage;
+import org.apache.brooklyn.core.internal.storage.Reference;
+import org.apache.brooklyn.core.internal.storage.impl.BasicReference;
+import org.apache.brooklyn.core.mgmt.internal.LocalLocationManager;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.mgmt.rebind.BasicLocationRebindSupport;
+import org.apache.brooklyn.core.objs.AbstractBrooklynObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.location.core.internal.LocationDynamicType;
+import org.apache.brooklyn.location.core.internal.LocationInternal;
+import org.apache.brooklyn.location.geo.HasHostGeoInfo;
+import org.apache.brooklyn.location.geo.HostGeoInfo;
+import org.apache.brooklyn.util.collections.SetFromLiveMap;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.flags.FlagUtils;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.stream.Streams;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * A basic implementation of the {@link Location} interface.
+ *
+ * This provides an implementation which works according to the requirements of
+ * the interface documentation, and is ready to be extended to make more specialized locations.
+ *
+ * Override {@link #configure(Map)} to add special initialization logic.
+ */
+public abstract class AbstractLocation extends AbstractBrooklynObject implements LocationInternal, HasHostGeoInfo, Configurable {
+
+ private static final long serialVersionUID = -7495805474138619830L;
+
+ /** @deprecated since 0.7.0 shouldn't be public */
+ @Deprecated
+ public static final Logger LOG = LoggerFactory.getLogger(AbstractLocation.class);
+
+ public static final ConfigKey<Location> PARENT_LOCATION = new BasicConfigKey<Location>(Location.class, "parentLocation");
+
+ public static final ConfigKey<Boolean> TEMPORARY_LOCATION = ConfigKeys.newBooleanConfigKey("temporaryLocation",
+ "Indicates that the location is a temporary location that has been created to test connectivity, and that" +
+ "the location's events should not be recorded by usage listeners", false);
+
+ private final AtomicBoolean configured = new AtomicBoolean();
+
+ private Reference<Long> creationTimeUtc = new BasicReference<Long>(System.currentTimeMillis());
+
+ // _not_ set from flag; configured explicitly in configure, because we also need to update the parent's list of children
+ private Reference<Location> parent = new BasicReference<Location>();
+
+ // NB: all accesses should be synchronized
+ private Set<Location> children = Sets.newLinkedHashSet();
+
+ private Reference<String> name = new BasicReference<String>();
+ private boolean displayNameAutoGenerated = true;
+
+ private Reference<HostGeoInfo> hostGeoInfo = new BasicReference<HostGeoInfo>();
+
+ private BasicConfigurationSupport config = new BasicConfigurationSupport();
+
+ private ConfigBag configBag = new ConfigBag();
+
+ private volatile boolean managed;
+
+ private boolean inConstruction;
+
+ private Reference<Map<Class<?>, Object>> extensions = new BasicReference<Map<Class<?>, Object>>(Maps.<Class<?>, Object>newConcurrentMap());
+
+ private final LocationDynamicType locationType;
+
+ /**
+ * Construct a new instance of an AbstractLocation.
+ */
+ public AbstractLocation() {
+ this(Maps.newLinkedHashMap());
+ }
+
+ /**
+ * Construct a new instance of an AbstractLocation.
+ *
+ * The properties map recognizes the following keys:
+ * <ul>
+ * <li>name - a name for the location
+ * <li>parentLocation - the parent {@link Location}
+ * </ul>
+ *
+ * Other common properties (retrieved via get/findLocationProperty) include:
+ * <ul>
+ * <li>latitude
+ * <li>longitude
+ * <li>displayName
+ * <li>iso3166 - list of iso3166-2 code strings
+ * <li>timeZone
+ * <li>abbreviatedName
+ * </ul>
+ */
+ public AbstractLocation(Map<?,?> properties) {
+ super(properties);
+ inConstruction = true;
+
+ // When one calls getConfig(key), we want to use the default value specified on *this* location
+ // if it overrides the default config, by using the type object
+ locationType = new LocationDynamicType(this);
+
+ if (isLegacyConstruction()) {
+ AbstractBrooklynObject checkWeGetThis = configure(properties);
+ assert this.equals(checkWeGetThis) : this+" configure method does not return itself; returns "+checkWeGetThis+" instead of "+this;
+
+ boolean deferConstructionChecks = (properties.containsKey("deferConstructionChecks") && TypeCoercions.coerce(properties.get("deferConstructionChecks"), Boolean.class));
+ if (!deferConstructionChecks) {
+ FlagUtils.checkRequiredFields(this);
+ }
+ }
+
+ inConstruction = false;
+ }
+
+ protected void assertNotYetManaged() {
+ if (!inConstruction && Locations.isManaged(this)) {
+ LOG.warn("Configuration being made to {} after deployment; may not be supported in future versions", this);
+ }
+ //throw new IllegalStateException("Cannot set configuration "+key+" on active location "+this)
+ }
+
+ public void setManagementContext(ManagementContextInternal managementContext) {
+ super.setManagementContext(managementContext);
+ if (displayNameAutoGenerated && getId() != null) name.set(getClass().getSimpleName()+":"+getId().substring(0, Math.min(getId().length(),4)));
+
+ if (BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE)) {
+ Location oldParent = parent.get();
+ Set<Location> oldChildren = children;
+ Map<String, Object> oldConfig = configBag.getAllConfig();
+ Long oldCreationTimeUtc = creationTimeUtc.get();
+ String oldDisplayName = name.get();
+ HostGeoInfo oldHostGeoInfo = hostGeoInfo.get();
+
+ parent = managementContext.getStorage().getReference(getId()+"-parent");
+ children = SetFromLiveMap.create(managementContext.getStorage().<Location,Boolean>getMap(getId()+"-children"));
+ creationTimeUtc = managementContext.getStorage().getReference(getId()+"-creationTime");
+ hostGeoInfo = managementContext.getStorage().getReference(getId()+"-hostGeoInfo");
+ name = managementContext.getStorage().getReference(getId()+"-displayName");
+
+ // Only override stored defaults if we have actual values. We might be in setManagementContext
+ // because we are reconstituting an existing entity in a new brooklyn management-node (in which
+ // case believe what is already in the storage), or we might be in the middle of creating a new
+ // entity. Normally for a new entity (using EntitySpec creation approach), this will get called
+ // before setting the parent etc. However, for backwards compatibility we still support some
+ // things calling the entity's constructor directly.
+ if (oldParent != null) parent.set(oldParent);
+ if (oldChildren.size() > 0) children.addAll(oldChildren);
+ if (creationTimeUtc.isNull()) creationTimeUtc.set(oldCreationTimeUtc);
+ if (hostGeoInfo.isNull()) hostGeoInfo.set(oldHostGeoInfo);
+ if (name.isNull()) {
+ name.set(oldDisplayName);
+ } else {
+ displayNameAutoGenerated = false;
+ }
+
+ configBag = ConfigBag.newLiveInstance(managementContext.getStorage().<String,Object>getMap(getId()+"-config"));
+ if (oldConfig.size() > 0) {
+ configBag.putAll(oldConfig);
+ }
+ }
+ }
+
+ /**
+ * @deprecated since 0.7.0; only used for legacy brooklyn types where constructor is called directly;
+ * see overridden method for more info
+ */
+ @SuppressWarnings("serial")
+ @Override
+ @Deprecated
+ public AbstractLocation configure(Map<?,?> properties) {
+ assertNotYetManaged();
+
+ boolean firstTime = !configured.getAndSet(true);
+
+ configBag.putAll(properties);
+
+ if (properties.containsKey(PARENT_LOCATION.getName())) {
+ // need to ensure parent's list of children is also updated
+ setParent(configBag.get(PARENT_LOCATION));
+
+ // don't include parentLocation in configBag, as breaks rebind
+ configBag.remove(PARENT_LOCATION);
+ }
+
+ // NB: flag-setting done here must also be done in BasicLocationRebindSupport
+ FlagUtils.setFieldsFromFlagsWithBag(this, properties, configBag, firstTime);
+ FlagUtils.setAllConfigKeys(this, configBag, false);
+
+ if (properties.containsKey("displayName")) {
+ name.set((String) removeIfPossible(properties, "displayName"));
+ displayNameAutoGenerated = false;
+ } else if (properties.containsKey("name")) {
+ name.set((String) removeIfPossible(properties, "name"));
+ displayNameAutoGenerated = false;
+ } else if (isLegacyConstruction()) {
+ name.set(getClass().getSimpleName()+":"+getId().substring(0, Math.min(getId().length(),4)));
+ displayNameAutoGenerated = true;
+ }
+
+ // TODO Explicitly dealing with iso3166 here because want custom splitter rule comma-separated string.
+ // Is there a better way to do it (e.g. more similar to latitude, where configKey+TypeCoercion is enough)?
+ if (groovyTruth(properties.get("iso3166"))) {
+ Object rawCodes = removeIfPossible(properties, "iso3166");
+ Set<String> codes;
+ if (rawCodes instanceof CharSequence) {
+ codes = ImmutableSet.copyOf(Splitter.on(",").trimResults().split((CharSequence)rawCodes));
+ } else {
+ codes = TypeCoercions.coerce(rawCodes, new TypeToken<Set<String>>() {});
+ }
+ configBag.put(LocationConfigKeys.ISO_3166, codes);
+ }
+
+ return this;
+ }
+
+ // TODO ensure no callers rely on 'remove' semantics, and don't remove;
+ // or perhaps better use a config bag so we know what is used v unused
+ private static Object removeIfPossible(Map<?,?> map, Object key) {
+ try {
+ return map.remove(key);
+ } catch (Exception e) {
+ return map.get(key);
+ }
+ }
+
+ public boolean isManaged() {
+ return getManagementContext() != null && managed;
+ }
+
+ public void onManagementStarted() {
+ if (displayNameAutoGenerated) name.set(getClass().getSimpleName()+":"+getId().substring(0, Math.min(getId().length(),4)));
+ this.managed = true;
+ }
+
+ public void onManagementStopped() {
+ this.managed = false;
+ if (getManagementContext().isRunning()) {
+ BrooklynStorage storage = ((ManagementContextInternal)getManagementContext()).getStorage();
+ storage.remove(getId()+"-parent");
+ storage.remove(getId()+"-children");
+ storage.remove(getId()+"-creationTime");
+ storage.remove(getId()+"-hostGeoInfo");
+ storage.remove(getId()+"-displayName");
+ storage.remove(getId()+"-config");
+ }
+ }
+
+ @Override
+ public String getDisplayName() {
+ return name.get();
+ }
+
+ protected boolean isDisplayNameAutoGenerated() {
+ return displayNameAutoGenerated;
+ }
+
+ @Override
+ public Location getParent() {
+ return parent.get();
+ }
+
+ @Override
+ public Collection<Location> getChildren() {
+ synchronized (children) {
+ return ImmutableList.copyOf(children);
+ }
+ }
+
+ @Override
+ public void setParent(Location newParent) {
+ setParent(newParent, true);
+ }
+
+ public void setParent(Location newParent, boolean updateChildListParents) {
+ if (newParent == this) {
+ throw new IllegalArgumentException("Location cannot be its own parent: "+this);
+ }
+ if (newParent == parent.get()) {
+ return; // no-op; already have desired parent
+ }
+
+ if (parent.get() != null) {
+ Location oldParent = parent.get();
+ parent.set(null);
+ if (updateChildListParents)
+ ((AbstractLocation)oldParent).removeChild(this);
+ }
+ // TODO Should we support a location changing parent? The resulting unmanage/manage might cause problems.
+ // The code above suggests we do, but maybe we should warn or throw error, or at least test it!
+
+ parent.set(newParent);
+ if (newParent != null) {
+ if (updateChildListParents)
+ ((AbstractLocation)newParent).addChild(this);
+ }
+
+ onChanged();
+ }
+
+ @Override
+ public ConfigurationSupportInternal config() {
+ return config ;
+ }
+
+ private class BasicConfigurationSupport implements ConfigurationSupportInternal {
+
+ @Override
+ public <T> T get(ConfigKey<T> key) {
+ if (hasConfig(key, false)) return getLocalBag().get(key);
+ if (getParent() != null && isInherited(key)) {
+ return getParent().getConfig(key);
+ }
+
+ // In case this entity class has overridden the given key (e.g. to set default), then retrieve this entity's key
+ // TODO when locations become entities, the duplication of this compared to EntityConfigMap.getConfig will disappear.
+ @SuppressWarnings("unchecked")
+ ConfigKey<T> ownKey = (ConfigKey<T>) elvis(locationType.getConfigKey(key.getName()), key);
+
+ return ownKey.getDefaultValue();
+ }
+
+ @Override
+ public <T> T get(HasConfigKey<T> key) {
+ return get(key.getConfigKey());
+ }
+
+ @Override
+ public <T> T set(ConfigKey<T> key, T val) {
+ T result = configBag.put(key, val);
+ onChanged();
+ return result;
+ }
+
+ @Override
+ public <T> T set(HasConfigKey<T> key, T val) {
+ return set(key.getConfigKey(), val);
+ }
+
+ @Override
+ public <T> T set(ConfigKey<T> key, Task<T> val) {
+ // TODO Support for locations
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T set(HasConfigKey<T> key, Task<T> val) {
+ // TODO Support for locations
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ConfigBag getBag() {
+ ConfigBag result = ConfigBag.newInstanceExtending(configBag, ImmutableMap.of());
+ Location p = getParent();
+ if (p!=null) result.putIfAbsent(((LocationInternal)p).config().getBag());
+ return result;
+ }
+
+ @Override
+ public ConfigBag getLocalBag() {
+ return configBag;
+ }
+
+ @Override
+ public Maybe<Object> getRaw(ConfigKey<?> key) {
+ if (hasConfig(key, false)) return Maybe.of(getLocalBag().getStringKey(key.getName()));
+ if (getParent() != null && isInherited(key)) return ((LocationInternal)getParent()).config().getRaw(key);
+ return Maybe.absent();
+ }
+
+ @Override
+ public Maybe<Object> getRaw(HasConfigKey<?> key) {
+ return getRaw(key.getConfigKey());
+ }
+
+ @Override
+ public Maybe<Object> getLocalRaw(ConfigKey<?> key) {
+ if (hasConfig(key, false)) return Maybe.of(getLocalBag().getStringKey(key.getName()));
+ return Maybe.absent();
+ }
+
+ @Override
+ public Maybe<Object> getLocalRaw(HasConfigKey<?> key) {
+ return getLocalRaw(key.getConfigKey());
+ }
+
+ @Override
+ public void addToLocalBag(Map<String, ?> vals) {
+ configBag.putAll(vals);
+ }
+
+ @Override
+ public void removeFromLocalBag(String key) {
+ configBag.remove(key);
+ }
+
+ @Override
+ public void refreshInheritedConfig() {
+ // no-op for location
+ }
+
+ @Override
+ public void refreshInheritedConfigOfChildren() {
+ // no-op for location
+ }
+
+ private boolean hasConfig(ConfigKey<?> key, boolean includeInherited) {
+ if (includeInherited && isInherited(key)) {
+ return getBag().containsKey(key);
+ } else {
+ return getLocalBag().containsKey(key);
+ }
+ }
+
+ private boolean isInherited(ConfigKey<?> key) {
+ ConfigInheritance inheritance = key.getInheritance();
+ if (inheritance==null) inheritance = getDefaultInheritance();
+ return inheritance.isInherited(key, getParent(), AbstractLocation.this);
+ }
+
+ private ConfigInheritance getDefaultInheritance() {
+ return ConfigInheritance.ALWAYS;
+ }
+ }
+
+ @Override
+ public <T> T getConfig(HasConfigKey<T> key) {
+ return config().get(key);
+ }
+
+ @Override
+ public <T> T getConfig(ConfigKey<T> key) {
+ return config().get(key);
+ }
+
+ @Override
+ @Deprecated
+ public boolean hasConfig(ConfigKey<?> key, boolean includeInherited) {
+ return config.hasConfig(key, includeInherited);
+ }
+
+ @Override
+ @Deprecated
+ public Map<String,Object> getAllConfig(boolean includeInherited) {
+ // TODO Have no information about what to include/exclude inheritance wise.
+ // however few things use getAllConfigBag()
+ ConfigBag bag = (includeInherited ? config().getBag() : config().getLocalBag());
+ return bag.getAllConfig();
+ }
+
+ @Override
+ @Deprecated
+ public ConfigBag getAllConfigBag() {
+ // TODO see comments in EntityConfigMap and on interface methods.
+ // here ConfigBag is used exclusively so
+ // we have no information about what to include/exclude inheritance wise.
+ // however few things use getAllConfigBag()
+ return config().getBag();
+ }
+
+ @Override
+ public ConfigBag getLocalConfigBag() {
+ return config().getLocalBag();
+ }
+
+ /**
+ * @deprecated since 0.7; use {@link #getLocalConfigBag()}
+ * @since 0.6
+ */
+ @Deprecated
+ public ConfigBag getRawLocalConfigBag() {
+ return config().getLocalBag();
+ }
+
+ @Override
+ @Deprecated
+ public <T> T setConfig(ConfigKey<T> key, T value) {
+ return config().set(key, value);
+ }
+
+ /**
+ * @since 0.6.0 (?) - use getDisplayName
+ * @deprecated since 0.7.0; use {@link #getDisplayName()}
+ */
+ @Deprecated
+ public void setName(String newName) {
+ setDisplayName(newName);
+ }
+
+ public void setDisplayName(String newName) {
+ name.set(newName);
+ displayNameAutoGenerated = false;
+ onChanged();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (! (o instanceof Location)) {
+ return false;
+ }
+
+ Location l = (Location) o;
+ return getId().equals(l.getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ @Override
+ public boolean containsLocation(Location potentialDescendent) {
+ Location loc = potentialDescendent;
+ while (loc != null) {
+ if (this == loc) return true;
+ loc = loc.getParent();
+ }
+ return false;
+ }
+
+ protected <T extends Location> T addChild(LocationSpec<T> spec) {
+ T child = getManagementContext().getLocationManager().createLocation(spec);
+ addChild(child);
+ return child;
+ }
+
+ @SuppressWarnings("deprecation")
+ public void addChild(Location child) {
+ // Previously, setParent delegated to addChildLocation and we sometimes ended up with
+ // duplicate entries here. Instead this now uses a similar scheme to
+ // AbstractLocation.setParent/addChild (with any weaknesses for distribution that such a
+ // scheme might have...).
+ //
+ // We continue to use a list to allow identical-looking locations, but they must be different
+ // instances.
+
+ synchronized (children) {
+ for (Location contender : children) {
+ if (contender == child) {
+ // don't re-add; no-op
+ return;
+ }
+ }
+
+ children.add(child);
+ }
+
+ if (isManaged()) {
+ if (!getManagementContext().getLocationManager().isManaged(child)) {
+ Locations.manage(child, getManagementContext());
+ }
+ } else if (getManagementContext() != null) {
+ if (((LocalLocationManager)getManagementContext().getLocationManager()).getLocationEvenIfPreManaged(child.getId()) == null) {
+ ((ManagementContextInternal)getManagementContext()).prePreManage(child);
+ }
+ }
+
+ children.add(child);
+ child.setParent(this);
+
+ onChanged();
+ }
+
+ public boolean removeChild(Location child) {
+ boolean removed;
+ synchronized (children) {
+ removed = children.remove(child);
+ }
+ if (removed) {
+ if (child instanceof Closeable) {
+ Streams.closeQuietly((Closeable)child);
+ }
+ child.setParent(null);
+
+ if (isManaged()) {
+ getManagementContext().getLocationManager().unmanage(child);
+ }
+ }
+ onChanged();
+ return removed;
+ }
+
+ protected void onChanged() {
+ // currently changes simply trigger re-persistence; there is no intermediate listener as we do for EntityChangeListener
+ if (isManaged()) {
+ getManagementContext().getRebindManager().getChangeListener().onChanged(this);
+ }
+ }
+
+ /** Default String representation is simplified name of class, together with selected fields. */
+ @Override
+ public String toString() {
+ return string().toString();
+ }
+
+ @Override
+ public String toVerboseString() {
+ return toString();
+ }
+
+ /** override this, adding to the returned value, to supply additional fields to include in the toString */
+ protected ToStringHelper string() {
+ return Objects.toStringHelper(getClass()).add("id", getId()).add("name", name);
+ }
+
+ @Override
+ public HostGeoInfo getHostGeoInfo() { return hostGeoInfo.get(); }
+
+ public void setHostGeoInfo(HostGeoInfo hostGeoInfo) {
+ if (hostGeoInfo!=null) {
+ this.hostGeoInfo.set(hostGeoInfo);
+ setConfig(LocationConfigKeys.LATITUDE, hostGeoInfo.latitude);
+ setConfig(LocationConfigKeys.LONGITUDE, hostGeoInfo.longitude);
+ }
+ }
+
+ @Override
+ public RebindSupport<LocationMemento> getRebindSupport() {
+ return new BasicLocationRebindSupport(this);
+ }
+
+ @Override
+ public boolean hasExtension(Class<?> extensionType) {
+ return extensions.get().containsKey(checkNotNull(extensionType, "extensionType"));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getExtension(Class<T> extensionType) {
+ Object extension = extensions.get().get(checkNotNull(extensionType, "extensionType"));
+ if (extension == null) {
+ throw new IllegalArgumentException("No extension of type "+extensionType+" registered for location "+this);
+ }
+ return (T) extension;
+ }
+
+ @Override
+ public <T> void addExtension(Class<T> extensionType, T extension) {
+ checkNotNull(extensionType, "extensionType");
+ checkNotNull(extension, "extension");
+ checkArgument(extensionType.isInstance(extension), "extension %s does not implement %s", extension, extensionType);
+ extensions.get().put(extensionType, extension);
+ }
+
+ @Override
+ public Map<String, String> toMetadataRecord() {
+ ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
+ if (getDisplayName() != null) builder.put("displayName", getDisplayName());
+ if (getParent() != null && getParent().getDisplayName() != null) {
+ builder.put("parentDisplayName", getParent().getDisplayName());
+ }
+ return builder.build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/core/AbstractLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/core/AbstractLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/core/AbstractLocationResolver.java
new file mode 100644
index 0000000..c7e80bc
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/core/AbstractLocationResolver.java
@@ -0,0 +1,188 @@
+/*
+ * 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.core;
+
+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.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationRegistry;
+import org.apache.brooklyn.api.location.LocationResolver;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.location.core.internal.LocationInternal;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.text.KeyValueParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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, 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, 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);
+ }
+
+ 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));
+ }
+ }
+
+ /**
+ * Parses a spec, by default of the general form "prefix:parts1:part2(arg1=val1,arg2=val2)"
+ */
+ protected static class SpecParser {
+
+ protected final String prefix;
+ protected final Pattern pattern;
+ private String exampleUsage;
+
+ public SpecParser(String prefix) {
+ this.prefix = prefix;
+ pattern = Pattern.compile("("+prefix+"|"+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/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/core/AggregatingMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/core/AggregatingMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/core/AggregatingMachineProvisioningLocation.java
new file mode 100644
index 0000000..6ea9ec9
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/core/AggregatingMachineProvisioningLocation.java
@@ -0,0 +1,141 @@
+/*
+ * 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.core;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.io.Closeable;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.apache.brooklyn.util.stream.Streams;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ * Takes a list of other provisioners, and round-robins across them when obtaining a machine.
+ */
+public class AggregatingMachineProvisioningLocation<T extends MachineLocation> extends AbstractLocation
+ implements MachineProvisioningLocation<T>, Closeable {
+
+ private static final long serialVersionUID = -8818006672883481775L;
+
+ private Object lock;
+
+ @SetFromFlag
+ protected List<MachineProvisioningLocation<T>> provisioners;
+
+ @SetFromFlag
+ protected Map<T, MachineProvisioningLocation<T>> inUse;
+
+ protected final AtomicInteger obtainCounter = new AtomicInteger();
+
+ public AggregatingMachineProvisioningLocation() {
+ this(Maps.newLinkedHashMap());
+ }
+
+ public AggregatingMachineProvisioningLocation(Map properties) {
+ super(properties);
+
+ if (isLegacyConstruction()) {
+ init();
+ }
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ }
+
+ @Override
+ public String toVerboseString() {
+ return Objects.toStringHelper(this).omitNullValues()
+ .add("id", getId()).add("name", getDisplayName())
+ .add("provisioners", provisioners)
+ .toString();
+ }
+
+ @Override
+ public AbstractLocation configure(Map<?,?> properties) {
+ if (lock == null) {
+ lock = new Object();
+ provisioners = Lists.<MachineProvisioningLocation<T>>newArrayList();
+ inUse = Maps.<T, MachineProvisioningLocation<T>>newLinkedHashMap();
+ }
+ return super.configure(properties);
+ }
+
+ @Override
+ public AggregatingMachineProvisioningLocation<T> newSubLocation(Map<?,?> newFlags) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void close() {
+ for (MachineProvisioningLocation<?> provisioner : provisioners) {
+ if (provisioner instanceof Closeable) {
+ Streams.closeQuietly((Closeable)provisioner);
+ }
+ }
+ }
+
+ public T obtain() throws NoMachinesAvailableException {
+ return obtain(Maps.<String,Object>newLinkedHashMap());
+ }
+
+ @Override
+ public T obtain(Map<?,?> flags) throws NoMachinesAvailableException {
+ checkState(provisioners.size() > 0, "no provisioners!");
+ int index = obtainCounter.getAndIncrement();
+ for (int i = 0; i < provisioners.size(); i++) {
+ MachineProvisioningLocation<T> provisioner = provisioners.get(index++ % provisioners.size());
+ try {
+ T machine = provisioner.obtain(flags);
+ inUse.put(machine, provisioner);
+ return machine;
+ } catch (NoMachinesAvailableException e) {
+ // move on; try next
+ }
+ }
+ throw new NoMachinesAvailableException("No machines available in "+toString());
+ }
+
+ @Override
+ public void release(T machine) {
+ MachineProvisioningLocation<T> provisioner = inUse.remove(machine);
+ if (provisioner != null) {
+ provisioner.release(machine);
+ } else {
+ throw new IllegalStateException("Request to release machine "+machine+", but this machine is not currently allocated");
+ }
+ }
+
+ @Override
+ public Map<String,Object> getProvisioningFlags(Collection<String> tags) {
+ return Maps.<String,Object>newLinkedHashMap();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/core/BasicHardwareDetails.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/core/BasicHardwareDetails.java b/core/src/main/java/org/apache/brooklyn/location/core/BasicHardwareDetails.java
new file mode 100644
index 0000000..09059ec
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/core/BasicHardwareDetails.java
@@ -0,0 +1,56 @@
+/*
+ * 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.core;
+
+import javax.annotation.concurrent.Immutable;
+
+import com.google.common.base.Objects;
+
+import org.apache.brooklyn.api.location.HardwareDetails;
+
+@Immutable
+public class BasicHardwareDetails implements HardwareDetails {
+
+ private final Integer cpuCount;
+ private final Integer ram;
+
+ public BasicHardwareDetails(Integer cpuCount, Integer ram) {
+ this.cpuCount = cpuCount;
+ this.ram = ram;
+ }
+
+ @Override
+ public Integer getCpuCount() {
+ return cpuCount;
+ }
+
+ @Override
+ public Integer getRam() {
+ return ram;
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(HardwareDetails.class)
+ .omitNullValues()
+ .add("cpuCount", cpuCount)
+ .add("ram", ram)
+ .toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a1ad34d7/core/src/main/java/org/apache/brooklyn/location/core/BasicLocationDefinition.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/core/BasicLocationDefinition.java b/core/src/main/java/org/apache/brooklyn/location/core/BasicLocationDefinition.java
new file mode 100644
index 0000000..df9c641
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/core/BasicLocationDefinition.java
@@ -0,0 +1,85 @@
+/*
+ * 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.core;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.util.text.Identifiers;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+public class BasicLocationDefinition implements LocationDefinition {
+
+ private final String id;
+ private final String name;
+ private final String spec;
+ private final Map<String,Object> config;
+
+ public BasicLocationDefinition(String name, String spec, Map<String,? extends Object> config) {
+ this(Identifiers.makeRandomId(8), name, spec, config);
+ }
+
+ public BasicLocationDefinition(String id, String name, String spec, Map<String,? extends Object> config) {
+ this.id = Preconditions.checkNotNull(id);
+ this.name = name;
+ this.spec = Preconditions.checkNotNull(spec);
+ this.config = config==null ? ImmutableMap.<String, Object>of() : ImmutableMap.<String, Object>copyOf(config);
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getSpec() {
+ return spec;
+ }
+
+ @Override
+ public Map<String, Object> getConfig() {
+ return config;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) return true;
+ if ((o instanceof LocationDefinition) && id.equals(((LocationDefinition)o).getId())) return true;
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "LocationDefinition{" +
+ "id='" + getId() + '\'' +
+ ", name='" + getName() + '\'' +
+ ", spec='" + getSpec() + '\'' +
+ ", config=" + getConfig() +
+ '}';
+ }
+}