Spring Boot R2DBC with MySQL - Exception: Table not found - java

I'm extremely new to String Boot and backend development (maybe three days or less) and I have the desire to build REST API to consume from different clients.
So I started by a simple demo app that has an endpoint called /register. We post a JSON string with username and password to create a new user if not exist.
I was using JPA with HSQLDB and it worked fine persisting on memory. But recently I wanted to use RxJava since I'm familiar with on Android, so I switched to R2DBC with MySQL.
MySQL server is running fine on port 3306 and the app was tested using PostMan on localhost:8080
The problem occurs when I try to query users table or insert entities and it looks like this:
{
"timestamp": "2020-03-22T11:54:43.466+0000",
"status": 500,
"error": "Internal Server Error",
"message": "execute; bad SQL grammar [UPDATE user_entity SET username = $1, password = $2 WHERE user_entity.id = $3]; nested exception is io.r2dbc.spi.R2dbcBadGrammarException: [42102] [42S02] Table \"USER_ENTITY\" not found; SQL statement:\nUPDATE user_entity SET username = $1, password = $2 WHERE user_entity.id = $3 [42102-200]",
"path": "/register"
}
Here's the full logfile for the exception.
I Have been looking for a solution for hours and I seem like not finding it anywhere, so I hope that I will find it here.
Let's break down the project so it's easier to find the solution:
1. database:
2. application.properties:
logging.level.org.springframework.data.r2dbc=DEBUG
spring.datasource.url=jdbc:mysql://localhost:3306/demodb
spring.datasource.username=root
spring.datasource.password=root
3. DatabaseConfiguration:
#Configuration
#EnableR2dbcRepositories
class DatabaseConfiguration : AbstractR2dbcConfiguration() {
override fun connectionFactory(): ConnectionFactory
= ConnectionFactories.get(
builder().option(DRIVER, "mysql")
.option(HOST, "localhost")
.option(USER, "root")
.option(PASSWORD, "root")
.option(DATABASE, "demodb")
.build()
)
}
4. RegistrationController:
#RequestMapping("/register")
#RestController
class RegistrationController #Autowired constructor(private val userService: UserService) {
#PostMapping
fun login(#RequestBody registrationRequest: RegistrationRequest): Single<ResponseEntity<String>>
= userService.userExists(registrationRequest.username)
.flatMap { exists -> handleUserExistance(exists, registrationRequest) }
private fun handleUserExistance(exists: Boolean, registrationRequest: RegistrationRequest): Single<ResponseEntity<String>>
= if (exists) Single.just(ResponseEntity("Username already exists. Please try an other one", HttpStatus.CONFLICT))
else userService.insert(User(registrationRequest.username, registrationRequest.password)).map { user ->
ResponseEntity("User was successfully created with the id: ${user.id}", HttpStatus.CREATED)
}
}
5. UserService:
#Service
class UserService #Autowired constructor(override val repository: IRxUserRepository) : RxSimpleService<User, UserEntity>(repository) {
override val converter: EntityConverter<User, UserEntity> = UserEntity.Converter
fun userExists(username: String): Single<Boolean>
= repository.existsByUsername(username)
}
6. RxSimpleService:
abstract class RxSimpleService<T, E>(protected open val repository: RxJava2CrudRepository<E, Long>) {
protected abstract val converter: EntityConverter<T, E>
open fun insert(model: T): Single<T>
= repository.save(converter.fromModel(model))
.map(converter::toModel)
open fun get(id: Long): Maybe<T>
= repository.findById(id)
.map(converter::toModel)
open fun getAll(): Single<ArrayList<T>>
= repository.findAll()
.toList()
.map(converter::toModels)
open fun delete(model: T): Completable
= repository.delete(converter.fromModel(model))
}
7. RxUserRepository:
#Repository
interface IRxUserRepository : RxJava2CrudRepository<UserEntity, Long> {
#Query("SELECT CASE WHEN EXISTS ( SELECT * FROM ${UserEntity.TABLE_NAME} WHERE username = :username) THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END")
fun existsByUsername(username: String): Single<Boolean>
}
8. And finally, here's my UserEntity
#Table(TABLE_NAME)
data class UserEntity(
#Id
val id: Long,
val username: String,
val password: String
) {
companion object {
const val TABLE_NAME = "user_entity"
}
object Converter : EntityConverter<User, UserEntity> {
override fun fromModel(model: User): UserEntity
= with(model) { UserEntity(id, username, password) }
override fun toModel(entity: UserEntity): User
= with(entity) { User(id, username, password) }
}
}
User and RegistrationRequest are just simple objects with username and password.
What I have missed?
Please leave a comment if you need more code.

I finally managed to solve this mistake!
The problems were so simple yet so sneaky for a beginner:
I was using JDBC in my URL instead of R2DBC
I was using the H2 runtime implementation so it was
expecting an H2 in-memory database
My ConnectionFactory was not very correct
So what I did was the following:
Updated my build.gradle:
Added:
implementation("io.r2dbc:r2dbc-pool") , implementation("dev.miku:r2dbc-mysql:0.8.1.RELEASE") and
runtimeOnly("mysql:mysql-connector-java")
Removed: runtimeOnly("io.r2dbc:r2dbc-h2")
It now looks like this:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.2.5.RELEASE"
id("io.spring.dependency-management") version "1.0.9.RELEASE"
kotlin("jvm") version "1.3.61"
kotlin("plugin.spring") version "1.3.61"
}
group = "com.tamimattafi.backend"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
maven(url = "https://repo.spring.io/milestone")
}
dependencies {
//SPRING BOOT
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot.experimental:spring-boot-starter-data-r2dbc")
//KOTLIN
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
//RX JAVA
implementation("io.reactivex.rxjava2:rxjava:2.2.0")
implementation("io.reactivex:rxjava-reactive-streams:1.2.1")
//MYSQL
implementation("dev.miku:r2dbc-mysql:0.8.1.RELEASE")
implementation("io.r2dbc:r2dbc-pool")
runtimeOnly("mysql:mysql-connector-java")
//TEST
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
testImplementation("org.springframework.security:spring-security-test")
testImplementation("io.projectreactor:reactor-test")
testImplementation("org.springframework.boot.experimental:spring-boot-test-autoconfigure-r2dbc")
}
dependencyManagement {
imports {
mavenBom("org.springframework.boot.experimental:spring-boot-bom-r2dbc:0.1.0.M3")
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "1.8"
}
}
Updated my application.properties to this:
spring.r2dbc.url=r2dbc:pool:mysql://127.0.0.1:3306/demodb
spring.r2dbc.username=root
spring.r2dbc.password=root
Updated my DatabaseConfiguration to this (Note that I removed the #EnableR2dbcRepositories because it should be elsewhere) :
#Configuration
class DatabaseConfiguration : AbstractR2dbcConfiguration() {
override fun connectionFactory(): ConnectionFactory
= MySqlConnectionFactory.from(
MySqlConnectionConfiguration.builder()
.host("127.0.0.1")
.username("root")
.port(3306)
.password("root")
.database("demodb")
.connectTimeout(Duration.ofSeconds(3))
.useServerPrepareStatement()
.build()
)
}
Updated my Application class (I brought the annotation here):
#SpringBootApplication
#EnableR2dbcRepositories
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
IT WORKS NOW! I hope someone will find this helpful, Happy Coding!

In application.properties you need to set the spring.jpa.hibernate.ddl-auto property.
The options are:
validate: validate the schema, makes no changes to the database.
update: update the schema.
create: creates the schema, destroying previous data.
create-drop: drop the schema when the SessionFactory is closed explicitly, typically when the application is stopped.
none: does nothing with the schema, makes no changes to the database

Related

Gradle-jooq-plugin task generateJooq only generates information_schema classes from my H2 DBMS , but not my created ones with Liquibase

I am creating Spring Boot project and using Liquibase, JOOQ , Gradle, Java 17 and H2 DBMS. I want to generate classes from my database and I am using gradle-jooq-plugin for that. Every time I try to to use task :generateJooq I get information_schema generated instead of my classes from Liquibase:
My build.gradle file looks like this:
plugins {
java
id("org.springframework.boot") version "3.0.2"
id("io.spring.dependency-management") version "1.1.0"
id("nu.studer.jooq") version "8.0"
}
buildscript {
configurations["classpath"].resolutionStrategy.eachDependency {
if (requested.group == "org.jooq") {
useVersion("3.16.3")
}
}
dependencies {
}
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-jooq")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.liquibase:liquibase-core")
runtimeOnly("com.h2database:h2")
jooqGenerator("com.h2database:h2")
testImplementation("org.springframework.boot:spring-boot-starter-test")
implementation("org.yaml:snakeyaml:1.28")
implementation("org.jooq:jooq-meta-extensions-liquibase")
implementation("org.slf4j:slf4j-jdk14:1.7.30")
implementation("org.jooq:jooq-codegen:3.16.3")
}
tasks.withType<Test> {
useJUnitPlatform()
}
jooq {
version.set("3.16.3")
edition.set(nu.studer.gradle.jooq.JooqEdition.OSS)
configurations {
create("main") {
generateSchemaSourceOnCompilation.set(true)
jooqConfiguration.apply {
jdbc.apply {
driver = "org.h2.Driver"
url = "jdbc:h2:~/jooqtest"
user = "db"
password = "db"
}
generator.apply {
name = "org.jooq.codegen.DefaultGenerator"
database.apply {
name = "org.jooq.meta.h2.H2Database"
forcedTypes.addAll(listOf(
org.jooq.meta.jaxb.ForcedType().apply {
name = "varchar"
includeExpression = ".*"
includeTypes = "JSONB?"
},
org.jooq.meta.jaxb.ForcedType().apply {
name = "varchar"
includeExpression = ".*"
includeTypes = "INET"
}
))
}
generate.apply {
isDeprecated = false
isRecords = true
isImmutablePojos = true
isFluentSetters = true
}
target.apply {
packageName = "com.example.main.db"
}
}
}
}
}
}
And my application.properties file looks like this:
spring.h2.console.enabled=true
spring.h2.console.path=/h2
spring.datasource.username=db
spring.datasource.password=db
spring.datasource.url=jdbc:h2:mem:jooqtest
spring.datasource.driver-class-name=org.h2.Driver
spring.liquibase.change-log=classpath:/db/changelog/db.changelog-master.yaml
I would be very thankful if someone could help me out.
As documented in the jOOQ code generation manual and also throughout the third party plugin documentation, you have to specify an inputSchema if you don't want the code generator to generate all schemas.
Specifically:
database.apply {
name = "org.jooq.meta.h2.H2Database"
inputSchema = "PUBLIC"
...
}

Micronaut - No bean of type [io.micronaut.transaction.SynchronousTransactionManager] exists

I'm migrating one of my Microservices from Spring to Micronaut and this is a problem I'm facing while calling a controller to load one entity. The error I get is:
{
"message": "Internal Server Error: No bean of type [io.micronaut.transaction.SynchronousTransactionManager] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor)."
}
I already enabled the io.micronaut.context.condition logger, setting it to TRACE level, but I see no reference of the class SynchronousTransactionManager in the logs.
This is my build.gradle:
buildscript {
repositories {
jcenter()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
maven {
url 'https://jitpack.io'
}
}
dependencies {
classpath "org.hibernate:hibernate-gradle-plugin:5.4.12.Final"
}
}
plugins {
id("com.github.johnrengelman.shadow") version "6.1.0"
id("io.micronaut.application") version "1.3.3"
id('com.google.protobuf') version '0.8.11'
id('java')
id('idea')
}
apply plugin: 'org.hibernate.orm'
hibernate {
enhance {
enableLazyInitialization = true
enableDirtyTracking = true
enableAssociationManagement = true
}
}
version = "0.1"
group = "com.elevenstars.service"
repositories {
mavenCentral()
jcenter()
}
micronaut {
runtime("netty")
testRuntime("junit5")
processing {
incremental(true)
annotations("com.elevenstars.service.*")
}
}
dependencies {
// Annotation processors - Order does matter!!
annotationProcessor("org.mapstruct:mapstruct-processor:1.4.1.Final")
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("io.micronaut.data:micronaut-data-processor")
annotationProcessor('io.micronaut:micronaut-inject-java')
annotationProcessor("io.micronaut:micronaut-graal")
annotationProcessor("io.micronaut.security:micronaut-security-annotations")
// Micronaut libs
implementation("io.micronaut:micronaut-validation")
implementation("io.micronaut:micronaut-runtime")
implementation("io.micronaut:micronaut-inject")
implementation("io.micronaut.xml:micronaut-jackson-xml")
implementation("javax.annotation:javax.annotation-api")
compileOnly("org.graalvm.nativeimage:svm")
// Kafka
implementation("io.micronaut.kafka:micronaut-kafka")
// Consul
implementation("io.micronaut:micronaut-discovery-client")
// Security
implementation("io.micronaut.security:micronaut-security-annotations")
implementation("io.micronaut.security:micronaut-security-jwt")
// Database access
implementation("io.micronaut.data:micronaut-data-hibernate-jpa")
implementation("io.micronaut.beanvalidation:micronaut-hibernate-validator")
implementation('com.vladmihalcea:hibernate-types-52:2.9.7')
implementation "org.hibernate:hibernate-graalvm:5.4.12.Final"
runtimeOnly('org.postgresql:postgresql')
//Mapping
implementation 'org.mapstruct:mapstruct:1.4.1.Final'
// gRPC
implementation 'io.grpc:grpc-protobuf:1.33.0'
implementation 'io.grpc:grpc-stub:1.33.0'
implementation 'io.grpc:grpc-netty:1.33.0'
implementation 'io.grpc:grpc-core:1.33.0'
implementation('io.grpc:grpc-netty-shaded:1.33.0') {
version {
strictly "1.33.0"
}
because "fails to run"
}
// Protobuf
implementation 'com.google.protobuf:protobuf-java-util:3.11.4'
// Dev tools
compileOnly 'org.projectlombok:lombok'
// Other stuff
implementation("io.micronaut:micronaut-http-client")
runtimeOnly("ch.qos.logback:logback-classic")
}
application {
mainClass.set("com.elevenstars.service.facilities.Application")
}
java {
sourceCompatibility = JavaVersion.toVersion("11")
targetCompatibility = JavaVersion.toVersion("11")
}
task mapStruct() {
sourceSets.main.java.srcDir file("${buildDir}/mappers")
tasks.withType(JavaCompile) {
options.setAnnotationProcessorGeneratedSourcesDirectory(file("${buildDir}/mappers"))
options.compilerArgs << "-Amapstruct.defaultComponentModel=jsr330"
}
}
compileJava.dependsOn(mapStruct)
This is my application.yml
micronaut:
application:
name: facilities
security:
authentication: bearer
token:
jwt:
signatures:
secret:
generator:
secret: my-secret
bearer:
enabled: true
enabled: true
redirect:
unauthorized:
enabled: false
server:
context-path: /v1/facilities
port: 8080
cors:
enabled: true
kafka:
bootstrap:
servers: localhost:9092
embedded:
enabled: false
jackson:
# time-zone: UTC
serialization:
write-dates-as-timestamps: false
deserialization:
fail-on-unknown-properties: false
generator:
write-numbers-as-strings: false
time-zone: UTC
consul:
client:
registration:
enabled: true
defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"
jpa:
default:
entity-scan:
packages: 'com.elevenstars.service.facilities.domain.entity'
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
jdbc:
time_zone: UTC
# ddl-auto: update
show-sql: false
open-in-view: false
datasource:
default:
url: jdbc:postgresql://localhost:5432/facilities
username: *hidden*
password: *hidden*
Controller:
#Controller
#Secured(SecurityRule.IS_AUTHENTICATED)
public class GymController extends BaseController
{
private final GymReconstructionService gymReconstructionService;
private final PatrimonyMapper patrimonyMapper;
#Inject
public GymController(
GymReconstructionService gymReconstructionService,
PatrimonyMapper patrimonyMapper
) {
this.gymReconstructionService = gymReconstructionService;
this.patrimonyMapper = patrimonyMapper;
}
#Get("/gym/upgrade/estimation")
public HttpResponse<ReconstructionEstimation> getUpgradeReconstructionEstimation() {
return HttpResponse.ok(gymReconstructionService.getUpgradeEstimations());
}
}
Application Service
#Singleton
#Transactional
public class GymReconstructionService extends ApplicationService
{
private final GymRepository gymRepository;
private final DebitMoneyClient debitMoneyClient;
private final LoggedUser loggedUser;
#Inject
public GymReconstructionService(
GymRepository gymRepository,
DebitMoneyClient debitMoneyClient,
LoggedUser loggedUser
) {
this.loggedUser = loggedUser;
this.gymRepository = gymRepository;
this.debitMoneyClient = debitMoneyClient;
}
public ReconstructionEstimation getUpgradeEstimations() {
var gym = getGym(loggedUser.getClubId());
return gym.getUpgradeReconstructionEstimation();
}
private Gym getGym(UUID clubId) throws GymNotFoundException {
return gymRepository
.findById(clubId)
.orElseThrow(GymNotFoundException::new);
}
}
One thing I've realized was if I remove the #Transactional annotation, this error doesn't happen anymore, but I end up with another error:
{
"message": "Internal Server Error: No backing RepositoryOperations configured for repository. Check your configuration and try again"
}
Any clue here about what's going on?
I have solved a similar problem.
Try adding the following to your application.yml
jpa.default.properties.hibernate.hbm2ddl.auto: update

Camel : ClassCastException: DefaultMessage cannot be cast to class SnmpMessage

I'm getting the following ClassCastException from a unit test when I try to call testTemplate.sendBody(String, Object) below:
SnmpRoute.kt
.process { exchange ->
val message = exchange.getIn() as SnmpMessage
SnmpRouteTest.kt
#RunWith(CamelSpringBootRunner::class)
#CamelSpringBootTest
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#DisableJmx(false)
#MockEndpoints("log:*")
class SnmpRouteTest {
object SnmpConstants {
const val SNMP_TRAP = "<snmp><entry><oid>1.3.6.1.2.1.1.3.0</oid><value>6 days, 3:44:57.82</value></entry><entry><oid>1.3.6.1.6.3.1.1.4.1.0</oid><value>1.3.6.1.4.1.8072.2.3.0.1</value></entry><entry><oid>1.3.6.1.4.1.8072.2.3.2.1</oid><value>123456</value></entry></snmp>"
}
#Autowired
lateinit var camelContext: CamelContext
#Produce
lateinit var testTemplate: ProducerTemplate
...
...
#Test
#Throws(Exception::class)
fun testSnmpRoute() {
AdviceWithRouteBuilder.adviceWith(camelContext, "CamelSnmpTrapRoute") { routeBuilder -> routeBuilder.replaceFromWith(SnmpConstants.DIRECT_SNMP_ENDPOINT) }
testTemplate.sendBody(SnmpConstants.DIRECT_SNMP_ENDPOINT, SnmpConstants.SNMP_TRAP)
...
}
}
Exception
java.lang.ClassCastException: class org.apache.camel.support.DefaultMessage cannot be cast to class
org.apache.camel.component.snmp.SnmpMessage (org.apache.camel.support.DefaultMessage and org.apache.
camel.component.snmp.SnmpMessage are in unnamed module of loader 'app')
I have tried to construct an SnmpMessage object and use that in the sendBody() call, as when I test this route manually with the snmptrap utility, I see the following in the logs:
Get In[SnmpMessage: <snmp><entry><oid>1.3.6.1.2.1.1.3.0</oid><value>12 days, 8:40:47.70</value></entry><entry><oid>1.3.6.1.6.3.1.1.4.1.0</oid><value>1.3.6.1.4.1.8072.2.3.0.1</value></entry><entry><oid>1.3.6.1.4.1.8072.2.3.2.1</oid><value>123456</value></entry></snmp>]
However I'm getting the same issue with this approach.
I am using Apache Camel v3.0.0-RC3
Thanks to #ShellDragon for help so far with this.
Your processor is casting to a SmppMessage, but your unit test, replaces the consumer (from endpoint) from a smpp to direct component and therefore the message implementation is DefaultMessage.
This is what worked for me. I needed to overwrite the exchange.getIn() message with an SnmpMessage, and add a PDU object, rather than the XML String block.
#CamelSpringBootTest
#SpringBootTest(classes = [SnmpTrapReceiverApplication::class])
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#DisableJmx(false)
#ExtendWith(MockitoExtension::class)
#EnableAutoConfiguration
class SnmpTrapRouteTest {
#MockBean
lateinit var repo: PojoRepo
#Produce
lateinit var producerTemplate: ProducerTemplate
#Autowired
lateinit var camelContext: CamelContext
#Test
#Throws(Exception::class)
fun testSnmpRoute() {
AdviceWithRouteBuilder.adviceWith(camelContext, "snmp-trap-route") { routeBuilder ->
routeBuilder.replaceFromWith("direct:snmp-from")
}
// Create a PDU object to send to the SNMP endpoint, rather than SNMP XML
val trap = PDU()
trap.type = PDU.TRAP
trap.requestID = Integer32(123456789)
trap.add(VariableBinding(OID("1.2.3.4.5"), OctetString("snmp-trap-payload")))
// Create a new DefaultExchange and add an SnmpMessage object as the in-message,
// constructed with the camelContext and the PDU object
val exchange = DefaultExchange(camelContext)
exchange.setIn(SnmpMessage(camelContext, trap))
producerTemplate.setDefaultEndpointUri("direct:snmp-from")
producerTemplate.send(exchange)
verify(repo, atLeast(1)).save(any())
}
}
Since I am using JUnit 5, I changed #RunWith(CamelSpringBootRunner::class) to #CamelSpringBootTest, as after downloading the 3.0.0-RC3 release source, I found a comment saying to do so.

Spring Batch Retry NEVER retries with simple XML configuration

We have a spring batch application developed 2 years back using the old style of XML configuration built using gradle. Simple setup added the job with retries for a custom runtime exception. We don't have the intention to migrate this application to latest annotation based configs as it is running in production now.
Recently discovered that retry is not at all working for some mysterious reason. I tried to upgrade the Spring batch versions locally to the latest but its not helping. Discovered this initially at one of the step calling a #Service class to fetch some data in the #PostContruct, thought it was because of #PostContruct, created an additional step using a Tasklet and tried. I tried all options, the latest change is just tried to throw an java.lang.Exception directly but does not work. Tried #Retryable at the class and method level, never worked, Now I started to wonder, would this retry ever works? I'm giving up on this but desperately needed some help. Anybody who can provide clue or solution to solve this problem would be my Hero! Thank you!
Some code section(Groovy code, some unnecessary Java syntax never used) here(I have cut short some of the methods to show the actual area of concern, but feel free to ask for more config changes) -
dependencies {
testCompile "org.hamcrest:hamcrest-all:1.3"
testCompile "info.cukes:cucumber-groovy:${cukes.version}"
testCompile "info.cukes:cucumber-junit:${cukes.version}"
testCompile "junit:junit:4.11"
testCompile "org.spockframework:spock-core:${spock.version}"
testCompile "org.spockframework:spock-spring:${spock.version}"
compile "org.codehaus.groovy:groovy-all:${groovy.version}"
compile "org.springframework:spring-test:${spring.version}"
compile "org.springframework:spring-core:${spring.version}"
compile "org.springframework.batch:spring-batch-core:4.0.1.RELEASE"
compile "org.springframework.batch:spring-batch-infrastructure:4.0.1.RELEASE"
compile "org.springframework.retry:spring-retry:1.2.2.RELEASE"
compile "org.springframework.batch:spring-batch-test:4.0.1.RELEASE"
compile "org.springframework.data:spring-data-jpa:1.9.0.RELEASE"
compile "org.springframework.data:spring-data-commons:1.9.0.RELEASE"
compile "org.springframework:spring-web:${spring.version}"
...
}
<batch:job id="facebookPermissionsReminderEmailJob" restartable="true">
<batch:step id="businessPageProcess" parent="faultTolerance" next="facebookPermissionsReminderEmailStep">
<tasklet ref="businessPageProcessor" retry-limit="5">
<retryable-exception-classes>
<include class="java.lang.Exception"/>
<include class="com.reachlocal.data.synchronizer.model.ApiException"/>
</retryable-exception-classes>
<batch:retry-listeners>
<batch:listener ref="retryLoggerListener" />
</batch:retry-listeners>
</tasklet>
</batch:step>
<bean id="faultTolerance" class="org.springframework.batch.core.step.item.FaultTolerantStepFactoryBean" abstract="true">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="180000" />
<property name="multiplier" value="2" />
</bean>
</property>
</bean>
#Slf4j
#Scope("step")
#Component("businessPageProcessor")
class BusinessPageProcessor implements Tasklet {
#Autowired
BusinessPagesService businessPagesService
#Autowired
PermissionReminderDetails reminderDetails
Map<String, BusinessPage> nameToPageMap = [:]
Date currentDate
#Override
RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Inside FacebookFirstReminderProcessor.initProcessorDetails() - retrieving facebook Business info")
List<BusinessPage> businessPages = businessPagesService.getAll()
log.info("Business Pages info retrieved from facebook: ${businessPages}")
businessPages.forEach({ page ->
String pageUrl = extractPageName(page.link ? page.link : page.pageResponse?.link)
nameToPageMap.put(pageUrl, page)
})
log.info("Generated Page to Status Map is: ${nameToPageMap}")
currentDate = new Date()
log.info("--------DATE-------- : run date is ${currentDate} for current batch process")
reminderDetails.currentDate = this.currentDate
reminderDetails.nameToPageMap = this.nameToPageMap
RepeatStatus.FINISHED
}
}
#Service
class BusinessPagesService {
#Autowired
PlatformProperty platformProperty
#Autowired
ApiCaller apiCaller
List<BusinessPage> getAll() {
String businessId = platformProperty.facebookReachLocalBusinessId
getAll(businessId)
}
List<BusinessPage> getAll(String businessId) {
List<BusinessPage> businessPages = []
Assert.isTrue(StringUtils.isNotBlank(businessId), EMPTY_BUSINESS_ID_ERROR_MESSAGE)
String approvedPagesEndPoint = enrichApiUrlForGetApprovedPages(businessId)
String pendingPagesEndPoint = enrichApiUrlForGetPendingPages(businessId)
List<BusinessPage> approvedPages = getBusinessPages(businessId, approvedPagesEndPoint)
//FB does not return access info, so manually setting the value for rest of the workflow logic to work as it is.
approvedPages.stream().forEach({page -> page.accessStatus = FacebookPageAccessStatus.CONFIRMED})
List<BusinessPage> pendingPages = getBusinessPages(businessId, pendingPagesEndPoint)
//FB does not return access info, so manually setting the value for rest of the workflow logic to work as it is.
pendingPages.stream().forEach({page -> page.accessStatus = FacebookPageAccessStatus.PENDING})
businessPages.addAll(approvedPages)
businessPages.addAll(pendingPages)
businessPages
}
List<BusinessPage> getBusinessPages(String businessId, String endPointUrl) {
List<BusinessPage> businessPages = []
while (true) {
log.info("Retrieving Business Pages details for Business Id of: ${businessId} using enriched URL of: ${endPointUrl}")
BusinessPagesResponse response = getSubSetOfBusinessPages(endPointUrl)
log.info("Successfully retrieve Business Pages details ${response}")
if(response.businessPages) {
businessPages.addAll(response.businessPages)
endPointUrl = response.paging?.next
} else {
break
}
if (!endPointUrl) {
break
}
}
businessPages
}
BusinessPagesResponse getSubSetOfBusinessPages (String endPointURL) {
BusinessPagesResponse response
try {
response = (BusinessPagesResponse) apiCaller.call(
endPointURL,
new ParameterizedTypeReference<BusinessPagesResponse>() {},
Optional.empty(),
HttpMethod.GET)
throw new Exception("Test Exception")
//log.info("Successfully retrieve Business Pages details ${response}")
} catch (ApiException apiEx){
if (apiEx.message.contains(EXCESS_DATA_REQUESTED_FAILURE_TEXT)) {
log.error("ExcessDataRequestedException occurred - retrying")
String exceptionClass = apiEx.class.name.concat("-")
throw new Exception(exceptionClass.concat(apiEx.message)) // will trigger retry upto defined max.
} else {
throw apiEx
}
}
//response
}
#Component
class RetryLoggerListener extends RetryListenerSupport {
private Logger logger = LoggerFactory.getLogger(this.getClass())
#Override
<T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,Throwable throwable) {
logger.error("Error occurred during operation {}",throwable.getMessage())
}
}

Spring Boot can't run single test in IntelliJ

This started happening recently, but I'm not sure what changed to cause it.
When I run all tests from IntelliJ, all is well. Also the gradle build is fine.
When I run a single unit test, all is well.
When I run a single web integration test, it fails because a config class has all null properties.
The config class looks like (Kotlin):
#Component
#ConfigurationProperties(prefix = "api")
public open class ApiConfigImpl : ApiConfig
{
A test looks like:
#RunWith(SpringJUnit4ClassRunner::class)
#ContextConfiguration(classes = arrayOf(ApplicationAssembly::class), loader = SpringApplicationContextLoader::class)
#WebIntegrationTest
open class CandidateProfileControllerTest
{
#Inject lateinit var profileRepo: CandidateProfileRepository
//etc a few more deps used to setup test data
#Test
open fun getById()
{
val greg = CandidateProfile("123", "12312", "Greg", "Jones", dateOfBirth = Date(), gender = Gender.MALE,
biography = "ABC", maxMatchableAge = null, maxMatchableDistance = null)
profileRepo.save(greg)
val auth = given().header("content-type", "application/json")
.body(testCredentials)
.post("/authorization/social").peek().asString()
val accessToken: String = from(auth).get("accessToken")
given().header("Access-Token", accessToken).
header("API-Key", testAPIKey()).
get("/profile/${greg.id}").
peek().then().
body("stageName", notNullValue())
}
I'm not sure what information I can add. Based on the limited information provided:
Is this a known problem with a known solution?
This is a bug, logged in the IntelliJ/Kotlin tracker, with a pending fix.

Categories

Resources