A default binder has been requested, but there are no binders available for 'org.springframework.cloud.stream.messaging.DirectWithAttributesChannel' - spring-boot

I am trying to create the simplest as possible hello world with Spring Cloud + Kafka Streams + Spring Boot 2.
I realize I miss basic concepts. Basically, I understand that:
1 - I need to define an outbound stream to write messages to a Kafka topic, and an inbound stream to read messages from a Kafka topic
public interface LoansStreams {
String INPUT = "loans-in";
String OUTPUT = "loans-out";
#Input(INPUT)
SubscribableChannel inboundLoans();
#Output(OUTPUT)
MessageChannel outboundLoans();
}
2 - configure Spring Cloud Stream to bind to my streams
#EnableBinding(LoansStreams.class)
public class StreamsConfig {
}
3 - configure Kafka properties
spring:
cloud:
stream:
kafka:
binder:
brokers: localhost:9092
bindings:
loans-in:
destination: loans
contentType: application/json
loans-out:
destination: loans
contentType: application/json
4 - create model for exchange messages
#Getter #Setter #ToString #Builder
public class Loans {
private long timestamp;
private String result;
}
5 - write to Kafka
#Service
#Slf4j
public class LoansService {
private final LoansStreams loansStreams;
public LoansService(LoansStreams loansStreams) {
this.loansStreams = loansStreams;
}
public void sendLoan(final Loans loans) {
log.info("Sending loans {}", loans);
MessageChannel messageChannel = loansStreams.outboundLoans();
messageChannel.send(MessageBuilder
.withPayload(loans)
.setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
.build());
}
}
6 - listen to Kafka topic
#Component
#Slf4j
public class LoansListener {
#StreamListener(LoansStreams.INPUT)
public void handleLoans(#Payload Loans loans) {
log.info("Received results: {}", loans);
}
}
I spent a whole day reading few blogs and I assume that the above code is at least workable. I amo not sure I realy coding the best aproach as possible. By the way, I get the error mentioned in the topic:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-04-26 18:33:05.619 ERROR 14784 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean 'outputBindingLifecycle'; nested exception is java.lang.IllegalStateException: A default binder has been requested, but there are no binders available for 'org.springframework.cloud.stream.messaging.DirectWithAttributesChannel' : , and no default binder has been set.
Googling for solution, I found someone saying to code StreamListe returning the model so I replaced it with:
#StreamListener(LoansStreams.INPUT)
#SendTo("loans-out")
public KStream<?, Loans> process(KStream<?, Loans> l) {
log.info("Received: {}", l);
return l;
}
and then I get an error even less clear at least to me (previous error clearly mentioned some binder issue):
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-04-26 19:01:06.016 ERROR 13276 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed
java.lang.IllegalArgumentException: Method must be declarative
at org.springframework.util.Assert.isTrue(Assert.java:118) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.cloud.stream.binder.kafka.streams.KafkaStreamsStreamListenerSetupMethodOrchestrator.validateStreamListenerMethod(KafkaStreamsStreamListenerSetupMethodOrchestrator.java:510) ~[spring-cloud-stream-binder-kafka-streams-2.1.2.RELEASE.jar:2.1.2.RELEASE]
at org.springframework.cloud.stream.binder.kafka.streams.KafkaStreamsStreamListenerSetupMethodOrchestrator.orchestrateStreamListenerSetupMethod(KafkaStreamsStreamListenerSetupMethodOrchestrator.java:168) ~[spring-cloud-stream-binder-kafka-streams-2.1.2.RELEASE.jar:2.1.2.RELEASE]
at org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor.doPostProcess(StreamListenerAnnotationBeanPostProcessor.java:226) ~[spring-cloud-stream-2.1.2.RELEASE.jar:2.1.2.RELEASE]
In case it helps somehow, I want to evoluate this idea to apply SAGAS but it is not the focus of this question. Firstly, I need get the basic up and running.
*edited
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.mybank</groupId>
<artifactId>kafka-cloud-stream</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>kafka-cloud-stream</name>
<description>Spring Cloud Stream With Kafka</description>
<properties>
<java.version>11</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-kafka-streams</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<!-- version>5.1.5.RELEASE</version-->
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

"A default binder has been requested, but there are no binders available ...", please add dependency as below.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>

You can define your default binder in application.yml (or application.properties)
spring:
cloud:
stream:
bindings:
...
default-binder: kafka

For me, with different application.properties for different contexts and multiple output bindings, the only way I could fix it was defining a general default binding like:
spring:
cloud:
stream:
default-binder: eventhub
...
And the rest of bindings type individually set in each input / output as well.

In the above pom file, you need to use binder-kafka and not binder-kafka-streams
So replace
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-kafka-streams</artifactId>
</dependency>
With
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>

Related

Unable to Access Spring Boot Actuator Endpoints

I try to use actuator endpoints in spring boot. The application runs smoothly. My pom file is given below:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.luv2code.springboot</groupId>
<artifactId>thymeleafdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>thymeleafdemo</name>
<description>Ab Jove principium</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- umumi bağımlılıklar -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Here is the content of the application.properties file:
spring.datasource.url=DATABASE_URL
spring.datasource.username=USERNAME
spring.datasource.password=PASSWORD
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect
# Spring Data JPA properties
spring.data.jpa.repository.packages=com.yok.springboot.thymeleafdemo.dao
spring.data.jpa.entity.packages-to-scan=com.yok.springboot.thymeleafdemo.entity
spring.jpa.hibernate.use-new-id-generator-mappings=false
spring.jpa.hibernate.ddl-auto=create
#
# JDBC properties
#
app.datasource.jdbc-url=DATABASE_URL
app.datasource.username=USERNAME
app.datasource.password=PASSWORD
#
# Hikari properties
spring.datasource.hikari.maximumPoolSize=10
spring.datasource.hikari.idleTimeout=2000
spring.datasource.hikari.poolName=SpringBootJPAHikariCP
spring.datasource.hikari.maxLifetime=20000
spring.datasource.hikari.connectionTimeout=30000
# Actuator properties
# expose all endpoints:
management.endpoints.web.exposure.include=*
management.endpoints.beans.enabled=true
management.endpoints.web.exposure.include=info,env
management.endpoint.env.enabled=true
management.endpoint.info.enabled=true
management.endpoints.enabled-by-default=true
This is the start of my Spring Boot Application:
package com.yok.springboot.thymeleafdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ThymeleafdemoApplication {
public static void main(String[] args) {
SpringApplication.run(ThymeleafdemoApplication.class, args);
}
}
Whenever I try to connect /health,/Info or /metrics endpoint by typing http://localhost:8080/health, the HTTP request transfers to http://localhost:8080/showMyLoginPage. I cannot reach endpoint. How can I solve this? Thanks in advance.
Edit -1
Mr. Fatih demands me to observe the result "http://localhost:8080/actuator" and this picture reveals: the picture
Here is the console output of the application:
https://drive.google.com/file/d/1zYP1qe-Ohbcan93ZO6rqjxX9LqlGiIIg/view?usp=sharing
Edit-2
The problem is partly solved. The actuators are available after the login of the application. But the problem is, after the login page, the homepage appears. All actuators are working, however, whenever I hit http://localhost:8080/actuator/health URL, {"status":"DOWN"} appears at the screen. Here is the console output taken during this operation:
reached urls:
http://localhost:8080/showMyLoginPage
http://localhost:8080/students/list/page/1
http://localhost:8080/actuator/health
http://localhost:8080/actuator/heapdump
http://localhost:8080/actuator/env
console output: (exception has thrown)
java.lang.IllegalArgumentException: dataSource or dataSourceClassName
or jdbcUrl is required. at
com.zaxxer.hikari.HikariConfig.validate(HikariConfig.java:1029)
~[HikariCP-4.0.3.jar:na] at
com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:109)
~[HikariCP-4.0.3.jar:na] at
org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:159)
~[spring-jdbc-5.3.22.jar:5.3.22] at
org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:117)
~[spring-jdbc-5.3.22.jar:5.3.22] at
org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
~[spring-jdbc-5.3.22.jar:5.3.22] at
org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:330)
~[spring-jdbc-5.3.22.jar:5.3.22] at
org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator.getProduct(DataSourceHealthIndicator.java:122)
~[spring-boot-actuator-2.7.3.jar:2.7.3] at
org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator.doDataSourceHealthCheck(DataSourceHealthIndicator.java:105)
~[spring-boot-actuator-2.7.3.jar:2.7.3] at
org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator.doHealthCheck(DataSourceHealthIndicator.java:100)
~[spring-boot-actuator-2.7.3.jar:2.7.3]
Edit-3
Mr Fatih pointed out some of the changes at the WebSecurityConfiguration. I have changed the code and I am getting this error:
java.lang.IllegalStateException: permitAll only works with either
HttpSecurity.authorizeRequests() or
HttpSecurity.authorizeHttpRequests(). Please define one or the other
but not both.
Here is the change I've made:
/*
* import section have omitted for brevity
*/
#Configuration
#EnableWebSecurity
public class DemoSecurityConfig {
/*
* other codes have omitted for brevity
*/
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(
(authz) -> authz.antMatchers("/actuator/**").permitAll().anyRequest().authenticated());
http.authorizeRequests(
configurer -> configurer.antMatchers("/**").hasRole("ADMIN").antMatchers("/**").hasRole("USER"))
.formLogin(configurer -> configurer.loginPage("/showMyLoginPage")
.loginProcessingUrl("/authenticateTheUser").permitAll())
.logout(configurer -> configurer.permitAll())
.exceptionHandling(configurer -> configurer.accessDeniedPage("/access-denied"));
return http.build();
}
}
Here is the console output: https://drive.google.com/file/d/1CtjRBHXVRqirZ0Vt_3FEhx_N9oEwyfFZ/view
You are using the spring-security package for application security. So when you want to access your /actuator endpoints, you need to log in first. If you want to access your /actuator endpoints without logging in, you must configure a security configuration. With the following configuration, you can exclude all endpoints starting with /actuator from security.
#EnableWebSecurity
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/actuator/**").permitAll().anyRequest().authenticated();
}
}
Since WebSecurityConfigurerAdapter has been deprecated, you can do this as well.
#Configuration
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(autz -> autz
.mvcMatchers("/actuator/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}
** is a wildcard definition and allows you to access this endpoint without logging in, regardless of what comes after the actuator part.
Please Consider using a version for your dependency as far as i remember 1.9.5 RELEASE or 1.9.5 might help in this context , i had the same issue a year ago.

How to cache PageImpl with Spring Data Geode?

When trying to cache a PageImpl response from a Spring Data JpaRepository using Spring Data Geode, it fails to cache the result with the following error:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.domain.PageImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.springframework.data.domain.PageImpl.<init>()
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:127) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:64) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.convert.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:86) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.gemfire.mapping.MappingPdxSerializer.fromData(MappingPdxSerializer.java:422) ~[spring-data-gemfire-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.apache.geode.pdx.internal.PdxReaderImpl.basicGetObject(PdxReaderImpl.java:741) ~[geode-core-9.1.1.jar:?]
at org.apache.geode.pdx.internal.PdxReaderImpl.getObject(PdxReaderImpl.java:682) ~[geode-core-9.1.1.jar:?]
at org.apache.geode.internal.InternalDataSerializer.readPdxSerializable(InternalDataSerializer.java:3054) ~[geode-core-9.1.1.jar:?]
It looks like the MappingPdxSerializer looks for a default constructor but doesn't find it for a PageImpl class.
Here is maven pom for the dependencies I have:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.BUILD-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.BUILD-SNAPSHOT</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-gemfire</artifactId>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
The JpaRepository I am using is:
#RepositoryRestResource
public interface RecordRepository extends JpaRepository<Record, Long>
{
#Override
#CacheEvict(cacheNames = { "Records" })
<S extends Record> S save(S s);
#Override
#Cacheable(value = "Records")
Optional<Record> findById(Long id);
#Override
#Cacheable(value = "Records", key = "#pageable.pageNumber + '.' + #pageable.pageSize + '.records'")
Page<Record> findAll(Pageable pageable);
#Override
#Cacheable(value = "Records")
Record getOne(Long aLong);
}
The code used to invoke a repository paged result is:
int PAGE=0,PAGE_SIZE=100;
Page<Record> recordPage;
do {
recordPage = recordRepository.findAll(PageRequest.of(PAGE, PAGE_SIZE));
log.info("Retrieved page: [{}]", recordPage);
} while (recordPage.hasNext());
I feel like it maybe a possible bug with the MappingPdxSerializer, but I'm not 100% sure. Any help in resolving this issue would be awesome!
Thanks
Why do you feel this is a possible bug with Spring Data Geode's (SDG) o.s.d.g.mapping.MappingPdxSerializer?
It is quite common, and even expected, that not all objects passed through SDG's MappingPdxSerializer will have a default (i.e. public, no-arg) constructor.
When using such types in your application (e.g. like the SD PageImpl class) and an instance of that type is read from Apache Geode (e.g. get(key)), the object is de-serialized and reconstructed on the (Region) data access operation (providing Apache Geode's read-serialized configuration attribute is not set to true; which cause you other problems and not recommended in this case), then you need to register an EntityInstantiator that informs SDG's MappingPdxSerializer how to instantiate the object, using an appropriate constructor.
The "appropriate" constructor is determined by the persistent entity's PreferredConstructor, which is evaluated during type evaluation by the SD Mapping Infrastructure, and can be specified with the #PersistenceContructor annotation, if necessary. This is useful in cases where you are using 1 of SD's canned EntityIntantiator types, e.g. ReflectionEntityInstantiator, and your application domain type has more than 1 non-default constructor.
Therefore, you can register 1 or more EntityInstantiator objects per application domain object by type using the EntityIntantiatiors composite class, perhaps with a "mapping" between application domain object Class type (e.g. Page) and EntityInstantiator, and then register the EntityInstantiators on SDG's MappingPdxSerializer.
Of course, you need to make sure that custom configured MappingPdxSerializer gets used by Apache Geode...
#Configuration
class ApacheGeodeConfiration {
#Bean
MappingPdxSerializer pdxSerializer() {
Map<Class<?>, EntityInstantiator> customInstantiators = new HashMap<>();
customInstantiators.put(Page.class, new MyPageEntityInstantiator());
customInstantiators.put...
MappingPdxSerializer pdxSerializer =
MappingPdxSerializer.newMappingPdxSerializer();
pdxSerializer.setGemfireInstantiators(
new EntityInstantiators(customInstantiators));
return pdxSerializer;
}
#Bean
CacheFactoryBean gemfireCache(MappingPdxSerializer pdxSerializer) {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setPdxSerializer(pdxSerializer);
gemfireCache.set...
return gemfireCache;
}
...
}
Hope this helps!
-j

throw ConverterNotFoundException using spring-cloud-consul for int property

I got the following error message when starting a spring boot/cloud application which gets some properties from consul:
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target demo.config.Config#5b4954b2 failed:
Property: config.number
Value: ${config.number:5}
Reason: Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'number'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [int]
Action:
Update your application's configuration
Here's my main class:
#SpringBootApplication
#WebAppConfiguration
#EnableDiscoveryClient
#EnableConfigurationProperties
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
And the configuration property class:
#Component
#ConfigurationProperties(prefix="config")
public class Config {
private int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
The application.yml file is:
server:
port: 8100
spring:
application:
name: demo
And the bootstrap.yml file:
spring:
cloud:
consul:
host: localhost
port: 8500
config:
enabled: true
prefix: config
config:
number: ${config.number:5}
Finally the related part of maven pom.xml file:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.SR1</spring-cloud.version>
<cucumber.version>1.2.5</cucumber.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
I found the problem is because PropertySourceBootstrapConfiguration cannot locate property source from consul in some case. If PropertySourceBootstrapConfiguration is properly initiated during the booting up, there's no such exception and the app works as expected.

Could not autowire. There is more than one bean of 'DataSource' type

I'm trying to Autowire a database by
#Autowired
private DataSource dataSource;
I have one datasource in my application.yml
spring:
profiles:
active: dev
---
spring:
profiles: dev
datasource:
driverClassName: org.mariadb.jdbc.Driver
url: jdbc:mariadb://localhost:3306/dbname
username: user
password: password
name: dev
logging:
level:
org.springframework: INFO
---
spring:
profiles: prod
name: prod
logging:
level:
org.springframework: INFO
But I get an error message saying.
Could not autowire. There is more than one bean of 'DataSource' type.
Beans:dataSource (DataSourceConfiguration.class)
dataSource (DataSourceConfiguration.class)
Which I find strange since I only have one datasource defined in my application.yml and to my knowledge I don't have any other datasource defined.
I gave a try with a config but I still get the same issue.
#Configuration
#EnableConfigurationProperties
public class AppConfig {
#Bean
#ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
This is my 'pom' file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>id.group</groupId>
<artifactId>ProjectName</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<!--Spring Boot dependencies-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--Spring Boot dependencies-->
<!--Spring Security dependencies-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--Spring Security dependencies-->
<!--JWT dependencies-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<!--JWT dependencies-->
<!--Actuator and HAL browser dependencies-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-hal-browser</artifactId>
</dependency>
<!--Actuator and HAL browser dependencies-->
<!--Database dependencies-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>1.5.7</version>
</dependency>
<!--Database dependencies-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
I'm using Spring Boot 1.5.2 and IntelliJ 2017.1
Try this it worked for me, use #Primary like this
#Primary
#Bean(name ="prodDataSource")
#ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "prodJdbc")
public JdbcTemplate prodJdbcTemplate(#Qualifier("prodDataSource") DataSource prodDataSource){
return new JdbcTemplate(prodJdbcTemplate);
}
#Bean(name = "devDataSource")
#ConfigurationProperties(prefix = "spring.datasource.dev")
public DataSource devDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "devJdbc")
public JdbcTemplate devJdbcTemplate(#Qualifier("devDataSource") DataSource devDataSource) {
return new JdbcTemplate(devDataSource);
}
And then use #autowire like this
#Autowired
#Qualifier("prodJdbc")
private JdbcTemplate prodJdbcTemplate;
I hope this can help you or someone else :)
Please note that Spring Boot autoconfigured beans are not supported 100% yet, see https://youtrack.jetbrains.com/issue/IDEA-139669 for progress and possible workaorunds.
I solved by adding qualifier above the property:
#Autowired
#Qualifier("dataSource")
private DataSource dataSource;

Spring Cloud Config client not loading the value from config server

I am facing below issue while I try to run my Spring Cloud Config Client:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'DATABASE_NAME' in string value "${DATABASE_NAME}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204
My dependencies in POM.xml are as below:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config</artifactId>
<version>1.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
The bootstrap.yml
spring:
application:
name: my-config-client
cloud:
services:
registrationMethod: route
config:
enabled: true
uri: http://localhost:${config.port:8888}
The application.yml is as below:
# HTTP Server
server:
port: 2222
# Spring properties
spring:
profiles:
active: dev
#Disable HTTP Basic Authentication
security:
basic:
enabled: false
The class where I am trying to access the property is as below:
#RefreshScope
#Component
public class MyProperty {
#Value("${DATABASE_NAME}")
private String databaseName;
public String getDatabaseName() {
return databaseName;
}
}
My config server is running fine. When I use this url on browser http://localhost:8888/configserver/dev, It gives the below result:
{
"name":"configserver",
"profiles":[
"dev"
],
"label":"master",
"version":"c991526a93fb776e37e18e138c7485d894d6ea4f",
"propertySources":[
{
"name":"https://onestash.abc.com/scm/kapmol/microservice-config-repo.git/configserver.properties",
"source":{
"DATABASE_NAME":"ABC",
"CONVERT_USERS":"Y",
"LRDS_JNDI_NAME":"jdbc/tds_new"
}
}
]
}
I tried with all the posts who were facing this issue. But, it is not working for me. May be, I am missing some points. If anybody can provide help, it would be great.
Thanks
There are some breaking changes with the new spring cloud module read more: here.
Bootstrap, provided by spring-cloud-commons, is no longer enabled by
default. If your project requires it, it can be re-enabled by
properties or by a new starter.
To re-enable by properties set spring.cloud.bootstrap.enabled=true or spring.config.use-legacy-processing=true. These need to be set as
an environment variable, java system property or a command line
argument.
The other option is to include the new spring-cloud-starter-bootstrap
It worked for me by adding these dependencies:
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>2020.0.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
I referred this post answered by 'spencergibb' and able to resolve the issue. I added "spring.config.name" in my bootstrap.yml file of client application and resolved the issue. Now, my bootstrap.yml look like as below:
spring:
application:
name: my-config-client
cloud:
services:
registrationMethod: route
config:
name: configserver
enabled: true
uri: http://localhost:${config.port:8888}

Resources