Good morning everybody,
I write you because i want to create an EJB timer.
But my #Timeout annotated method must throw an application exception.
And i see in documentation that a #Timeout method can't throw an application exception.
#Stateless#Local
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class AccesCacheImpl implements AccesCache {
#Resource
private TimerService serviceMinuteur;
// i have to make an updateDataBase every 30 minutes
public void creerTimer() {
serviceMinuteur.createTimer(1000, 1800000, "MinuteurCache");
}
public void detruireTimer() {
Timer timerCourant;
for (final Object timer : serviceMinuteur.getTimers()) {
timerCourant = (Timer) timer;
if ("MinuteurCache".equals(timer)) {
timerCourant.cancel();
}
}
}
#Timeout
public void updateDataBase(final Timer pTimer) throws AccesNomenclatureException {
.....
.....
.....
}
}
I begin in EJB programming : do you find my code OK ?
And i have necessary to keep this exception.
Have you faced such a problem ?
Thank you very much.
Application exceptions are intended to provide convey additional information to the caller. In this case, the caller of an #Timeout method is the EJB container, which cannot know the intent of your application exception, so they are not allowed. If you need to signal failure to the container so that it will retry, I recommend catching the AccesNomenclatureException and rethrowing it as EJBException, which is a system exception rather than an application exception. Otherwise, if you just want to ignore the failure, then catch it and log it rather than rethrowing it.
Related
public class TaskletConfiguration {
...
#Bean
public Step step() {
return steps.get("step")
.tasklet(tasklet)
.exceptionHandler(logExceptionHandler()) // Handler for logging exception to specific channel
.build();
}
#Bean
public Job job() {
return jobs.get("job")
.start(step())
.build();
}
}
public class ExampleTasklet implements Tasklet, StepExecutionListener {
...
#Override
public RepeatStatus execute(...) throws Exception {
// Do my tasklet
// Throw if it fails, and be handled by logExceptionHandler()
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
// Want to throw so that logExceptionHandler() can handle it as throwing in execute().
throwable_function();
}
}
This is my example code using tasklet in spring boot.
My problem is: I want to throw exception from afterstep(), but the interface does not allow it.
Despite this limitation, why I obsessed with afterstep() is that I want to make abstract class to make Tasklet template which can verify each execution in afterstep(). I want verification to run after all execute() is done, which will be overridden by subclass. So I have no choices but using afterstep().
Any idea to run verification method after each execute() with throwable or afterstep() can pass Exception to logExceptionHandler()? I hope to define logExceptionHandler() in TaskletConfiguration class. It will be obese if it is defined in Tasklet class, as I will make abstract class, which will be inherited by many subclasses.
The StepExecutionListener#afterStep is not designed to throw checked exceptions. Here is an excerpt from its Javadoc:
Called after execution of step's processing logic (both successful or failed).
Throwing exception in this method has no effect, it will only be logged.
Moreover, even if you throw a (runtime) exception in afterStep, the exception won't be passed to the exception handler, it will only be logged as mentioned in the Javadoc.
I think it is too late to throw exceptions in StepExecutionListener#afterStep, this method can be used to check the status of the step execution and modify the ExitStatus if needed to drive the rest of the job execution flow.
I have service calls in my application that make remote network calls to other services as well as DB calls. Spring Boot has good support for rolling back bad transactions with #Transactional, but I wanted to know if I could define a custom rollback procedure using an annotation.
I would need to rollback the data on the other services as well as the database.
In code, I could do it like this:
#Transactional
public void doSomethingComplicated() {
try {
srvcOne.makeRemoteNetworkCall();
srvcTwo.makeDatabaseCall();
} catch(Exception e) {
srvcOne.rollBackNetworkCall();
}
}
but I was hoping I could do something like this:
#Transactional
#MyCustomRollbackListener(handler = MyCustomRollBackHandler.class)
public void doSomethingComplicated() {
srvcOne.makeRemoteNetworkCall();
srvcTwo.makeDatabaseCall();
}
and in the handler:
public class MyCustomRollBackHandler {
public void handleRollback() {
srvcOne.rollBackNetworkCall();
}
}
I implemented a global exception listener and I am able to get the class the exception came from, but I have no way to get the method and to retrieve any annotations on it. Here is my initial attempt:
#ControllerAdvice
public class RollbackExceptionListener{
private static final Logger logger = LoggerFactory.getLogger(RollbackExceptionListener.class);
#ExceptionHandler(Exception.class)
public void lookForAnnotationClassForException(final Exception exception) {
logger.error("Exception thrown", exception);
final StackTraceElement topElement = exception.getStackTrace()[0];
final Class callingClass = topElement.getClass();
final String methodName = topElement.getMethodName();
try {
// Can't get the method with just the name, need to
// know the params as well.
final Method method = callingClass.getMethod(methodName);
// Retrieve the annotation on the method and call the handler
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
Is there anyway to do something like this?
Arguments are not part of the Stacktrace. If the method is unique, i.e. not overloaded, you can probably find it with getMethods()? Something else that comes to mind, maybe you can look at Aspects to wrap the method in some handler before it is executed. Can be done either at compile time or runtime.
The aspect can do the rollback itself, it can enrich the exception with the information you need, or it can set some ThreadLocal variable with the handler class that was defined in the method before re-throwing the exception. You can then get this value from the ThreadLocal at the point where you catch the exception.
Recently, I've been working with Spring boot + spring data jpa + hibernate. I faced one problem with spring transactions. Here is my service class and two questions:
#Transactional
#Service
class MyService {
#Autowired
private MyRepository myRep;
public void method_A() {
try {
method_C();
.....
method_B();
} catch(Exception e) {}
}
public void method_B() {
Entity e = new Entity();
e.set(...);
myRep.save(e);
}
public void method_C() throws Exception {
....
}
}
1.If method method_C() throws an Exception and I want to catch it and log it, the transaction is not rollbacked in method method_B(), because the Exception does not reach Spring framework. So how should I do in order to catch Exceptions from method_C() and at the same time do not lose capability of method method_B() be rollbacked?
2.Consider new method method_A().
public void method_A() {
for(...) {
...
...
method_B();
}
}
I want invoke method_B() in a loop. If an exception occurs in a method_B() I want transaction of method_B() be rollbacked but method_A() should not exit and the loop should continue excuting. How can I achieve this?
I solved my 2 problems this way: created another #Service class and moved method_B() into it. I've annotated this class as #Transactional. Now the method method_A() looks like this:
public void method_A() {
for(...) {
...
try {
anotherService.method_B();
} catch (Exception e) {
logger.error(...);
}
}
}
If RuntimeException occurs in the method_B() method, the exception is propertly logged, transaction of method_B() is rollbacked and the loop continuous. Thanks everybody for responses.
Instead of throwing exceptions do the following. (return error code).
Update: I read your question after posting. if you call method_b from method_A both are under same transaction. Unfortunately you cannot rollback the method_b changes alone. Spring considers it as one transaction if they are all under one service class. (all methods).
One thing you can try is the following.
request to--> Controller() ---> (spring opens transaction) service_method_a(); (spring closes transaction)
Controller() ---> (spring opens transaction) service_method_c(); (spring closes transaction)
Controller() ---> (spring opens transaction) service_method_b(); (spring closes transaction)
return <--
I hope it makes sense
Each of your methods a,b,c throw exceptions if it likes to be rolledback.
Update:
another approach. This one is much better.
If each of your method are in a different service then you can use the following annotations of the spring to run each of the method in a different transaction boundaries
p v serviceA{
#transactional
method_a(){
serviceb.method_b();
}
}
p v serviceB{
#Transactional(propagation=Propagation.REQUIRED)
method_b(){
}
}
more on it here
Spring transactional story here . Read the points below this article. Those are most important when developing the spring transactional app.
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?
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 :)