Skip to content

Commit 1369ea1

Browse files
lryanejona86
authored andcommitted
Metadata improvements.
------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=76175838
1 parent 5579411 commit 1369ea1

File tree

3 files changed

+188
-69
lines changed

3 files changed

+188
-69
lines changed

core/src/main/java/com/google/net/stubby/Metadata.java

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import static java.nio.charset.StandardCharsets.UTF_8;
44

5-
import com.google.common.annotations.VisibleForTesting;
65
import com.google.common.base.Function;
76
import com.google.common.base.Preconditions;
87
import com.google.common.collect.Iterables;
@@ -27,7 +26,7 @@
2726
* </p>
2827
*/
2928
@NotThreadSafe
30-
public abstract class Metadata<S extends Metadata> {
29+
public abstract class Metadata {
3130

3231
/**
3332
* Interleave keys and values into a single iterator.
@@ -167,26 +166,32 @@ public <T> boolean containsKey(Key key) {
167166

168167
/**
169168
* Returns the last metadata entry added with the name 'name' parsed as T.
170-
* @return the parsed metadata entry or null if not defined
169+
* @return the parsed metadata entry or null if there are none.
171170
*/
172171
public <T> T get(Key<T> key) {
173-
MetadataEntry metadataEntry = Iterables.getLast(store.get(key.name()));
174-
return metadataEntry == null ? null : metadataEntry.getParsed(key);
172+
if (containsKey(key)) {
173+
MetadataEntry metadataEntry = Iterables.getLast(store.get(key.name()));
174+
return metadataEntry.getParsed(key);
175+
}
176+
return null;
175177
}
176178

177179
/**
178180
* Returns all the metadata entries named 'name', in the order they were received,
179-
* parsed as T.
181+
* parsed as T or null if there are none.
180182
*/
181183
public <T> Iterable<T> getAll(final Key<T> key) {
182-
return Iterables.transform(
183-
store.get(key.name()),
184-
new Function<MetadataEntry, T>() {
185-
@Override
186-
public T apply(MetadataEntry entry) {
187-
return entry.getParsed(key);
188-
}
189-
});
184+
if (containsKey(key)) {
185+
return Iterables.transform(
186+
store.get(key.name()),
187+
new Function<MetadataEntry, T>() {
188+
@Override
189+
public T apply(MetadataEntry entry) {
190+
return entry.getParsed(key);
191+
}
192+
});
193+
}
194+
return null;
190195
}
191196

192197
public <T> void put(Key<T> key, T value) {
@@ -284,7 +289,7 @@ public void merge(Metadata other, Set<Key> keys) {
284289
/**
285290
* Concrete instance for metadata attached to the start of a call.
286291
*/
287-
public static class Headers extends Metadata<Headers> {
292+
public static class Headers extends Metadata {
288293
private String path;
289294
private String authority;
290295

@@ -363,7 +368,7 @@ private void mergePathAndAuthority(Metadata other) {
363368
* Concrete instance for metadata attached to the end of the call. Only provided by
364369
* servers.
365370
*/
366-
public static class Trailers extends Metadata<Headers> {
371+
public static class Trailers extends Metadata {
367372
/**
368373
* Called by the transport layer to create trailers from their binary serialized values.
369374
*/
@@ -443,7 +448,7 @@ public static <T> Key<T> of(String name, Marshaller<T> marshaller) {
443448
* Keys have a name and a marshaller used for serialization.
444449
*/
445450
private Key(String name, Marshaller<T> marshaller) {
446-
this.name = Preconditions.checkNotNull(name, "name").intern();
451+
this.name = Preconditions.checkNotNull(name, "name").toLowerCase().intern();
447452
this.asciiName = name.getBytes(StandardCharsets.US_ASCII);
448453
this.marshaller = Preconditions.checkNotNull(marshaller);
449454
}
@@ -452,14 +457,32 @@ public String name() {
452457
return name;
453458
}
454459

455-
@VisibleForTesting
456-
byte[] asciiName() {
460+
// TODO (lryan): Migrate to ByteString
461+
public byte[] asciiName() {
457462
return asciiName;
458463
}
459464

460465
public Marshaller<T> getMarshaller() {
461466
return marshaller;
462467
}
468+
469+
@Override
470+
public boolean equals(Object o) {
471+
if (this == o) return true;
472+
if (o == null || getClass() != o.getClass()) return false;
473+
Key key = (Key) o;
474+
return !(name != null ? !name.equals(key.name) : key.name != null);
475+
}
476+
477+
@Override
478+
public int hashCode() {
479+
return name != null ? name.hashCode() : 0;
480+
}
481+
482+
@Override
483+
public String toString() {
484+
return "Key{name='" + name + "'}";
485+
}
463486
}
464487

465488
private static class MetadataEntry {

stub/src/main/java/com/google/net/stubby/stub/HeadersInterceptor.java

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package com.google.net.stubby.stub;
2+
3+
import com.google.common.io.BaseEncoding;
4+
import com.google.common.util.concurrent.ListenableFuture;
5+
import com.google.net.stubby.Call;
6+
import com.google.net.stubby.Channel;
7+
import com.google.net.stubby.Metadata;
8+
import com.google.net.stubby.MethodDescriptor;
9+
import com.google.net.stubby.Status;
10+
import com.google.net.stubby.context.ForwardingChannel;
11+
import com.google.protobuf.GeneratedMessage;
12+
import com.google.protobuf.InvalidProtocolBufferException;
13+
14+
import java.util.concurrent.atomic.AtomicReference;
15+
16+
/**
17+
* Utility functions for binding and receiving headers
18+
*/
19+
public class MetadataUtils {
20+
21+
/**
22+
* Attach a set of request headers to a stub.
23+
* @param stub to bind the headers to.
24+
* @param extraHeaders the headers to be passed by each call on the returned stub.
25+
* @return an implementation of the stub with extraHeaders bound to each call.
26+
*/
27+
@SuppressWarnings("unchecked")
28+
public static <T extends AbstractStub> T attachHeaders(
29+
T stub,
30+
final Metadata.Headers extraHeaders) {
31+
return (T) stub.configureNewStub().setChannel(attachHeaders(stub.getChannel(), extraHeaders))
32+
.build();
33+
}
34+
35+
/**
36+
* Attach a set of request headers to a channel.
37+
*
38+
* @param channel to channel to intercept.
39+
* @param extraHeaders the headers to be passed by each call on the returned stub.
40+
* @return an implementation of the channel with extraHeaders bound to each call.
41+
*/
42+
@SuppressWarnings("unchecked")
43+
public static Channel attachHeaders(Channel channel, final Metadata.Headers extraHeaders) {
44+
return new ForwardingChannel(channel) {
45+
@Override
46+
public <ReqT, RespT> Call<ReqT, RespT> newCall(MethodDescriptor<ReqT, RespT> method) {
47+
return new ForwardingCall<ReqT, RespT>(delegate.newCall(method)) {
48+
@Override
49+
public void start(Listener<RespT> responseListener, Metadata.Headers headers) {
50+
headers.merge(extraHeaders);
51+
delegate.start(responseListener, headers);
52+
}
53+
};
54+
}
55+
};
56+
}
57+
58+
/**
59+
* Capture the last received metadata for a stub. Useful for testing
60+
* @param stub to capture for
61+
* @param headersCapture to record the last received headers
62+
* @param trailersCapture to record the last received trailers
63+
* @return an implementation of the stub with extraHeaders bound to each call.
64+
*/
65+
@SuppressWarnings("unchecked")
66+
public static <T extends AbstractStub> T captureMetadata(
67+
T stub,
68+
AtomicReference<Metadata.Headers> headersCapture,
69+
AtomicReference<Metadata.Trailers> trailersCapture) {
70+
return (T) stub.configureNewStub().setChannel(
71+
captureMetadata(stub.getChannel(), headersCapture, trailersCapture))
72+
.build();
73+
}
74+
75+
/**
76+
* Capture the last received metadata on a channel. Useful for testing
77+
*
78+
* @param channel to channel to capture for.
79+
* @param headersCapture to record the last received headers
80+
* @param trailersCapture to record the last received trailers
81+
* @return an implementation of the channel with captures installed.
82+
*/
83+
@SuppressWarnings("unchecked")
84+
public static Channel captureMetadata(Channel channel,
85+
final AtomicReference<Metadata.Headers> headersCapture,
86+
final AtomicReference<Metadata.Trailers> trailersCapture) {
87+
return new ForwardingChannel(channel) {
88+
@Override
89+
public <ReqT, RespT> Call<ReqT, RespT> newCall(MethodDescriptor<ReqT, RespT> method) {
90+
return new ForwardingCall<ReqT, RespT>(delegate.newCall(method)) {
91+
@Override
92+
public void start(Listener<RespT> responseListener, Metadata.Headers headers) {
93+
headersCapture.set(null);
94+
trailersCapture.set(null);
95+
delegate.start(new ForwardingListener<RespT>(responseListener) {
96+
@Override
97+
public ListenableFuture<Void> onHeaders(Metadata.Headers headers) {
98+
headersCapture.set(headers);
99+
return super.onHeaders(headers);
100+
}
101+
102+
@Override
103+
public void onClose(Status status, Metadata.Trailers trailers) {
104+
trailersCapture.set(trailers);
105+
super.onClose(status, trailers);
106+
}
107+
}, headers);
108+
}
109+
};
110+
}
111+
};
112+
}
113+
114+
/**
115+
* Produce a metadata key for a generated protobuf type.
116+
*/
117+
public static <T extends GeneratedMessage> Metadata.Key<T> keyForProto(final T instance) {
118+
return Metadata.Key.of(instance.getDescriptorForType().getFullName(),
119+
new Metadata.Marshaller<T>() {
120+
@Override
121+
public byte[] toBytes(T value) {
122+
return value.toByteArray();
123+
}
124+
125+
@Override
126+
public String toAscii(T value) {
127+
return BaseEncoding.base64().encode(value.toByteArray());
128+
}
129+
130+
@Override
131+
@SuppressWarnings("unchecked")
132+
public T parseBytes(byte[] serialized) {
133+
try {
134+
return (T) instance.getParserForType().parseFrom(serialized);
135+
} catch (InvalidProtocolBufferException ipbe) {
136+
throw new IllegalArgumentException(ipbe);
137+
}
138+
}
139+
140+
@Override
141+
public T parseAscii(String ascii) {
142+
return parseBytes(BaseEncoding.base64().decode(ascii));
143+
}
144+
});
145+
}
146+
}

0 commit comments

Comments
 (0)