SpringBoot Service Injection using private final not working - spring-boot

I am new to springboot and am trying to follow this example: https://github.com/eugenp/tutorials/tree/master/spring-caching-2
In my app I keep getting "error: variable myApplicationService not initialized in the default constructor" but in comparison to the tutorial I am following, I don't understand how it gets initialized, this is my controller:
package com.springbootredis.controllers;
import com.springbootredis.service.MyApplicationService;
import lombok.AllArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
#RestController
#AllArgsConstructor
#RequestMapping(value = "/person", produces = { MediaType.APPLICATION_JSON_VALUE })
public class PersonController {
private final MyApplicationService myApplicationService;//ERROR HERE
#GetMapping("/uuid")
public String generateRandomUUID() {
return UUID.randomUUID().toString();
}
#GetMapping("/addperson/{name}")
public String addPerson(#PathVariable String name) {
String ret = myApplicationService.addNewPerson(name);
return "Added person with name: " + name + " and id: " + ret;
}
#GetMapping("/deleteperson/{id}")
public String deletePerson(#PathVariable String id) {
String ret = myApplicationService.delete(id);
return "Deleted person. ID:" + id + " Name: " + ret;
}
#GetMapping("/updateperson/{id}/{name}")
public String updatePerson(#PathVariable String id, #PathVariable String name) {
myApplicationService.updatePerson(id, name);
return "Updated person. ID:" + id + " with Name: " + name;
}
#GetMapping("/getperson/{id}")
public String getPerson(#PathVariable String id) {
String ret = myApplicationService.findById(id);
return "Got person. ID:" + id + " Name: " + ret;
}
}
I tried autowired annotation but it says it is not recommended, and the build still fails. My build.gradle looks like this:
plugins {
id 'org.springframework.boot' version '2.7.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'war'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-data-redis:2.7.0")
implementation("org.springframework.boot:spring-boot-starter-cache:2.7.1")
implementation("org.projectlombok:lombok:1.18.24")
implementation("org.springframework.boot:spring-boot-dependencies:2.7.1")
runtimeOnly("mysql:mysql-connector-java:8.0.29")
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
Any help/pointers would be much appreciated.

Edit: (For completeness' sake) As you've already found out, the annotationProcessor entry for Lombok is missing from your build.gradle file. In addition, your Lombok entry can be compileOnly and does not need to be included at runtime.
Original Answer follows.
Your code as-is should still work. As mentioned by #m-deinum, you should also avoid doing manual dependency management on Spring versions.
That said, Lombok does its magic via Annotation Processing, a feature that might not be enabled by default in your IDE Project.
One possible culprit for your error is that this feature is disabled, hence Lombok is not generating the constructor and only a default, no-args constructor is available. Once you enable it, the compile-error should go away.
That said, I've found #AllArgsConstructor to not be very robust when designing classes. Prefer #RequiredArgsConstructor or simply explicit constructors and design your classes to have immutable state.

To resolve my issue I added the following to the build.gradle file under the dependencies section:
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok:1.18.24")

Related

IntelliJ Could not set unknown property 'mainClassName' for root project 'Blue Bot' of type org.gradle.api.Project

I am following a tutorial to make a basic Discord Bot, (https://medium.com/discord-bots/making-a-basic-discord-bot-with-java-834949008c2b) and i got the error, Could not set unknown property 'mainClassName' for root project 'Blue Bot' of type org.gradle.api.Project and I don't know how to fix it. Here is my build.grade code
plugins {
id 'java'
}
mainClassName = "Main"
group 'BlueBot'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
jcenter()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile 'net.dv8tion:JDA:4.0.0_62'
}
and here is my Main.java code
import net.dv8tion.jda.api.AccountType;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import javax.security.auth.login.LoginException;
public class Main extends ListenerAdapter {
public static void main(String[] args) throws LoginException {
JDABuilder builder = new JDABuilder(AccountType.BOT);
String token = "enter token here";
builder.setToken(token);
builder.addEventListeners(new Main());
builder.build();
}
#Override
public void onMessageReceived(MessageReceivedEvent event) {
System.out.println("We received a message from " +
event.getAuthor().getName() + ": " +
event.getMessage().getContentDisplay()
);
if (event.getMessage().getContentRaw().equals("I am lonely")) {
event.getChannel().sendMessage("Who isn't?").queue();
}
}
}
Let me know if you need more info.
You need to apply the application plugin:
plugins {
id 'application'
}

NoSuchMethodException QueryDSL with Spring Boot & Spring Data Mongo

I am trying to implement Query DSL on my Spring Boot 2.0.4.RELEASE app that uses Spring Data Mongo 2.0.4.RELEASE & Gradle 4.10.
I am using Spring Tool Suite for running it locally.
Did the following steps which I found from multiple sources including Spring data documentation:
created gradle/querydsl.gradle which has below content to generate Q classes
apply plugin: "com.ewerk.gradle.plugins.querydsl"
sourceSets {
main {
java {
srcDir "$buildDir/generated/source/apt/main"
}
}
}
querydsl {
springDataMongo = true
querydslSourcesDir = "$buildDir/generated/source/apt/main"
}
dependencies {
compile "com.querydsl:querydsl-mongodb:4.1.4"
compileOnly "com.querydsl:querydsl-apt:4.1.4"
}
sourceSets.main.java.srcDirs = ['src/main/java']
Calling above gradle file from main build.gradle as shown below
buildscript {
ext { springBootVersion = "2.0.4.RELEASE" }
repositories { mavenCentral() }
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath "gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.9"
}
}
plugins {
id "java"
id "eclipse"
id "org.springframework.boot" version "2.0.4.RELEASE"
id "io.spring.dependency-management" version "1.0.6.RELEASE"
}
sourceCompatibility = 1.8
repositories { mavenCentral() }
dependencies {
...
compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-data-mongodb:${springBootVersion}")
...
}
apply from: 'gradle/querydsl.gradle'
/* Added this because Eclipse was not able to find generated classes */
sourceSets.main.java.srcDirs = ['build/generated/source/apt/main','src/main/java']
compileJava.dependsOn processResources
processResources.dependsOn cleanResources
After this updated the Repository annotated interface as below. Note: I also use Fragment Repository FragmentOrderRepository for some custom queries.
public interface OrderRepository<D extends OrderDAO>
extends EntityRepository<D>, PagingAndSortingRepository<D, String>, FragmentOrderRepository<D>, QuerydslPredicateExecutor<D> {}
Then in controller created a GET mapping as shown here
#RestController
public class OrderController {
#GetMapping(value="/orders/dsl", produces = { "application/json" })
public ResponseEntity<List> getOrdersDSL(#QuerydslPredicate(root = OrderDAO.class) Predicate predicate, Pageable pageable, #RequestParam final MultiValueMap<String, String> parameters) {
return (ResponseEntity<List>) orderService.getTools().getRepository().findAll(predicate, pageable);
}
}
Then in my runner class I added EnableSpringDataWebSupport annotation
#SpringBootApplication
#EnableSpringDataWebSupport
public class SampleApp {
public static void main(String[] args) {
SpringApplication.run(SampleApp.class, args);
}
}
With this my app starts up without any errors but when I try hitting the path http://localhost:5057/orders/dsl?email=test#test.com
I get a NoSuchMethodException with message No primary or default constructor found for interface com.querydsl.core.types.Predicate.
Can anyone please help with some pointers to solve this issue?
It seems that parameters are not getting resolved to a type.
---- UPDATE 09/19/19 ----
While debugging I found that a class HandlerMethodArgumentResolverComposite which finds ArgumentResolver for given MethodParameter from a List of argumentResolvers(of type HandlerMethodArgumentResolver). This list does not contain QuerydslPredicateArgumentResolver. Hence it is not able to resolve the arguments.
This means QuerydslWebConfiguration which adds above resolver for Predicate type is not getting called, which in turn indicates that some AutoConfiguration is not happening.
Probably I am missing some annotation here.
Found the mistake I was doing, was missing EnableWebMvc annotation on my Configuration annotated class.
Details are in this documentation.

Circular view path [error] while json output expected in springboot

newbie to Springboot with gradle, I am creating a restful service which queries the db2 database and returns the result in json.
The desired output
{
resource: {
results: [
{
currencyCode: "JPY",
conversionRateToUSD: "0.010286580",
conversionRateFromUSD: "97.214040040",
startDate: "2011-01-01",
endDate: "2011-01-29"
}
]
}
}
The api i am trying to build is http://localhost:8080/apis/exchange-rates/referenceDate=2015-01-01&currencyCode=JPY
I have created the below controller class
#RestController
#Slf4j
#RequestMapping("/apis")
public class IndividualExchangeRateController {
#Autowired
private IndividualExchangeRateService individualExchangeRateService;
public IndividualExchangeRateController(IndividualExchangeRateService individualExchangeRateService) {
this.individualExchangeRateService = individualExchangeRateService;
}
#RequestMapping(value = "/exchange-rates", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public #ResponseBody IndividualResource getIndividual(#RequestParam("referenceDate") #DateTimeFormat(pattern = "YYYY-MM-DD")Date referenceDate,
#RequestParam(value = "currencyCode", required = false) String currencyCode){
try {
System.out.println("Inside Controller");
return individualExchangeRateService.getIndividualExchangeRate(referenceDate, currencyCode);
}
catch (HttpClientErrorException e){
throw new InvalidRequestException(e.getMessage());
}
}
}
I am getting the below error when i call the api
javax.servlet.ServletException: Circular view path [error]: would dispatch back to the current handler URL [/error] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
Can anybody help out on this ?
As the output is json i do not have thymeleaf dependencies on my application
Below is the gradle build file
plugins {
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.abc.service'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
maven { url "http://artifactory.abcinc.dev/artifactory/maven-repos" }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
compileOnly 'org.projectlombok:lombok:1.18.8'
annotationProcessor 'org.projectlombok:lombok:1.18.8'
runtime 'com.ibm.db2.jcc:db2jcc4:4.19.49'
compile group: 'org.springframework', name: 'spring-jdbc', version: '5.1.9.RELEASE'
compile group: 'com.zaxxer', name: 'HikariCP', version: '3.3.1'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.9'
}
public class IndividualExchangeRate {
private String currencyCode;
private double conversionRateFromUSD;
private double conversionRateToUSD;
}
public class IndividualResource {
private List<IndividualExchangeRate> individualExchangeRates;
}
All the classes are annotated with lombok.
Spring Boot uses a default Whitelabel error page in case server error.So there might be some code snippets which is breaking your appliaction flow.
Add server.error.whitelabel.enabled=false in your application.properties file .
OR add the following code snippets :
#Controller
public class AppErrorController implements ErrorController{
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public String error() {
return "Error handling";
}
#Override
public String getErrorPath() {
return PATH;
}
}

How to persist enums as ordinals with Spring Boot and Cassandra?

To the enum field of my entity I have added #CassandraType(type = DataType.Name.INT). However not the ordinal of the enum, but the string representation instead, is used in the statement sent to Cassandra. Thus I get the following error:
org.springframework.data.cassandra.CassandraInvalidQueryException: SessionCallback; CQL [INSERT INTO thing (thing_id,some_enum) VALUES (1,'Foo');]; Expected 4 or 0 byte int (3); nested exception is com.datastax.driver.core.exceptions.InvalidQueryException: Expected 4 or 0 byte int (3)
Below you can find a minimal example, reproducing the problem.
What am I doing wrong?
test/src/main/kotlin/enumtest/Application.kt
package enumtest
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
#SpringBootApplication
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
test/src/main/kotlin/enumtest/SomeEnum.kt
package enumtest
enum class SomeEnum {
Foo,
Bar
}
test/src/main/kotlin/enumtest/Thing.kt
package enumtest
import com.datastax.driver.core.DataType
import org.springframework.data.cassandra.core.cql.PrimaryKeyType
import org.springframework.data.cassandra.core.mapping.CassandraType
import org.springframework.data.cassandra.core.mapping.Column
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn
import org.springframework.data.cassandra.core.mapping.Table
#Table("thing")
#Suppress("unused")
class Thing(
#PrimaryKeyColumn(name = "thing_id", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
var thingId: Long,
#CassandraType(type = DataType.Name.INT)
#Column("some_enum")
var someEnum: SomeEnum
)
test/src/main/kotlin/enumtest/ThingRepository.kt
package enumtest
import org.springframework.data.cassandra.repository.CassandraRepository
import org.springframework.stereotype.Repository
#Repository
interface ThingRepository : CassandraRepository<Thing, Long>
test/src/main/resources/application.yml
spring:
data:
cassandra:
contact-points: localhost
port: 9142
keyspace_name: enumtest
test/src/test/kotlin/enumtest/PersistenceTest.kt
package enumtest
import org.cassandraunit.spring.CassandraDataSet
import org.cassandraunit.spring.CassandraUnitDependencyInjectionTestExecutionListener
import org.cassandraunit.spring.EmbeddedCassandra
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.TestExecutionListeners
import org.springframework.test.context.junit4.SpringRunner
#RunWith(SpringRunner::class)
#SpringBootTest
#TestExecutionListeners(
listeners = [CassandraUnitDependencyInjectionTestExecutionListener::class],
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
#CassandraDataSet(value = ["cql/cassandra_schema.cql"], keyspace = "enumtest")
#EmbeddedCassandra
class PersistenceTest {
#Autowired
lateinit var thingRepository: ThingRepository
#Test
fun `test save`() {
thingRepository.save(Thing(1, SomeEnum.Foo))
val things = thingRepository.findAll()
Assert.assertEquals(1, things.size)
val thing = things[0]
Assert.assertEquals(SomeEnum.Foo, thing.someEnum)
}
}
test/src/test/resources/cql/cassandra_schema.cql
CREATE KEYSPACE IF NOT exists enumtest
WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':1};
CREATE TABLE IF NOT exists enumtest.thing (
thing_id bigint,
some_enum int,
PRIMARY KEY (thing_id)
);
test/build.gradle
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'org.jetbrains.kotlin.jvm' version '1.3.30'
id 'org.jetbrains.kotlin.plugin.spring' version '1.3.30'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
maven { url "https://repository.apache.org/snapshots/" }
}
dependencies {
implementation group: 'org.springframework.boot', name: 'spring-boot-starter'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-cassandra'
implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8'
implementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect'
testImplementation group: 'org.cassandraunit', name: 'cassandra-unit-spring', version: '3.5.0.1'
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
}
compileKotlin {
kotlinOptions {
freeCompilerArgs = ['-Xjsr305=strict']
jvmTarget = '1.8'
}
}
compileTestKotlin {
kotlinOptions {
freeCompilerArgs = ['-Xjsr305=strict']
jvmTarget = '1.8'
}
}
Here is the full version of the minimal example as a download to faciliate experimentation: https://drive.google.com/open?id=1zzIDhbWycaj4WXrze2sAmw8xRPacA8Js
Edit: Since it seems to be a bug, I just opened a Jira issue.
I've been trying to get this working for quite awhile and it seems I finally got it!
I was running into the same issue you were with the codec...I have no idea why that's not working. According to their documentation you were doing it exactly right.
So I implemented my own Cassandra Write Converter. See below
#Configuration
class CassandraConfig(val cluster: Cluster){
#Bean
fun setCustomCassandraConversions() = CassandraCustomConversions(listOf(EnumWriteConverter.INSTANCE, EnumReadConverter.INSTANCE))
#WritingConverter
enum class EnumWriteConverter : Converter<Enum<MyEnum>, Int> {
INSTANCE;
override fun convert(source: Enum<MyEnum>) = source.ordinal
}
#ReadingConverter
enum class EnumReadConverter : Converter<Int, Enum<MyEnum>> {
INSTANCE;
override fun convert(source: Int) = MyEnum.values()[source]
}
}
This should on every write you do to Cassandra convert all enums it sees of type MyEnum to an Int using the overridden converter. This opens you up to the possibility of having multiple of these for different types of Enums where maybe for some reason you would like to write other custom values from them instead of always converting all enums.
Hope this works!
EDIT
Note the change in removing { } for INSTANCE on each converter, and registering the ReadingConverter with the CassandraCustomConversions
This is fixed since Spring Boot version 2.1.5.
However, the #CassandraType needs to be placed at the getter explicitly in Kotlin, because otherwise it is not seen at runtime.
In practice this simply means replacing this:
#CassandraType(type = DataType.Name.INT)
var someEnum: SomeEnum
with that:
#get: CassandraType(type = DataType.Name.INT)
var someEnum: SomeEnum

Why Spring #Service methods appear with 0% code coverage in JaCoCo?

These libraries are loaded:
JUnit 5.3.2
JaCoCo 0.8.2
Mockito 2.10.0
Only element "static {...}" appears with 100% coverage. All the rest is at 0%:
The unit test class has annotations #ExtendWith(SpringExtension.class) and #AutoConfigureMockMvc. The service is injected with #Mock.
doReturn(actual).when(service).get(param);
when(service.get(param)).thenReturn(actual);
expected = service.get(param);
verify(service, times(1)).get(param);
assertEquals(expected, actual);
assertEquals(actual, expected);
My ServiceImpl class is red when I click any method. It extends an abstract class. Jackson's ObjectMapper is red, and also the entire lines within the methods. For example:
public CustomReturnObject get(final CustomParamObject paramObject) {
try {
return retryTemplate.execute(status -> {
String json = repository.get(paramObject);
CustomReturnObject returnObject = json2CustomObject(json, paramObject);
if (returnObject == null) {
returnObject = new CustomReturnObject();
returnObject.setId(paramObject.getId());
}
return returnObject;
});
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
return null;
}
}
Similarly to https://stackoverflow.com/a/46614216/244993 let's put aside Spring, because there is IMO clearly something wrong with your expectations/understanding about core thing here - mocking.
By
doReturn(actual).when(service).get(param);
expected = service.get(param);
verify(service, times(1)).get(param);
assertEquals(expected, actual);
you are not testing get method, you are testing something that always returns actual, no matter what is actually written in get, because in this case it is not executed.
Here is complete example as a proof:
src/main/java/hello/GreetingService.java:
package hello;
class GreetingService {
Object get(Object param) {
throw new UnsupportedOperationException();
}
}
src/test/java/hello/GreetingServiceTest.java:
package hello;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
#ExtendWith(MockitoExtension.class)
public class GreetingServiceTest {
#Mock
public GreetingService service;
#Test
void test() {
Object param = new Object();
Object actual = new Object();
doReturn(actual).when(service).get(param);
Object expected = service.get(param);
verify(service, Mockito.times(1)).get(param);
assertEquals(expected, actual);
}
}
build.gradle :
apply plugin: 'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testCompile 'org.mockito:mockito-junit-jupiter:2.23.4'
}
Real method get throws UnsupportedOperationException, however above test succeeds, so real method was not executed. As another way to proof that get not executed: put a breakpoint into it and execute test in debug mode from IDE - breakpoint won't be reached.
Coverage shows what was really executed and hence absolutely correct that it is zero for methods that are not executed.

Resources