Avoiding multi-instance singletons in single context Spring app - spring

A #PostConstruct function returns a System.identityHashCode(this) different from an #Autowired version of the "same" singleton...
I do not have multiple application contexts there is only a single context involved. I don't expect the presence of threading or static inner classes to be relevant, but I have included them just in case.
#Service
public class A extends Thread {
#PostContruct
public void init() {
start();
}
#Service
public static class B extends A {
public B() {
}
public void run() throws Throwable {
System.err.println("B("+System.identityHashCode(this)+") waiting...");
synchronized(this) {wait(6000000);}
System.err.println("B("+System.identityHashCode(this)+") done waiting the full 6000 seconds and never less");
}
}
private B b;
#Autowired(required=true)
public A(B b) {
this.b = b;
}
public void run() throws Throwable {
Thread.sleep(10000);//just for example
//the following lines could be triggered from any thread
System.err.println("autowire B("+System.identityHashCode(b)+").notifyAll()");
synchronized(b) {b.notifyAll();}
}
}
I expect the original hash code of B to match the Autowired hash code found from class A. However they are different objects and so notifyAll never interrupts B's wait.
B(329759586) waiting...
//10 seconds latter
autowire B(932795037).notifyAll()
//5990 seconds latter
B(329759586) done waiting the full 6000 seconds and never less

Related

Execute 2 mthods in a songle transaction with separate Retry logic

Below is my requirement
begin trans
insertData();
updateData();
end trans
Also lets say insertDta method throws some error then I need to retry 5 times .Same with updateData().I should not retry both the methods at the same time i.e if i retry m2() 5 times them m1() should not be retried.
Is below the correct way to do it? I call m3() from another class .
My concern is that interceptors are added in correct and deterministic order.
#Repository
DaoA
{
void insertData();
}
#Repository
DaoB
{
void updateData();
}
Below is my Service class .
#Service
ServiceA
{
#Retryable( maxAttempts = 5)
public void m1 ()
{
daoA.insertData();
}
#Retryable( maxAttempts = 5)
public void m2 ()
{
daoB.updateData();
}
#Transactional
public void m3 ()
{
m1();
m2();
}
m3() needs to be in a different bean - calling m1() and m2() directly within the class bypasses the proxy and they won't be retried.
In any case, the transaction should be within the retry logic, not the other way around; you need to start a new transaction for each attempt.
If I got your requirement right, this should work for you.
#Service
ServiceA {
public void m1 () {
daoA.insertData();
}
public void m2 () {
daoB.updateData();
}
#Transactional
#Retryable(value = {Exception.class}, maxAttempts = 5)
public void m3 () {
m1();
m2();
}
}
This will make sure the total number of retries is maxAttempts = 5.

Calling #Transactional methods asynchronously in quarkus

I have an application scoped bean as follows
#ApplicationScoped
public class Worker {
public void process(Long id) {
final Runnable runnable = () -> {
doATransaction(id);
};
executor.execute(runnable);
}
#Transactional
public void doATransaction(Long id) {
User user = User.findById(id);
}
}
I am getting a javax.enterprise.context.ContextNotActiveException. I also tried adding the doATransaction() to another Bean, which was injected in this Worker, as suggested here. Still had the same problem.
Does anyone have an idea of what I could do next?
The Exception I am getting is
Exception in thread "pool-13-thread-3" javax.enterprise.context.ContextNotActiveException
at io.quarkus.arc.impl.ClientProxies.getDelegate(ClientProxies.java:40)
at io.quarkus.hibernate.orm.runtime.RequestScopedSessionHolder_ClientProxy.arc$delegate(RequestScopedSessionHolder_ClientProxy.zig:42)
at io.quarkus.hibernate.orm.runtime.RequestScopedSessionHolder_ClientProxy.getOrCreateSession(RequestScopedSessionHolder_ClientProxy.zig:160)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.acquireSession(TransactionScopedSession.java:103)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.find(TransactionScopedSession.java:168)
at io.quarkus.hibernate.orm.runtime.session.ForwardingSession.find(ForwardingSession.java:68)
at io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.findById(AbstractJpaOperations.java:173)
at com.example.User.findById(User.java)

Redesign to avoid circular dependency in Spring boot

I've read many articles about the DI principle in spring framework, but I still don't understand how to resolve circular dependency in below case:
Entity class
class Thread {
...
String status;
}
class Reply {
Thread thread;
String status;
...
}
Service layer to handle CRUD of these 2 object
class ThreadService {
#Autowired private ReplyService replySvc;
public void suspend (Thread t) {
t.setStatus("suspended");
replySvc.getReplies(t).forEach(r -> {
r.setStatus("suspended");
replySvc.update(r);});
dao.save(t);
}
public void update(Thread t) {
...
dao.save(t);
}
...
}
class ReplyService {
#Autowired private ThreadService threadSvc;
public Reply create(...) {
thread.setStatus("replied");
threadSvc.update(thread);
...
return reply;
}
public List<Reply> getReplies(Thread t) {
...
return replies;
}
public void update(Reply r) {
...
dao.save(r);
}
}
Both services involve an update call of another service to update the corresponding entity.
If I'd like to apply DI principle, what should I change?
Based on your design, the most dangerous dependency is
ThreadService (this should be isolated) -> ReplyService.
You can decouple your components using event based design:
ThreadService
method call : suspend
sent event : ThreadSuspendedEvent
ReplyService should be a listener for event ThreadSuspendedEvent and when the event is received, this code should be executed:
getReplies(t).forEach(r -> {
r.setStatus("suspended");
update(r);});

Mockito: Verifying a method was called with a functional parameter

I have a simple scenario in which am trying to verify some behavior when a method is called (i.e. that a certain method was called with given parameter, a function pointer in this scenario). Below are my classes:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
AppBootStrapper bootStrapper = context.getBean(AppBootStrapper.class);
bootStrapper.start();
}
}
#Component
public class AppBootStrapper {
private NetworkScanner networkScanner;
private PacketConsumer packetConsumer;
public AppBootStrapper(NetworkScanner networkScanner, PacketConsumer packetConsumer) {
this.networkScanner = networkScanner;
this.packetConsumer = packetConsumer;
}
public void start() {
networkScanner.addConsumer(packetConsumer::consumePacket);
networkScanner.startScan();
}
}
#Component
public class NetworkScanner {
private List<Consumer<String>> consumers = new ArrayList<>();
public void startScan(){
Executors.newSingleThreadExecutor().submit(() -> {
while(true) {
// do some scanning and get/parse packets
consumers.forEach(consumer -> consumer.accept("Package Data"));
}
});
}
public void addConsumer(Consumer<String> consumer) {
this.consumers.add(consumer);
}
}
#Component
public class PacketConsumer {
public void consumePacket(String packet) {
System.out.println("Packet received: " + packet);
}
}
#RunWith(JUnit4.class)
public class AppBootStrapperTest {
#Test
public void start() throws Exception {
NetworkScanner networkScanner = mock(NetworkScanner.class);
PacketConsumer packetConsumer = mock(PacketConsumer.class);
AppBootStrapper appBootStrapper = new AppBootStrapper(networkScanner, packetConsumer);
appBootStrapper.start();
verify(networkScanner).addConsumer(packetConsumer::consumePacket);
verify(networkScanner, times(1)).startScan();
}
}
I want to verify that bootStrapper did in fact do proper setup by registering the packet consumer(there might be other consumers registered later on, but this one is mandatory) and then called startScan. I get the following error message when I execute the test case:
Argument(s) are different! Wanted:
networkScanner bean.addConsumer(
com.spring.starter.AppBootStrapperTest$$Lambda$8/438123546#282308c3
);
-> at com.spring.starter.AppBootStrapperTest.start(AppBootStrapperTest.java:24)
Actual invocation has different arguments:
networkScanner bean.addConsumer(
com.spring.starter.AppBootStrapper$$Lambda$7/920446957#5dda14d0
);
-> at com.spring.starter.AppBootStrapper.start(AppBootStrapper.java:12)
From the exception, clearly the function pointers aren't the same.
Am I approaching this the right way? Is there something basic I am missing? I played around and had a consumer injected into PacketConsumer just to see if it made a different and that was OK, but I know that's certainly not the right way to go.
Any help, perspectives on this would be greatly appreciated.
Java doesn't have any concept of "function pointers"; when you see:
networkScanner.addConsumer(packetConsumer::consumePacket);
What Java actually compiles is (the equivalent of):
networkScanner.addConsumer(new Consumer<String>() {
#Override void accept(String packet) {
packetConsumer.consumePacket(packet);
}
});
This anonymous inner class happens to be called AppBootStrapper$$Lambda$7. Because it doesn't (and shouldn't) define an equals method, it will never be equal to the anonymous inner class that the compiler generates in your test, which happens to be called AppBootStrapperTest$$Lambda$8. This is regardless of the fact that the method bodies are the same, and are built in the same way from the same method reference.
If you generate the Consumer explicitly in your test and save it as a static final Consumer<String> field, then you can pass that reference in the test and compare it; at that point, reference equality should hold. This should work with a lambda expression or method reference just fine.
A more apt test would probably verify(packetConsumer, atLeastOnce()).consumePacket(...), as the contents of the lambda are an implementation detail and you're really more concerned about how your component collaborates with other components. The abstraction here should be at the consumePacket level, not at the addConsumer level.
See the comments and answer on this SO question.

What could cause a class implementing "ApplicationListener<ContextRefreshedEvent>" not to be notified of a "ContextRefreshedEvent"

I have a Spring application listener implementing ApplicationListener<ContextRefreshedEvent> as follows:
#Profile({ Profiles.DEFAULT, Profiles.CLOUD, Profiles.TEST, Profiles.DEV })
#Component
public class BootstrapLoaderListener implements ApplicationListener<ContextRefreshedEvent>, ResourceLoaderAware, Ordered {
private static final Logger log = Logger.getLogger(BootstrapLoaderListener.class);
#Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
#Autowired
private DayToTimeSlotRepository dayToTimeSlotRepository;
#Autowired
private LanguageRepository languageRepository;
private ResourceLoader resourceLoader;
#Override
#Transactional
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
initApplication();
}
private void initApplication() {
if (dayToTimeSlotRepository.count() == 0) {
initDayToTimeSlots();
}
if (languageRepository.count() == 0) {
initLanguages();
}
}
private void initDayToTimeSlots() {
for (Day day : Day.values()) {
for (TimeSlot timeSlot : TimeSlot.values()) {
DayToTimeSlot dayToTimeSlot = new DayToTimeSlot();
dayToTimeSlot.setDay(day);
dayToTimeSlot.setTimeSlot(timeSlot);
dayToTimeSlot.setDisabled(isDayToTimeSlotDisabled(timeSlot, day));
dayToTimeSlotRepository.save(dayToTimeSlot);
}
}
}
...
I rely on this listener class to insert reference data that is not updated nor deleted and I have a number of Spring integration tests that use this class, one of which fails because the listener is not notified (initDayToTimeSlots is not invoked).
I am trying to pinpoint where the problem comes from by debugging the tests and I noticed that when I run the problematic test class on its own, the tests contained in the class pass (indicating that the listener is notified) but when I run all of my application test classes together, the listener is not notified causing the test to fail (indicating that some other test changes/dirties the context).
Here is the problematic test class:
#ActiveProfiles({ Profiles.TEST })
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { FullIntegrationTestConfiguration.class, BaseTestConfiguration.class })
public class RegularDayToTimeSlotsTest {
private static int NUMBER_OF_REGULAR_DAY_TO_TIME_SLOTS_IN_WEEK = 25;
#Before
public void setup() {
//org.hsqldb.util.DatabaseManagerSwing.main(new String[] { "--url", "jdbc:hsqldb:mem:bignibou", "--noexit" });
}
#Autowired
private AdvertisementService advertisementService;
#Test
public void shouldNotContainSaturdayNorSunday() {
Set<DayToTimeSlot> regularDayToTimeSlots = advertisementService.retrieveRegularDayToTimeSlots();
assertThat(regularDayToTimeSlots).onProperty("day").excludes(Day.SATURDAY, Day.SUNDAY);
assertThat(regularDayToTimeSlots).onProperty("day").contains(Day.MONDAY, Day.THUESDAY);
}
#Test
public void shouldNotContainEveningNorNighttime() {
Set<DayToTimeSlot> regularDayToTimeSlots = advertisementService.retrieveRegularDayToTimeSlots();
assertThat(regularDayToTimeSlots).onProperty("timeSlot").excludes(TimeSlot.EVENING, TimeSlot.NIGHTTIME);
assertThat(regularDayToTimeSlots).onProperty("timeSlot").contains(TimeSlot.MORNING, TimeSlot.LUNCHTIME);
}
#Test
public void shouldContainCorrectNumberOfDayToTimeSlots() {
Set<DayToTimeSlot> regularDayToTimeSlots = advertisementService.retrieveRegularDayToTimeSlots();
assertThat(regularDayToTimeSlots).hasSize(NUMBER_OF_REGULAR_DAY_TO_TIME_SLOTS_IN_WEEK);
}
}
I am puzzled to see that both the prepareRefresh() and finishRefresh() methods within AbstractApplicationContext.refresh method are indeed called but that my listener is not notified...
Has anyone got any clue?
P.S. I know I could use #DirtiesContext in order to get a fresh context and I also know it would be preferable not to rely on an application listener for my tests but I am very anxious to understand what is going wrong here. Hence this post.
edit 1: When I debug the problematic test class in isolation, I notice that the event source is of type GenericApplicationContext and as explained above the test passes OK because the listener is notified. However when all test classes are run together, the event source is, oddly enough, of type GenericWebApplicationContext and no listener is found here in SimpleApplicationEventMulticaster:
#Override
public void multicastEvent(final ApplicationEvent event) {
for (final ApplicationListener<?> listener : getApplicationListeners(event)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
#Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
edit 2: my comments in edit 1 make me asks myself what is responsible for determining the uniqueness of context configuration...
For instance, I have only two test classes with the following context configuration:
#ContextConfiguration(classes = { FullIntegrationTestConfiguration.class, BaseTestConfiguration.class })
I guess they both will use the same cached context, won't they? Now can a third class use the same cached context even though it does not have exactly the same context configuration?
Why does my test get a GenericWebApplicationContext above?
my comments in edit 1 make me asks myself what is responsible for
determining the uniqueness of context configuration...
The elements that make up the context cache key are described in the Context caching section of the "Testing" chapter in the reference manual.
For instance, I have only two test classes with the following context
configuration:
#ContextConfiguration(classes = {
FullIntegrationTestConfiguration.class, BaseTestConfiguration.class })
I guess they both will use the same cached context, won't they?
If they declare only those two configuration classes in that exact order, then yes.
Now can a third class use the same cached context even though it does not
have exactly the same context configuration?
No.
Why does my test get a GenericWebApplicationContext above?
A GenericWebApplicationContext is only loaded if your test class (or one of its superclasses) is annotated with #WebAppConfiguration.
If you are experiencing behavior that contradicts this, then you have discovered a bug in which case we would appreciate it if you could produce a scaled down test project in the issue repository and create a corresponding JIRA issue against the "Spring Framework" and its "Test" component.
Thanks,
Sam (author of the Spring TestContext Framework)

Resources