JUnit 5 tests for a sub project without a SpringBootApplication - java

In particular I have a subproject which belongs to a Multi-Project-Build.
The subproject has no SpringBootApplication of its own.
After some research I found out that in ClassPath #SpringBootConfiguration is searched for
But now I can't get any further because the DataSource that is configured in the application.yml is not found.
The following Exception is thrown:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'de.acme.storage.DBConnectionTest': Unsatisfied dependency expressed through field 'dataSource'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#javax.inject.Inject()}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
...
My expectation was to see the output: DataSource: HikariDataSource (HikariPool-1) as explained in a guide about Spring Boot 2 and Hikari.
However, this guide has been satisfied with a "test" in the SpringBootApplication class.
Where's my fault?
The structure of the subproject is as follows:
src
+---main
| \---java
| | \---de
| | \---acme
| | \---storage
| | +---model
| | | User.java
| | |
| | \---mysql
| | UserDAOImpl.java
|
\---test
+---java
| \---de
| \---acme
| \---storage
| DBConnectionTest.java
| TestConfig.java
|
\---resources
application.yml
The build.gradle of the subproject:
plugins {
id 'org.springframework.boot'
}
apply plugin: 'io.spring.dependency-management'
dependencies {
implementation project(':api'),
'javax.inject:javax.inject:1',
'org.springframework.boot:spring-boot-starter-data-jpa'
runtime 'mysql:mysql-connector-java'
}
The build.gradle in the main project contains this dependencies section:
dependencies {
implementation 'org.slf4j:slf4j-api'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'junit', module: 'junit' //by both name and group
}
testImplementation 'org.junit.jupiter:junit-jupiter-api',
'org.junit.jupiter:junit-jupiter-params'
testRuntime 'org.junit.jupiter:junit-jupiter-engine'
}
The application.yml content:
spring:
datasource:
url: jdbc:mysql://localhost:13306/testdb
username: test-user
password: geheim
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
format_sql: true
id:
new_generator_mappings: false
The JUnit test:
#SpringBootTest
class DBConnectionTest {
private static final Logger LOG = LoggerFactory.getLogger(DBConnectionTest.class);
#Inject
private DataSource dataSource;
#Test
void test() {
LOG.debug("DataSource: {}", dataSource);
assertThat(dataSource).as("Es gibt eine Datasource").isNotNull();
}
}
And finally the empty configuration with #SpringBootConfiguration annotation:
package de.acme.storage;
import org.springframework.boot.SpringBootConfiguration;
#SpringBootConfiguration
class TestConfig {
}

There was just one little thing missing, the annotation #EnableAutoConfiguration. Thought this would be done by #SpringBootConfiguration...
package de.acme.storage;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
#SpringBootConfiguration
#EnableAutoConfiguration
class TestConfig {
}

Related

Cannot resolve symbol 'EnableJpaRespositories' even though specified in build.gradle

For some reason, I'm unable to get a module within my project to detect that it has the dependencies in place to pick up #EnableJpaRepositories. Despite having implementation 'org.springframework.boot:spring-boot-starter-data-jpa' specified in my build.gradle file, when I run gradle to try and compile, I get the following error:
./gradlew clean build
> Task :rest:compileJava FAILED
/Users/pasdeignan/git/pasciifinance/rest/src/main/java/com/pasciitools/pasciifinance/rest/PasciifinanceApplication.java:22: error: cannot find symbol
#EnableJpaRespositories(basePackages="com.pasciitools.pasciifinance")
^
symbol: class EnableJpaRespositories
1 error
FAILURE: Build failed with an exception.
Background
I have a multi-module Spring project that I'm building where I'm trying to store the JPA pieces in a common module and the REST services in a rest module. The rest module is also where my SpringBootApplication lives. When I start up the application, I get the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field accountRepo in com.pasciitools.pasciifinance.rest.PasciifinanceApplication required a bean of type 'com.pasciitools.pasciifinance.common.repositories.AccountRepository' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.pasciitools.pasciifinance.common.repositories.AccountRepository' in your configuration.
Project Structure
pasciifinance
└── build.gradle (empty)
└── settings.gradle
└── common
└── build.gradle
└── src
└── main/java/com/pasciitools/pasciifinance/common
└── entity
└── repository
└── AccountRepository.java
└── rest
└── build.gradle
└── src
└── main/java/com/pasciitools/pasciifinance/rest
└── PasciiFinanceApplication.java
└── restservice
└── RestService.java
PasciiFinanceApplication.java
package com.pasciitools.pasciifinance.rest;
import com.pasciitools.pasciifinance.common.entity.Account;
import com.pasciitools.pasciifinance.common.entity.AccountEntry;
import com.pasciitools.pasciifinance.common.repository.AccountEntryRepository;
import com.pasciitools.pasciifinance.common.repository.AccountRepository;
import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.List;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#EnableEncryptableProperties
#EnableJpaRespositories(basePackages="com.pasciitools.pasciifinance")
#SpringBootApplication(scanBasePackages = "com.pasciitools.pasciifinance")
public class PasciifinanceApplication {
#Autowired
private AccountRepository accountRepo;
#Autowired
private AccountEntryRepository entryRepo;
private static final Logger log = LoggerFactory.getLogger(PasciifinanceApplication.class);
public static void main(String[] args) {
SpringApplication.run(PasciifinanceApplication.class, args);
}
}
build.gradle (rest module)
plugins {
id 'org.springframework.boot' version '2.4.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.pascii-tools'
version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation project(':common')
developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'org.apache.poi:poi:5.0.0'
implementation 'org.apache.poi:poi-ooxml:5.0.0'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.seleniumhq.selenium:selenium-java:3.141.59'
implementation 'com.github.ulisesbocchio:jasypt-spring-boot:3.0.3'
implementation 'org.springframework.boot:spring-boot-starter-batch'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
runtimeOnly 'com.h2database:h2'
}
test {
useJUnitPlatform()
}
build.gradle (common)
plugins {
id 'org.springframework.boot' version '2.4.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.pascii-tools'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 11
repositories {
mavenCentral()
}
bootJar {
enabled = false
}
jar {
enabled = true
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
You have a typo in #EnableJpaRespositories.
You'll need to rename it to #EnableJpaRepositories, as you've asked in your question.

Unable to start web server; nested exception is Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean

Related Project is here (actual Code is from fixThisSpring branch):
https://github.com/sekassel/CoronaTrackerEsp32/tree/master/server
Similar Question, but no Solution worked out for me:
ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean
The main in a nutshell:
#SpringBootApplication
public class Main extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
I build a small Vaadin App with Springboot and it works local.
If i try to build a Dockerfile and run it, it crashes:
19.2.2021 13:14:30 __ __ ____ _ _
19.2.2021 13:14:30| \/ | _ _ | _ \ _ __ ___ (_) ___ ___ | |_
19.2.2021 13:14:30| |\/| || | | | | |_) || '__| / _ \ | | / _ \ / __|| __|
19.2.2021 13:14:30| | | || |_| | | __/ | | | (_) | | || __/| (__ | |_
19.2.2021 13:14:30|_| |_| \__, | |_| |_| \___/ _/ | \___| \___| \__|
19.2.2021 13:14:30 |___/ |__/
19.2.2021 13:14:30
19.2.2021 13:14:30[pool-2-thread-1] INFO org.springframework.boot.SpringApplication - Starting application using Java 12-ea on accda21c508b with PID 1 (started by root in /server)
19.2.2021 13:14:30[pool-2-thread-1] INFO org.springframework.boot.SpringApplication - No active profile set, falling back to default profiles: default
19.2.2021 13:14:30[pool-2-thread-1] WARN org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
19.2.2021 13:14:30[pool-2-thread-1] ERROR org.springframework.boot.SpringApplication - Application run failed
19.2.2021 13:14:30org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
I would guess it would be something dependency related?
So here the build.gradle:
plugins {
id 'org.springframework.boot' version '2.4.2'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'com.vaadin' version '0.14.3.7'
id 'java'
id 'com.github.johnrengelman.shadow' version '5.2.0'
}
group 'projekt'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
ext {
set('vaadinVersion', "14.4.6")
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile "com.sparkjava:spark-core:2.8.0"
compile 'org.slf4j:slf4j-simple:1.7.21'
compile 'org.json:json:20200518'
compileOnly 'org.projectlombok:lombok:1.18.12'
annotationProcessor 'org.projectlombok:lombok:1.18.8'
compile 'com.fasterxml.jackson.core:jackson-core:2.11.0'
compile 'com.fasterxml.jackson.core:jackson-databind:2.11.0'
compile 'com.google.protobuf:protobuf-java:3.12.2'
compile 'at.favre.lib:hkdf:1.1.0'
compile group: 'org.xerial', name: 'sqlite-jdbc', version: '3.32.3.2'
// Vaadin
implementation 'com.vaadin:vaadin-spring-boot-starter'
implementation 'com.vaadin:vaadin-avatar-flow:1.0.1'
}
configurations {
all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
exclude group: 'ch.qos.logback', module: 'logback-classic'
exclude group: 'org.apache.logging.log4j', module: 'log4j-to-slf4j'
}
}
dependencyManagement {
imports {
mavenBom "com.vaadin:vaadin-bom:${vaadinVersion}"
}
}
test {
useJUnitPlatform()
}
bootJar {
mainClassName = 'Main'
}
jar {
manifest {
attributes 'Main-Class': 'Main'
}
}
And Dockerfile:
FROM gradle:5.6.4-jdk11 as builder
COPY --chown=gradle:gradle . /server
WORKDIR /server
RUN gradle shadowJar
FROM openjdk:8-jdk-alpine
WORKDIR /server
COPY --from=builder /server/build/libs .
EXPOSE 4567 8080
CMD ["java", "-jar", "server-1.0-SNAPSHOT-all.jar"]
Update:
I changed the Dockerfile to: RUN gradle bootJar
Now Spring start up, but vaadin seems to have a problem to find .vaadin/node/node
[pool-2-thread-1] INFO NodeInstaller - Extracting NPM
[pool-2-thread-1] INFO NodeInstaller - Local node installation successful.
[pool-2-thread-1] ERROR dev-updater - Error when running `npm install`
java.io.IOException: Cannot run program "/root/.vaadin/node/node" (in directory "/server"): error=2, No such file or directory
First you have to run bootJar instead of shadowJar. Second you have to build in production mode
RUN gradle bootJar -Pvaadin.productionMode
Please read more about the production mode in the Vaadin documentation
https://vaadin.com/docs/flow/production/tutorial-production-mode-basic.html

import extra project which is one level above of main gradle project file

I have a big project which depends on an another project. My git structure is following:
/gitRepository
|
|--> /ConfigFacade
| |
| |--> /ejbModule/
| | --> META-INF/
| | --> com/myApp/com/name/myClass.java
|--> /myMainApp
| |
| |--> /src/
| | --> main/java/com/name/allJavaFiles.java
| |--> build.gradle
How should I add correctly ConfigFacade project to my gradle file?
I tried something like that but it doesnt work
dependencies {
providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
providedCompile group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '2.3.1'
providedCompile project(':ConfigFacade')
}
How gradle should import another project one level above where gradle file is?
Create a settings.gradle file next to your build.gradle file with the following content:
includeFlat 'ConfigFacade'
That should do the trick. See also the docs of includeFlat.

Spring Boot JPA not finding data source

I have a spring boot application with the following configuration:
Application
#SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class,
DataSourceAutoConfiguration.class,JpaRepositoriesAutoConfiguration.class})
public class Application extends SpringBootServletInitializer {
Main configuration class:
#Configuration
#ComponentScan("com.mycompany.it")
#Import(DatabaseConfiguration.class)
public class Configuration extends WebMvcConfigurationSupport {
Database configuration:
#Configuration
#ComponentScan(basePackages = {"com.mycompany.it.xp2.integration.workday.dao","com.mycompany.it.xp2.integration.workday.application","com.company.it.xp2.integration.workday.model"})
#EnableAspectJAutoProxy
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {"com.company.it.xp2.integration.workday.dao","com.company.it.xp2.integration.workday.model"})
public class DatabaseConfiguration {
With DataSource bean declaration:
#Bean(destroyMethod = "close")
#Primary
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource source = new ComboPooledDataSource();
source.setDriverClass(driverClass);
source.setJdbcUrl(jdbcUrl);
source.setUser(jdbcUsername);
source.setPassword(jdbcPassword);
source.setInitialPoolSize(initialSize);
source.setMaxPoolSize(maxActive);
source.setMinPoolSize(minIdle);
return source;
}
However when I start up the application I get the following error:
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name
'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration':
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'javax.sql.DataSource' available: expected at
least 1 bean which qualifies as autowire candidate. Dependency
annotations: {}
Here are the related lines from gradle:
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-aop'
compile group: 'org.springframework', name: 'spring-orm'
compile group: "org.springframework.boot", name: "spring-boot-starter-jdbc"
compile "org.springframework.boot:spring-boot-starter-data-jpa"
Spring boot version 1.5.3
Check the error log
1 bean which qualifies as autowire candidate. Dependency annotations
It seems that there is an error when you configured the bean.

Is my transaction xml to java config conversion right?

Here's the portion of our xml that is related to transaction management
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice id="txAdvice"/>
<aop:config proxy-target-class="true">
<aop:pointcut id="transactionalMethods"
expression="#annotation(org.springframework.transaction.annotation.Transactional)" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionalMethods" />
</aop:config>
I came up with this
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement( proxyTargetClass = true )
public class TransactionConfig
{
#Bean
PlatformTransactionManager transactionManager( final DataSource dataSource, final EntityManagerFactory emf ) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setDataSource( dataSource );
jpaTransactionManager.setEntityManagerFactory( emf );
return jpaTransactionManager;
}
#Bean
PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor() {
return new PersistenceExceptionTranslationPostProcessor();
}
#Bean
PersistenceAnnotationBeanPostProcessor persistenceAnnotationBeanPostProcessor() {
return new PersistenceAnnotationBeanPostProcessor();
}
}
and it seems to work, but reading the config I can't help but wonder if I'm supposed to use mode = ASPECTJ, but when I tried that the context blew up with an exception about not able to inject "Concrete class", as some things depend on the class and not the interface.
I'm not sure why we added that pointcut, or whether we make use of it, if I have to rewrite it as a pointcut, I'm actually not sure how I should do that.
Do I need to add something more to get parity? is this already the same? if I need to add more what should I write?
update
When trying
#Configuration
#EnableAspectJAutoProxy( proxyTargetClass = true )
#EnableTransactionManagement( mode = AdviceMode.ASPECTJ )
and aspectj itself was working via our xml config (the aspects do work in our xml config)
<context:spring-configured/>
<aop:aspectj-autoproxy/>
with deps
[INFO] +- org.springframework.data:spring-data-jpa:jar:1.9.4.RELEASE:compile
[INFO] | +- org.springframework:spring-orm:jar:4.2.7.RELEASE:compile
[INFO] | +- org.springframework:spring-context:jar:4.2.7.RELEASE:compile
[INFO] | | \- org.springframework:spring-expression:jar:4.2.7.RELEASE:compile
[INFO] | +- org.springframework:spring-aop:jar:4.2.7.RELEASE:compile
[INFO] | | \- aopalliance:aopalliance:jar:1.0:compile
[INFO] | +- org.springframework:spring-beans:jar:4.2.7.RELEASE:compile
[INFO] | +- org.springframework:spring-core:jar:4.2.7.RELEASE:compile
[INFO] | \- org.aspectj:aspectjrt:jar:1.8.9:compile
[INFO] +- org.springframework:spring-aspects:jar:4.2.7.RELEASE:compile
[INFO] +- org.aspectj:aspectjweaver:jar:1.8.9:compile
I get errors like
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.myapp.CatalogItemViewDao com.myapp.ProviderTestHistoryDao.catalogItemViewDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.myapp.CatalogItemViewDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 40 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.myapp.CatalogItemViewDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
No you dont need to set mode = ASPECTJ , unless you expect transaction to be applied on self invocation calls, means calling from another methods in the same class. Other mode proxy which default, will only support calls that coming externally through proxy interfaces.
In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
transaction at runtime even if the invoked method is marked with
#Transactional. Also, the proxy must be fully initialized to provide
the expected behaviour so you should not rely on this feature in your
initialization code,
if you set mode = aspectj , it will use spring AspectJ weaving using byte code modification for affected class and methods. But This will require spring-aspect.jar and CGLIB jars to be present in the class path.
And,
if you are just using #Transactional annotation you don't need define any aop point cuts. You only need to define the transaction manager
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-declarative
If you use ASPECTJ (with spring-aspects-4.x.y.RELEASE.jar), a pointcut definition shouldn't be necessary.
Take a look at aop.xml in the mentioned jar, the AnnotationTransactionAspect is defined there.

Categories