Skip to content

Commit 62c6960

Browse files
committed
Support 3rd party gRPC Java compilers via StubFactory
1 parent 256efeb commit 62c6960

File tree

7 files changed

+242
-54
lines changed

7 files changed

+242
-54
lines changed

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/autoconfigure/GrpcClientAutoConfiguration.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
import net.devh.boot.grpc.client.interceptor.AnnotationGlobalClientInterceptorConfigurer;
4747
import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
4848
import net.devh.boot.grpc.client.nameresolver.NameResolverRegistration;
49+
import net.devh.boot.grpc.client.stubfactory.AsyncStubFactory;
50+
import net.devh.boot.grpc.client.stubfactory.BlockingStubFactory;
51+
import net.devh.boot.grpc.client.stubfactory.FutureStubFactory;
4952
import net.devh.boot.grpc.common.autoconfigure.GrpcCommonCodecAutoConfiguration;
5053

5154
/**
@@ -65,6 +68,21 @@ static GrpcClientBeanPostProcessor grpcClientBeanPostProcessor(final Application
6568
return new GrpcClientBeanPostProcessor(applicationContext);
6669
}
6770

71+
@Bean
72+
AsyncStubFactory asyncStubFactory() {
73+
return new AsyncStubFactory();
74+
}
75+
76+
@Bean
77+
BlockingStubFactory blockingStubFactory() {
78+
return new BlockingStubFactory();
79+
}
80+
81+
@Bean
82+
FutureStubFactory futureStubFactory() {
83+
return new FutureStubFactory();
84+
}
85+
6886
@ConditionalOnMissingBean
6987
@Bean
7088
GrpcChannelsProperties grpcChannelsProperties() {

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/GrpcClientBeanPostProcessor.java

Lines changed: 23 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,11 @@
1919

2020
import static java.util.Objects.requireNonNull;
2121

22-
import java.lang.reflect.Constructor;
2322
import java.lang.reflect.Field;
2423
import java.lang.reflect.Member;
2524
import java.lang.reflect.Method;
26-
import java.util.ArrayList;
27-
import java.util.Collection;
28-
import java.util.List;
25+
import java.util.*;
2926

30-
import org.springframework.beans.BeanInstantiationException;
3127
import org.springframework.beans.BeansException;
3228
import org.springframework.beans.InvalidPropertyException;
3329
import org.springframework.beans.factory.BeanCreationException;
@@ -41,12 +37,10 @@
4137

4238
import io.grpc.Channel;
4339
import io.grpc.ClientInterceptor;
44-
import io.grpc.stub.AbstractAsyncStub;
45-
import io.grpc.stub.AbstractBlockingStub;
46-
import io.grpc.stub.AbstractFutureStub;
4740
import io.grpc.stub.AbstractStub;
4841
import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory;
4942
import net.devh.boot.grpc.client.nameresolver.NameResolverRegistration;
43+
import net.devh.boot.grpc.client.stubfactory.StubFactory;
5044

5145
/**
5246
* This {@link BeanPostProcessor} searches for fields and methods in beans that are annotated with {@link GrpcClient}
@@ -63,6 +57,7 @@ public class GrpcClientBeanPostProcessor implements BeanPostProcessor {
6357
// which could lead to problems with the correct bean setup.
6458
private GrpcChannelFactory channelFactory = null;
6559
private List<StubTransformer> stubTransformers = null;
60+
private final List<StubFactory> stubFactories;
6661

6762
/**
6863
* Creates a new GrpcClientBeanPostProcessor with the given ApplicationContext.
@@ -72,6 +67,7 @@ public class GrpcClientBeanPostProcessor implements BeanPostProcessor {
7267
*/
7368
public GrpcClientBeanPostProcessor(final ApplicationContext applicationContext) {
7469
this.applicationContext = requireNonNull(applicationContext, "applicationContext");
70+
stubFactories = new ArrayList<>(applicationContext.getBeansOfType(StubFactory.class).values());
7571
}
7672

7773
@Override
@@ -217,7 +213,8 @@ protected <T> T valueForMember(final String name, final Member injectionTarget,
217213
} else if (AbstractStub.class.isAssignableFrom(injectionType)) {
218214

219215
@SuppressWarnings("unchecked") // Eclipse incorrectly marks this as not required
220-
AbstractStub<?> stub = createStub(injectionType.asSubclass(AbstractStub.class), channel);
216+
AbstractStub<?> stub = createStub(
217+
(Class<? extends AbstractStub<?>>) injectionType.asSubclass(AbstractStub.class), channel);
221218
for (final StubTransformer stubTransformer : getStubTransformers()) {
222219
stub = stubTransformer.transform(name, stub);
223220
}
@@ -229,53 +226,25 @@ protected <T> T valueForMember(final String name, final Member injectionTarget,
229226
}
230227

231228
/**
232-
* Creates a stub of the given type.
233-
*
234-
* @param <T> The type of the instance to be injected.
235-
* @param stubType The type of the stub to create.
236-
* @param channel The channel used to create the stub.
237-
* @return The newly created stub.
238-
*
239-
* @throws BeanInstantiationException If the stub couldn't be created.
240-
*/
241-
protected <T extends AbstractStub<T>> T createStub(final Class<T> stubType, final Channel channel) {
242-
try {
243-
// First try the public static factory method
244-
final String methodName = deriveStubFactoryMethodName(stubType);
245-
final Class<?> enclosingClass = stubType.getEnclosingClass();
246-
final Method factoryMethod = enclosingClass.getMethod(methodName, Channel.class);
247-
return stubType.cast(factoryMethod.invoke(null, channel));
248-
} catch (final Exception e) {
249-
try {
250-
// Use the private constructor as backup
251-
final Constructor<T> constructor = stubType.getDeclaredConstructor(Channel.class);
252-
constructor.setAccessible(true);
253-
return constructor.newInstance(channel);
254-
} catch (final Exception e1) {
255-
e.addSuppressed(e1);
256-
}
257-
throw new BeanInstantiationException(stubType, "Failed to create gRPC client", e);
258-
}
259-
}
260-
261-
/**
262-
* Derives the name of the factory method from the given stub type.
263-
*
264-
* @param stubType The type of the stub to get it for.
265-
* @return The name of the factory method.
229+
* Creates a stub instance for the specified stub type.
230+
*
231+
* @param stubClass
232+
* @param channel
266233
* @throws IllegalArgumentException If the method was called with an unsupported stub type.
234+
* @throws IllegalStateException If failed creating the stub instance.
235+
* @return
267236
*/
268-
protected String deriveStubFactoryMethodName(final Class<? extends AbstractStub<?>> stubType) {
269-
if (AbstractAsyncStub.class.isAssignableFrom(stubType)) {
270-
return "newStub";
271-
} else if (AbstractBlockingStub.class.isAssignableFrom(stubType)) {
272-
return "newBlockingStub";
273-
} else if (AbstractFutureStub.class.isAssignableFrom(stubType)) {
274-
return "newFutureStub";
275-
} else {
276-
throw new IllegalArgumentException(
277-
"Unsupported stub type: " + stubType.getName() + " -> Please report this issue.");
237+
private AbstractStub<?> createStub(Class<? extends AbstractStub<?>> stubClass, Channel channel) {
238+
final StubFactory factory = this.stubFactories.stream()
239+
.filter(stubFactory -> stubFactory.isApplicable(stubClass))
240+
.findFirst()
241+
.orElseThrow(() -> new IllegalArgumentException(
242+
"Unsupported stub type: " + stubClass.getName() + " -> Please report this issue."));
243+
244+
try {
245+
return factory.createStub(stubClass, channel);
246+
} catch (Exception exception) {
247+
throw new IllegalStateException("Failed to create gRPC stub of type " + stubClass.getName(), exception);
278248
}
279249
}
280-
281250
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2016-2020 Michael Zhang <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package net.devh.boot.grpc.client.stubfactory;
19+
20+
import io.grpc.stub.AbstractAsyncStub;
21+
import io.grpc.stub.AbstractStub;
22+
23+
public class AsyncStubFactory extends StandardJavaGrpcStubFactory {
24+
25+
@Override
26+
public boolean isApplicable(Class<? extends AbstractStub<?>> stubType) {
27+
return AbstractAsyncStub.class.isAssignableFrom(stubType);
28+
}
29+
30+
@Override
31+
protected String getFactoryMethodName() {
32+
return "newStub";
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2016-2020 Michael Zhang <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package net.devh.boot.grpc.client.stubfactory;
19+
20+
import io.grpc.stub.AbstractBlockingStub;
21+
import io.grpc.stub.AbstractStub;
22+
23+
public class BlockingStubFactory extends StandardJavaGrpcStubFactory {
24+
25+
@Override
26+
public boolean isApplicable(Class<? extends AbstractStub<?>> stubType) {
27+
return AbstractBlockingStub.class.isAssignableFrom(stubType);
28+
}
29+
30+
@Override
31+
protected String getFactoryMethodName() {
32+
return "newBlockingStub";
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2016-2020 Michael Zhang <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package net.devh.boot.grpc.client.stubfactory;
19+
20+
import io.grpc.stub.AbstractFutureStub;
21+
import io.grpc.stub.AbstractStub;
22+
23+
public class FutureStubFactory extends StandardJavaGrpcStubFactory {
24+
25+
@Override
26+
public boolean isApplicable(Class<? extends AbstractStub<?>> stubType) {
27+
return AbstractFutureStub.class.isAssignableFrom(stubType);
28+
}
29+
30+
@Override
31+
protected String getFactoryMethodName() {
32+
return "newFutureStub";
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2016-2020 Michael Zhang <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package net.devh.boot.grpc.client.stubfactory;
19+
20+
import java.lang.reflect.Constructor;
21+
import java.lang.reflect.Method;
22+
23+
import org.springframework.beans.BeanInstantiationException;
24+
25+
import io.grpc.Channel;
26+
import io.grpc.stub.AbstractStub;
27+
28+
/**
29+
* A factory for creating stubs provided by standard grpc Java library. This is an abstract super-type that can be
30+
* extended to support the different provided types.
31+
*/
32+
public abstract class StandardJavaGrpcStubFactory implements StubFactory {
33+
34+
/**
35+
* Creates a stub of the given type.
36+
*
37+
* @param stubType The type of the stub to create.
38+
* @param channel The channel used to create the stub.
39+
* @return The newly created stub.
40+
*
41+
* @throws BeanInstantiationException If the stub couldn't be created.
42+
*/
43+
@Override
44+
public AbstractStub<?> createStub(final Class<? extends AbstractStub<?>> stubType, final Channel channel) {
45+
try {
46+
// First try the public static factory method
47+
final String methodName = getFactoryMethodName();
48+
final Class<?> enclosingClass = stubType.getEnclosingClass();
49+
final Method factoryMethod = enclosingClass.getMethod(methodName, Channel.class);
50+
return stubType.cast(factoryMethod.invoke(null, channel));
51+
} catch (final Exception e) {
52+
try {
53+
// Use the private constructor as backup
54+
final Constructor<? extends AbstractStub<?>> constructor =
55+
stubType.getDeclaredConstructor(Channel.class);
56+
constructor.setAccessible(true);
57+
return constructor.newInstance(channel);
58+
} catch (final Exception e1) {
59+
e.addSuppressed(e1);
60+
}
61+
throw new BeanInstantiationException(stubType, "Failed to create gRPC client", e);
62+
}
63+
}
64+
65+
/**
66+
* Derives the name of the factory method from the given stub type.
67+
*
68+
* @return The name of the factory method.
69+
*/
70+
protected abstract String getFactoryMethodName();
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2016-2020 Michael Zhang <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package net.devh.boot.grpc.client.stubfactory;
19+
20+
import io.grpc.Channel;
21+
import io.grpc.stub.AbstractStub;
22+
23+
public interface StubFactory {
24+
25+
AbstractStub<?> createStub(final Class<? extends AbstractStub<?>> stubType, final Channel channel);
26+
27+
boolean isApplicable(Class<? extends AbstractStub<?>> stubType);
28+
}

0 commit comments

Comments
 (0)