Be more careful with RoutingTable.Builder

RoutingTable.Builder#build can only be called once but didn't have any
checks to make sure it _was_ only called once. And the error message it
threw when called more than once was a NullPointerException. This makes
it throw IllegalStateException when you try to reuse the builder in any way.
This commit is contained in:
Nik Everett 2015-10-09 10:28:13 -04:00
parent bfb9054a11
commit 56318df10c
2 changed files with 54 additions and 5 deletions

View File

@ -350,7 +350,7 @@ public class RoutingTable implements Iterable<IndexRoutingTable>, Diffable<Routi
public static class Builder { public static class Builder {
private long version; private long version;
private final ImmutableOpenMap.Builder<String, IndexRoutingTable> indicesRouting = ImmutableOpenMap.builder(); private ImmutableOpenMap.Builder<String, IndexRoutingTable> indicesRouting = ImmutableOpenMap.builder();
public Builder() { public Builder() {
@ -406,6 +406,9 @@ public class RoutingTable implements Iterable<IndexRoutingTable>, Diffable<Routi
} }
public Builder updateNumberOfReplicas(int numberOfReplicas, String... indices) { public Builder updateNumberOfReplicas(int numberOfReplicas, String... indices) {
if (indicesRouting == null) {
throw new IllegalStateException("once build is called the builder cannot be reused");
}
if (indices == null || indices.length == 0) { if (indices == null || indices.length == 0) {
indices = indicesRouting.keys().toArray(String.class); indices = indicesRouting.keys().toArray(String.class);
} }
@ -492,6 +495,9 @@ public class RoutingTable implements Iterable<IndexRoutingTable>, Diffable<Routi
} }
public Builder add(IndexRoutingTable indexRoutingTable) { public Builder add(IndexRoutingTable indexRoutingTable) {
if (indicesRouting == null) {
throw new IllegalStateException("once build is called the builder cannot be reused");
}
indexRoutingTable.validate(); indexRoutingTable.validate();
indicesRouting.put(indexRoutingTable.index(), indexRoutingTable); indicesRouting.put(indexRoutingTable.index(), indexRoutingTable);
return this; return this;
@ -503,11 +509,17 @@ public class RoutingTable implements Iterable<IndexRoutingTable>, Diffable<Routi
} }
public Builder indicesRouting(Map<String, IndexRoutingTable> indicesRouting) { public Builder indicesRouting(Map<String, IndexRoutingTable> indicesRouting) {
if (indicesRouting == null) {
throw new IllegalStateException("once build is called the builder cannot be reused");
}
this.indicesRouting.putAll(indicesRouting); this.indicesRouting.putAll(indicesRouting);
return this; return this;
} }
public Builder remove(String index) { public Builder remove(String index) {
if (indicesRouting == null) {
throw new IllegalStateException("once build is called the builder cannot be reused");
}
indicesRouting.remove(index); indicesRouting.remove(index);
return this; return this;
} }
@ -518,16 +530,21 @@ public class RoutingTable implements Iterable<IndexRoutingTable>, Diffable<Routi
} }
/** /**
* Builds the routing table. Note that this can only be called one time. * Builds the routing table. Note that once this is called the builder
* If you need to build a new RoutingTable as a copy of this one you'll * must be thrown away. If you need to build a new RoutingTable as a
* need to build a new RoutingTable.Builder. * copy of this one you'll need to build a new RoutingTable.Builder.
*/ */
public RoutingTable build() { public RoutingTable build() {
if (indicesRouting == null) {
throw new IllegalStateException("once build is called the builder cannot be reused");
}
// normalize the versions right before we build it... // normalize the versions right before we build it...
for (ObjectCursor<IndexRoutingTable> indexRoutingTable : indicesRouting.values()) { for (ObjectCursor<IndexRoutingTable> indexRoutingTable : indicesRouting.values()) {
indicesRouting.put(indexRoutingTable.value.index(), indexRoutingTable.value.normalizeVersions()); indicesRouting.put(indexRoutingTable.value.index(), indexRoutingTable.value.normalizeVersions());
} }
return new RoutingTable(version, indicesRouting.build()); RoutingTable table = new RoutingTable(version, indicesRouting.build());
indicesRouting = null;
return table;
} }
public static RoutingTable readFrom(StreamInput in) throws IOException { public static RoutingTable readFrom(StreamInput in) throws IOException {

View File

@ -35,6 +35,7 @@ import org.junit.Test;
import static org.elasticsearch.cluster.routing.ShardRoutingState.INITIALIZING; import static org.elasticsearch.cluster.routing.ShardRoutingState.INITIALIZING;
import static org.elasticsearch.common.settings.Settings.settingsBuilder; import static org.elasticsearch.common.settings.Settings.settingsBuilder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
@ -55,6 +56,7 @@ public class RoutingTableTests extends ESAllocationTestCase {
.build()); .build());
private ClusterState clusterState; private ClusterState clusterState;
@Override
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
@ -264,4 +266,34 @@ public class RoutingTableTests extends ESAllocationTestCase {
fail("Calling with non-existing index should be ignored at the moment"); fail("Calling with non-existing index should be ignored at the moment");
} }
} }
public void testRoutingTableBuiltMoreThanOnce() {
RoutingTable.Builder b = RoutingTable.builder();
b.build(); // Ok the first time
try {
b.build();
fail("expected exception");
} catch (IllegalStateException e) {
assertThat(e.getMessage(), containsString("cannot be reused"));
}
try {
b.add((IndexRoutingTable) null);
fail("expected exception");
} catch (IllegalStateException e) {
assertThat(e.getMessage(), containsString("cannot be reused"));
}
try {
b.updateNumberOfReplicas(1, "foo");
fail("expected exception");
} catch (IllegalStateException e) {
assertThat(e.getMessage(), containsString("cannot be reused"));
}
try {
b.remove("foo");
fail("expected exception");
} catch (IllegalStateException e) {
assertThat(e.getMessage(), containsString("cannot be reused"));
}
}
} }