Remove serializable distinction for Metadata

This does make use of the fact we are no longer using Multimap. Doing
entries() for ArrayListMultimap must create a new Map.Entry for every
entry. Since we are now using HashMap, we are able to use entries() with
no extra cost.

Merging particular Keys no longer needs to deserialize.
This commit is contained in:
Eric Anderson 2015-07-19 12:34:05 -07:00
parent a2292faf43
commit 8fe667676c
2 changed files with 34 additions and 43 deletions

View File

@ -102,7 +102,6 @@ public abstract class Metadata {
// Use LinkedHashMap for consistent ordering for tests. // Use LinkedHashMap for consistent ordering for tests.
private final Map<String, List<MetadataEntry>> store = private final Map<String, List<MetadataEntry>> store =
new LinkedHashMap<String, List<MetadataEntry>>(); new LinkedHashMap<String, List<MetadataEntry>>();
private final boolean serializable;
/** /**
* Constructor called by the transport layer when it receives binary metadata. * Constructor called by the transport layer when it receives binary metadata.
@ -113,15 +112,12 @@ public abstract class Metadata {
String name = new String(binaryValues[i], US_ASCII); String name = new String(binaryValues[i], US_ASCII);
storeAdd(name, new MetadataEntry(name.endsWith(BINARY_HEADER_SUFFIX), binaryValues[++i])); storeAdd(name, new MetadataEntry(name.endsWith(BINARY_HEADER_SUFFIX), binaryValues[++i]));
} }
this.serializable = false;
} }
/** /**
* Constructor called by the application layer when it wants to send metadata. * Constructor called by the application layer when it wants to send metadata.
*/ */
private Metadata() { private Metadata() {}
this.serializable = true;
}
private void storeAdd(String name, MetadataEntry value) { private void storeAdd(String name, MetadataEntry value) {
List<MetadataEntry> values = store.get(name); List<MetadataEntry> values = store.get(name);
@ -225,15 +221,6 @@ public abstract class Metadata {
}); });
} }
/**
* Can this metadata be serialized. Metadata constructed from raw binary or ascii values
* cannot be serialized without merging it into a serializable instance using
* {@link #merge(Metadata, java.util.Set)}
*/
public boolean isSerializable() {
return serializable;
}
/** /**
* Serialize all the metadata entries. * Serialize all the metadata entries.
* *
@ -248,13 +235,18 @@ public abstract class Metadata {
* <p>This method is intended for transport use only. * <p>This method is intended for transport use only.
*/ */
public byte[][] serialize() { public byte[][] serialize() {
Preconditions.checkState(serializable, "Can't serialize raw metadata");
// One *2 for keys+values, one *2 to prevent resizing if a single key has multiple values // One *2 for keys+values, one *2 to prevent resizing if a single key has multiple values
List<byte[]> serialized = new ArrayList<byte[]>(store.size() * 2 * 2); List<byte[]> serialized = new ArrayList<byte[]>(store.size() * 2 * 2);
for (List<MetadataEntry> values : store.values()) { for (Map.Entry<String, List<MetadataEntry>> keyEntry : store.entrySet()) {
for (int i = 0; i < values.size(); i++) { for (int i = 0; i < keyEntry.getValue().size(); i++) {
MetadataEntry entry = values.get(i); MetadataEntry entry = keyEntry.getValue().get(i);
serialized.add(entry.key.asciiName()); byte[] asciiName;
if (entry.key != null) {
asciiName = entry.key.asciiName();
} else {
asciiName = keyEntry.getKey().getBytes(US_ASCII);
}
serialized.add(asciiName);
serialized.add(entry.getSerialized()); serialized.add(entry.getSerialized());
} }
} }
@ -263,24 +255,14 @@ public abstract class Metadata {
/** /**
* Perform a simple merge of two sets of metadata. * Perform a simple merge of two sets of metadata.
* <p>
* Note that we can't merge non-serializable metadata into serializable.
* </p>
*/ */
public void merge(Metadata other) { public void merge(Metadata other) {
Preconditions.checkNotNull(other); Preconditions.checkNotNull(other);
if (this.serializable) { for (Map.Entry<String, List<MetadataEntry>> keyEntry : other.store.entrySet()) {
if (!other.serializable) { for (int i = 0; i < keyEntry.getValue().size(); i++) {
throw new IllegalArgumentException(
"Cannot merge non-serializable metadata into serializable metadata without keys");
}
}
for (List<MetadataEntry> values : other.store.values()) {
for (int i = 0; i < values.size(); i++) {
MetadataEntry entry = values.get(i);
// Must copy the MetadataEntries since they are mutated. If the two Metadata objects are // Must copy the MetadataEntries since they are mutated. If the two Metadata objects are
// used from different threads it would cause thread-safety issues. // used from different threads it would cause thread-safety issues.
storeAdd(entry.key.name(), new MetadataEntry(entry)); storeAdd(keyEntry.getKey(), new MetadataEntry(keyEntry.getValue().get(i)));
} }
} }
} }
@ -288,15 +270,17 @@ public abstract class Metadata {
/** /**
* Merge values for the given set of keys into this set of metadata. * Merge values for the given set of keys into this set of metadata.
*/ */
@SuppressWarnings({"rawtypes", "unchecked"})
public void merge(Metadata other, Set<Key<?>> keys) { public void merge(Metadata other, Set<Key<?>> keys) {
Preconditions.checkNotNull(other); Preconditions.checkNotNull(other);
for (Key<?> key : keys) { for (Key<?> key : keys) {
if (other.containsKey(key)) { List<MetadataEntry> values = other.store.get(key.name);
Iterable<?> values = other.getAll(key); if (values == null) {
for (Object value : values) { continue;
put((Key) key, value);
} }
for (int i = 0; i < values.size(); i++) {
// Must copy the MetadataEntries since they are mutated. If the two Metadata objects are
// used from different threads it would cause thread-safety issues.
storeAdd(key.name, new MetadataEntry(values.get(i)));
} }
} }
} }

View File

@ -134,17 +134,24 @@ public class MetadataTest {
assertSame(lance, raw.get(KEY)); assertSame(lance, raw.get(KEY));
} }
@Test(expected = IllegalStateException.class) @Test
public void testFailSerializeRaw() { public void testSerializeRaw() {
Metadata.Headers raw = new Metadata.Headers(KEY.asciiName(), LANCE_BYTES); Metadata.Headers raw = new Metadata.Headers(KEY.asciiName(), LANCE_BYTES);
raw.serialize(); byte[][] serialized = raw.serialize();
assertArrayEquals(serialized[0], KEY.asciiName());
assertArrayEquals(serialized[1], LANCE_BYTES);
} }
@Test(expected = IllegalArgumentException.class) @Test
public void testFailMergeRawIntoSerializable() { public void testMergeByteConstructed() {
Metadata.Headers raw = new Metadata.Headers(KEY.asciiName(), LANCE_BYTES); Metadata.Headers raw = new Metadata.Headers(KEY.asciiName(), LANCE_BYTES);
Metadata.Headers serializable = new Metadata.Headers(); Metadata.Headers serializable = new Metadata.Headers();
serializable.merge(raw); serializable.merge(raw);
byte[][] serialized = serializable.serialize();
assertArrayEquals(serialized[0], KEY.asciiName());
assertArrayEquals(serialized[1], LANCE_BYTES);
assertEquals(new Fish(LANCE), serializable.get(KEY));
} }
@Test @Test