Component bean is not injected (autowired) by spring when running AWS lambda function using Spring Cloud function AWS Adapter - spring

I am implementing AWS lambda function creating handler using Spring Cloud function AWS Adapter SpringBootRequestHandler. The functional bean registered in GenericApplicationContext is invoked, but autowiring of component class is giving NullPointer Exception.
I have tried #ComponentScan for base package at Spring Application.
Application class:
#Slf4j
#SpringBootApplication
#ComponentScan({ "com.poc.evthub" })
#EnableConfigurationProperties(EventHubProperties.class)
public class EventHubServerlessApplication implements ApplicationContextInitializer<GenericApplicationContext> {
public EventHubServerlessApplication() {
}
public static void main(String[] args) throws Exception {
FunctionalSpringApplication.run(EventHubServerlessApplication.class, args);
}
#Bean
public KinesisEventFunction ingestEvents() {
return new KinesisEventFunction();
}
#Override
public void initialize(GenericApplicationContext context) {
log.debug("======== initialize ========");
context.registerBean("ingestEvents", FunctionRegistration.class,
() -> new FunctionRegistration<Function<KinesisEvent, ApiResponse>>(ingestEvents())
.type(FunctionType.from(KinesisEvent.class).to(ApiResponse.class).getType()));
}
}
Handler:
public class KinesisEventHandler extends SpringBootRequestHandler<KinesisEvent, ApiResponse> {
}
Functional Bean:
package com.poc.evthub.function;
import com.amazonaws.services.lambda.runtime.events.KinesisEvent;
import com.poc.evthub.beans.ApiResponse;
import com.poc.evthub.constant.Constants;
import com.poc.evthub.service.IngestionServiceFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.function.Function;
#Slf4j
#Component
public class KinesisEventFunction implements Function<KinesisEvent, ApiResponse> {
private Context context = null;
#Autowired
private IngestionServiceFactory ingestionServiceFactory;
#Autowired
#Qualifier("targetExecutionContext")
public void setContext(Context context) {
log.info("Context: {}", context);
this.context = context;
}
#Override
public ApiResponse apply(final KinesisEvent kinesisEvent) {
log.info("KinesisEventFunction apply called...");
String sourceDomain = System.getenv(Constants.SYSENV.SOURCE_DOMAIN);
log.info("Source Domain = {}", sourceDomain);
if(null == kinesisEvent || null == kinesisEvent.getRecords()) {
log.error("Event contains no data. {}", System.lineSeparator());
//TODO build response NOT FOUND
return null;
}
else
log.info("Received {} records from {}. {}",
kinesisEvent.getRecords().size(),
kinesisEvent.getRecords().get(0).getEventSourceARN(),
System.lineSeparator());
log.info("ingestionServiceFactory = {}",ingestionServiceFactory);
ingestionServiceFactory.ingest();
return null;
}
}
Complete code and pom is uploaded at:
https://github.com/rjavaria/eventhub-serverless
KinesisEventFunction apply called...
Also able to read the environment value(source_domain) from lambda, and receiving Kinesis event record.
But #Autowired ingestionServiceFactory is null. I am injecting this component bean to delegate the business logic.
What is missed here, so spring is not able to inject this component bean?
Thanks in advance!

You could try manual injection of your IngestionServiceFactory bean into your function class via constructor or setter injection.
In your function class, add a constructor and remove #Autowired from your IngestionServiceFactory field, like:
...
public class KinesisEventFunction implements Function<KinesisEvent, ApiResponse> {
...
// No #Autowired here...
private final IngestionServiceFactory ingestionServiceFactory;
...
// The new constructor here...
public KinesisEventFunction(final IngestionServiceFactory pIngestionServiceFactory) {
this.ingestionServiceFactory = pIngestionServiceFactory;
}
...
}
Then in your main class (the one implementing ApplicationContextInitializer<GenericApplicationContext>), pass the reference to the factory when registering the function bean, like:
...
public class EventHubServerlessApplication implements ApplicationContextInitializer<GenericApplicationContext> {
...
#Autowired
private IngestionServiceFactory ingestionServiceFactory;
...
#Bean
public KinesisEventFunction ingestEvents() {
return new KinesisEventFunction(this.ingestionServiceFactory);
}
#Override
public void initialize(GenericApplicationContext context) {
log.debug("======== initialize ========");
context.registerBean("ingestEvents", FunctionRegistration.class,
() -> new FunctionRegistration<>(ingestEvents())
.type(FunctionType.from(KinesisEvent.class).to(ApiResponse.class).getType()));
}
}
Or, as you are already manually registering the function bean with context.registerBean(), you could just:
...
public class EventHubServerlessApplication implements ApplicationContextInitializer<GenericApplicationContext> {
...
#Autowired
private IngestionServiceFactory ingestionServiceFactory;
...
#Override
public void initialize(GenericApplicationContext context) {
log.debug("======== initialize ========");
context.registerBean("ingestEvents", FunctionRegistration.class,
() -> new FunctionRegistration<>(new KinesisEventFunction(this.ingestionServiceFactory))
.type(FunctionType.from(KinesisEvent.class).to(ApiResponse.class).getType()));
}
}
Please, let me know if it works!

Related

Field created in spring component in not initialized with new keyword

I have spring component class annotated with #Component and in it I have field ConcurrentHashMap map, which is init in constructor of component and used in spring stream listener:
#Component
public class FooService {
private ConcurrentHashMap<Long, String> fooMap;
public FooService () {
fooMap = new ConcurrentHashMap<>();
}
#StreamListener(value = Sink.INPUT)
private void handler(Foo foo) {
fooMap.put(foo.id, foo.body);
}
}
Listener handle messages sent by rest controller. Can you tell me why I always got there fooMap.put(...) NullPointerException because fooMap is null and not initialzied.
EDIT:
After #OlegZhurakousky answer I find out problem is with async method. When I add #Async on some method and add #EnableAsync I can't anymore use private modificator for my #StreamListener method. Do you have idea why and how to fix it?
https://github.com/schwantner92/spring-cloud-stream-issue
Thanks.
Could you try using #PostConstruct instead of constructor?
#PostConstruct
public void init(){
this.fooMap = new ConcurrentHashMap<>();
}
#Denis Stephanov
When I say bare minimum, here is what I mean. So try this as a start, you'll see that the map is not null and start evolving your app from there.
#SpringBootApplication
#EnableBinding(Processor.class)
public class DemoApplication {
private final Map<String, String> map;
public static void main(String[] args) {
SpringApplication.run(DemoRabbit174Application.class, args);
}
public DemoApplication() {
this.map = new HashMap<>();
}
#StreamListener(Processor.INPUT)
public void sink(String string) {
System.out.println(string);
}
}
With Spring everything has to be injected.
You need to declare a #Bean for the ConcurrentHashMap, that will be injected in you Component. So create a Configuration class like:
#Configuration
public class FooMapConfiguration {
#Bean("myFooMap")
public ConcurrentHashMap<Long, String> myFooMap() {
return new ConcurrentHashMap<>();
}
}
Then modify your Component:
#Component
public class FooService {
#Autowired
#Qualifier("myFooMap")
private ConcurrentHashMap<Long, String> fooMap;
public FooService () {
}
#StreamListener(value = Sink.INPUT)
private void handler(Foo foo) {
fooMap.put(foo.id, foo.body); // <= No more NPE here
}
}

#RefreshScope stops #Scheduled task

I have a monitoring app wherein I am running a fixedRate task. This is pulling in a config parameter configured with Consul. I want to pull in updated configuration, so I added #RefreshScope. But as soon as I update the config value on Consul, the fixedRate task stops running.
#Service
#RefreshScope
public class MonitorService {
#Autowired
private AppConfig appConfig;
#PostConstruct
public void postConstRun() {
System.out.println(appConfig.getMonitorConfig());
}
#Scheduled(fixedRate = 1000)
public void scheduledMonitorScan() {
System.out.println("MonitorConfig:" + appConfig.getMonitorConfig());
}
}
AppConfig class just has a single String parameter:
#Configuration
#Getter
#Setter
public class AppConfig {
#Value("${monitor-config:default value}")
private String monitorConfig;
}
As soon as I update the value in consul, the scheduled task just stops running (display in sheduledMonitorScan method) stop showing up.
I'm successfully get & override the values from consul config server using RefreshScopeRefreshedEvent
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
#RefreshScope
public class AlertSchedulerCron implements ApplicationListener<RefreshScopeRefreshedEvent> {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
#Value("${pollingtime}")
private String pollingtime;
/*
* #Value("${interval}") private String interval;
*/
#Scheduled(cron = "${pollingtime}")
//#Scheduled(fixedRateString = "${interval}" )
public void task() {
System.out.println(pollingtime);
System.out.println("Scheduler (cron expression) task with duration : " + sdf.format(new Date()));
}
#Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
// TODO Auto-generated method stub
}
}
Here's how we've solved this issue.
/**
* Listener of Spring's lifecycle to revive Scheduler beans, when spring's
* scope is refreshed.
* <p>
* Spring is able to restart beans, when we change their properties. Such a
* beans marked with RefreshScope annotation. To make it work, spring creates
* <b>lazy</b> proxies and push them instead of real object. The issue with
* scope refresh is that right after refresh in order for such a lazy proxy
* to be actually instantiated again someone has to call for any method of it.
* <p>
* It creates a tricky case with Schedulers, because there is no bean, which
* directly call anything on any Scheduler. Scheduler lifecycle is to start
* few threads upon instantiation and schedule tasks. No other bean needs
* anything from them.
* <p>
* To overcome this, we had to create artificial method on Schedulers and call
* them, when there is a scope refresh event. This actually instantiates.
*/
#RequiredArgsConstructor
public class RefreshScopeListener implements ApplicationListener<RefreshScopeRefreshedEvent> {
private final List<RefreshScheduler> refreshSchedulers;
#Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
refreshSchedulers.forEach(RefreshScheduler::materializeAfterRefresh);
}
}
So, we've defined an interface, which does nothing in particular, but allows us to call for a refreshed job.
public interface RefreshScheduler {
/**
* Used after refresh context for scheduler bean initialization
*/
default void materializeAfterRefresh() {
}
}
And here is actual job, whose parameter from.properties can be refreshed.
public class AJob implements RefreshScheduler {
#Scheduled(cron = "${from.properties}")
public void aTask() {
// do something useful
}
}
UPDATED:
Of course AJob bean must be marked with #RefreshScope in #Configuration
#Configuration
#EnableScheduling
public class SchedulingConfiguration {
#Bean
#RefreshScope
public AJob aJob() {
return new AJob();
}
}
I have done workaround for this kind of scenario by implementing SchedulingConfigurer interface.
Here I am dynamically updating "scheduler.interval" property from external property file and scheduler is working fine even after actuator refresh as I am not using #RefreshScope anymore.
Hope this might help you in your case also.
public class MySchedulerImpl implements SchedulingConfigurer {
#Autowired
private Environment env;
#Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10);
}
#Override
public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(this.taskExecutor());
taskRegistrar.addTriggerTask(() -> {
//put your code here that to be scheduled
}, triggerContext -> {
final Calendar nextExecutionTime = new GregorianCalendar();
final Date lastActualExecutionTime = triggerContext.lastActualExecutionTime();
if (lastActualExecutionTime == null) {
nextExecutionTime.setTime(new Date());
} else {
nextExecutionTime.setTime(lastActualExecutionTime);
nextExecutionTime.add(Calendar.MILLISECOND, env.getProperty("scheduler.interval", Integer.class));
}
return nextExecutionTime.getTime();
});
}
}
My solution consists of listening to EnvironmentChangeEvent
#Configuration
public class SchedulingSpringConfig implements ApplicationListener<EnvironmentChangeEvent>, SchedulingConfigurer {
private static final Logger LOGGER = LoggerFactory.getLogger(SchedulingSpringConfig.class);
private final DemoProperties demoProperties;
public SchedulingSpringConfig(DemoProperties demoProperties) {
this.demoProperties = demoProperties;
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
LOGGER.info("Configuring scheduled task with cron expression: {}", demoProperties.getCronExpression());
taskRegistrar.addTriggerTask(triggerTask());
taskRegistrar.setTaskScheduler(taskScheduler());
}
#Bean
public TriggerTask triggerTask() {
return new TriggerTask(this::work, cronTrigger());
}
private void work() {
LOGGER.info("Doing work!");
}
#Bean
#RefreshScope
public CronTrigger cronTrigger() {
return new CronTrigger(demoProperties.getCronExpression());
}
#Bean
public ThreadPoolTaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
#Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
if (event.getKeys().contains("demo.config.cronExpression")) {
ScheduledTasksRefresher scheduledTasksRefresher = new ScheduledTasksRefresher(triggerTask());
scheduledTasksRefresher.afterPropertiesSet();
}
}
}
Then I use the ContextLifecycleScheduledTaskRegistrar to recreate the task.
public class ScheduledTasksRefresher extends ContextLifecycleScheduledTaskRegistrar {
private final TriggerTask triggerTask;
ScheduledTasksRefresher(TriggerTask triggerTask) {
this.triggerTask = triggerTask;
}
#Override
public void afterPropertiesSet() {
super.destroy();
super.addTriggerTask(triggerTask);
super.afterSingletonsInstantiated();
}
}
Properties definition:
#ConfigurationProperties(prefix = "demo.config", ignoreUnknownFields = false)
public class DemoProperties {
private String cronExpression;
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
}
Main definition:
#SpringBootApplication
#EnableConfigurationProperties(DemoProperties.class)
#EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Based on previous answers I added the following interface and used it on #RefreshScope annotated beans:
public interface RefreshScopeScheduled {
#EventListener(RefreshScopeRefreshedEvent.class)
default void onApplicationEvent() { /*do nothing*/ }
}

Spring Bean Factory Configuration passing input parameter

I'm trying to create a BeanFactory called TaskBeanFactory that I can Autowire into another prototype class that's running on a thread. I want a different instance of a bean returned by the Factory based on a taskName that i want to pass into it but when i start the application i get a null pointer exception because the taskName is null. I had a look at this article but i'm confused about how I should configure the Factory and then pass in the taskName.
The Factory:
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.stereotype.Component;
#Data
#Component
#NoArgsConstructor
public class TaskBeanFactory extends AbstractFactoryBean<GenericTask>{
private TaskNameEnum taskName;
public TaskBeanFactory(TaskNameEnum taskName) {
setSingleton(false);
}
#Override
public Class<?> getObjectType() {
return GenericTask.class;
}
#Override
protected GenericTask createInstance() throws Exception {
switch (taskName) {
case FILE_OPERATION:
return new FileTask();
case DATA_OPERATION:
return new DataTask();
default:
return new GenericTask();
}
}
}
The classes used by the Factory:
#Data
public class GenericTask {
private String idTask;
public void executeTask(Work work) {};
}
#Component
#Scope(value="prototype")
public class FileTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
#Component
#Scope(value="prototype")
public class DataTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
and the thread that's calling the Factory:
#Slf4j
#Data
#Scope("prototype")
#Component
public class WorkerThread implements Runnable {
#Autowired
private TaskBeanFactory taskBeanFactory;
#Autowired
private DataService dataService;
#Override
public void run() {
//iterate a Map of taskIds from the dataService
taskBeanFactory.setTaskName(TaskNameEnum.valueOf(taskEntry.getKey()));
GenericTask genericTask = taskBeanFactory.getObject();
//expecting genericTask to be of Type FileTask if called with one Key
//or of Type DataTask if called with another
}
}
}

Microservice return response first and then process the request

I am working on a solution for which i am trying to create a microservice which returns response immediately and then processes the request.
I am trying to use Java 8 and Spring for this.
This can be achieved in several ways.
In order to return a result from the current thread (a controller in this case) while still doing some long-running operation, you will need another thread.
Use Executor directly.
A controller:
#Controller
public class AsyncController {
private AsyncService asyncService;
#Autowired
public void setAsyncService(AsyncService asyncService) {
this.asyncService = asyncService;
}
private ResponseEntity asyncMethod(#RequestBody Object request) {
asyncService.process(new MyLongRunningRunnable());
// returns immediately
return ResponseEntity.ok("ok");
}
}
And a service:
#Service
public class AsyncService {
private ExecutorService executorService;
#PostConstruct
private void create() {
executorService = Executors.newSingleThreadExecutor();
}
public void process(Runnable operation) {
// no result operation
executorService.submit(operation);
}
#PreDestroy
private void destroy() {
executorService.shutdown();
}
}
More details can be found here https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html
Another way is to use Spring built-in async capabilities
You can simply annotate a method with #Async, having void or Future return type.
If you still want to supply your own executor, you may implement AsyncConfigurer interface in your spring configuration bean.
This approach also requires #EnableAsync annotation.
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
return Executors.newSingleThreadExecutor();
}
}
More on this topic https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Async.html
Here is an example with ExecutorService:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PreDestroy;
import javax.servlet.http.HttpServletRequest;
#RestController
public class MyController {
// Instantiate an executor service
private ExecutorService executor = Executors.newSingleThreadExecutor();
#PreDestroy
public void shutdonw() {
// needed to avoid resource leak
executor.shutdown();
}
#GetMapping
public Object gerUrl(HttpServletRequest request) {
// execute the async action, you can use a Runnable or Callable instances
executor.submit(() -> doStuff());
return "ok";
}
private void doStuff(){}
}
You can use the Executors factory class to build a ExecutorService. Those methods might help you:
java.util.concurrent.Executors
Executors.newSingleThreadExecutor() // jobs are queued and executed by a single thread
Executors.newCachedThreadPool() // new threads are instantiated as needed and cached
Executors.newFixedThreadPool(int nThreads) // user defined number of threads
.
#EnableAsync
#SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApplication.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(MyApplication.class, args);
}
}
import javax.annotation.PreDestroy;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
#Configuration
public class AsyncConfiguration extends AsyncConfigurerSupport {
private ThreadPoolTaskExecutor executor;
#Override
public Executor getAsyncExecutor() {
executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(1000);
executor.initialize();
return executor;
}
#PreDestroy
public void shutdownExecutors() {
executor.shutdown();
}
}
#Service
public class MyService {
#Async
public void doStuff(){
// Async method
}
}
Both techniques are quite good, but the first one with ExecutorService give you more control.

How to set Spring application context through setter or constructor in another class

I have a Spring class.
#Service("dbManager")
#Repository
#Transactional
public class DatabaseManager {
GenericXmlApplicationContext context;
#PersistenceContext
private EntityManager em;
public DatabaseManager(GenericXmlApplicationContext context) {
this.context = context;
}
....
} //end of class DatabaseManager
I have SpringUtil class
public class SpringUtil {
public static GenericXmlApplicationContext loadSpringContext(String springXmlFile) {
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
context.load(springXmlFile);
context.refresh();
return context;
} //end of loadSpringContext()
} //end of class SpringUtil
Now in main i am using some thing like
public class Regulator {
public static void main( String[] args ) {
Test test = new Test;
test.start();
} //end of main()
} //end of class Regulator
Here is test class
public class Test {
public void start() {
String springXmlFile = "classpath:spring/plcb-app-context-xml.xml";
GenericXmlApplicationContext context = SpringUtil.loadSpringContext(springXmlFile);
} //end of reportStudent()
} //end of class Test
But i am getting error that
Could not instantiate bean class [...DatabaseManager]: No default constructor
found; nested exception is java.lang.NoSuchMethodException:
...DatabaseManager.<init>()
I want that when DatabaseManager class created then spring context taht i am creating using SpringUtil.loadSpringContext(springXmlFile) must pass to it. How can i do it ?
Thanks
Edit
-------------------
public void switchDataSource(DatabaseType databaseType) {
DriverManagerDataSource dataSource = null;
if (databaseType == DatabaseType.LEGACY) {
dataSource = (DriverManagerDataSource)context.getBean("myLegacyDataSource");
} else if (databaseType == DatabaseType.LS360) {
dataSource = (DriverManagerDataSource)context.getBean("myLs360DataSource");
}
LocalContainerEntityManagerFactoryBean emf = context.getBean("myEmf", LocalContainerEntityManagerFactoryBean.class);
emf.setDataSource(dataSource);
}
#SuppressWarnings("unchecked")
#Transactional(readOnly=true)
public List<Object> getResultList(String query, Class mappingClass) throws Exception {
Query emQuery = em.createNativeQuery(query, mappingClass);
return emQuery.getResultList();
} //end of findTraineeFromLegacy()
Actually i have these two methods in my DatabaseManager class. I am setting context so i can get bean from the context in switchDataSource() method.
One thing that i can do is remove instance filed and change the method to
public void switchDataSource(DatabaseType databaseType, GenericXmlApplicationContext context) {
....
}
This is why i am doing this ?
Thanks
Have a no-arg constructor for DatabaseManager.
Implements ApplicationContextAware in DatabaseManager. Spring will know this bean needs to be notified of the application context:
#Service("dbManager")
#Repository
#Transactional
public class DatabaseManager implements ApplicationContextAware {
private ApplicationContext context;
public DatabaseManager() {...}
#Override
public void setApplicationContext(ApplicationContext appContext) {
this.context = appContext;
}
} //end of class DatabaseManager
however, double think if you really need that injected. In most case you are doing something wrong.
Update:
For your requirement in your update, which you want your DB Manager to switch datasource base on input type, although it doesn't seems very normal doing such thing, you can simply have your DB Manager injected with a Map and do whatever you want, instead of injecting the app context.
#Service("dbManager")
#Repository
#Transactional
public class DatabaseManager implements ApplicationContextAware {
#Resource("&emfBean")
private LocalContainerEntityManagerFactoryBean emfBean;
#Resource("dbManagerDsMap")
private Map<DatabaseType, Datasource> dsMapping;
public DatabaseManager() {...}
public void switchDataSource(DatabaseType databaseType) {
emfBean.setDatasource(dsMapping.get(databaseType));
}
} //end of class DatabaseManager
However I strongly suggest you not doing such thing. Consider having individual entityManagerFactory for each DB you are connecting to, and use the correct emf to connect to DB, instead doing this weird switching logic. I believe it is not supposed to be changed after your application start.

Resources