Skip to content

Commit 39c266c

Browse files
authored
core: introduce io.grpc.internal.BinaryLogProvider (grpc#3872)
1 parent 1bbe126 commit 39c266c

File tree

4 files changed

+257
-0
lines changed

4 files changed

+257
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright 2017, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.internal;
18+
19+
import com.google.common.annotations.VisibleForTesting;
20+
import io.grpc.ClientInterceptor;
21+
import io.grpc.ManagedChannelProvider;
22+
import io.grpc.ServerInterceptor;
23+
import java.util.ArrayList;
24+
import java.util.Collections;
25+
import java.util.Comparator;
26+
import java.util.Iterator;
27+
import java.util.List;
28+
import java.util.ServiceConfigurationError;
29+
import java.util.ServiceLoader;
30+
import java.util.logging.Level;
31+
import java.util.logging.Logger;
32+
import javax.annotation.Nullable;
33+
34+
public abstract class BinaryLogProvider {
35+
private static final Logger logger = Logger.getLogger(BinaryLogProvider.class.getName());
36+
private static final BinaryLogProvider NULL_PROVIDER = new NullProvider();
37+
private static final BinaryLogProvider PROVIDER = load(BinaryLogProvider.class.getClassLoader());
38+
39+
/**
40+
* Always returns a {@code BinaryLogProvider}, even if the provider always returns null
41+
* interceptors.
42+
*/
43+
public static BinaryLogProvider provider() {
44+
return PROVIDER;
45+
}
46+
47+
@VisibleForTesting
48+
static BinaryLogProvider load(ClassLoader classLoader) {
49+
try {
50+
return loadHelper(classLoader);
51+
} catch (Throwable t) {
52+
logger.log(
53+
Level.SEVERE, "caught exception loading BinaryLogProvider, will disable binary log", t);
54+
return NULL_PROVIDER;
55+
}
56+
}
57+
58+
private static BinaryLogProvider loadHelper(ClassLoader classLoader) {
59+
if (isAndroid()) {
60+
return NULL_PROVIDER;
61+
}
62+
63+
Iterator<BinaryLogProvider> iter = getCandidatesViaServiceLoader(classLoader).iterator();
64+
List<BinaryLogProvider> list = new ArrayList<BinaryLogProvider>();
65+
while (iter.hasNext()) {
66+
// The iterator comes from ServiceLoader and may throw when next() is called
67+
try {
68+
list.add(iter.next());
69+
} catch (ServiceConfigurationError e) {
70+
logger.log(Level.SEVERE, "caught exception creating an instance of BinaryLogProvider", e);
71+
}
72+
}
73+
if (list.isEmpty()) {
74+
return NULL_PROVIDER;
75+
} else {
76+
return Collections.max(list, new Comparator<BinaryLogProvider>() {
77+
@Override
78+
public int compare(BinaryLogProvider f1, BinaryLogProvider f2) {
79+
return f1.priority() - f2.priority();
80+
}
81+
});
82+
}
83+
}
84+
85+
private static ServiceLoader<BinaryLogProvider> getCandidatesViaServiceLoader(
86+
ClassLoader classLoader) {
87+
ServiceLoader<BinaryLogProvider> i = ServiceLoader.load(BinaryLogProvider.class, classLoader);
88+
// Attempt to load using the context class loader and ServiceLoader.
89+
// This allows frameworks like http://aries.apache.org/modules/spi-fly.html to plug in.
90+
if (!i.iterator().hasNext()) {
91+
i = ServiceLoader.load(BinaryLogProvider.class);
92+
}
93+
return i;
94+
}
95+
96+
/**
97+
* Returns a {@link ServerInterceptor} for binary logging. gRPC is free to cache the interceptor,
98+
* so the interceptor must be reusable across server calls. At runtime, the request and response
99+
* types passed into the interceptor is always {@link java.io.InputStream}.
100+
* Returns {@code null} if this method is not binary logged.
101+
*/
102+
@Nullable
103+
public abstract ServerInterceptor getServerInterceptor(String fullMethodName);
104+
105+
/**
106+
* Returns a {@link ClientInterceptor} for binary logging. gRPC is free to cache the interceptor,
107+
* so the interceptor must be reusable across server calls. At runtime, the request and response
108+
* types passed into the interceptor is always {@link java.io.InputStream}.
109+
* Returns {@code null} if this method is not binary logged.
110+
*/
111+
@Nullable
112+
public abstract ClientInterceptor getClientInterceptor(String fullMethodName);
113+
114+
/**
115+
* A priority, from 0 to 10 that this provider should be used, taking the current environment into
116+
* consideration. 5 should be considered the default, and then tweaked based on environment
117+
* detection. A priority of 0 does not imply that the provider wouldn't work; just that it should
118+
* be last in line.
119+
*/
120+
protected abstract int priority();
121+
122+
/**
123+
* A provider that always returns null interceptors.
124+
*/
125+
@VisibleForTesting
126+
static final class NullProvider extends BinaryLogProvider {
127+
@Nullable
128+
@Override
129+
public ServerInterceptor getServerInterceptor(String fullMethodName) {
130+
return null;
131+
}
132+
133+
@Override
134+
public ClientInterceptor getClientInterceptor(String fullMethodName) {
135+
return null;
136+
}
137+
138+
@Override
139+
protected int priority() {
140+
return 0;
141+
}
142+
}
143+
144+
/**
145+
* Returns whether current platform is Android.
146+
*/
147+
protected static boolean isAndroid() {
148+
try {
149+
// Specify a class loader instead of null because we may be running under Robolectric
150+
Class.forName("android.app.Application", /*initialize=*/ false,
151+
ManagedChannelProvider.class.getClassLoader());
152+
return true;
153+
} catch (Exception e) {
154+
// If Application isn't loaded, it might as well not be Android.
155+
return false;
156+
}
157+
}
158+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2017, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.internal;
18+
19+
import static org.junit.Assert.assertSame;
20+
21+
import io.grpc.ClientInterceptor;
22+
import io.grpc.ReplacingClassLoader;
23+
import io.grpc.ServerInterceptor;
24+
import javax.annotation.Nullable;
25+
import org.junit.Test;
26+
import org.junit.runner.RunWith;
27+
import org.junit.runners.JUnit4;
28+
29+
/** Unit tests for {@link BinaryLogProvider}. */
30+
@RunWith(JUnit4.class)
31+
public class BinaryLogProviderTest {
32+
private final String serviceFile = "META-INF/services/io.grpc.internal.BinaryLogProvider";
33+
34+
@Test
35+
public void noProvider() {
36+
assertSame(BinaryLogProvider.NullProvider.class, BinaryLogProvider.provider().getClass());
37+
}
38+
39+
@Test
40+
public void multipleProvider() {
41+
ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile,
42+
"io/grpc/internal/BinaryLogProviderTest-multipleProvider.txt");
43+
assertSame(Provider7.class, BinaryLogProvider.load(cl).getClass());
44+
}
45+
46+
@Test
47+
public void unavailableProvider() {
48+
ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile,
49+
"io/grpc/internal/BinaryLogProviderTest-unavailableProvider.txt");
50+
assertSame(BinaryLogProvider.NullProvider.class, BinaryLogProvider.load(cl).getClass());
51+
}
52+
53+
public static final class Provider0 extends BaseProvider {
54+
public Provider0() {
55+
super(0);
56+
}
57+
}
58+
59+
public static final class Provider5 extends BaseProvider {
60+
public Provider5() {
61+
super(5);
62+
}
63+
}
64+
65+
public static final class Provider7 extends BaseProvider {
66+
public Provider7() {
67+
super(7);
68+
}
69+
}
70+
71+
public static class BaseProvider extends BinaryLogProvider {
72+
private int priority;
73+
74+
BaseProvider(int priority) {
75+
this.priority = priority;
76+
}
77+
78+
@Nullable
79+
@Override
80+
public ServerInterceptor getServerInterceptor(String fullMethodName) {
81+
throw new UnsupportedOperationException();
82+
}
83+
84+
@Nullable
85+
@Override
86+
public ClientInterceptor getClientInterceptor(String fullMethodName) {
87+
throw new UnsupportedOperationException();
88+
}
89+
90+
@Override
91+
protected int priority() {
92+
return priority;
93+
}
94+
}
95+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
io.grpc.internal.BinaryLogProviderTest$Provider5
2+
io.grpc.internal.BinaryLogProviderTest$Provider7
3+
io.grpc.internal.BinaryLogProviderTest$Provider0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.grpc.internal.BinaryLogProviderTest$UnavailableProvider

0 commit comments

Comments
 (0)