How to mock log4j2 logger object using slf4j in Junit - spring-boot

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}")
}
}

Related

How to get osgi configuration values to a servlet

This is my OSGI configuration file which is having three names. I want to read these values in a servlet and sort them Alphabetically and send that response to a ajax to display in a custom component AEM.
package com.demo.training.core.services.impl;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import com.demo.training.core.services.MyProjectServ;
#Component(service=MyProjectServ.class,immediate = true)
#Designate(ocd= MyProject.ServiceConfig.class)
public class MyProject implements MyProjectServ {
#ObjectClassDefinition(name="My-Project OSGI",
description="Demo OSGI configuration")
public #interface ServiceConfig {
#AttributeDefinition(
name="Name1",
description="Add First name",
type = AttributeType.STRING
)
public String Name1() default "Abhinay";
#AttributeDefinition(
name="Name2",
description="Add second name ",
type = AttributeType.STRING
)
public String Name2() default "Pavan";
#AttributeDefinition(
name="Name3",
description="Add third name ",
type = AttributeType.STRING )
public String Name3() default "Ram";
}
private String Name1;
private String Name2;
private String Name3;
#Activate
protected void activate(ServiceConfig myconfig) {
Name1=myconfig.Name1();
Name2=myconfig.Name2();
Name3=myconfig.Name3();
}
#Override
public String getNAME1() {
return Name1; }
#Override
public String getNAME2() {
return Name2; }
#Override
public String getNAME3() {
return Name3;
} }
'''This is my Servlet code , I have wrote multiple resp.getwriter() to see upto which line it is working. It is working upto response named a1(i.e below dictionary command). Could anyone please help to get values from osgi configuration to this servlet ?
package com.demo.training.core.servlets;
import java.io.IOException;
import java.util.Arrays;
import java.util.Dictionary;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.framework.Constants;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
#Component(service=Servlet.class,
property={
Constants.SERVICE_DESCRIPTION + "=Practice Servlet",
"sling.servlet.methods=" + HttpConstants.METHOD_GET,
"sling.servlet.methods=" + HttpConstants.METHOD_POST,
"sling.servlet.paths=/bin/myproject",
"sling.servlet.extensions=" + "txt"
})
public class MyProjectServlet extends SlingAllMethodsServlet {/**
*
*/
private static final long serialVersionUID = 1L;
#Reference
private ConfigurationAdmin MYPROJECT_CONFIG;
private static final String MY_PROJECT="com.demo.training.core.services.impl.MyProject";
#Override
protected void doGet(final SlingHttpServletRequest req,
final SlingHttpServletResponse resp) throws ServletException, IOException {
Configuration My_Servlet=MYPROJECT_CONFIG.getConfiguration(MY_PROJECT);
Dictionary<String,Object> property =My_Servlet.getProperties();
resp.getWriter().write("a1");
String first=property.get("Name1").toString();
String second=property.get("Name2").toString();
String third=property.get("Name3").toString();
resp.getWriter().write("a2");
resp.getWriter().write(first);
resp.getWriter().write("a3");
String[] myArray = new String[]{first,second,third};
Arrays.sort(myArray);
String js=myArray.toString();
resp.getWriter().write(js);
}
}
You try to use the #reference annotation for your service. If this object null you can use the ResourceResolverFactoy. This object does always exists, else you have your instance has a serious problem:
Map<String, Object> serviceParameter = new HashMap<>();
serviceParameter.put(ResourceResolverFactory.SUBSERVICE, Put the name name of your service here);
return resolverFactory.getServiceResourceResolver(serviceParameter);
In Servlet use annotation #reference to inject the ResourceResolverFactoy:
#Reference
private ResourceResolverFactory ...;
By the way, have an eye to java code convetions. Method names starts always with smal letters even in service configs.

How to find interfaces with specified annotation using ClassPathScanningCandidateComponentProvider?

I try to replace org.reflections:reflections library by org.springframework:spring-context and ClassPathScanningCandidateComponentProvider because reflections library has a bug that is critical for me.
I want to find all classes, interfaces and subclasses that contain specific annotation. It was easy in reflections:
var subTypeScanner = new SubTypesScanner( true );
var typeAnnotationScanner = new TypeAnnotationsScanner();
var configBuilder = new ConfigurationBuilder().setUrls( ClasspathHelper.forPackage( "com.example" ) )
.setScanners( subTypeScanner, typeAnnotationScanner );
Reflections reflections= new Reflections( configBuilder );
Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(Path.class);
Unfortunately it is not easy for me using ClassPathScanningCandidateComponentProvider. I have made workaround to find subclasses. First I find set of classes and after that I search for subclasses separately for each class:
protected Set< Class< ? > > findClasses( ClassPathScanningCandidateComponentProvider aScanner )
{
return aScanner.findCandidateComponents( "com.example" )
.stream()
.map( BeanDefinition::getBeanClassName )
.map( e -> {
try
{
return Class.forName( e );
}
catch( ClassNotFoundException aE )
{
throw new RuntimeException( aE );
}
} )
.collect( Collectors.toUnmodifiableSet() );
}
protected Set< Class< ? > > findSubClasses( Set< Class< ? > > aClasses )
{
return aClasses.stream()
.map( aClass -> {
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider( false );
scanner.addIncludeFilter( new AssignableTypeFilter( aClass ) );
return scanner.findCandidateComponents( "com.example" );
} )
.flatMap(Collection::stream)
.map( BeanDefinition::getBeanClassName )
.map( e -> {
try
{
return Class.forName( e );
}
catch( ClassNotFoundException aE )
{
throw new RuntimeException( aE );
}
} )
.collect( Collectors.toUnmodifiableSet() );
}
Unfortunately I do not know how to find interfaces with annotation. Following code does not finds interfaces. It only finds classes that has Path annotation or extends class or implements interface that has Path annotation.
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider( false );
scanner.addIncludeFilter( new AnnotationTypeFilter( Path.class, true, true ) );
Set<Class<?>> classes = findClasses(scanner);
//findSubClasses(findClasses( scanner ))
How to find interfaces with Path annotation?
The current implementation is also clear as mentioned on method level docs
The default implementation checks whether the class is not an interface and not dependent on an enclosing class.
As per this github issue we can fix this by overridden behaviour of isCandidateComponent() method
Here is the sample reference working code from the github issue
provider = new ClassPathScanningCandidateComponentProvider(false) {
#Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return super.isCandidateComponent(beanDefinition) || beanDefinition.getMetadata().isAbstract();
}
};
Annotation
package in.silentsudo.classpathannotationscanner.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(value = RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface MyCustomAnnotation {
}
My Simple interface
package in.silentsudo.classpathannotationscanner;
import in.silentsudo.classpathannotationscanner.annotation.MyCustomAnnotation;
#MyCustomAnnotation
public interface SimpleInterface {
}
Sample test code
package in.silentsudo.classpathannotationscanner;
import in.silentsudo.classpathannotationscanner.annotation.MyCustomAnnotation;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
#RestController
#RequestMapping("/list")
public class SampleController {
private final ClassPathScanningCandidateComponentProvider provider;
SampleController() {
provider = new ClassPathScanningCandidateComponentProvider(false) {
#Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return super.isCandidateComponent(beanDefinition) || beanDefinition.getMetadata().isAbstract();
}
};
provider.addIncludeFilter(new AnnotationTypeFilter(MyCustomAnnotation.class, true, true));
}
#GetMapping
public Map<String, Object> list() throws Exception {
return Map.of("mycustomannotationsmarkedinterafce", getData());
}
public List<String> getData() throws Exception {
final Set<BeanDefinition> classes = provider.findCandidateComponents("in.silentsudo.classpathannotationscanner");
List<String> names = new ArrayList<>();
for (BeanDefinition bean : classes) {
Class<?> clazz = Class.forName(bean.getBeanClassName());
names.add(clazz.getName());
}
return names;
}
}
This should give following response:
{
"mycustomannotationsmarkedinterafce": [
"in.silentsudo.classpathannotationscanner.SimpleInterface"
]
}

Spring boot autowired service all variable fields are null

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.

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--

Resources