Threading issue with google guice and spring - java

I need to integrate an existing spring application with a custom elasticsearch river, ES rivers manage their dependencies using Google Guice and run in their own set of threads.
I've created a simple class which returns a static reference to spring context and configured a Guice module that returns objects from spring context. To ensure proper synchronization across guice threads and spring ones I've used a CountDownLatch released after the context is fully initialized. Here is some code
public class GuiceSpringIntegrator implements ApplicationListener<ContextRefreshedEvent> {
private static ApplicationContext context;
private static final CountDownLatch contextLatch = new CountDownLatch(1);
#Override public void onApplicationEvent(ContextRefreshedEvent event) {
try {
// check some stuff in context
} finally {
log.debug("Setting application context as static field of {}", getClass().getSimpleName());
GuiceSpringIntegrator.context = event.getApplicationContext();
log.info("Releasing latch for application context");
contextLatch.countDown();
}
}
public static ApplicationContext getApplicationContext() {
if (null == context) {
log.info("ApplicationContext not yet initialized, wait for it in thread {}", Thread.currentThread().getName());
Uninterruptibles.awaitUninterruptibly(contextLatch); <-- !!! SPRING INITIALIZATION CODE HANGS HERE
log.debug("Returning application context since now context is initialized");
Preconditions.checkState(context != null, "ApplicationContext should have been initialized properly");
}
return context;
}
}
This is the guice module that uses the class above
/**
* Guice module configuration
*/
public class ElasticSearchModule extends AbstractModule {
#Override
protected void configure() {}
#Provides #Singleton TaskScheduler getSchedulerInstance() {
return GuiceSpringIntegrator.getApplicationContext().getBean(TaskScheduler.class);
}
// and so on...
}
However when I start the application (especially on fast servers) occasionally the application hangs at the line marked in the above code. I double checked every code path which lead to the #getApplicationContext() method call and they are (should be) invoked by guice, so eventually the latch should be released and the code should proceed.
Is there a better way to handle this case?
Is there a way to check if I'm inside spring initialization code, something like isEventDispatchThread for swing? I'd like to use that code to trace if I'm calling #getApplicationContext from spring initialization code in some way?
Why #onApplicationEvent seems to be invoked before the context initialization is actually completed?
Any hint on how to debug this issue? I was reading about taking thread dumps on live server, is that right?

This doesn't really answer your question except for point #4, but I want to write code so I'm making it into an answer instead of a comment.
I would try changing this:
Uninterruptibles.awaitUninterruptibly(contextLatch); <-- !!! SPRING INITIALIZATION CODE HANGS HERE
to this:
boolean awaitResult = Uninterruptibles.awaitUninterruptibly(contextLatch, 2, TimeUnit.MINUTES);
if(awaitResult) {
throw new IllegalStateException("Unable to load Spring Context");
}
Then you can catch that exception somewhere and write it's stack trace to a log; this won't fix your problem but it likely will give you some more visibility into where the deadlock is?

Related

Spring boot - task on startup preventing application interface exposure

Is it possible to somehow in Spring boot application achieve some startup procedure that blocks every exposure of endpoints (and possibly other application public interfaces) until the startup procedure is completed?
I mean something like
#Component
public class MyBlockingStartupRunner implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) {
// doing some task, calling external API, processing return values, ...
startTask();
// at this point app should be available for rest calls, scheduled tasks etc.
someObject.appIsReadyToGo(); // alternatively app would be ready at the end of the method
}
}
Problem with this approach of using ApplicationRunner is there might be some API calls to the server that I am unable to serve and therefore I would need to add some check at every API endpoint to prevent this. Or, alternatively, create some interceptor that would "block" all public communication and which would probably read some property from some service which tells it if app is ready or not. But thats not the approach I would like and I wonder if Spring implemented this somehow.
If it is acceptable to run your start-up tasks before the web server instance has started at all, you could use SmartLifecycle to add a start task.
#Component
class MyStartup implements SmartLifecycle {
private final ServletWebServerApplicationContext ctx;
private final Log logger = LogFactory.getLog(MyStartup.class);
#Autowired
MyStartup(ServletWebServerApplicationContext ctx) {
this.ctx = ctx;
}
#Override
public void start() {
logger.info("doing start stuff: " + ctx.getWebServer());
startTask();
}
#Override
public void stop() {}
#Override
public boolean isRunning() {
return false;
}
#Override
public int getPhase() {
return 100;
}
}
Because the task runs before the web server has started (rather than blocking access) this might be a different approach.

How to add SynchronizationCallbacks to #TransactionalEventListener during spring boot application startup?

I have a spring boot application that uses a few #TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT). I noticed that spring boot doesn't do any exception logging for them when they end up with an exception being thrown.
Because of this I wanted to add some generic logging facility for such exceptions. I found that TransactionalApplicationListener.SynchronizationCallback is the interface I need to implement. However it seems complicated to register these callbacks. I didn't find any call of TransactionalApplicationListener#addCallback in the spring dependencies that would achieve this.
Trying to get a list of TransactionalApplicationListener and the SynchronizationCallback injected and then call addCallback in a #PostConstruct didn't get me further because there were always no listeners injected even though the application did make successful use of them.
So how do I add SynchronizationCallbacks to TransactionalApplicationListeners during spring boot application startup?
The first thing to note is that TransactionalApplicationListeners like all ApplicationListener are not beans in the spring context. They live somewhat outside of it (see org.springframework.context.ConfigurableApplicationContext#addApplicationListener). So injecting them is not possible for the application context.
While debugging and looking through spring sources one finds that these listeners are being created by org.springframework.transaction.event.TransactionalEventListenerFactory. And that is where my solution steps into. We decorate that factory with another one that is aware of SynchronizationCallbacks:
public class SynchronizationCallbackAwareFactory implements EventListenerFactory, Ordered {
private final TransactionalEventListenerFactory delegate;
private final Provider<List<SynchronizationCallback>> synchronizationCallbacks;
private final int order;
public SynchronizationCallbackAwareFactory(TransactionalEventListenerFactory transactionalEventListenerFactory,
Provider<List<SynchronizationCallback>> synchronizationCallbacks,
int order) {
this.delegate = transactionalEventListenerFactory;
this.synchronizationCallbacks = synchronizationCallbacks;
this.order = order;
}
#Override
public boolean supportsMethod(Method method) {
return delegate.supportsMethod(method);
}
#Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
ApplicationListener<?> applicationListener = delegate.createApplicationListener(beanName, type, method);
if (applicationListener instanceof TransactionalApplicationListener) {
TransactionalApplicationListener<?> listener = (TransactionalApplicationListener<?>) applicationListener;
Collection<SynchronizationCallback> callbacks = this.synchronizationCallbacks.get();
callbacks.forEach(listener::addCallback);
}
return applicationListener;
}
#Override
public int getOrder() {
return order;
}
}
Note that I use a javax.inject.Provider in my case to make the retrieval of the callbacks at the latest possible time.
The decorator has to be Ordered because spring will use the first factory supporting the method it gets across. And therefore the order of an instance of this class has to have higher precedence as the order value 50 of TransactionEventListenerFactory.
I had simmilar problem with code as below
#Transactional(propagation = Propagation.REQUIRES_NEW)
public class SomeListenerFacade {
#TransactionalEventListener
public void onSomething(SomeEvent event) {
throw new RuntimeException("some cause");
}
}
I followed your solution. It worked. On the way I've found an alternative way for at least seeing that exception in the logfile
# application.properties
logging.level.org.springframework.transaction.support.TransactionSynchronizationUtils = DEBUG

Using ConfigProperty in manually instantiated classes in JakartaEE / Helidon / Microprofile

I have a small application in Helidon start. It is mostly a REST interface, but I also want to start some background monitoring / logging on startup.
I would like that monitoring to be activated / deactivated by config.
The issue I am facing is that the config is not being picked up if my class is instantiated manually.
Here is a very short code snippet :
Starting the application
public class Main {
private Main() { }
public static void main(final String[] args) throws IOException {
Server server = startServer();
CellarMonitoring monitoring = new CellarMonitoring();
monitoring.start();
}
static Server startServer() {
return Server.create().start();
}
}
Starting monitoring or not based on Configuration :
package nl.lengrand.cellar;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
public class CellarMonitoring {
#Inject
#ConfigProperty(name = "monitoring.enabled", defaultValue = "true")
private volatile boolean monitoringEnabled; <= Always false
public void start(){
if(monitoringEnabled) {
System.out.println("Monitoring enabled by config. Starting up");
}
else System.out.println("Monitoring disabled by config");
}
}
This code will always return "Monitoring disabled by config", whatever I do.
Accessing the config directly like described in the documentation is not really an option either since the onStartup method will never be fired.
What is the proper way to inject a class in my server so it can access the config as desired?
Your question is actually about CDI.
In order for any kind of dependency injection to work with CDI, CDI must instantiate the thing to be injected. In this case, you instantiate the thing to be injected, so CDI never "sees" it, so it is never injected.
I am speculating here, but I'm guessing your use case is really just: "I'd like my CellarMonitoring component to be notified when CDI comes up. How do I do that?"
There are many answers to that question on this site and elsewhere. Essentially you take advantage of the fact that CDI will fire an event notifying any interested listeners in the initialization of the application scope. The application scope is effectively the lifespan of the application itself, so you can think of it as a startup event.
A full CDI tutorial is beyond the scope of this question and answer, but, to cut to the chase, here's a way to do it. I have had to make various assumptions, such as that you want CellarMonitoring to be singleton-like:
#ApplicationScoped
public class CellarMonitoring {
#Inject
#ConfigProperty(name = "monitoring.enabled", defaultValue = "true")
private volatile boolean monitoringEnabled; // <= Always false
public void start() {
if (monitoringEnabled) {
System.out.println("Monitoring enabled by config. Starting up");
} else {
System.out.println("Monitoring disabled by config");
}
}
private void onStartup(#Observes #Initialized(ApplicationScoped.class) final Object event) {
// The container has started. You can now do what you want to do.
this.start();
}
}

Testing application restart with persistence with JUnit and Spring

I have an application that uses persistent JMS queues. Imagine I have an application failure after reading a message and before ack'ing it. The persistent queue must provide that message again after the app restarts. How can I implement a junit integration test for this? I'm testing application restart after a (simulated) application crash mid-"transaction".
I've looked at #DirtiesContext as a way to reset all the Spring parts of the app: reading configs, recreating JMS connections. I could have one test case A) write a message, allow the message to be read and then "exit" (shut down the spring context?) without acking. Then another test case (after the context is reloaded B) read the message and assert that it was not lost after the simulated application restart. But the builtin context reload provided by #DirtiesContext only happens between test cases. And JUnit does not provide for a means to sequence two test cases or make B) dependent on A), such that A) will always run (and run first) if you decide to run B).
In a previous life, I wrote manual code that shut down the spring context, and manually restarted a new context. E.g. between A) and B). That could be done within a single test case. It wouldn't play nicely with #RunWith(SpringRunner.class), I'm guessing, and seems pretty old school. Is that really the only option, given all the wonderful Spring and JUnit support these days?
This seems like a pretty useful technique. It could be used to test re-arrival of messages after they've been rolled back (and are stuck on a dead letter queue); or that sequence numbers written to a DB are really persisting during a "crash". Any number of failure cases that wind up affecting the next application startup due to persisted (or not) data. How do we simulate spring restart in junit tests? Either within one test, or create a sequence of dependent tests with #DirtiesContext between them.
The following article How to Restart a Spring Application Context within a JUnit test describes a solution to the question.
The solution consist to expend SpringJUnit4ClassRunner in order to inject a SpringRestarter singleton which takes care of restating the application context.
public class SpringRestarter {
private static SpringRestarter INSTANCE = null;
private TestContextManager testContextManager;
public static SpringRestarter getInstance() {
if (INSTANCE == null) {
INSTANCE = new SpringRestarter();
}
return INSTANCE;
}
public void init(TestContextManager testContextManager) {
this.testContextManager = testContextManager;
}
public void restart(Runnable stoppedLogic) {
testContextManager.getTestContext().markApplicationContextDirty(DirtiesContext.HierarchyMode.EXHAUSTIVE);
if (stoppedLogic != null) {
stoppedLogic.run();
}
testContextManager.getTestContext().getApplicationContext();
reinjectDependencies();
}
private void reinjectDependencies() {
testContextManager
.getTestExecutionListeners()
.stream()
.filter(listener -> listener instanceof DependencyInjectionTestExecutionListener)
.findFirst()
.ifPresent(listener -> {
try {
listener.prepareTestInstance(testContextManager.getTestContext());
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
RestartingSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner and initialise the singleton SpringRestarter.
public class RestartingSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {
public RestartingSpringJUnit4ClassRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
#Override
protected TestContextManager createTestContextManager(Class<?> clazz) {
final TestContextManager testContextManager = super.createTestContextManager(clazz);
SpringRestarter.getInstance().init(testContextManager);
return testContextManager;
}
}
RestartingSpringRunner extends RestartingSpringJUnit4ClassRunner in order to extend SpringRunner
public class RestartingSpringRunner extends RestartingSpringJUnit4ClassRunner {
public RestartingSpringRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
}
Using Within a JUnit 4 Test
#RunWith(RestartingSpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyIntegrationTests {
public void myRestartingTest() {
//Some test logic before the context restart
SpringRestarter.getInstance().restart(() -> {/* Some logic after context stopped */});
//Some test logic after the context restart
}
}
Full details of the explanation

Schedule a task to run only once

I'm working on a recovery monitor which waits for 5 minutes and fires an alert if system has not been recovered yet. The monitor needs to be started at start up and to fire alert only once. The source code looks like this:
#Stateless
public class RecoveryMonitor {
#Inject TimerService timerService;
#Inject MyAlertService alertService;
#Inject SystemRecovery systemRecovery;
public void scheduleMonitor() {
timerService.createSingleActionTimer(TimeUnit.MINUTES.toMillis(5),
new TimerConfig);
}
#Timeout
public void timeout() {
if (!systemRecovery.isDone) {
alertService.alert("System recovery failed");
}
}
}
So, the problem here is how to schedule a task, i.e. invoke scheduleMonitor method. I cannot use #PostConstruct as it's not allowed to. I think about using #Schedule, but it executes a method periodically while I only to do it once. Any solutions and/or suggestions are welcome. Thanks.
L
UPDATE: by making the class not Stateless anymore, e.g. make it a #Singleton, I am able to start scheduling using #PostConstruct. This is not a complete solution but it works for me:
#Singleton
public class RecoveryMonitor {
#Inject TimerService timerService;
#Inject MyAlertService alertService;
#Inject SystemRecovery systemRecovery;
#PostConstruct
public void scheduleMonitor() {
timerService.createSingleActionTimer(TimeUnit.MINUTES.toMillis(5),
new TimerConfig);
}
#Timeout
public void timeout() {
if (!systemRecovery.isDone) {
alertService.alert("System recovery failed");
}
}
}
If you have a Servlet Environment you could fire a CDI Event(e.g. ApplicationStartedEvent) within a ServletContextListener and observe that event in your EJB. This kind of startup logic has to be done manually in CDI 1.0. Future versions will probably contain something similar.
If you have questions on how to do that, just ask :)

Categories

Resources