How to make spring manage the thread of anonymous class - spring

I understand that if I do following I can make spring manage the thread
#Component
#Scope("prototype")
public class ATask implements Runnable{....}
ThreadPoolTaskExecutor taskExecutor = (ThreadPoolTaskExecutor) context.getBean("taskExecutor");
ATask aTask1 = (ATask) ctx.getBean("aTask");
taskExecutor.execute(aTask1);
What I don't understand is what is the difference between
taskExecutor.execute(aTask1);
and
taskExecutor.execute(new ATask("A task 1"));
My guess is in second case, creation of thread is not managed by spring by execution is.
Second question,
For below code,
this.taskExecutor.execute(new Runnable() {
#Override
public void run() {...}});
Does spring manage/control the number of threads run simultaneously?
How do I make it work same as a thread with #Component and #Scope("prototype")? - One option is to move the code to a different class, but we are trying to avoid that as we have many small such methods.

Answer to your first question - there is no difference between first and second, in both the cases thread pool management will be handed by Spring because you are calling execute method on Spring's ThreadPoolTaskExecutor, if you were calling it on java.util.concurrent.ThreadPoolExecutor then thread pool management would not be handled by Spring.
Answer to your second question - if you are using Spring's ThreadPoolTaskExecutor then thread pool management will be handled by Spring. You are read about core pool size, max pool size and queue capacity to understand how to control number of thread running simultaneously.
Overall I think you are confused between the fact that Spring is fundamentally a IOC container and Spring provides ThreadPoolTaskExecutor for thread pool management (which is nothing but Spring's version of Java's java.util.concurrent.ThreadPoolExecutor).
Understand it this way: when you do taskExecutor.execute(aTask1); then thread pool management will be handled by Spring because you are using Spring's ThreadPoolTaskExecutor, ALSO bean management will be handled by Spring because aTask1 is object of Spring bean. And when you do taskExecutor.execute(new ATask("A task 1")); then thread pool management will be handled by Spring because you are using Spring's ThreadPoolTaskExecutor, BUT this time bean management will not be done by Spring because you are using new to create object yourself.

Related

Issue with Spring Boot Gemfire Integration

I am currently working on a project which uses Spring Boot Apache Kafka and Gemfire Integration . In this project I have to subscribe the topic from the kafka and delete some matching keys from the Gemfire Region .
I am able to successfully subscribe the topic but while deleting the keys from the Gemfire region it throws No bean such exception when I try to delete from that region, from the Gemfire Configuration I am using #EnableClusterDefined Regions . The issue is that Spring has a weird behavior that it loads the gemfire regions after the spring application context is loaded . To overcome this I made a custom repository implementing the Application Context Aware Overridden the setApplicationContext and wrote a method getRegion where I am getting the region by context. getBean("Region Name") ... but still I am not able to load the required region bean. Can someone suggest something
Regarding...
The issue is that Spring has a weird behavior that it loads the GemFire Regions after the Spring ApplicationContext is loaded.
Technically (from here, to here, and finally, here), this happens after the ClientCache bean is initialized, not necessarily after the Spring ApplicationContext is (fully) loaded, or rather after the ContextRefreshEvent. It is an issue in your Spring application configuration.
The feature to which you are referring is from Spring Data for Apache Geode, or alternatively VMware Tanzu GemFire (SDG).
The feature is used by declaring the SDG #EnableClusterDefinedRegions annotation (Javadoc) in your Spring application configuration.
The behavior might seem "weird", but is in fact quite necessary.
PREREQUISITE KNOWLEDGE
With Spring configuration, regardless of source: [XML, JavaConfig, Groovy, Annotations or otherwise], there are 2 primary phases: parsing and initialization.
Spring uses a generic, common representation to model the configuration (i.e. BeanDefinition) for each bean defined, declared and managed by the Spring container when parsing the bean definition(s) from any configuration source. This model is then used to create the resolved beans during initialization.
Parsing allows the Spring container to determine (for one) the necessary dependencies between beans and the proper order of initialization on startup.
When using SDG's #EnableClusterDefinedRegions annotation, the GemFire/Geode client Spring application (a GemFire/Geode ClientCache application) must be connected to an existing GemFire/Geode cluster, where the Regions have already been defined, to create matching client-side Regions.
In order to connect to a cluster from the client, you would have to have defined (explicitly or implicitly) a connection (or connections) to the cluster using a GemFire/Geode Pool (Javadoc). This Pool (or Pools) is also registered as a bean in the Spring container by SDG.
The ClientCache or client Pool beans contain the metadata used to create connections to the cluster. The connections are necessary to perform Region data access operations, or even determine the Regions that need to be created on the client-side to be able to perform Region data access operations and persist data on the server-side in the first place.
All of this cannot happen until the client Pools are "initialized", thereby forming connections to the cluster where the necessary request can then be made to determine the Regions in the cluster. This is not unlike how the Gfsh list regions command works, in fact. Gfsh must be connected to execute the list regions command.
The main purpose of using SDG's #EnableClusterDefinedRegions annotation is so you do not have to explicitly define client-side ([CACHING_]PROXY) Regions that have already been determined by an (existing) cluster. It is for convenience. But, it doesn't mean there are no (implied) dependencies on the resulting (client) Region imposed by your Spring application that must be carefully considered and ordered.
Now...
I suspect your Spring application is using Spring for Apache Kafka (??) to define Kafka Topic subscriptions/listeners to receive messages? Somehow you loosely coupled the Kafka Topic listener receiving messages from the Kafka queue to the GemFire/Geode client Region.
The real question then is, how did you initially get a reference to the client Region from which you delete keys when an event is received from the Kafka topic?
You say that, "I am able to successfully subscribe the topic but while deleting the keys from the Gemfire region it throws No bean such exception when i try to delete from that region."
Do you mean the NoSuchBeanDefinitionException? This Exception is typically thrown on startup when using Spring container dependency injection, such as when defining a #KafkaListener as described here), like so:
#Component
class MyApplicationListeners {
#Autowired
#Qualifier("myRegion")
private Region<String, Object> clientRegion;
#KafkaListener(id = "foo", topics = "myTopic")
public void listener(String key) {
clientRegion.remove(key);
}
}
However, when you specifically say, "..while deleting the keys from the GemFire Region..", would imply you were initially doing some sort of lookup (e.g. clientCache.getRegion(..)):
#Component
class MyApplicationListeners {
#Autowired
private ApplicationContext applicationContext;
#KafkaListener(id = "foo", topics = "myTopic")
public void listener(String key) {
applicationContext.getBean("myRegion", Region.class).remove(key);
}
}
Not unlike to your attempted workaround using a ApplicationContextAware implementation.
At any rate, you definitely have a bean initialization ordering problem, and I am nearly certain it is caused by a loose coupling between the bean dependencies (not to be confused with "tight coupling in code").
Not knowing all your Spring application configuration details for sure, you can solve this 1 of several ways.
First, and the easiest and most explicit (obvious and recommended) way to solve this with an explicit Region bean definition matching the server-side Region on the client:
#Configuration
#EnableClusterDefinedRegions
class MyApplicationConfiguration {
#Bean("myRegion")
ClientRegionFactoryBean myRegion(ClientCache cache) {
ClientRegionFactoryBean myRegion = new ClientRegionFactoryBean();
myRegion.setCache(cache);
myRegion.setName("myRegion");
myRegion.setShortcut(ClientRegionShortcut.PROXY);
return myRegion;
}
// other declared application bean definitions
}
Then when the Region is injected by the Spring container in:
#Autowired
#Qualifier("myRegion")
private Region<String, Object> clientRegion;
#KafkaListener(id = "foo", topics = "myTopic")
public void listener(String key) {
clientRegion.remove(key);
}
}
It will definitely exist!
SDG's #EnableClusterDefinedRegions is also careful not to stomp on explicit Region bean definitions if a Region bean is already defined (explicitly) in your Spring application configuration, as demonstrated above. Just be careful that the client Region (bean name) matches the server-side Region by "name".
Otherwise, you can play on the fact that the SDG framework attempts to early initialized client Regions from the cluster in the BeanPostProcessor by defining an "order", https://github.com/spring-projects/spring-data-geode/blob/2.7.1/spring-data-geode/src/main/java/org/springframework/data/gemfire/config/annotation/ClusterDefinedRegionsConfiguration.java#L90.
Then, you could simply do:
#Component
#Order(1)
class MyApplicationListeners {
#Autowired
#Qualifier("myRegion")
private Region<String, Object> clientRegion;
#KafkaListener(id = "foo", topics = "myTopic")
public void listener(String key) {
clientRegion.remove(key);
}
}
Using the Spring Framework #Order annotation on the MyApplicationListeners class containing your Kafka Listener used to delete keys from the cluster/server Region using the client Region.
In this case, no explicit client-side Region bean definition is necessary.
Of course, other, maybe, non-obvious dependency on your MyApplicationListener class in your Spring application configuration could force an eager initialization of the MyApplicationListener class and you could potentially still hit a NoSuchBeanDefinitionException on startup during DI. In this case, the Spring container must respect dependency order and therefor overrides the #Order definition on the MyApplicationListener class (bean).
Still, you could also delay the reception of events from the Kafka queues subscriptions for all topics by setting autoStartup to false; see here. Then, you could subsequently listen for a Spring container, ContextRefreshedEvent to startup the Kafka Listener Container to start receiving events in your #KafkaListeners once the Spring application is properly initialized. Remember, all automatic client Region bean creation using the SDG #EnableClusterDefinedRegions annotation happens inside a BeanPostProcessor, and all BeanPostProcessers are called by the Spring container before the context is completely refreshed (i.e. the ContextRefreshedEvent). See the Spring Framework documentation for more details on BPPs.
Anyway, you have a lot of options.

Async Transactional Saves do not work in Spring after upgrade

We upgraded Spring Boot from 1.5.8 to 2.6.2. It has introduced a problem that has us perplexed: Transactional saves are not processing from inside spawned threads.
We use JPA managed entities on a Mysql database and make calls down to the CrudRepository to save them.
Transactions inside the main thread work fine. However, when called from an asynchronous operation things go awry:
both async and sync calls go through the Spring SimpleJpaRepository.save() method. But the entityManager returns the object to persist with a null id in the case of the async operation.
I followed the flow through in both types of calls and can see that the save propagates down to the org.hibernate.internal.SessionImpl service.
From there it makes its way to AbstractSaveEventListener.class and that is where the discrepancy appears to be. In the performSaveOrReplicate method (hibernate-core:5.6.3), inTrx boolean is false in the async workflow whereas it is true in the synch one. Because of that the shouldDelayIdentityInserts flag gets set and an id does not appear to be generated for any entities in this thread.
We have tried different things to get this to work. For example, we used the transactionTemplate to have some specific control here, but that has not changed the behavior.
We were originally creating this async process by using the ApplicationEventPublisher to create an event. We also tried using completablefuture and other constructs with the same result as well as annotating the method with #Async and calling it directly.
The issue was that, with the upgrade to Spring Boot 2.6, Spring Batch implements a new Transaction Manager.
What we didn't realize is that this transaction manager was being autowired into our other services and did not work in this threaded context. You do not want to share a Batch processing Tx Manager with your API/misc services. Declaring a specific Transaction Manager there to keep them separate solved the issue.
Here is an example marking a PlatformTransactionManager with the Primary annotation to test its usage explicitly.
#Primary
#Bean
public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory(dataSource).getObject());
return transactionManager;
}

Default TaskExecutor implementation for Async event listeners

I am using an annotation-based event listener which I have also tagged as #Async. (See Asynchronous Listeners).
Which TaskExecutor implementation is Spring using to serve these requests? The documentation reads:
By default, when specifying #Async on a method, the executor that is used is the one configured when enabling async support, i.e. the “annotation-driven” element if you are using XML or your AsyncConfigurer implementation, if any.
The problem is that I haven't done any configuration for this at all, so I am not sure what my AsyncConfigurer implementation is to begin with.
My guess is that we're dealing a SimpleAsyncTaskExecutor.
I found the answer in the documentation for EnableAsync:
By default, Spring will be searching for an associated thread pool definition: either a unique TaskExecutor bean in the context, or an Executor bean named "taskExecutor" otherwise. If neither of the two is resolvable, a SimpleAsyncTaskExecutor will be used to process async method invocations.
So, it's the SimpleAsyncTaskExecutor.

Spring ThreadPoolTaskScheduler vs ThreadPoolTaskExecutor

It is mentioned in the Spring documentation that:
ThreadPoolTaskScheduler actually implements Spring's TaskExecutor interface as well, so that a single instance can be used for asynchronous execution as soon as possible as well as scheduled, and potentially recurring, executions.
So which are the scenarios where we would want to use ThreadPoolTaskExecutor instance over ThreadPoolTaskScheduler instance?
I am using currently using Spring XML. I am creating bean of ThreadPoolTaskScheduler as follows:
<task:scheduler id="myScheduler" pool-size="1"/>
while bean of ThreadPoolTaskExecutor instance can be created as
<task:executor id="executor" pool-size="10"/>
ThreadPoolTaskExecutor is a specialized class for executing tasks.
ThreadPoolTaskScheduler is a specialized class for scheduling tasks.
The sentence you quoted in the Spring documentation is only saying that you can use a scheduler to execute tasks, but that it is not its main purpose. A ThreadPoolTaskExecutor provides fine-grained configuration over the thread pool through its corePoolSize, maxPoolSize, keepAliveSeconds and queueCapacity properties. A scheduler such as ThreadPoolTaskScheduler does not provide such configuration.
As such, choosing between the two comes down the following question: do I need to execute or schedule execution of tasks?

Spring JPA and Sharing Entity Manager across multiple WARS and #Persistence Context thread safety

I read about about Persistence Context but not able to get a clear picture about my doubts which are as follows.
1. I have a DAO class which as a #PersistenceContext(unitName="") private EntityManager entityManager and from my Service method i am starting the transaction(Spring Managed) using #Transactional(propagation = Propagation.REQUIRED). I understand here is every time this Service method is called, a Transaction will start and when it reaches to DAO class it will use the same Transaction but for every EntityManager operation it look for Active PersistenceContext and create it as required. Is this approach correct and thread safe?
So, if a new thread starts the same service method then a new Transaction and a persistence Context will be created and flushed away when the method ends?
2. I have multiple WARS which need to interact with database so I am sharing the EntityManagerFactory using Spring Shared contexts. I am having all the Hibernate related configurations at a common place and in every WAR i am specifying where transactionManager will be shared. Is it right?
Please clarify my doubts and your comments are valued. Thanks in advance.

Resources