Reactive GraphQL - graphql

Trying to implement reactive graphql and running into some issues.
pom dependencies,
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-kickstart-spring-boot-starter-webflux</artifactId>
<version>7.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.graphql-java-kickstart/graphql-kickstart-spring-boot-starter-tools -->
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-kickstart-spring-boot-starter-tools</artifactId>
<version>7.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.graphql-java-kickstart/graphiql-spring-boot-starter -->
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>7.0.1</version>
</dependency>
Schema:-
type Query {
store(storeId: Int!): Store!
stores: [Store!]!
}
type Store {
storeId: Int!
name: String!
address: String!
city: String!
zip: String!
}
Resolver:-
#Component
public class StoreQuery implements GraphQLQueryResolver {
#Autowired
private final StoreDao dao;
public Store store(int storeId) {
return dao.findById(storeId);
}
public List<Store> stores() {
return dao.findAll();
}
}
Here, If I return Flux or Mono, getting the following error.
"class": "org.springframework.beans.factory.BeanCreationException",
"msg": "Error creating bean with name 'schemaParser' defined in class path resource [graphql/kickstart/tools/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [graphql.kickstart.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is java.lang.ClassCastException: class graphql.kickstart.tools.ParameterizedTypeImpl cannot be cast to class java.lang.Class (graphql.kickstart.tools.ParameterizedTypeImpl is in unnamed module of loader 'app'; java.lang.Class is in module java.base of loader 'bootstrap')",
"class": "java.lang.ClassCastException",
"msg": "class graphql.kickstart.tools.ParameterizedTypeImpl cannot be cast to class java.lang.Class (graphql.kickstart.tools.ParameterizedTypeImpl is in unnamed module of loader 'app'; java.lang.Class is in module java.base of loader 'bootstrap')",
So, trying to return as List.
But converting Flux to List using blocking calls also throwing error,
public List<Store> findAll() {
return Flux.from(storeCollection.find()).collectList().block();
}
Here, getting the error below.
Exception while fetching data (/stores) : block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-kqueue-2
Using graphiql for testing.
http://localhost:8089/graphiql

As the error suggests, you can't use blocking operation in non-blocking webflux.
public Flux<Store> findAll() {
return Flux.fromIterable(storeCollection.find());
or
return Flux.fromStream(storeCollection.find());
}

You can't use blocking operation in non-blocking webflux. If you use kickstart v.7.0.1 you should return CompletableFuture
public CompletableFuture<List<Store>> findAll() {
return Flux.fromIterable(dao.findAll())
.collectList()
.toFuture();
}

Related

Start Springboot without any Undertow Configuration

I have a project which mixes 2 dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-installed-adapter</artifactId>
<version>11.0.2</version>
</dependency>
I'm getting a transitiv dependency for Undertow, which comes from
keycloak-installed-adapter
As far as I understood, Undertow is used internally within the KeyCloak libraries.
I don't want my Springboot Application to start with Undertow, nor I want that anything related with Undertow gets configured at Springboot level.
I can't find to completely exclude any Undertow configuration for Springboot. Does anyone have an idea?
Update
I now have the following:
#SpringBootApplication
#EnableAutoConfiguration(exclude = {EmbeddedUndertow.class})
public class SpringbootTest {
public static void main(String[] args) {
SpringApplication.run(SpringbootTest.class, args);
}
}
But i'm getting following exception when starting my service:
org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'undertowEmbeddedServletContainerFactory' defined in class path resource [org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration$EmbeddedUndertow.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory]: Factory method 'undertowEmbeddedServletContainerFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: io/undertow/servlet/api/SessionPersistenceManager
Knowing that the EmbeddedUndertow Class lokks like:
#Configuration
#ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
#ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow {
#Bean
public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
return new UndertowEmbeddedServletContainerFactory();
}
}
How would you ignore this calss from the scanning?

Startup error in Spring Cloud Stream after upgrading to Spring Boot 2.6.1 and Spring Cloud 2021.0.0

I just upgraded a Spring Boot application that uses Spring Cloud Stream Kafka producers and consumers to
plugins {
id("org.springframework.boot") version "2.6.1"
...
}
extra["springCloudVersion"] = "2021.0.0"
extra["springCloudStreamVersion"] = "3.2.1"
The applications doesn't start anymore with the following exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'functionInitializer' defined in class path resource [org/springframework/cloud/stream/function/FunctionConfiguration.class]: Invocation of init method failed; nested exception is java.lang.ClassCastException: class reactor.core.publisher.MonoPeekTerminal cannot be cast to class reactor.core.publisher.Flux (reactor.core.publisher.MonoPeekTerminal and reactor.core.publisher.Flux are in unnamed module of loader 'app')
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)
...
Caused by: java.lang.ClassCastException: class reactor.core.publisher.MonoPeekTerminal cannot be cast to class reactor.core.publisher.Flux (reactor.core.publisher.MonoPeekTerminal and reactor.core.publisher.Flux are in unnamed module of loader 'app')
at org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAroundWrapper.reactorFluxStream(TraceFunctionAroundWrapper.java:187)
at org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAroundWrapper.reactorStream(TraceFunctionAroundWrapper.java:120)
at org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAroundWrapper.doApply(TraceFunctionAroundWrapper.java:97)
at org.springframework.cloud.function.context.catalog.FunctionAroundWrapper.apply(FunctionAroundWrapper.java:47)
at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$1.doApply(SimpleFunctionRegistry.java:256)
at org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry$FunctionInvocationWrapper.apply(SimpleFunctionRegistry.java:550)
at org.springframework.cloud.stream.function.FunctionConfiguration$FunctionToDestinationBinder.bindFunctionToDestinations(FunctionConfiguration.java:512)
at org.springframework.cloud.stream.function.FunctionConfiguration$FunctionToDestinationBinder.afterPropertiesSet(FunctionConfiguration.java:418)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
... 16 common frames omitted
Did I miss any upgrade guide or is it a bug?
Producer
#Component
class EventProducer(#Qualifier("eventSink") private val eventProcessor: Sinks.Many<Message<EventReceived>>) {
private val logger = LoggerFactory.getLogger(javaClass)
fun send(event: EventReceived): Mono<EventReceived> {
return Mono.defer {
val message = MessageBuilder.withPayload(event)
.setHeader(MESSAGE_KEY, event.id)
.setHeader(TIMESTAMP, OffsetDateTime.now().toInstant().toEpochMilli())
.build()
logger.info("Sending event {}", event)
while (eventProcessor.tryEmitNext(message).isFailure) {
LockSupport.parkNanos(10)
}
event.toMono()
}.subscribeOn(Schedulers.boundedElastic())
}
Consumer
#Configuration
class MetricConsumer(...) {
private val logger = LoggerFactory.getLogger(javaClass)
#Bean
fun consumeMetricUpdated(): Function<Flux<Message<MetricUpdated>>, Mono<Void>> {
...
}
This looks like a bug in s-c-sleuth. I'll follow up with Marcin about this.
Can you please post the signature of your function as well, need to confirm something?
Meanwhile, you can temporarily disconnect sleuth's TraceFunctionAroundWrapper by setting spring.sleuth.function.enabled to false.

JedisConnectionFactory bean instantiation failure during application startup and throws java.lang.NullPointerException

I am using spring-boot-data, redis and jedis. I created jedisConnectionFactory and redisTemplate beans in a Configuration class. JedisConnectionFactory bean instantiation failure during application startup.
I use the latest libraries. This is the exception I get:
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-
maven-plugin:2.2.0.BUILD-SNAPSHOT:run (default-cli) on project
Console: An exception occurred while running. null:
InvocationTargetException: Error creating bean with name
'consoleApplication': Unsatisfied dependency expressed through field
'bookRepository'; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'bookRepository': Cannot resolve reference
to bean 'redisKeyValueTemplate' while setting bean property
'keyValueOperations'; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'redisKeyValueTemplate': Cannot resolve
reference to bean 'redisKeyValueAdapter' while setting constructor
argument; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'redisKeyValueAdapter': Cannot resolve
reference to bean 'redisTemplate' while setting constructor
argument; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'redisTemplate' defined in class path
resource [com/console/config/AppConfig.class]: Bean instantiation
via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [org.springframework.data.redis.core.RedisTemplate]:
Factory method 'redisTemplate' threw exception; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'jedisConnectionFactory' defined in class
path resource [com/console/config/AppConfig.class]: Bean
instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [org.springframework.data.redis.connection.jedis.JedisConnectionFactory]: Factory method 'jedisConnectionFactory' threw exception; nested exception is java.lang.NullPointerException -> [Help 1]
This is my pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<!-- <version>2.2.0.BUILD-SNAPSHOT</version> -->
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<!-- <version>3.0.1</version> -->
<!-- <type>jar</type> -->
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
This is my code:
#Configuration
public class AppConfig {
#Autowired
private RedisProperties redisProperties;
#Bean
JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisProperties.getHost(), redisProperties.getPort());redisStandaloneConfiguration.setPassword(redisProperties.getPassword());
JedisClientConfiguration.JedisClientConfigurationBuilder builder = JedisClientConfiguration.builder()
.connectTimeout(redisProperties.getTimeout())
.readTimeout(redisProperties.getJedis().getPool().getMaxWait());
if (redisProperties.isSsl())
builder.useSsl();
// Final JedisClientConfiguration
JedisClientConfiguration clientConfig = builder.build();//.usePooling().build();
return new JedisConnectionFactory(redisStandaloneConfiguration, clientConfig);
}
#Bean
public RedisTemplate<Object, Object> redisTemplate() {
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
JedisConnectionFactory factory = jedisConnectionFactory();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
#Repository
public interface BookRepository extends CrudRepository<Book, Long> {
}
I expect the simple application to run without exception.
Any advice and insight is appreciated.
From reviewing JedisConnectionFactory code it seem like you can get such error only if one of the arguments to the Ctor is null.
public JedisConnectionFactory(RedisStandaloneConfiguration standaloneConfig, JedisClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(standaloneConfig, "RedisStandaloneConfiguration must not be null!");
this.standaloneConfig = standaloneConfig;
}
I had the same problem. For me, the problem was the order in which the beans were created.
It's important that the jedisConnectionFactory bean is created before any of its dependencies. A way of telling Spring about this is by using the #DependsOn annotation. You could do something like this:
#SpringBootApplication
#DependsOn("jedisConnectionFactory")
public class Application extends SpringBootServletInitializer {
...
}
There are probably better ways of solving this! My suggestion is just a workaround.

Jackson Kotlin plugin not used by Spring Boot

I can't force Spring to use Kotlin module for Jackson.
The problem is that Jackson can't parse JSON into data class.
//Exception
2018-02-23 13:29:09.046 ERROR 24730 --- [nio-9300-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/services] threw exception [Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [*.model.User]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Parameter specified as non-null is null: method *.model.User.<init>, parameter name] with root cause
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method *.User.<init>, parameter name
//JSON
{
"name": "name",
"surname": "surname",
"email": "email",
"password": "pswd"
}
//Model
#Entity
#Table
data class User(
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
var userId: Long?,
var name: String,
var surname: String,
var email: String,
var password: String,
(...)
): Resource() {
(...)
}
I tried to config Jackson, but it didn't help much. What's strange, inside #Bean method where I configure ObjectMapper, everything works fine.
Also, when I added default values for non-nullable fields, they weren't overwritten.
#Configuration
class JacksonConfig {
#Bean
fun mappingJackson2HttpMessageConverter(): MappingJackson2HttpMessageConverter {
val mapper = ObjectMapper().registerKotlinModule()
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
var user = mapper.readValue<User>("{\n" +
"\t\"name\": \"name\",\n" +
"\t\"surname\": \"surname\",\n" +
"\t\"email\": \"email\",\n" +
"\t\"password\": \"pswd\"\n" +
"}")
return MappingJackson2HttpMessageConverter(mapper)
}
}
What might be important is that model is in the different project than the application itself.
Kotlin version is 1.20
Jackson dependencies:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version> <!--2.9.4-->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>${jackson.version}</version>
</dependency>
And what I tried to this moment:
All of these answers
Similiar problem but probably different case anyway
And some other, not worth noticing.
I had structure like:
abstract class AbstractController (...) {
fun save(#RequestMapping entity: T) {
(...)
}
And to enable it I used
class DeriveredController (...) {
#PostMapping
override fun save(entity: Derivered) {
(...)
}
}
Problem was with lack of #RequestMapping in derivered function.

Spring - can't get simple aspect working

ERROR
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/callcentre] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: object is not an instance of declaring class
HandlerMethod details:
Controller [$Proxy109]
Method [public final java.lang.String au.com.mycompany.web.controllers.DummyControllerImpl.dummy()]
Resolved arguments:
] with root cause
java.lang.IllegalArgumentException: object is not an instance of declaring class
...
MAVEN DEPENDENCY
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.framework.version}</version>
</dependency>
SERVLET-CONTEXT.XML
<aop:aspectj-autoproxy />
ASPECT
#Aspect
#Component
public class JsonAspect {
#Before("execution(public * au.com.mycompany.web.controllers.DummyController.dummy(..))")
public final void beforeMethod1(final JoinPoint joinPoint) {
System.out.println("Intercepted.............");
System.out.println(joinPoint.getSignature().getName());
}
}
CONTROLLER INTERFACE
#Controller
public interface DummyController {
#RequestMapping(value = "/dummy", method = RequestMethod.GET)
#ResponseBody
String dummy();
}
CONTROLLER IMPL
#Controller
public class DummyControllerImpl implements DummyController {
#Override
public final String dummy() {
System.out.println("IT WORKED..........");
return "it returned";
}
}
I assume this is no longer relevant, but I had the exact same issue and the solution was to set this in the application context:
<aop:aspectj-autoproxy proxy-target-class="true"/>
Root cause, is that the AOP Auto Proxy generated for this class is a JdkAutoProxy instead of a CGLIB generated proxy - which causes a failure in the spring MVC invocation of the API since the proxy is not of the same type as the controller.
The reason for this JdkAutoProxy is since this specific controller implements an interface, which causes spring to automatically use the JDK auto proxy.

Resources