Skip to content
Permalink
Browse files
This closes #1161
  • Loading branch information
ahgittin committed Jan 19, 2016
2 parents 0fa772a + ba8bb7f commit 7d2ae06ecf1a3907b73625436180c23fa3eab99a
Showing 12 changed files with 76 additions and 59 deletions.
@@ -28,6 +28,7 @@
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.feed.ConfigToAttributes;
import org.apache.brooklyn.core.location.Locations;
import org.apache.brooklyn.enricher.stock.Enrichers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -104,11 +105,14 @@ public DynamicCluster getCluster() {
@Override
public void start(Collection<? extends Location> locations) {
if (isLegacyConstruction()) {
// TODO should no longer be needed?
init();
}

if (locations.isEmpty()) locations = getLocations();
locations = MutableList.copyOf(Locations.getLocationsCheckingAncestors(locations, this));

Iterables.getOnlyElement(locations); // Assert just one
// set it; here we don't allow changing locations
addLocations(locations);

List<Entity> childrenToStart = MutableList.<Entity>of(getCluster());
@@ -38,6 +38,7 @@
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.entity.trait.StartableMethods;
import org.apache.brooklyn.core.feed.ConfigToAttributes;
import org.apache.brooklyn.core.location.Locations;
import org.apache.brooklyn.enricher.stock.Enrichers;
import org.apache.brooklyn.entity.group.DynamicGroupImpl;
import org.apache.brooklyn.entity.proxy.LoadBalancer;
@@ -207,7 +208,8 @@ public void start(Collection<? extends Location> locations) {
init();
}

if (locations.isEmpty()) locations = getLocations();
locations = Locations.getLocationsCheckingAncestors(locations, this);
// store inherited locations
addLocations(locations);

LoadBalancer loadBalancer = getController();
@@ -229,7 +231,8 @@ public void start(Collection<? extends Location> locations) {
}
}

Entities.invokeEffectorList(this, childrenToStart, Startable.START, ImmutableMap.of("locations", locations)).get();
// don't propagate start locations
Entities.invokeEffectorList(this, childrenToStart, Startable.START, ImmutableMap.of("locations", MutableList.of())).get();
if (startControllerTask != null) {
startControllerTask.get();
}
@@ -874,15 +874,18 @@ public Collection<Location> getLocations() {

@Override
public void addLocations(Collection<? extends Location> newLocations) {
if (newLocations==null || newLocations.isEmpty()) {
return;
}
synchronized (locations) {
List<Location> oldLocations = locations.get();
Set<Location> truelyNewLocations = Sets.newLinkedHashSet(newLocations);
truelyNewLocations.removeAll(oldLocations);
if (truelyNewLocations.size() > 0) {
locations.set(ImmutableList.<Location>builder().addAll(oldLocations).addAll(truelyNewLocations).build());
Set<Location> trulyNewLocations = Sets.newLinkedHashSet(newLocations);
trulyNewLocations.removeAll(oldLocations);
if (trulyNewLocations.size() > 0) {
locations.set(ImmutableList.<Location>builder().addAll(oldLocations).addAll(trulyNewLocations).build());
}

for (Location loc : truelyNewLocations) {
for (Location loc : trulyNewLocations) {
sensors().emit(AbstractEntity.LOCATION_ADDED, loc);
}
}
@@ -21,6 +21,8 @@
import java.util.Collection;
import java.util.Map;

import javax.annotation.Nullable;

import org.apache.brooklyn.api.effector.Effector;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntityLocal;
@@ -48,7 +50,7 @@
@Beta
public interface EntityInternal extends BrooklynObjectInternal, EntityLocal, Rebindable {

void addLocations(Collection<? extends Location> locations);
void addLocations(@Nullable Collection<? extends Location> locations);

void removeLocations(Collection<? extends Location> locations);

@@ -430,7 +430,9 @@ public static boolean isResolverPrefixForSpec(LocationResolver resolver, String
public List<Location> resolve(Iterable<?> spec) {
List<Location> result = new ArrayList<Location>();
for (Object id : spec) {
if (id instanceof String) {
if (id==null) {
// drop a null entry
} if (id instanceof String) {
result.add(resolve((String) id));
} else if (id instanceof Location) {
result.add((Location) id);
@@ -21,6 +21,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -311,15 +312,16 @@ public void setFactory(EntityFactory<?> factory) {
setConfigEvenIfOwned(FACTORY, factory);
}

private Location getLocation() {
private Location getLocation(boolean required) {
Collection<? extends Location> ll = Locations.getLocationsCheckingAncestors(getLocations(), this);
try {
return Iterables.getOnlyElement(ll);
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
if (ll.isEmpty()) throw new IllegalStateException("No location available for "+this);
else throw new IllegalStateException("Ambiguous location for "+this+"; expected one but had "+ll);
if (ll.isEmpty()) {
if (!required) return null;
throw new IllegalStateException("No location available for "+this);
}
if (ll.size()>1) {
throw new IllegalStateException("Ambiguous location for "+this+"; expected one but had "+ll);
}
return Iterables.getOnlyElement(ll);
}

protected boolean isAvailabilityZoneEnabled() {
@@ -360,18 +362,17 @@ protected int getInitialQuorumSize() {

@Override
public void start(Collection<? extends Location> locsO) {
if (locsO!=null) {
checkArgument(locsO.size() <= 1, "Wrong number of locations supplied to start %s: %s", this, locsO);
addLocations(locsO);
}
Location loc = getLocation();
addLocations(locsO);
Location loc = getLocation(false);

EntitySpec<?> spec = getConfig(MEMBER_SPEC);
if (spec!=null) {
setDefaultDisplayName("Cluster of "+JavaClassNames.simpleClassName(spec.getType()) +" ("+loc+")");
setDefaultDisplayName("Cluster of "+JavaClassNames.simpleClassName(spec.getType()) +
(loc!=null ? " ("+loc+")" : ""));
}

if (isAvailabilityZoneEnabled()) {
if (loc==null) throw new IllegalStateException("When using availability zones, a location must be specified on the cluster");
sensors().set(SUB_LOCATIONS, findSubLocations(loc));
}

@@ -574,7 +575,7 @@ public String replaceMember(String memberId) {
Location memberLoc = null;
if (isAvailabilityZoneEnabled()) {
// this member's location could be a machine provisioned by a sub-location, or the actual sub-location
List<Location> subLocations = findSubLocations(getLocation());
List<Location> subLocations = findSubLocations(getLocation(true));
Collection<Location> actualMemberLocs = member.getLocations();
boolean foundMatch = false;
for (Iterator<Location> iter = actualMemberLocs.iterator(); !foundMatch && iter.hasNext();) {
@@ -605,7 +606,7 @@ public String replaceMember(String memberId) {
// Replacing member, so new member should be in the same location as that being replaced.
// Expect this to agree with `getMemberSpec().getLocations()` (if set). If not, then
// presumably there was a reason this specific member was started somewhere else!
memberLoc = getLocation();
memberLoc = getLocation(false);
}

Entity replacement = replaceMember(member, memberLoc, ImmutableMap.of());
@@ -616,7 +617,7 @@ public String replaceMember(String memberId) {
/**
* @throws StopFailedRuntimeException If stop failed, after successfully starting replacement
*/
protected Entity replaceMember(Entity member, Location memberLoc, Map<?, ?> extraFlags) {
protected Entity replaceMember(Entity member, @Nullable Location memberLoc, Map<?, ?> extraFlags) {
synchronized (mutex) {
ReferenceWithError<Optional<Entity>> added = addInSingleLocation(memberLoc, extraFlags);

@@ -653,7 +654,7 @@ protected Multimap<Location, Entity> getMembersByLocation() {
protected List<Location> getNonFailedSubLocations() {
List<Location> result = Lists.newArrayList();
Set<Location> failed = Sets.newLinkedHashSet();
List<Location> subLocations = findSubLocations(getLocation());
List<Location> subLocations = findSubLocations(getLocation(true));
Set<Location> oldFailedSubLocations = getAttribute(FAILED_SUB_LOCATIONS);
if (oldFailedSubLocations == null)
oldFailedSubLocations = ImmutableSet.<Location> of();
@@ -722,7 +723,7 @@ protected Collection<Entity> grow(int delta) {
+ ", when expected delta " + delta + " in " + this);
}
} else {
chosenLocations = Collections.nCopies(delta, getLocation());
chosenLocations = Collections.nCopies(delta, getLocation(false));
}

// create and start the entities.
@@ -759,8 +760,8 @@ protected Collection<Entity> shrink(int delta) {
}
}

protected ReferenceWithError<Optional<Entity>> addInSingleLocation(Location location, Map<?,?> flags) {
ReferenceWithError<Collection<Entity>> added = addInEachLocation(ImmutableList.of(location), flags);
protected ReferenceWithError<Optional<Entity>> addInSingleLocation(@Nullable Location location, Map<?,?> flags) {
ReferenceWithError<Collection<Entity>> added = addInEachLocation(Arrays.asList(location), flags);

Optional<Entity> result = Iterables.isEmpty(added.getWithoutError()) ? Optional.<Entity>absent() : Optional.of(Iterables.getOnlyElement(added.get()));
if (!added.hasError()) {
@@ -885,7 +886,7 @@ protected Map<?,?> getCustomChildFlags() {
}

@Override
public Entity addNode(Location loc, Map<?, ?> extraFlags) {
public Entity addNode(@Nullable Location loc, Map<?, ?> extraFlags) {
// In case subclasses are foolish and do not call super.init() when overriding.
initialiseMemberId();
Map<?, ?> createFlags = MutableMap.builder()
@@ -917,7 +918,9 @@ protected Entity createNode(@Nullable Location loc, Map<?,?> flags) {
if (memberSpec == null) memberSpec = getMemberSpec();

if (memberSpec != null) {
return addChild(EntitySpec.create(memberSpec).configure(flags).location(loc));
EntitySpec<?> specConfigured = EntitySpec.create(memberSpec).configure(flags);
if (loc!=null) specConfigured.location(loc);
return addChild(specConfigured);
}

EntityFactory<?> factory = getFactory();
@@ -18,13 +18,11 @@
*/
package org.apache.brooklyn.entity.group;

import static com.google.common.base.Preconditions.checkArgument;
import static org.apache.brooklyn.util.groovy.GroovyJavaMethods.elvis;
import static org.apache.brooklyn.util.groovy.GroovyJavaMethods.truth;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@@ -112,16 +110,11 @@ public void setFactory(EntityFactory<?> factory) {

@Override
public void start(Collection<? extends Location> locsO) {
if (locsO!=null) {
addLocations(locsO);
}
Collection<Location> locs = Collections.unmodifiableCollection(Locations.getLocationsCheckingAncestors(getLocations(), this));

List<Location> newLocations = MutableList.copyOf(locsO);
if (newLocations.isEmpty()) newLocations.addAll(locs);
addLocations(locsO);
List<Location> locationsToStart = MutableList.copyOf(Locations.getLocationsCheckingAncestors(locsO, this));

Preconditions.checkNotNull(newLocations, "locations must be supplied");
Preconditions.checkArgument(newLocations.size() >= 1, "One or more locations must be supplied");
Preconditions.checkNotNull(locationsToStart, "locations must be supplied");
Preconditions.checkArgument(locationsToStart.size() >= 1, "One or more locations must be supplied");

int locIndex = 0;

@@ -137,8 +130,8 @@ public void start(Collection<? extends Location> locsO) {
Location it = null;
if (child.getLocations().isEmpty())
// give him any of these locations if he has none, allowing round robin here
if (!newLocations.isEmpty()) {
it = newLocations.get(locIndex++ % newLocations.size());
if (!locationsToStart.isEmpty()) {
it = locationsToStart.get(locIndex++ % locationsToStart.size());
((EntityInternal)child).addLocations(Arrays.asList(it));
}

@@ -148,12 +141,12 @@ public void start(Collection<? extends Location> locsO) {
}
}
// remove all the locations we applied to existing nodes
while (locIndex-->0 && !newLocations.isEmpty())
newLocations.remove(0);
while (locIndex-->0 && !locationsToStart.isEmpty())
locationsToStart.remove(0);

// finally (and usually) we create new entities for locations passed in
// (unless they were consumed by pre-existing children which didn't have locations)
for (Location it : newLocations) {
for (Location it : locationsToStart) {
Entity e = addCluster(it);

((EntityInternal)e).addLocations(Arrays.asList(it));
@@ -48,11 +48,12 @@ public class BasicStartableImpl extends AbstractEntity implements BasicStartable

@Override
public void start(Collection<? extends Location> locations) {
log.info("Starting entity "+this+" at "+locations);
try {
ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);

addLocations(locations);
locations = Locations.getLocationsCheckingAncestors(locations, this);
log.info("Starting entity "+this+" at "+locations);

// essentially does StartableMethods.start(this, locations),
// but optionally filters locations for each child
@@ -38,6 +38,7 @@ public DataEntityImpl() { }

@Override
public void start(Collection<? extends Location> locations) {
addLocations(locations);
connectSensors();
sensors().set(SERVICE_UP, Boolean.TRUE);
}
@@ -110,9 +110,9 @@ public void constructionRequiresThatNewEntityArgumentIsAnEntityFactory() throws
try {
app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
.configure("factory", "error"));
fail();
Asserts.shouldHaveFailedPreviously();
} catch (Exception e) {
if (Exceptions.getFirstThrowableOfType(e, IllegalArgumentException.class) == null) throw e;
Asserts.expectedFailure(e);
}
}

@@ -121,9 +121,9 @@ public void startRequiresThatNewEntityArgumentIsGiven() throws Exception {
DynamicCluster c = app.createAndManageChild(EntitySpec.create(DynamicCluster.class));
try {
c.start(ImmutableList.of(loc));
fail();
Asserts.shouldHaveFailedPreviously();
} catch (Exception e) {
if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) throw e;
Asserts.expectedFailureOfType(e, IllegalStateException.class);
}
}

@@ -135,9 +135,9 @@ public void startThenStopThenStartWithNewLocationFails() throws Exception {
cluster.start(ImmutableList.of(loc));
cluster.stop();
cluster.start(ImmutableList.of(loc2));
fail();
Asserts.shouldHaveFailedPreviously();
} catch (Exception e) {
if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) throw e;
Asserts.expectedFailureOfType(e, IllegalStateException.class);
}
}

@@ -147,9 +147,9 @@ public void startMethodFailsIfLocationsParameterHasMoreThanOneElement() throws E
.configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(TestEntity.class)));
try {
cluster.start(ImmutableList.of(loc, loc2));
fail();
Asserts.shouldHaveFailedPreviously();
} catch (Exception e) {
if (Exceptions.getFirstThrowableOfType(e, IllegalArgumentException.class) == null) throw e;
Asserts.expectedFailureContainsIgnoreCase(e, "ambiguous");
}
}

@@ -21,13 +21,15 @@
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Collection;
import java.util.List;

import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.core.entity.AbstractEntity;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers;
import org.apache.brooklyn.core.location.Locations;
import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
import org.apache.brooklyn.util.collections.QuorumCheck;
import org.apache.brooklyn.util.core.config.ConfigBag;
@@ -68,7 +70,10 @@ public final void restart() {
* Subclasses should override {@link #doStart} to customise behaviour.
*/
@Override
public final void start(final Collection<? extends Location> locations) {
public final void start(Collection<? extends Location> locsO) {
addLocations(locsO);
final Collection<? extends Location> locations = Locations.getLocationsCheckingAncestors(locsO, this);

checkNotNull(locations, "locations");
if (DynamicTasks.getTaskQueuingContext() != null) {
doStart(locations);

0 comments on commit 7d2ae06

Please sign in to comment.