Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fallback stubfactory #413

Merged
merged 3 commits into from
Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ buildscript {
}
}
ext {
projectVersion = '2.10.0.RELEASE'
projectVersion = '2.10.1-SNAPSHOT'

// https://github.com/grpc/grpc-java/releases
grpcVersion = '1.31.1'
Expand Down
1 change: 1 addition & 0 deletions docs/en/versions.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Current version.
| Version | spring-boot | gRPC | Date |
|:-------:|:-----------:|:----:| ---: |
| 2.11.0* | 2.3.3 | 1.31.1 | TBA |
| 2.10.1* | 2.3.3 | 1.31.1 | Aug, 2020 |
| 2.10.0 | 2.3.3 | 1.31.1 | Aug, 2020 |
| 2.9.0 | 2.3.1 | 1.30.0 | Jun, 2020 |
| 2.8.0 | 2.2.7 | 1.29.0 | Jun, 2020 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import io.grpc.stub.AbstractStub;
import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory;
import net.devh.boot.grpc.client.nameresolver.NameResolverRegistration;
import net.devh.boot.grpc.client.stubfactory.FallbackStubFactory;
import net.devh.boot.grpc.client.stubfactory.StubFactory;

/**
Expand Down Expand Up @@ -229,14 +230,14 @@ protected <T> T valueForMember(final String name, final Member injectionTarget,

/**
* Creates a stub instance for the specified stub type using the resolved {@link StubFactory}.
*
*
* @param stubClass The stub class that needs to be created.
* @param channel The gRPC channel associated with the created stub, passed as a parameter to the stub factory.
* @throws BeanInstantiationException If the stub couldn't be created, either because the type isn't supported or
* because of a failure in creation.
* @return A newly created gRPC stub.
*/
private AbstractStub<?> createStub(Class<? extends AbstractStub<?>> stubClass, Channel channel) {
private AbstractStub<?> createStub(final Class<? extends AbstractStub<?>> stubClass, final Channel channel) {
final StubFactory factory = getStubFactories().stream()
.filter(stubFactory -> stubFactory.isApplicable(stubClass))
.findFirst()
Expand All @@ -245,7 +246,7 @@ private AbstractStub<?> createStub(Class<? extends AbstractStub<?>> stubClass, C

try {
return factory.createStub(stubClass, channel);
} catch (Exception exception) {
} catch (final Exception exception) {
throw new BeanInstantiationException(stubClass, "Failed to create gRPC stub of type " + stubClass.getName(),
exception);
}
Expand All @@ -258,8 +259,10 @@ private AbstractStub<?> createStub(Class<? extends AbstractStub<?>> stubClass, C
*/
private List<StubFactory> getStubFactories() {
if (this.stubFactories == null) {
stubFactories = new ArrayList<>(applicationContext.getBeansOfType(StubFactory.class).values());
this.stubFactories = new ArrayList<>(this.applicationContext.getBeansOfType(StubFactory.class).values());
this.stubFactories.add(new FallbackStubFactory());
}
return stubFactories;
return this.stubFactories;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2016-2020 Michael Zhang <yidongnan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package net.devh.boot.grpc.client.stubfactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

import org.springframework.beans.BeanInstantiationException;

import io.grpc.Channel;
import io.grpc.stub.AbstractStub;

/**
* The StubFactory which tries to find a suitable factory method or constructor as a last resort. This factory will
* always be the last one that is attempted.
*
* @author Daniel Theuke (daniel.theuke@heuboe.de)
*/
public final class FallbackStubFactory implements StubFactory {

@Override
public boolean isApplicable(final Class<? extends AbstractStub<?>> stubType) {
return true;
}

@Override
public AbstractStub<?> createStub(final Class<? extends AbstractStub<?>> stubType, final Channel channel) {
try {
// Search for public static *Grpc#new*Stub(Channel)
final Class<?> declaringClass = stubType.getDeclaringClass();
if (declaringClass != null) {
for (final Method method : declaringClass.getMethods()) {
final String name = method.getName();
final int modifiers = method.getModifiers();
final Parameter[] parameters = method.getParameters();
if (name.startsWith("new") && name.endsWith("Stub")
&& Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)
&& method.getReturnType().isAssignableFrom(stubType)
&& parameters.length == 1
&& Channel.class.equals(parameters[0].getType())) {
return AbstractStub.class.cast(method.invoke(null, channel));
}
}
}

// Search for a public constructor *Stub(Channel)
final Constructor<? extends AbstractStub<?>> constructor = stubType.getConstructor(Channel.class);
return constructor.newInstance(channel);

} catch (final Exception e) {
throw new BeanInstantiationException(stubType, "Failed to create gRPC client via FallbackStubFactory", e);
}
}

}
87 changes: 87 additions & 0 deletions tests/src/test/java/net/devh/boot/grpc/test/inject/CustomGrpc.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2016-2020 Michael Zhang <yidongnan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package net.devh.boot.grpc.test.inject;

import io.grpc.CallOptions;
import io.grpc.Channel;

/**
* Fake generated grpc class.
*/
public class CustomGrpc {

public static CustomAccessibleStub custom(final Channel channel) {
return new CustomAccessibleStub(channel);
}

public static FactoryMethodAccessibleStub newFactoryMethodAccessibleStubStub(final Channel channel) {
return new FactoryMethodAccessibleStub(channel);
}

public static class CustomAccessibleStub extends CustomStub<CustomAccessibleStub> {

private CustomAccessibleStub(final Channel channel) {
super(channel);
}

private CustomAccessibleStub(final Channel channel, final CallOptions callOptions) {
super(channel, callOptions);
}

@Override
protected CustomAccessibleStub build(final Channel channel, final CallOptions callOptions) {
return new CustomAccessibleStub(channel, callOptions);
}

}

public static class FactoryMethodAccessibleStub extends OtherStub<FactoryMethodAccessibleStub> {

private FactoryMethodAccessibleStub(final Channel channel) {
super(channel);
}

private FactoryMethodAccessibleStub(final Channel channel, final CallOptions callOptions) {
super(channel, callOptions);
}

@Override
protected FactoryMethodAccessibleStub build(final Channel channel, final CallOptions callOptions) {
return new FactoryMethodAccessibleStub(channel, callOptions);
}

}

public static class ConstructorAccessibleStub extends OtherStub<ConstructorAccessibleStub> {

public ConstructorAccessibleStub(final Channel channel) {
super(channel);
}

public ConstructorAccessibleStub(final Channel channel, final CallOptions callOptions) {
super(channel, callOptions);
}

@Override
protected ConstructorAccessibleStub build(final Channel channel, final CallOptions callOptions) {
return new ConstructorAccessibleStub(channel, callOptions);
}

}

}
40 changes: 40 additions & 0 deletions tests/src/test/java/net/devh/boot/grpc/test/inject/CustomStub.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2016-2020 Michael Zhang <yidongnan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package net.devh.boot.grpc.test.inject;

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.stub.AbstractStub;

/**
* Simulates a custom stub type provided by a third party library, that requires a custom
* {@link net.devh.boot.grpc.client.stubfactory.StubFactory StubFactory}.
*
* @param <S> The type of the stub implementation.
*/
public abstract class CustomStub<S extends CustomStub<S>> extends AbstractStub<S> {

protected CustomStub(final Channel channel) {
super(channel);
}

protected CustomStub(final Channel channel, final CallOptions callOptions) {
super(channel, callOptions);
}

}
Loading