Spring boot autowired service all variable fields are null - spring

Currently I have many spring boot services annotated with #Service.
Every service works fine and there are no problems. There is a problem only with LogsService. LogsService is implemented the same way as other services such as StaticPageService.
In order to log in a user must use method "onLoginClicked" from LoginViewModel class.
LoginViewModel class:
package com.flyingdynamite.jvmbackend.views.login
import com.flyingdynamite.jvmbackend.data.constants.LogCategory
import com.flyingdynamite.jvmbackend.extensions.isNotNull
import com.flyingdynamite.jvmbackend.security.hash.Hash
import com.flyingdynamite.jvmbackend.service.AdminAuthorizationService
import com.flyingdynamite.jvmbackend.service.LogsService
import com.flyingdynamite.jvmbackend.service.UserModelService
import com.flyingdynamite.jvmbackend.util.SystemInformation
import com.flyingdynamite.jvmbackend.util.base.validation.Argon2PasswordHash
import com.flyingdynamite.jvmbackend.util.generator.ApiAccessCredentialsGenerator
import com.flyingdynamite.jvmbackend.util.generator.TextIdGenerator
import com.flyingdynamite.jvmbackend.views.AdminPanelRoute
import com.flyingdynamite.jvmbackend.views.BaseViewModel
import com.flyingdynamite.jvmbackend.views.dashboard.DashboardView
import kotlinx.coroutines.*
import org.apache.commons.lang3.RandomStringUtils
import org.apache.commons.lang3.time.StopWatch
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.config.ConfigurableBeanFactory
import org.springframework.context.annotation.Scope
import org.springframework.stereotype.Component
import org.springframework.util.unit.DataSize
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*
import java.util.concurrent.TimeUnit
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
open class LoginViewModel : BaseViewModel() {
private val TAG = "LoginViewModel"
#Autowired(required = true)
private lateinit var logsService: LogsService
#Autowired(required = true)
private lateinit var userModelService: UserModelService
#Autowired(required = true)
private lateinit var adminAuthorizationService: AdminAuthorizationService
private var loginJob: Job? = null
private var developerTasksJob: Job? = null
init {
viewModelScope.launch {
setPageTitle("Login")
}
}
override fun onCleared() {
loginJob?.cancel("onCleared")
developerTasksJob?.cancel("onCleared")
}
// #PostConstruct
private fun runScheduledDeveloperTask() {
logger.warn("runScheduledDeveloperTask")
if (productionMode) {
return
}
Timer(TAG).schedule(object : TimerTask() {
override fun run() {
logger.warn("runDeveloperComputationTask")
runDeveloperComputationTask()
}
}, Date.from(LocalDateTime.now().plusHours(6).atZone(ZoneId.systemDefault()).toInstant()))
}
fun onLoginClicked(login: String?, password: String?) {
if (loginJob != null) {
return
}
loginJob = computationViewModelScope.launch {
logsService.enqueue(TAG, LogCategory.INFO, "onLoginClicked / login _> ${login} / password _> ${password}")
println("$TAG -> onLoginClicked / login _> ${login} / password _> ${password}")
setIsLoading(true)
setErrorText(null)
val inputValuesErrorText = inputValuesErrorText(login, password)
println("$TAG -> onLoginClicked / inputValuesErrorText _> ${inputValuesErrorText} / isEmpty _> ${inputValuesErrorText.isEmpty()}")
val authorized = adminAuthorizationService.isAuthorized(login!!, password!!)
val resultError = if (inputValuesErrorText.isNotNull()) {
inputValuesErrorText
} else {
if (authorized) null else localizedTexts.getOrEmpty("wrong_credentials")
}
println("$TAG -> onLoginClicked / resultError _> ${resultError}")
setErrorText(resultError)
delay(100)
setIsLoading(false)
setNavigateTo(
if (authorized) Triple(
DashboardView::class.java,
AdminPanelRoute.DASHBOARD,
userModelService.getUserByUsernameOrThrow(login)
) else null
)
loginJob = null
// setNavigateTo(null)
}
}
userModelService and adminAuthorizationService have no issues.
LogsService class:
package com.flyingdynamite.jvmbackend.service
import com.flyingdynamite.jvmbackend.data.constants.LogCategory
import com.flyingdynamite.jvmbackend.data.model.LogItem
import com.flyingdynamite.jvmbackend.repository.jpa.LogsJpaRepository
import com.flyingdynamite.jvmbackend.service.exception.NotFoundException
import com.flyingdynamite.jvmbackend.util.AppInfo
import com.flyingdynamite.jvmbackend.util.LogUtils
import com.vaadin.flow.server.WebBrowser
import kotlinx.coroutines.*
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.config.ConfigurableBeanFactory
import org.springframework.context.annotation.Scope
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.Duration
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*
import java.util.concurrent.TimeUnit
import javax.persistence.EntityManager
import javax.persistence.EntityManagerFactory
import javax.persistence.PersistenceContext
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.Predicate
import java.util.concurrent.ConcurrentLinkedQueue
#Service
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
open class LogsService : BaseService() {
private val TAG = "LogService"
#Autowired(required = true)
private lateinit var appInfo: AppInfo
#Autowired(required = true)
private lateinit var logRepository: LogsJpaRepository
#Autowired(required = true)
#PersistenceContext
private lateinit var entityManager: EntityManager
#Autowired(required = true)
private lateinit var entityManagerFactory: EntityManagerFactory
private val queue: ConcurrentLinkedQueue<LogItem> = ConcurrentLinkedQueue<LogItem>()
private val cmdLog: Logger by lazy {
LoggerFactory.getLogger(this::class.java)
}
// private val queue: ConcurrentLinkedQueue<LogItem> by lazy {
// ConcurrentLinkedQueue<LogItem>()
// }
private var deleteOlderDays: Int = 30
private var insertMinLogItemsCount: Int = 100
private var insertMaxSecondsDifference: Int = 5
private var isActive: Boolean = true
private var queueJob: Job? = null
private val terminatingMessage: String = "TERMINATING !"
private var displayInCMD: Boolean = true
#Synchronized
fun enableDisplayInCMD() {
displayInCMD = true
if (isActive) {
startQueueThreadIfPossible()
}
}
#Synchronized
fun disableDisplayInCMD() {
displayInCMD = false
if (isActive) {
startQueueThreadIfPossible()
}
}
#Synchronized
fun activate() {
if (displayInCMD) {
cmdLog.trace("activating _ !")
}
isActive = true
startQueueThreadIfPossible()
}
#Synchronized
fun deactivate() {
if (displayInCMD) {
cmdLog.warn("DEACTIVATED _ ! ")
}
onCleared()
}
#Synchronized
fun onCleared() {
isActive = false
queueJob?.cancel("onCleared")
queueJob = serviceScope.launch {
delay(TimeUnit.SECONDS.toMillis(2))
if (displayInCMD) {
cmdLog.warn("inserting queue all remaining items -> ${queue.size} ")
}
insertList(queue.toList())
queue.clear()
queueJob = null
}
}
}
BaseService class:
package com.flyingdynamite.jvmbackend.service
import com.flyingdynamite.jvmbackend.extensions.FD_CPU_INTENSIVE_LIMITLESS
import com.flyingdynamite.jvmbackend.extensions.FD_IO_LIMITLESS
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
open class BaseService() {
protected val serviceScope:CoroutineScope = CoroutineScope(Dispatchers.FD_IO_LIMITLESS)
protected val serviceComputationScope: CoroutineScope = CoroutineScope(Dispatchers.FD_CPU_INTENSIVE_LIMITLESS)
protected fun onException(e: Exception) {
println("[Exception] -> ${e.message}")
e.printStackTrace()
}
}
When user executes onLoginClicked this error occurs:
Exception in thread "pool-2-thread-1" java.lang.NullPointerException
at com.flyingdynamite.jvmbackend.service.LogsService.enqueue(LogsService.kt:171)
at com.flyingdynamite.jvmbackend.service.LogsService.enqueue(LogsService.kt:162)
at com.flyingdynamite.jvmbackend.views.login.LoginViewModel$onLoginClicked$1.invokeSuspend(LoginViewModel.kt:81)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
LogsService.kt:171 -> queue.offer(log) in method enqueue from LogsService class.
If I change the code and execute for example something with serviceScope variable the same error occurs. Then serviceScope is null. Every variable inside LogsService is null when I am trying to use it.
Please help mi with this. Where is the mistake ?

Resolved !
Issue was in the logs but only when logging.level.root=DEBUG was set in application.properties
After long search this was somewhere in the logs:
cannot get proxied via CGLIB: Calls to this method will NOT be routed to the >target instance and might lead to NPEs against uninitialized fields in the >proxy instance.
After adding "open" to every method of LogsService class - log entry is not displaying anymore. Seems it is fixed. Not getting any NPE anymore.

Related

page.nextPageable() endless loop in spring boot

in the code below data is fetched for the first three days without problems, at day 4 the code in the while loop that should fetch the next page always starts with day 2022-03-01 and loops. why?
the code should fetch one page from the database, do a computation on the elements for day 1. In the next run it checks if the already fetched data does contain all data for the new day otherwise it gets new data from the database.
But after day3 the while loop in getMoreData gets stuck and runs in definitively, apparently one java process keeps one core busy all the time even after aborting the eclipse test.
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import com.javainuse.data.EmployeeRepository;
import com.javainuse.model.Employee;
#Component
public class EmployeeController {
#Autowired
private EmployeeRepository employeeData;
private Pageable pageable = PageRequest.of(0, 2);
public void compareData(LocalDateTime start, LocalDateTime end) {
List<Employee> futureDays = new ArrayList<>();
Page<Employee> fromDB = employeeData.findAllByHiringDateGreaterThanEqual(
LocalDateTime.of(2022,3,1, 1,1), pageable);
boolean firstRun = true;
while (true) {
if (start.isAfter(end)) {
break;
}
LocalDateTime tmpEnd = start.plusHours(23).plusMinutes(59).plusSeconds(59);
// works, the commented lines go trough the data and fetch all 5 employees without problems
// Page<Employee> fromDB = employeeData.findAllByHiringDateGreaterThanEqual(
// LocalDateTime.of(2022,3,1, 1,1), pageable);
// while(fromDB.hasNext()) {
// pageable = fromDB.nextPageable();
// fromDB = employeeData.findAllByHiringDateGreaterThanEqual(
// LocalDateTime.of(2022,3,1, 1,1), pageable);
// System.out.println();
// }
List<Employee> employees = new ArrayList<Employee>();
List<Employee> currentDay = new ArrayList<>();
if (firstRun) {
employees.addAll(fromDB.toList());
firstRun = false;
}
if (futureDays.size() != 0) {
//currentDay.addAll(futureDays);
employees.addAll(futureDays);
futureDays = new ArrayList<>();
}
if (employees.size() != 0) {
if (!employees.get(employees.size()-1).getHiringDate().isAfter(tmpEnd)) {
getMoreData(employees, fromDB, start, tmpEnd);
}
}
for (Employee x: employees) {
if (x.getHiringDate().isEqual(start) || x.getHiringDate().isEqual(tmpEnd) ||
x.getHiringDate().isAfter(start) && x.getHiringDate().isBefore(tmpEnd)) {
currentDay.add(x);
}
else {
futureDays.add(x);
}
}
for (Employee x: currentDay) {
System.out.println(x.toString());
}
start = start.plusDays(1);
}
}
private void getMoreData(
List<Employee> data, Page<Employee> page, LocalDateTime start, LocalDateTime end) {
while (page.hasNext()) {
pageable = page.nextPageable();
data.addAll(employeeData.findAllByHiringDateGreaterThanEqual(
LocalDateTime.of(2022,3,1, 1,1), this.pageable).toList());
// stop when the last entry in a page is after the current day
if (data.get(data.size()-1).getHiringDate().isAfter(
LocalDateTime.of(start.toLocalDate(), LocalTime.of(23,59,59)))) {
break;
}
}
}
}
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.javainuse.controllers.EmployeeController;
import com.javainuse.data.EmployeeRepository;
import com.javainuse.model.Employee;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
#SpringBootTest
#ActiveProfiles("test")
public class MyTest {
#Autowired
EmployeeRepository repo;
#Autowired
EmployeeController controller;
#Test
public void testx() {
List<Employee> data = new ArrayList<>();
for (int n=1; n<=5; n++) {
Employee one = Employee.builder()
.dept(String.valueOf(n))
.name(String.valueOf(n))
.hiringDate(LocalDateTime.of(2022, 03, n, 10,00))
.build();
data.add(one);
}
repo.saveAll(data);
List<Employee> all = repo.findAll();
assertEquals(5, all.size());
controller.compareData(LocalDateTime.of(2022,3,1,00,00),LocalDateTime.of(2022,3,7,23,59,59));
}
}

Error while binding parameter from AWS Parameter Store in Spring Boot

Due to some know vulnerabilities, I can't use spring-cloud-starter-aws-parameter-store-config in my org. So I simply tried to extract code from jar and use it in my project. But I'm not able to bind parameter from aws store to spring boot. I'm getting following error-
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'db.username' in value "${db.username}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:178) ~[spring-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124) ~[spring-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:236) ~[spring-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210) ~[spring-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175) ~[spring-context-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$$Lambda$220/254979217.resolveStringValue(Unknown Source) ~[na:na]
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:908) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1228) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:636) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:116) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:397) ~[spring-beans-5.2.0.RELEASE.jar:5.2.0.RELEASE]
... 37 common frames omitted
These are the files that I'm using
import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableConfigurationProperties({AwsParamStoreProperties.class})
#ConditionalOnClass({AWSSimpleSystemsManagement.class, AwsParamStorePropertySourceLocator.class})
#ConditionalOnProperty(
prefix = "aws.paramstore",
name = {"enabled"},
matchIfMissing = true
)
public class AwsParamStoreBootstrapConfiguration {
}
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
#ConfigurationProperties("aws.paramstore")
#Validated
public class AwsParamStoreProperties {
public static final String CONFIG_PREFIX = "aws.paramstore";
#NotNull
#Pattern(
regexp = "(/[a-zA-Z0-9.\\-_]+)*"
)
private String prefix = "/config";
#NotEmpty
private String defaultContext = "application";
#NotNull
#Pattern(
regexp = "[a-zA-Z0-9.\\-_/]+"
)
private String profileSeparator = "_";
private boolean failFast = true;
private String name;
private boolean enabled = true;
public AwsParamStoreProperties() {
}
//Getter and Setters
}
import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement;
import com.amazonaws.services.simplesystemsmanagement.model.GetParametersByPathRequest;
import com.amazonaws.services.simplesystemsmanagement.model.GetParametersByPathResult;
import com.amazonaws.services.simplesystemsmanagement.model.Parameter;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.core.env.EnumerablePropertySource;
public class AwsParamStorePropertySource extends EnumerablePropertySource<AWSSimpleSystemsManagement> {
private String context;
private Map<String, Object> properties = new LinkedHashMap();
public AwsParamStorePropertySource(String context, AWSSimpleSystemsManagement ssmClient) {
super(context, ssmClient);
this.context = context;
}
public void init() {
GetParametersByPathRequest paramsRequest = (new GetParametersByPathRequest()).withPath(this.context).withRecursive(true).withWithDecryption(true);
this.getParameters(paramsRequest);
}
public String[] getPropertyNames() {
Set<String> strings = this.properties.keySet();
return (String[])strings.toArray(new String[strings.size()]);
}
public Object getProperty(String name) {
return this.properties.get(name);
}
private void getParameters(GetParametersByPathRequest paramsRequest) {
GetParametersByPathResult paramsResult = ((AWSSimpleSystemsManagement)this.source).getParametersByPath(paramsRequest);
Iterator var3 = paramsResult.getParameters().iterator();
while(var3.hasNext()) {
Parameter parameter = (Parameter)var3.next();
String key = parameter.getName().replace(this.context, "").replace('/', '.');
this.properties.put(key, parameter.getValue());
}
if (paramsResult.getNextToken() != null) {
this.getParameters(paramsRequest.withNextToken(paramsResult.getNextToken()));
}
}
}
import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.util.ReflectionUtils;
public class AwsParamStorePropertySourceLocator implements PropertySourceLocator {
private AWSSimpleSystemsManagement ssmClient;
private AwsParamStoreProperties properties;
private List<String> contexts = new ArrayList();
private Log logger = LogFactory.getLog(this.getClass());
public AwsParamStorePropertySourceLocator(AWSSimpleSystemsManagement ssmClient, AwsParamStoreProperties properties) {
this.ssmClient = ssmClient;
this.properties = properties;
}
public List<String> getContexts() {
return this.contexts;
}
public PropertySource<?> locate(Environment environment) {
if (!(environment instanceof ConfigurableEnvironment)) {
return null;
} else {
ConfigurableEnvironment env = (ConfigurableEnvironment)environment;
String appName = this.properties.getName();
if (appName == null) {
appName = env.getProperty("spring.application.name");
}
List<String> profiles = Arrays.asList(env.getActiveProfiles());
String prefix = this.properties.getPrefix();
String defaultContext = prefix + "/" + this.properties.getDefaultContext();
this.contexts.add(defaultContext + "/");
this.addProfiles(this.contexts, defaultContext, profiles);
String baseContext = prefix + "/" + appName;
this.contexts.add(baseContext + "/");
this.addProfiles(this.contexts, baseContext, profiles);
Collections.reverse(this.contexts);
CompositePropertySource composite = new CompositePropertySource("aws-param-store");
Iterator var9 = this.contexts.iterator();
while(var9.hasNext()) {
String propertySourceContext = (String)var9.next();
try {
composite.addPropertySource(this.create(propertySourceContext));
} catch (Exception var12) {
if (this.properties.isFailFast()) {
this.logger.error("Fail fast is set and there was an error reading configuration from AWS Parameter Store:\n" + var12.getMessage());
ReflectionUtils.rethrowRuntimeException(var12);
} else {
this.logger.warn("Unable to load AWS config from " + propertySourceContext, var12);
}
}
}
return composite;
}
}
private AwsParamStorePropertySource create(String context) {
AwsParamStorePropertySource propertySource = new AwsParamStorePropertySource(context, this.ssmClient);
propertySource.init();
return propertySource;
}
private void addProfiles(List<String> contexts, String baseContext, List<String> profiles) {
Iterator var4 = profiles.iterator();
while(var4.hasNext()) {
String profile = (String)var4.next();
contexts.add(baseContext + this.properties.getProfileSeparator() + profile + "/");
}
}
}
bootstrap.yml
aws:
paramstore:
prefix: /config
name: sample
enabled: true
profileSeparator: _
spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=\com.app.paramstore.AwsParamStoreBootstrapConfiguration

Create a custom RestControllerAnotation to execute a requestMapping

Good afternoon,
I have a restController and I want to create an annotation that allows or not to execute a method based on an a custom header value.
If custom header tag equals something then the method must execute, if the custom header dont match, the method musth not execute
I have followed several articles but I have not been able.
I attached the code I created:
Annotation Code:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface ApiVersion {
int[] value();
}
ApiVersionRequestMappingHandlerMapping
public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private final String prefix;
public ApiVersionRequestMappingHandlerMapping(String prefix) {
this.prefix = prefix;
}
#Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = super.getMappingForMethod(method, handlerType);
if(info == null) return null;
ApiVersion methodAnnotation = AnnotationUtils.findAnnotation(method, ApiVersion.class);
if(methodAnnotation != null) {
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
// Concatenate our ApiVersion with the usual request mapping
info = createApiVersionInfo(methodAnnotation, methodCondition).combine(info);
} else {
ApiVersion typeAnnotation = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
if(typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
// Concatenate our ApiVersion with the usual request mapping
info = createApiVersionInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
private RequestMappingInfo createApiVersionInfo(ApiVersion annotation, RequestCondition<?> customCondition) {
int[] values = annotation.value();
String[] patterns = new String[values.length];
for(int i=0; i<values.length; i++) {
// Build the URL prefix
patterns[i] = prefix+values[i];
}
return new RequestMappingInfo(
new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(), useSuffixPatternMatch(), useTrailingSlashMatch(), getFileExtensions()),
new RequestMethodsRequestCondition(),
new ParamsRequestCondition(),
new HeadersRequestCondition(),
new ConsumesRequestCondition(),
new ProducesRequestCondition(),
customCondition);
}
}
Rest Controller
#RestController
#RequiredArgsConstructor
#RequestMapping("/api/example")
public class ExampleController {
private final UserService userService;
#ApiVersion (1)
#GetMapping("/myMethod")
public String myMethod(#AuthenticationPrincipal UserAuthenticatedDetails userAuthenticated) {
return userAuthenticated.getUsername();
}
}
ApiConfig
package xx.package.sample;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#ComponentScan("xx.package")
#Configuration
#EnableTransactionManagement
#EntityScan("xx.package.domain.entity")
#EnableJpaRepositories("xx.package.domain.repository")
#EnableAutoConfiguration
public class ApiConfig {
}
I know I'm missing something but I can't see what.
Regards, and thank you very much!
You could use #GetMapping(path = "/myMethod", headers = "My-Header=myValue").
a sequence of "My-Header=myValue" style expressions, with a request
only mapped if each such header is found to have the given value
see https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/GetMapping.html#headers--

How to mock log4j2 logger object using slf4j in Junit

I have below code where I am creating different log4j2 loggers object using slf4j factory and I need to write to specific log file based on input param.
#Service
class WriterService {
val logger1: Logger = LoggerFactory.getLogger("t_logger")
val logger2: Logger = LoggerFactory.getLogger("b_logger")
val labelKey = "Label"
fun writeLog(payload: Map<String, Any>){
if(payload.containsKey(labelKey)) {
val label = payload[labelKey].toString().toLowerCase()
if (label == "t") {
logger1.info("{}", payload)
} else if (label == "b") {
logger2.info("{}", payload)
}
}
}
I am wondering how can I mock Logger objects using MockitoJUnitRunner so that I can mock which logger object was invoked ?
I found solution using Appender and ArgumentCaptor...
Solution below for someone who might be interested...
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.core.Appender
import org.apache.logging.log4j.core.LogEvent
import org.apache.logging.log4j.core.Logger
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.runners.MockitoJUnitRunner
import java.lang.StringBuilder
#RunWith(MockitoJUnitRunner::class)
class ServiceTest {
#InjectMocks
var service: WriterService
#Mock
private val mockAppender: Appender? = null
#Captor
private val captorLoggingEvent: ArgumentCaptor<LogEvent>? = null
private var logger1: Logger? = null
private var logger2: Logger? = null
#Before
fun setup() {
`when`(mockAppender!!.name).thenReturn("MockAppender")
`when`(mockAppender.isStarted).thenReturn(true)
`when`(mockAppender.isStopped).thenReturn(false)
logger1 = LogManager.getLogger("t_logger") as Logger
logger1!!.addAppender(mockAppender)
logger1!!.level = Level.INFO
logger2 = LogManager.getLogger("b_logger") as Logger
logger2!!.addAppender(mockAppender)
logger2!!.level = Level.INFO
}
#After
fun tearDown() {
logger1!!.removeAppender(mockAppender!!)
logger2!!.removeAppender(mockAppender!!)
}
#Test
fun writeLog_shouldWriteTransactionLogWhenLabelIs_T() {
val payload = hashMapOf("Label" to "t")
service.writeLog(payload)
verify(mockAppender, times(1))!!.append(captorLoggingEvent!!.capture())
assertEquals(captorLoggingEvent.value.level, Level.INFO)
assertEquals(captorLoggingEvent.value.loggerName, "t_logger")
assertEquals(captorLoggingEvent.value.message.formattedMessage, "{Label=t}")
}
#Test
fun writeLog_shouldWriteBackendLogWhenLabelIs_B() {
val payload = hashMapOf("Label" to "b")
service.writeLog(payload)
verify(mockAppender, times(1))!!.append(captorLoggingEvent!!.capture())
assertEquals(captorLoggingEvent.value.level, Level.INFO)
assertEquals(captorLoggingEvent.value.loggerName, "b_logger")
assertEquals(captorLoggingEvent.value.message.formattedMessage, "{Label=b}")
}
}

Spring Test DBUnit: Unable to load dataset from file

I'm trying to use Spring Test DBUnit for running an integration test that checks if a DAO's services are running correctly. For two similar entities, I was able to create tests that run OK, but for this particular entity, the test can't run properly.
The test will be ignored, and the only Exception I will see in the console is:
java.lang.IllegalArgumentException: Unable to load dataset from "data/offline_message.xml" using class com.github.springtestdbunit.dataset.FlatXmlDataSetLoader
Here are the relevant files. XML file:
<dataset>
<mp_account id="1" auth_hash="ted.mosby" first_name="Ted" last_name="Mosby" credential="EMAIL" transport_session="someTransportSession"/>
<mp_account id="2" auth_hash="lily.aldrin" first_name="Lily" last_name="Aldrin" credential="MEH" transport_session="someTransportSession"/>
<mp_message id="1" recipient_account_id="1" sender_account_id="2"/>
</dataset>
Test class that is failing:
import com.github.springtestdbunit.annotation.DatabaseSetup;
import com.somecompany.messaging.domain.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.Date;
import java.util.List;
#DatabaseSetup("data/message.xml")
public class MessageDaoTest extends AbstractDaoTest<Message> {
private static final Long ACCOUNT_ID = 1L;
public static final long DATE_LONG = 1431018764154L;
private static final Date LAST_UPDATE_TS = new Date(DATE_LONG);
#Autowired
MessageDao MessageDao;
#BeforeMethod
public void setUp() throws Exception {
this.setDao(MessageDao);
}
#Test
#Transactional
public void testFindMessages() throws Exception {
List<Message> Messages = this.MessageDao.findMessages(ACCOUNT_ID, LAST_UPDATE_TS);
Assert.assertNotNull(Messages);
Assert.assertEquals(Messages.size(), 1);
}
}
Abstract test class, that extends from TestNG's class:
import com.github.springtestdbunit.DbUnitTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class })
#ContextConfiguration(locations = { "classpath:test-context.xml" })
public class AbstractDaoTest <T> extends AbstractTestNGSpringContextTests {
private GenericDao<T> dao;
#Transactional
public T create(T t){
return dao.create(t);
}
#Transactional
public void delete(Object id){
dao.delete(id);
}
#Transactional
public T find(Object id){
return dao.find(id);
}
#Transactional
public T update(T t){
return dao.update(t);
}
public void setDao(GenericDao<T> dao) {
this.dao = dao;
}
}
Finally, the Entity:
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import javax.persistence.*;
#NamedQueries({
#NamedQuery(
name = "findMessagesQuery",
query = "select om from Message om where om.recipientAccount.id=:accountId " +
"and om.lastUpdatedTs>:time and om.lastUpdatedTs+om.expirationPeriod>:now " +
"order by om.lastUpdatedTs asc"
),
#NamedQuery(
name = "findExpiredMessagesQuery",
query = "select om from Message om where om.lastUpdatedTs+om.expirationPeriod<:now"
)
})
#Entity
#Table(name="mp_message")
public class Message extends AbstractModel {
#JoinColumn(name="recipient_account_id", updatable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Account recipientAccount;
#JoinColumn(name="sender_account_id", updatable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Account senderAccount;
#Column(name="message_body", length=2048)
private String messageBody;
#Column(name="expiration_period")
private Long expirationPeriod;
// Getters, setters, etc excluded for brevity
}
Just a sidenote: I have "obscured" the Entity name (dammned lawyers!), so there could be some small name mistakes. Please bear with me on this ;)
If you need some additional details, please let me know.
Thanks in advance
try to add classpath: before your path, so:
#DatabaseSetup("classpath:data/message.xml")
Just do this, it works. the relative path can be used.
#DatabaseSetup("/data/message.xml"")

Resources