How to make both validation annotations in a ConfigurationProperties bean and a #FeignClient interface work together? - spring-boot

Let's say I have this application.yml (which will be environment-dependent e.g. via Spring profiles):
app.remote:
url: http://whatever.url.it.is:8080/
and matching Java-style configuration properties class:
#Configuration
#ConfigurationProperties("app.remote")
public class MyRemoteProperties {
#NotBlank
private String url;
// matching getter/setter...
}
I want some kind of client for my remote url:
#Service
#FeignClient(value = "remote", url = "${app.remote.url}")
public interface MyRemote {
#GetMapping("/what/ever/rest/api")
String stuff();
}
Unfortunately I can't get the validation work for MyRemoteProperties e.g. when the app.remote.url property is blank (empty) the application doesn't start (Spring fails at wiring the MyRemote bean) and I get this error:
Caused by: java.lang.IllegalStateException: No Feign Client for
loadBalancing defined. Did you forget to include
spring-cloud-starter-netflix-ribbon?
(and I don't want load-balancing; I assume this is because the URL is empty at some point, then it expects some load-balancer config hence Ribbon here in the error message).
Or maybe I don't known how to plug it into the MyRemote interface's configuration, e.g. I also tried:
#FeignClient(value = "remote", configuration = MyRemoteProperties.class)
But same result.
How do I get this validation thing to work?
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
At some point where the interface is called:
#Service
public RandomServiceOrController {
#Autowired
private MyRemote myRemote;
public void processMyStuff() {
// ...
String myStuff = myRemote.stuff();
// ...
}
}

Don't forget the #Validated annotation on your Java properties class:
#Validated
#Configuration
#ConfigurationProperties("app.remote")
public class MyRemoteProperties {
#NotBlank
private String url;
// matching getter/setter...
}
Your application won't start because of the missing property, not because of a non-defined-loadbalancing-client-you-don't-need (thus making its error message more awkward).

Related

Error when adding custom revision in Hibernate envers

When I add custom revision entity, I start getting error:
2020-12-13 00:22:29.418 ERROR 80983 --- [ost-startStop-1] o.s.b.web.embedded.tomcat.TomcatStarter : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'webSecurityConfig': Unsatisfied dependency expressed through field 'userDetailsService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userDetailsServiceImpl': Unsatisfied dependency expressed through field 'userRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Cannot create inner bean '(inner bean)#4384acd' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#4384acd': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: org/hibernate/resource/beans/spi/ManagedBeanRegistry
MyRevision:
package ...;
import org.hibernate.envers.DefaultRevisionEntity;
import org.hibernate.envers.RevisionEntity;
import javax.persistence.Entity;
#Entity
#RevisionEntity(MyRevisionListener.class)
public class MyRevision extends DefaultRevisionEntity {
private String username;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
}
MyRevisionListener:
package ...;
// import de.xxxxx.carorderprocess.models.User;
import org.hibernate.envers.RevisionListener;
// import org.springframework.security.core.Authentication;
// import org.springframework.security.core.context.SecurityContext;
// import org.springframework.security.core.context.SecurityContextHolder;
// import java.util.Optional;
public class MyRevisionListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
/* String currentUser = Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getPrincipal)
.map(User.class::cast)
.map(User::getUsername)
.orElse("Unknown-User"); */
MyRevision audit = (MyRevision) revisionEntity;
audit.setUsername("dd");
}
}
WebSecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsServiceImpl userDetailsService;
UserDetailsServiceImpl:
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
return UserDetailsImpl.build(user);
}
}
UserRepository:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Boolean existsByUsername(String username);
}
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 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.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>de.xxxxxxx</groupId>
<artifactId>carorderprocess</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>carorderprocess</name>
<description>Demo project for Spring Boot</description>
<dependencyManagement>
<dependencies>
</dependencies>
</dependencyManagement>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</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-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</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-jdbc</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
<version>5.4.25.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
I think your problem could be related with the different dependencies in your pom.xml.
Please, first, remove the spring-data-envers dependency, unless you are querying your audit tables you do not need it. Even in that case, you can use Envers on its own to obtain that information if required.
Be aware that, as indicated in the comments of the answer from Sunit, you will need to remove the attribute repositoryFactoryBeanClass, it could not longer take the value EnversRevisionRepositoryFactoryBean. But you probably still need to include the #EnableJpaRepositories annotation.
Although I initially indicated that you can let Spring Boot manage your versions, due to the one of spring-boot-starter-parent, the framework is providing you versions of hibernate-xxx similar to 5.2.17.Final.
But, as you indicated, you need to use the method forRevisionsOfEntityWithChanges for querying your audit entities. As you can see in the java docs, that method was introduced in AuditQueryCreator in version 5.3.
As a consequence, you need to provide the following dependency:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
<version>5.3.20.Final</version>
</dependency>
But in addition you also need to provide a compatible version of both hibernate-entitymanager and hibernate-core:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.3.20.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.20.Final</version>
</dependency>
From what I understood from all the comments above, your requirement is
to use Envers Auditing
and use method forRevisionsOfEntityWithChanges to get list of all revisions with what changed in them
Please start by doing these
Remove dependency of spring-data-envers library.
Just keep library hibernate-envers - version 5.4.23.Final also worked for me
Remove repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class from #EnableJpaRepositories annotation
All Repository classes should only extend from JpaRespository and NOT from RevisionRepository. You dont need RevisionRepository
You should be able to get your application up and running now.
Now coming back to the question, how to get all revisions with changes using forRevisionsOfEntityWithChanges method.
Create an AuditConfiguration class like this, to create the AuditReader bean
#Configuration
public class AuditConfiguration {
private final EntityManagerFactory entityManagerFactory;
AuditConfiguration(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
#Bean
AuditReader auditReader() {
return AuditReaderFactory.get(entityManagerFactory.createEntityManager());
}
}
In your AuditRevisionEntity class, add following annotation. Without this the serialization of this class wont work. e.g
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class AuditRevisionEntity extends DefaultRevisionEntity {
In your entity class add option withModifiedFlag = true to #Audited annotation. Without this you cannot get entity revisions with all changes. e.g
#Audited(withModifiedFlag = true)
public class Customer {
Modify your database table for this entity audit table and fields *_mod. e.g if you have a customer table with fields name, age, address columns, then add columns name_mod, age_mod, address_mod to the customer_audit table
Last, add following code in your service method to get audit revisions with changes
#Autowired
private AuditReader auditReader;
public List<?> getRevisions(Long id) {
AuditQuery auditQuery = auditReader.createQuery()
.forRevisionsOfEntityWithChanges(Customer.class, true)
.add(AuditEntity.id().eq(id));
return auditQuery.getResultList();
}
I will try to post the same code in Github sometime today, so that you can take a look at working code.
Your code looks fine. But it may not be sufficient to identify the root cause.
Looking at the exception it is clear that application is failing since it is not able to find bean dependency
Could you try following
Check your library imports first in your build.gradle or pom.xml. Generally you should not require any other Hibernate library other than Spring Boot Data JPA and Hibernate Envers
Try removing/disabling the Hibernate Envers audit code and library dependencies and see if can you get your application up and running. This will help you identify if error is due to Hibernate Envers or if your application code has other issues.
If above does not works, then please provide more information
Which version of Spring Boot are you on
What libraries have you imported (build.gradle or maven pom file)
What other Configurations you have in your project - do you have any other JPA configuration file or any other custom configuration related to Hibernate or JPA
What annotations are on the main application class
Directory structure of your Repository class, and the directory on which you do component scan (in case you have overridden it)

log4j2 java classes do not load application.properties entries with spring boot

I am using log4j2(log4j-core and log4j-api) with Spring boot 2.2.0.RELEASE. I want to masking sensitive data inmy json payload in the logs. I have written a custom LogMaskingConverter which extends LogEventPatternConverter.
#Plugin(name = "LogMaskingConverter", category = "Converter")
#ConverterKeys({ "spi", "trscId" })
#Configuration
public class LogMaskingConverter extends LogEventPatternConverter {
#Value("${maskingFields}")
private String maskingFields;
protected LogMaskingConverter(String name, String style) {
super(name, style);
}
public static LogMaskingConverter newInstance(String[] options) {
return new LogMaskingConverter("spi", Thread.currentThread().getName());
}
#Override
public void format(LogEvent event, StringBuilder outputMessage) {
........
}
private String mask(String message) {
....
}
The problem is that the maskingFields does not get loaded from the application properties file.I read that log4j2 gets loaded before spring boot. Hence I have renamed my xml to log4j2-spring.xml so that spring boot is the first to intialise and load all the properties before it gets read via LogMaskingConverter.
Also below are my pom.xml dependencies related to log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
To summarise below code does not fetch any values:
#Value("${maskingFields}")
private String maskingFields;

Difficulty to configure Activiti into a Springboot application api

First of all is it a viable thing to embed Activiti into an API type application for use within that application or should Activiti be run standalone?
The error below is due to bean definition but I'm not sure where the beans should be defined and how - if thats correct approach for version 6. Our standards with Springhboot 2 is to annotate beans in java rather than xml context
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-04-10 21:17:43.924 ERROR 19516 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Field runtimeService in ma.cvmeeting.workflow.WorkflowApplication$MyrestController required a bean of type 'org.activiti.engine.RuntimeService' 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 'org.activiti.engine.RuntimeService' in your configuration.
Process finished with exit code 0
code:
import org.activiti.engine.RuntimeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
public class WorkflowApplication {
public static void main(String[] args) {
SpringApplication.run(WorkflowApplication.class, args);
}
#RestController
public static class MyrestController{
#Autowired
private RuntimeService runtimeService;
#GetMapping("/start-process")
public String startProcess() {
runtimeService.startProcessInstanceByKey("Potulerauneoffre");
return "Process started. Number of currently running"
+ "process instances = "
+ runtimeService.createProcessInstanceQuery().count();
}
}
pom.xml:
<project>
<groupId>ma.cvmeeting</groupId>
<artifactId>workflow</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>workflow</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>7-201802-EA</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.h2database</groupId>
<artifactId>h2database</artifactId>
<version>1.0.20061217</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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
There are two ways to initialize the engine when you embed it in your spring based application:
1.) let spring initialize it for you so you can use all the engine services right away without need of any configuration. this requires activiti-spring-boot-starter as dependency.
2.) You initialize engine by your self and provide the services beans from #Configuration class. for this you will require only activiti-engine core as dependency
The reason your application cannot find the RuntimeService because you are trying the second approach add the below dependency in your pom.xml and remove the engine one
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
</dependency>
you should follow documentation for more help.
We recommend activiti 7 core if you are planning to use spring boot 2.x and the use of the new APIs. This is great time if you want to get involved with the new APIs and project initiatives
You could write a #Configuration class and define Activiti services, like this :
#Configuration
public class ActivityConfig {
#Autowired
DataSource dataSource;
#Bean
public DataSourceTransactionManager getTransactionManager() {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public ProcessEngineConfigurationImpl getProcessEngineConfiguration() {
SpringProcessEngineConfiguration res = new SpringProcessEngineConfiguration();
res.setDataSource(dataSource);
res.setTransactionManager(getTransactionManager());
return res;
}
#Bean
public ProcessEngineFactoryBean getProcessEngine() {
ProcessEngineFactoryBean res = new ProcessEngineFactoryBean();
res.setProcessEngineConfiguration(getProcessEngineConfiguration());
return res;
}
#Bean
public RepositoryService getRepositoryService() throws Exception {
return getProcessEngine().getObject().getRepositoryService();
}
#Bean
public FormService getFormService() throws Exception {
return getProcessEngine().getObject().getFormService();
}
#Bean
public TaskService getTaskService() throws Exception {
return getProcessEngine().getObject().getTaskService();
}
#Bean
public RuntimeService getRuntimeService() throws Exception {
return getProcessEngine().getObject().getRuntimeService();
}
#Bean
public HistoryService getHistoryService() throws Exception {
return getProcessEngine().getObject().getHistoryService();
}
#Bean
public IdentityService getIdentityService() throws Exception {
return getProcessEngine().getObject().getIdentityService();
}
}

Error creating bean with name 'gemfireCache': FactoryBean threw exception on object creation

I am trying to create an "employee" Region and put some data into it. But, I am getting Exception below:
[warn 2018/12/27 17:15:46.518 IST tid=0x1] Exception
encountered during context initialization - cancelling refresh
attempt: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'gemfireConfiguration': Injection of
resource dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'gemfireCache': FactoryBean threw exception on
object creation; nested exception is java.lang.NoClassDefFoundError:
it/unimi/dsi/fastutil/ints/Int2ObjectOpenHashMap
[warn 2018/12/27 17:15:46.519 IST tid=0x1] Invocation of
destroy method failed on bean with name 'gemfireCache':
org.apache.geode.cache.CacheClosedException: A cache has not yet been
created.
[error 2018/12/27 17:15:46.522 IST tid=0x1] Caught exception
while allowing TestExecutionListener
[org.springframework.test.context.web.ServletTestExecutionListener#c667f46]
to prepare test instance
[com.gemfire.demo.Gemfire1ApplicationTests#48bfb884]
Domain class
#Region("employee")
public class Employee {
#Id
public String name;
public double salary;
...
}
Repository class
#Repository
public interface EmployeeRepository extends CrudRepository<Employee, String> {
Employee findByName(String name);
}
Configuration class
#Configuration
#ComponentScan
#EnableGemfireRepositories(basePackages = "com.gemfire.demo")
public class GemfireConfiguration {
#Autowired
EmployeeRepository employeeRepository;
#Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", "SpringDataGemFireApplication");
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", "config");
return gemfireProperties;
}
#Bean
#Autowired
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
#Bean(name="employee")
#Autowired
LocalRegionFactoryBean<String, Employee> getEmployee(final GemFireCache cache) {
LocalRegionFactoryBean<String, Employee> employeeRegion = new LocalRegionFactoryBean<String, Employee>();
employeeRegion.setCache(cache);
employeeRegion.setClose(false);
employeeRegion.setName("employee");
employeeRegion.setPersistent(false);
employeeRegion.setDataPolicy(DataPolicy.PRELOADED);
return employeeRegion;
}
}
POM.XML
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</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.data</groupId>
<artifactId>spring-data-gemfire</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.0</version>
</dependency>
Adding additional tips with your above GemFire/Spring JavaConfig configuration class above.
Given you are using Spring Data Kay (implied by your use of the Spring Boot 2.0.x parent POM, i.e. org.springframework.boot:spring-boot-dependencies; see here), then you could be using Spring Data GemFire's (relatively) new and convenient Annotation-based configuration model.
By doing so, your GemfireConfiguration class above would become...
#PeerCacheApplication
#EnableGemfireRepositories(basePackages = "com.gemfire.demo")
class GemfireConfiguration {
#Bean(name="employee")
LocalRegionFactoryBean<String, Employee> getEmployee(GemFireCache cache) {
LocalRegionFactoryBean<String, Employee> employeeRegion =
new LocalRegionFactoryBean<String, Employee>();
employeeRegion.setCache(cache);
employeeRegion.setClose(false);
employeeRegion.setDataPolicy(DataPolicy.PRELOADED);
return employeeRegion;
}
}
A few things to keep in mind:
#PeerCacheApplication is meta-annotated with #Configuration so you do not need the explicit Spring #Configuration annotation on the configuration class.
#PeerCacheApplication allows you to adjust the GemFire log-level (along with other logging configuration) using the logLevel annotation attribute. Similarly, you can set the log-level using the corresponding property, spring.data.gemfire.cache.log-level in a Spring Boot application.properties file (see here). There are many other attributes and corresponding properties (e.g. name) you can use to adjust and customize other configuration.
While String-based package names are supported on #EnableGemfireRepositories and similar annotations, we generally prefer and recommend users to use the type-safe variant basePacakgeClasses. You only need to refer to a single type from each top-level package where your application Repositories are kept.
The explicit #Autowired annotation is not needed on your bean definitions. You do not need to explicit inject the EmployeeRepository in the configuration class to have it initialized; just inject it into the #Service class where it will be used.
For convenience, the name ("employee") of the Region bean definition on your LOCAL "employee" Region, will also be used as the name of the Region, so employeeRegion.setName("employee") is unnecessary.
You should not combine LocalRegionFactoryBean.setPersistent(:boolean) with LocalRegionFactoryBean.setDataPolicy(:DataPolicy) since the DataPolicy is going to take precedence.
While #ComponentScan is perfectly acceptable and even convenient in development, I generally do not prefer nor recommend users to use component-scanning. It is usually always better to be explicit.
As stated in the comments, you chould remove <relativePath/> from your parent definition in your application Maven POM file.
Final note, as of this post, Spring Boot 2.0.8.RELEASE is the latest release.
As for your classpath issues, if you are using Maven correctly, then Maven should take care of pulling in the correct transitive dependencies.
You can refer to the many examples I have in this repo for further clarification.
Hope this helps!
As mentioned in comments, the error shows some dependencies (java.lang.NoClassDefFoundError: it/unimi/dsi/fastutil/ints/Int2ObjectOpenHashMap) are missing. Please add corresponding dependencies in your pom.xml

SpringBoot 2 + Junit5: null with #Value

I have an application with SpringBoot2 and Junit5, and now I'm trying to make a test. I have a this class called OrderService that looks like this:
#Component
public class OrderService {
#Value("#{'${food.requires.box}'.split(',')}")
private List<String> foodRequiresBox;
#Value("#{'${properties.prioritization}'.split(',')}")
private List<String> prioritizationProperties;
#Value("${further.distance}")
private Integer slotMeterRange;
#Value("${slot.meters.long}")
private Double slotMetersLong;
As you can see, the class has many #Value annotations that extracts values from application.properties file.
In the POM file I have these dependences:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.0.5.RELEASE</version>
</dependency>
Tn the test/resources folder I have the application.properties file with this information:
properties.prioritization:vip,food
food.requires.box:pizza,cake,flamingo
further.distance:2
slot.meters.long:0.5
The test file looks like this:
#ExtendWith(SpringExtension.class)
#TestPropertySource(locations="classpath:application.properties")
public class OrderServiceTest {
OrderService orderService;
#BeforeEach
void before(){
orderService = new OrderService();
}
#Test
void findAll() {
Order order = new Order().withDescription("2x Pizza with Salad\\n2x Kebab with Fries\\n1x Hot dog with Fries\\n2x Pizza with Fries");
assertTrue(orderService.orderHasFood.test(order));
}
}
But the test throws NullPointerException when it tries to use foodRequiresBox, so there is a problem to read the application.properties file.
Could you tell how can I read the application.properties file for the tests?
1st Solution
I would recommend to use Spring's internal annotation called #SpringJUnitConfig
This annotation is actually the same as #ExtendWith(SpringExtension.class) BUT you can configure your spring application contexts for your test in the same way you used to use #ContextConfiguration.
Or if you want a full Spring Boot Test you could just combine:
#SpringJUnitConfig
#SpringBootTest
public class OrderServiceTest {
...
}
2nd Solution
Another way is to not use Spring at all, but mock all the internal stuff with e.g. Mockito and write a plain simple Unit Test.
You could then set your normally via Spring injected annotated #Value fields via org.springframework.test.util.ReflectionTestUtils.
I'd recommend using org.springframework.test.util.ReflectionTestUtils (as indicated in #mrkernelpanic second solution) using the setField() method, in order to avoid initializing the full Spring context.
Here is a snippet:
// ReflexionTestUtils.setField(targetObject, "targetFieldName", valueToInject);
ReflexionTestUtils.setField(orderService, "foodRequiresBox", "pizza,cake,flamingo");

Resources