Transaction synchronization in Spring Boot - spring

I have a small Spring Boot application with spring-boot-starter-web, spring-boot-starter-data-jpa, and postgresql as dependencies.
I'm able to use the #Transactional annotation and use JPA to fetch and save entities to the database. However, if I were to add afterCommit/afterCompletion hooks via registering a synchronization, it gives an IllegalStateException saying that Transaction synchronization is not active.
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
//this doesn't get called
log.info("do something here");
}
});
Doing TransactionSynchronizationManager.initSynchronization(); gets rid of the error, but the hooks don't get called (eg: the afterCommit hook doesn't get called even though the transaction has committed.)
Any clues on how to debug this?

It turned out that I had forgotten to include the build plugin that is used to create the AoP-proxies for beans having #Transactional annotations.
In the absence of this plugin, no proxies would get generated, and the code would run non-transactionally; except for when it enters the JpaRepository methods where it would create a short-lived transaction for the duration of the call (such as save/findAll/delete).
This is the plugin that I missed including in my pom.xml (this got generated in the pom output by the spring initializr (https://start.spring.io/) but I didn't notice it at first and didn't copy it over into my pom)
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

I think you need #TransactionalEventListener annotation. It supports hooks BEFORE_COMMIT, AFTER_ROLLBACK, AFTER_COMPLETION, AFTER_COMMIT and AFTER_ROLLBACK.
More info in this post: Better application events in Spring Framework 4.2.

Related

'org.springframework.boot.web.server.LocalServerPort' is deprecated

Since Spring boot 2.7.1, #LocalServerPort (in the package org.springframework.boot.web.server.LocalServerPort) is deprecated.
What can I use in stead of this annotation?
Import the below package in your spring boot 2.7.1. use #LocalServerPort for the below-mentioned package.
org.springframework.boot.test.web.server
You can read about it here in the link
Once go through you query again for the SQL error.
You may try using #Value("${server.port}") to get the port. One thing to note here is since Spring Boot release 2.7.0, #LocalServerPort is moved to the test jar because the Spring Boot team only intended that they be used for tests. However, what Puneet suggests will also work provided that you have the below dependency in your pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
You can also use an event listener to grab the port once the web server has started. Depending on what you're trying to do this might work, but be aware that they even fires after beans have been created.
#EventListener
void onWebInit(WebServerInitializedEvent event) {
int port = event.getWebServer().getPort();
}
The simplest approach here is to use either #Value("${server.port}") or what Puneet suggests. Use the import from the test jar. And having the above-mentioned dependency in your pom.xml is vital for this to work.
You can checkout the github issue related to this migration.
The org.springframework.boot.web.server.LocalServerPort is deprecated.
You can import org.springframework.boot.test.web.server.LocalServerPort

How to use and config caching in Spring MVC

I want to cache the following getMessagesList method. I want to call one time When user log into the system. Therefor I think caching is the best solution for that. And I need to remove when user log out. How I can do this.
public List<String> getMessagesList(String username)
{ // return messages list in DB by username}
My project was create using Maven 4.0 and Spring MVC. spring version 5.3
Assuming you use Spring Security as part of your app, it should be managing your session, and every time you log out, it will create a new session. Unless you had posted this code, I'm not going to be able to help you there. However, assuming you can log in/out, this should be covered already.
As for the cacheing, in general, this sounds like a Database Caching need, which is something that you would use Spring Boot Caching on.
To use this in Spring Boot, you would add the following dependency to maven (or the equivalent in Gradle, etc):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
Adjust your application to allow using Cacheing, which can be done by adding the annotation #EnableCaching to your Spring Boot application
#SpringBootApplication
#EnableCaching
public class MyApplication {
...
}
Create a Java Service Object, called something like MessagesService.class:
#CacheConfig(cacheNames={"Messages"})
public class MessagesService {
#Cacheable(value="cacheMessages")
List<String> getMessages() {
//access the database to load data here
...
}
...
}

Spring - How to cache in self-invocation with aspectJ?

Thank you to click my question.
I want to call a caching method in self-invocation, so I need to use AspectJ.
(cache's config is okay)
add AspectJ dependencies
implementation 'org.springframework.boot:spring-boot-starter-aop'
add #EnableCaching(mode = AdviceMode.ASPECTJ) to my application.java
#EnableJpaAuditing
#EnableCaching(mode = AdviceMode.ASPECTJ) // <-- here
#SpringBootApplication
public class DoctorAnswerApplication {
public static void main(String[] args) {
SpringApplication.run(DoctorAnswerApplication.class, args);
}
}
my service.java
#Service
public class PredictionService {
#Cacheable(value = "findCompletedRecordCache")
public HealthCheckupRecord getRecordComplete(Long memberId, String checkupDate) {
Optional<HealthCheckupRecord> recordCheckupData;
recordCheckupData = healthCheckupRecordRepository.findByMemberIdAndCheckupDateAndStep(memberId, checkupDate, RecordStep.COMPLETE);
return recordCheckupData.orElseThrow(NoSuchElementException::new);
}
}
my test code
#Test
public void getRecordCompleteCacheCreate() {
// given
Long memberId = (long)this.testUserId;
List<HealthCheckupRecord> recordDesc = healthCheckupRecordRepository.findByMemberIdAndStepOrderByCheckupDateDesc(testUserId, RecordStep.COMPLETE);
String checkupDate = recordDesc.get(0).getCheckupDate();
String checkupDate2 = recordDesc.get(1).getCheckupDate();
// when
HealthCheckupRecord first = predictionService.getRecordComplete(memberId,checkupDate);
HealthCheckupRecord second = predictionService.getRecordComplete(memberId,checkupDate);
HealthCheckupRecord third = predictionService.getRecordComplete(memberId,checkupDate2);
// then
assertThat(first).isEqualTo(second);
assertThat(first).isNotEqualTo(third);
}
What did I don't...?
I didn't make any class related with aspectJ.
I think #EnableCaching(mode = AdviceMode.ASPECTJ) make #Cacheable work by AspectJ instead Spring AOP(proxy).
With thanks to #kriegaex, he fixed me by pointing out the spring-aspects dependency and the load-time-weaving javaagent requirement. For the convenience of others, the configuration snippets for Spring Boot and Maven follow.
(Note: In the end, I didn't feel all this (and the side-effects) were worth it for my project. See my other answer for a simple, if somewhat ugly, workaround.)
POM:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
Application Config:
#Configuration
#EnableCaching(mode = AdviceMode.ASPECTJ)
public class ApplicationConfig { ... }
Target Method:
#Cacheable(cacheNames = { "cache-name" })
public Thingy fetchThingy(String identifier) { ... }
Weaving mechanism:
Option 1: Load Time Weaving (Spring default)
Use JVM javaagent argument or add to your servlet container libs
-javaagent:<path-to-jar>/aspectjweaver-<version>.jar
Option 2: Compile Time Weaving
(This supposedly works, but I found a lack of coherent examples for use with Spring Caching - see further reading below)
Use aspectj-maven-plugin: https://www.mojohaus.org/aspectj-maven-plugin/index.html
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<showWeaveInfo>false</showWeaveInfo>
<Xlint>warning</Xlint>
<verbose>false</verbose>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<complianceLevel>${java.version}</complianceLevel>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
For reference/search purposes, here is the error that started all this:
Caused by: java.io.FileNotFoundException: class path resource [org/springframework/cache/aspectj/AspectJCachingConfiguration.class] cannot be opened because it does not exist
More reading:
AOP and Spring: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop
AspectJ tutorial (Baeldung): https://www.baeldung.com/aspectj
Complie-Time Weaving vs Load-Time Weaving: https://stackoverflow.com/a/23042793/631272
CTW vs LTW in spring brief: https://stackoverflow.com/a/41370471/631272
CTW vs LTW Tutorial: https://satenblog.wordpress.com/2017/09/22/spring-aspectj-compile-time-weaving/
Getting CTW to work in Eclipse M2e: https://stackoverflow.com/a/19616845/631272
CTW and Java 11 issues (may have been part of my struggles with it): https://www.geekyhacker.com/2020/03/28/how-to-configure-aspectj-in-spring-boot/
Did you read the Javadoc for EnableCaching?
Note that if the mode() is set to AdviceMode.ASPECTJ, then the value of the proxyTargetClass() attribute will be ignored. Note also that in this case the spring-aspects module JAR must be present on the classpath, with compile-time weaving or load-time weaving applying the aspect to the affected classes. There is no proxy involved in such a scenario; local calls will be intercepted as well.
So please check if you
have spring-aspects on the class path and
started your application with the parameter java -javaagent:/path/to/aspectjweaver.jar.
There is an alternative to #2, but using the Java agent is the easiest. I am not a Spring user, so I am not an expert in Spring configuration, but even a Spring noob like me succeeded with the Java agent, so please give that a shot first.
TL;DR: If AspectJ is giving you headaches and you don't really need it other than to work around Spring Caching self-invocation, it might actually be cleaner/lighter/easier to add a simple "cache delegate" bean that your service layers can re-use. No extra dependencies, no performance impacts, no unintended side-effects to changing the way spring proxies work by default.
Code:
#Component
public class CacheDelegateImpl implements CacheDelegate {
#Override #Cacheable(cacheNames = { "things" })
public Object getTheThing(String id) { ... }
}
#Service
public class ThingServiceImpl implements ThingService {
#Resource
private CacheDelegate cacheDelegate;
public Object getTheThing(String id) {
return cacheDelegate.getTheThing(id);
}
public Collection<Object> getAllTheThings() {
return CollectionUtils.emptyIfNull(findAllTheIds())
.parallelStream()
.map(this::getTheThing)
.collect(Collectors.toSet());
}
}
Adding another answer, because to solve this same issue for myself I ended up changing direction. The more direct solutions are noted by #kriegaex and myself earlier, but this is a different option for people that have issues getting AspectJ to work when you don't fundamentally need it.
For my project, adding AspectJ only to allow cacheable same-bean references was a disaster that caused 10 new headaches instead of one simple (but annoying) one.
A brief non-exhaustive rundown is:
Introduction of multiple new dependencies
Introduction of complex POM plugins to either compile-time weave (which never worked quite right for me) OR marshal the run-time byte-weaving jar into the correct place
Adding a runtime javaagent JVM argument to all our deployments
Much poorer performance at either build-time or start-time (to do the weaving)
AspectJ picking up and failing on Spring Transactional annotations in other areas of the codebase (where I was otherwise happy to use Spring proxies)
Java versioning issues
Somehow a dependency on the ancient Sun Microsystems tools.jar (which is not present in later OpenJDK versions)
Generally poor and scattershot doc on how to implement the caching use case in isolation from Spring Transactions and/or without full-blown AOP with AspectJ (which I don't need/want)
In the end, I just reverted to Spring Caching by proxy and introduced a "cache delegate" that both my service methods could refer to instead. This workaround is not the prettiest, but for me was preferable to all the AspectJ hoops I was jumping through when I really didn't need AspectJ. I just want seamless caching and DRY service code, which this workaround achieves.

Using ServiceTasks with TimerCatchingEvents in activiti bpmn workflow behaving inconsistently

When using ServiceTasks with IntermediateTimerEvents sometimes it did not execute the service task due to exception raised in ACT_RU_JOB table. EXCEPTION_MSG_ column in ACT_RU_JOB table is populated with Unknown property used in expression: #{serviceTask1.execute(execution). value for that particular job.
Please refer the activiti workflow image i have attached below for clear picture of the usecase.
NOTE: I am using spring-boot microservice using maven to run the workflow.
spring-boot version: 1.5.5.RELEASE
activity version: 5.22.0
Activity and Connection Pool dependencies in pom.xml:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>${activiti.version}</version>
</dependency>
Connection Pooling dependency:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>
application.properties configuration attached below:
spring.activiti.job-executor-activate=false
spring.activiti.async-executor-enabled=true
spring.activiti.async-executor-activate=true
spring.datasource.tomcat.initial-size=15
spring.datasource.tomcat.max-active=100
ANALYSIS: The error i am getting above is not consistent.
activiti workflow
Project link on Github:
https://github.com/cloudnativeview/spring-activiti-bpmn-kickstart
The exception is totally expected because you don't set the appropriate process variable when starting your process instance. Also Activiti engine is not able to resolve named bean serviceTask1.
From the Activiti 5.22.0 userguide docs (inside the file activiti-5.22.0.zip), more precisely section 8.5.3. Java Service Task you have 4 ways of declaring how to invoke Java logic.
Specifying a class that implements JavaDelegate or ActivityBehavior.
Evaluating an expression that resolves to a delegation object.
Invoking a method expression.
Evaluating a value expression.
In your BPMN process definition file you are invoking service task Java logic with method expression technique. If you want to successfully invoke your method you have to make sure your bean serviceTask1 is a process variable from your execution or a named object in Spring context.
Unfortunately you don't provide necessary information to your BPM engine on where to find your bean with Java logic.
I suggest you the following steps.
Read the technical and userguide documentation (chapter 8.5.3. Java Service Task), from the provided links.
Try to invoke Java logic with the way of specifying a class that implements JavaDelegate or ActivityBehavior. In my humble opinion it is far more easier to get the first touch with Activiti BPM engine.

WildFly 10, JCache - method caching

i have simple application using Spring Boot. I wanted allow method caching with JSR107 - JCache. So with help of tutorial i put together this code :
#CacheResult(cacheName = "testpoc")
public Country getCountry(Integer id){
System.out.println("---> Loading country with code '" + id + "'");
return new Country(id, "X", "Title");
}
with this POM file
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
...
(dependency 'spring-boot-starter-web' is there for simple REST service which call getCountry method)
Everything works like documentations says - method is invoked only once.
Now i wanted to try it on WildFly 10 application server
I have modified pom file :
excluded tomcat
exluded spring-boot-starter-cache
added infinispan-jcache (because i want to use cache configured / managed by wildfly in standalone/domain.xml)
Check pom file here on pastebin.
Problem is, that i am receiving following error :
Cannot find cache named 'java:jboss/infinispan/app-cache'
(i have tried to use both JNDI assigned and name to infinispan cache configured in wildfly).
Following code created Cache object (so i can used it) :
CacheManager cacheManager = Caching.getCachingProvider().getCacheManager();
Cache<String, String> cache = cacheManager.createCache("testpoc", new MutableConfiguration<String, String>());
Question :
It is possible to use JCache method caching on WildFly 10 using Infinispan managed by WildFly ?
Or Infinispan should be used for method caching like JCache, hence JCache has "more functionality" than Infinispan.
Thank you very much
PS :It is not problem for me to put whole code on github and post link - it is few lines of code ...
There are a couple of problems with your approach so let me go through them in steps.
At first you need to use proper Infinispan setup. Infinispan bits shipped with WF should be considered as internal or private. In order to use Infinispan in your app properly - either add org.infinispan:infinispan-embedded to your deployment or install Infinispan Wildfly modules. You can find installation guide here (it's a bit outdated but still, the procedure is exactly the same - unpack modules into WF and use Dependencies MANIFEST.MF entry).
Once you have successfully installed Infinispan (or added it to your app), you need to consider whether you want to use Spring Cache or JCache. If you're only interested in using annotations - I would recommend the former since it's much easier to setup (all you need to do is to add #EnableCaching to one of your configurations). Finally with Spring Cache you will create an Infinispan CacheManager bean. An example can be found here.
Final note - if you still need to use JCache - use this manual to setup Caching Provider.

Resources