Issue with a Spring #Async method not catching/rethrowing an exception - spring

I am facing an issue since I enabled turned a synchronous method into an asynchronous one: if an exception is thrown from within the body of the method, I can no longer rethrow it.
Let me first show the code:
My async/task executor configuration:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
taskExecutor.initialize();
return taskExecutor;
}
}
My asynchronous method:
#Async
#Override
public void sendPasswordResetInfo(String email) {
Assert.hasText(email);
Member member = memberRepository.findByEmail(email);
try {
mailerService.doMailPasswordResetInfo(member);//EXCEPTION THROWN HERE
} catch (MessagingException | MailSendException e) {
log.error("MessagingException | MailSendException", e);
// TODO: not thrown since #Async is used
throw new MailerException("MessagingException | MailSendException");//NOT CALLED
}
}
All I see in the console when an exception is raised by mailerService.doMailPasswordResetInfo is the following stacktrace:
2014-06-20 18:46:29,249 [ThreadPoolTaskExecutor-1] ERROR com.bignibou.service.preference.PreferenceServiceImpl - MessagingException | MailSendException
org.springframework.mail.MailSendException: Failed messages: javax.mail.SendFailedException: Invalid Addresses;
nested exception is:
com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 Adresse d au moins un destinataire invalide. Invalid recipient. OFR204_418 [418]
; message exception details (1) are:
Failed message 1:
javax.mail.SendFailedException: Invalid Addresses;
nested exception is:
com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 Adresse d au moins un destinataire invalide. Invalid recipient. OFR204_418 [418]
at com.sun.mail.smtp.SMTPTransport.rcptTo(SMTPTransport.java:1294)
at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:635)
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:424)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:346)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:341)
at com.bignibou.service.mailer.MailerServiceImpl.doMailPasswordResetInfo(MailerServiceImpl.java:82)
at com.bignibou.service.preference.PreferenceServiceImpl.sendPasswordResetInfo_aroundBody10(PreferenceServiceImpl.java:112)
at com.bignibou.service.preference.PreferenceServiceImpl$AjcClosure11.run(PreferenceServiceImpl.java:1)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(AbstractTransactionAspect.aj:59)
at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation(AbstractTransactionAspect.aj:65)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(AbstractTransactionAspect.aj:63)
at com.bignibou.service.preference.PreferenceServiceImpl.sendPasswordResetInfo_aroundBody12(PreferenceServiceImpl.java:108)
at com.bignibou.service.preference.PreferenceServiceImpl$AjcClosure13.run(PreferenceServiceImpl.java:1)
at org.springframework.scheduling.aspectj.AbstractAsyncExecutionAspect.ajc$around$org_springframework_scheduling_aspectj_AbstractAsyncExecutionAspect$1$6c004c3eproceed(AbstractAsyncExecutionAspect.aj:58)
at org.springframework.scheduling.aspectj.AbstractAsyncExecutionAspect.ajc$around$org_springframework_scheduling_aspectj_AbstractAsyncExecutionAspect$1$6c004c3e(AbstractAsyncExecutionAspect.aj:62)
at com.bignibou.service.preference.PreferenceServiceImpl.sendPasswordResetInfo(PreferenceServiceImpl.java:108)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:97)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 Adresse d au moins un destinataire invalide. Invalid recipient. OFR204_418 [418]
at com.sun.mail.smtp.SMTPTransport.rcptTo(SMTPTransport.java:1145)
... 28 more
FYI, MailerException is a custom exception I used in the app. What really strikes me is that this MailerException exception does not appear to be caught nor rethrown...
Can anyone please help?
edit 1: I have modified my async service method as follows:
#Async
#Override
public Future<Void> sendPasswordResetInfo(String email) {
Assert.hasText(email);
Member member = memberRepository.findByEmail(email);
try {
mailerService.doMailPasswordResetInfo(member);
return new AsyncResult<Void>(null);
} catch (MessagingException | MailSendException e) {
log.error("MessagingException | MailSendException", e);
//HERE: HOW DO I SEND THE MailerException USING THE FUTURE??
throw new MailerException("MessagingException | MailSendException");
}
}
However, I am not sure how to send the MailerException to the web layer using the AsyncResult from within the catch block above...

Your logs show
2014-06-20 18:46:29,249 [ThreadPoolTaskExecutor-1] ERROR com.bignibou.service.preference.PreferenceServiceImpl - MessagingException | MailSendException
which is logged by
log.error("MessagingException | MailSendException", e);
the Exception that is then thrown
throw new MailerException("MessagingException | MailSendException");//NOT CALLED
is caught by the Thread executed by the underlying ThreadPoolTaskExecutor. This code is run asynchronously so the exception is thrown in a different thread than the thread that invokes the method.
someCode();
yourProxy.sendPasswordResetInfo(someValue()); // exception thrown in other thread
moreCode();
If you make your method return a Future as shown here you'll be able to invoke get() on it and that will rethrow any exception that was thrown inside the #Async method, wrapped in an ExecutionException.

By following this link http://www.baeldung.com/spring-async, you should create:
CustomAsyncExceptionHandler that implements AsyncUncaughtExceptionHandler
#Slf4j
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
log.error("**********************************************");
log.error("Exception message - " + throwable.getMessage());
log.error("Method name - " + method.getName());
for (Object param : obj) {
log.error("Parameter value - " + param);
}
log.error("**********************************************");
}
}
SpringAsyncConfig that implements AsyncConfigurer
#Configuration
#EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}

The solution i had was fixed by creating a configuration class that extends AsyncConfigurerSupport, since the above solutions did not work in my implementation. In this configuration class, i had to define SimpleAsyncTaskExecutor and an async exception handler. Inside the handler, i checked the throwable instance and based on its value, i implemented the needed behavior:
if ( throwable instanceOf CustomException) {
// TODO add code
} else {
// TODO
}
Reference: Effective advice on spring async exception handler

Related

Catch optimistick lock exception hibernate spring boot

i'm trying to intercept an optimistick lock exception and throw another exception but it doesn't work in my case, the exception is catched but i still have error optimistick lock in my console.
//MY DAO
public Entity getEntitieSimple(...) throws CustomException{
Entity entity="my select";
}
//MY SERVICE
#Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public Entity recupererEntity() throws CustomException{
Entity entity =null;
try {
entity = dao.getEntitieSimple(...);
}catch (Exception exec){
throw new CustomException("custom message");
}
return entity;
}
#Transactional(propagation = Propagation.REQUIRED)
public void myUpdate() throws HibernateException{
try {
// entity update here
}catch (HibernateException exec){
log.error("OPTIMISTIC: "+ exec.getMessage());
throw new HibernateException("optimi update");
}
}
I'm getting this error evern after catching the exception:
ERROR - 29-09-2022 09:59:44.204 - http-nio-8090-exec-5 - 3933807 - null - c:96e7d7ca8f7d5637 - org.hibernate.internal.ExceptionMapperStandardImpl - HHH000346: Error during managed flush [Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.apicil.cosy.dop.domain.impl.Entity#1420]]
You have to catch ConcurrencyFailureException
private void updateStatus(UpdateOrderDto dto, OrdersStatusUpdaterService self) {
try {
updateStatusHelper(dto, self);
} catch (ConcurrencyFailureException ex) {
log.error("Concurrent modification error. Will try next time. orderId={}", dto.getId(), ex);
}
}

Camel no error handler invoked on exception with multipleConsumer=true and POJO #Consume Annotation

I've been trying to figure out how route errors to my own error handler with the following, seemingly simple configuration, but Camel is swallowing the exception without routing it to any error handler I configure. I've run out of ideas. Any help would be much appreciated.
I've got a seda route that supports multiple consumers:
#Component
public class MessageGenerator {
public static final String ERROR_GENERATOR_CHANNEL = "seda:my-error-generator?multipleConsumers=true&concurrentConsumers=3";
private final FluentProducerTemplate producerTemplate;
public MessageGenerator(FluentProducerTemplate producerTemplate) {
this.producerTemplate = producerTemplate;
}
public void generateMessage() {
producerTemplate
.to(ERROR_GENERATOR_CHANNEL)
.withBody("Hello World")
.asyncSend();
}
}
I've got two separate POJO consumers:
#Configuration
public class MessageConsumer1 {
#Consume(ERROR_GENERATOR_CHANNEL)
void receiveMessage(String message) {
System.out.println("Received message 1: " + message);
throw new NullPointerException("Error generated");
}
}
#Configuration
public class MessageConsumer2 {
#Consume(ERROR_GENERATOR_CHANNEL)
void receiveMessage(String message) {
System.out.println("Received message 2: " + message);
}
}
When I run the following example, the NullPointerException gets swallowed by the underlying Camel MulticastProcessor as we can see in the logs:
Received message 2: Hello World
Received message 1: Hello World
2022-01-15 13:40:23.711 DEBUG 32945 --- [error-generator] o.a.camel.processor.MulticastProcessor : Message exchange has failed: Multicast processing failed for number 0 for exchange: Exchange[] Exception: java.lang.NullPointerException: Error generated
2022-01-15 13:40:23.711 DEBUG 32945 --- [error-generator] o.a.camel.processor.MulticastProcessor : Message exchange has failed: Multicast processing failed for number 0 for exchange: Exchange[] Exception: java.lang.NullPointerException: Error generated
The exception only gets logged as debug and never gets propagated to any error handler I set up.
Any thoughts on how I could receive the error in my own error handler rather than Camel swallowing the exception as a debug statement?
Note1: I've attempted many variations on both default error handling and default dead letter handling to no avail. I could just be doing it wrong...
Note2: that I'm using Spring[Boot] here too, hence the #Configuration annotation.
Note1: I've attempted many variations on both default error handling and default dead letter handling to no avail. I could just be doing it wrong...
Haven't used #Consume annotations but generally if you want Camel route not to handle any errors you can use .errorHandler(noErrorHandler()). This can be used to pass the error back to parent route or all the way to the code calling ProducerTemplate.sendBody.
Example:
public class ExampleTest extends CamelTestSupport {
#Test
public void noErrorHandlerTest() {
try {
template.sendBody("direct:noErrorHandler", null);
fail();
} catch (Exception e) {
System.out.println("Caught Exception: " + e.getMessage());
}
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder(){
#Override
public void configure() throws Exception {
from("direct:noErrorHandler")
.errorHandler(noErrorHandler())
.log("Throwing error")
.throwException(Exception.class, "Test Exception");
}
};
}
}

Spring ExceptionHandler for Caused By / Wrapped Exception

I couldn't find a good solution: In my Spring Boot app, as an #ExceptionHandler method, I need to define a handler not for a specific exception, but for any exception caused by a specific exception (i.e. a wrapped exception).
Example: Sometimes I get this:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:541) ~[spring-orm-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746) ~[spring-tx-5.1.4.RELEASE.jar:5.1.4.RELEASE]
... 121 common frames omitted
Caused by: custom.TechRoleException: My custom TechRoleException
at myapp.method1[..]
at myapp.methodOuter[..]
My custom TechRoleException is an exception I throw inside some Hibernate EventListener's pre-update method, and the direct exception is that Persistence couldn't occur.
However, the following method that tries to use my custom exception is never reached:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(TechRoleException.class)
public String techRoleException(HttpServletRequest request, Exception ex) {
System.out.println("Got here");
return "home";
}
}
Here's a similar thread where the answer is wrong and didn't answer this question:
#ExceptionHandler for Wrapped Exception / getCause() in Spring
Maybe something like that?
#ExceptionHandler(Exception.class)
public String techRoleException(HttpServletRequest request, Exception ex) {
if(ex instanceof TechRoleException) {
System.out.println("Got here");
return "home";
} else {
throw ex; //or something else
}
}
My final working answer is to handle a General Exception, and then use Apache ExceptionUtils.getRootCause() to detect the specific Caused-By I'm looking for within this general handler.
(Other specific Exceptions won't come to this method if they have their dedicated Handlers. But if there's no dedicated Handler, the exception will come here.) This is the only way to detect some target Caused-By.
#ExceptionHandler(Exception.class)
public String handleGeneralException(HttpServletRequest request, Exception ex) {
Throwable rootCause = ExceptionUtils.getRootCause(ex);
if (rootCause != null && "com.myapp.TechRoleException".equals(rootCause.getClass().getName())
{
//... handle this Caused-By Exception here
// ...
}
// All other exceptions that don't have dedicated handlers can also be handled below...
// ...
}

Camel lookup RemoteConnectionFactory on Wildfly

I'm very new to Apache Camel and completely new to Spring. I'm tryin to send some JMS messages to the embedded hornetq in Wildfly (ver.8.1.0). Here is my code:
public class CamelJMS {
public static void main(String[] args) throws Exception {
try {
CamelContext cc = new DefaultCamelContext();
Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jboss.naming.remote.client.InitialContextFactory");
prop.put(Context.URL_PKG_PREFIXES,
"org.jboss.jms.jndi.JNDIProviderAdapter");
prop.put(Context.PROVIDER_URL, "http-remoting://localhost:8080");
prop.put(Context.SECURITY_PRINCIPAL,
System.getProperty("username", "usr"));
prop.put(Context.SECURITY_CREDENTIALS,
System.getProperty("password", "pwd"));
JndiTemplate jndiT = new JndiTemplate(prop);
jndiT.bind("ccf", "jms/RemoteConnectionFactory");
JndiObjectFactoryBean jndiCFB = new JndiObjectFactoryBean();
jndiCFB.setJndiTemplate(jndiT);
JmsComponent jmsC = JmsComponent.jmsComponent((ConnectionFactory)jndiCFB.getObject());
cc.addComponent("jmsrc", jmsC);
cc.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
System.out.println("Go!");
onException(Throwable.class)
.handled(true)
.process(new Processor() {
#Override
public void process(Exchange arg0) throws Exception {
System.out.println("error.");
((Exception) arg0.getProperty("CamelExceptionCaught", Exception.class)).printStackTrace();
}
});
from("file:///Users/Foo/Desktop/IN")
.process(new Processor() {
#Override
public void process(Exchange arg0) throws Exception {
System.out.println(arg0.getIn().getHeader("CamelFileAbsolutePath", String.class));
System.out.println(arg0.getIn().getBody(String.class));
}
})
.to("jms:jms/generatoreQueue?connectionFactory=ccf");
}
});
cc.start();
Thread.sleep(10000);
cc.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
I'm sure about my Wildfly's configuration because I can access the same queue using a non Camel client. When I launch my client I got this error:
org.jboss.naming.remote.protocol.NamingIOException: Failed to bind [Root exception is java.io.IOException: Internal server error.]
at org.jboss.naming.remote.client.ClientUtil.namingException(ClientUtil.java:49)
at org.jboss.naming.remote.protocol.v1.Protocol$2.execute(Protocol.java:220)
at org.jboss.naming.remote.protocol.v1.Protocol$2.execute(Protocol.java:179)
at org.jboss.naming.remote.protocol.v1.RemoteNamingStoreV1.bind(RemoteNamingStoreV1.java:108)
at org.jboss.naming.remote.client.HaRemoteNamingStore$2.operation(HaRemoteNamingStore.java:288)
at org.jboss.naming.remote.client.HaRemoteNamingStore$2.operation(HaRemoteNamingStore.java:285)
at org.jboss.naming.remote.client.HaRemoteNamingStore.namingOperation(HaRemoteNamingStore.java:137)
at org.jboss.naming.remote.client.HaRemoteNamingStore.bind(HaRemoteNamingStore.java:284)
at org.jboss.naming.remote.client.RemoteContext.bind(RemoteContext.java:133)
at org.jboss.naming.remote.client.RemoteContext.bind(RemoteContext.java:137)
at javax.naming.InitialContext.bind(InitialContext.java:419)
at org.springframework.jndi.JndiTemplate$2.doInContext(JndiTemplate.java:198)
at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:87)
at org.springframework.jndi.JndiTemplate.bind(JndiTemplate.java:196)
at edu.pezzati.camel.jms.broker.CamelJMSBroker.main(CamelJMSBroker.java:38)
Caused by: java.io.IOException: Internal server error.
at org.jboss.naming.remote.protocol.v1.RemoteNamingServerV1$MessageReciever$1.run(RemoteNamingServerV1.java:82)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Looking to the server log, I found this:
...
09:12:02,585 INFO [org.jboss.as.naming] (default task-5) JBAS011806: Channel end notification received, closing channel Channel ID 793d9f9e (inbound) of Remoting c
onnection 4406e6f5 to /127.0.0.1:49289
09:12:20,121 ERROR [org.jboss.as.naming] (pool-1-thread-1) JBAS011807: Unexpected internal error: java.lang.UnsupportedOperationException: JBAS011859: Naming context is read-only
at org.jboss.as.naming.WritableServiceBasedNamingStore.requireOwner(WritableServiceBasedNamingStore.java:161)
at org.jboss.as.naming.WritableServiceBasedNamingStore.bind(WritableServiceBasedNamingStore.java:66)
at org.jboss.as.naming.NamingContext.bind(NamingContext.java:253)
at org.jboss.naming.remote.protocol.v1.Protocol$2.handleServerMessage(Protocol.java:249)
at org.jboss.naming.remote.protocol.v1.RemoteNamingServerV1$MessageReciever$1.run(RemoteNamingServerV1.java:73)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_45]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_45]
at java.lang.Thread.run(Thread.java:744) [rt.jar:1.7.0_45]
...
Of course I'm misconfiguring something in Spring's JndiTemplate but I can't figure out what.
I'm not familiar with Wildfly and only a little familiar with JBoss, but you said the configuration parameters should be correct. So based off of my experience configuring the Camel JmsComponent...
Try this:
Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jboss.naming.remote.client.InitialContextFactory");
prop.put(Context.URL_PKG_PREFIXES,
"org.jboss.jms.jndi.JNDIProviderAdapter");
prop.put(Context.PROVIDER_URL, "http-remoting://localhost:8080");
prop.put(Context.SECURITY_PRINCIPAL,
System.getProperty("username", "usr"));
prop.put(Context.SECURITY_CREDENTIALS,
System.getProperty("password", "pwd"));
Context context = new InitialContext(prop);
ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup("jms/RemoteConnectionFactory");
JmsComponent jmsC = new JmsComponent(connectionFactory);
cc.addComponent("jms", jmsC);
And change your endpoint to:
.to("jms:queue:generatoreQueue");
You shouldn't need the "jms/" in front of the queue name, and the component name should be the same as what you bound it to above in the addComponent method.

Spring4 + and Hibernate 4 Transactions

Using spring 4.0.6.RELEASE, Hibernate 4.3.6.Final and hsqldb 2.3.2. My integration test looks like the following;
#Test(expected = DataIntegrityViolationException.class)
public final void testDuplicateItems() {
final ServerEntity serverEntity1 = new ServerEntity("DuplicateItem");
opService.save(serverEntity1);
opService.save(serverEntity1);
}
This works as expected. However, when I run my standalone java component i can save the first item, the second item which is a duplicate is not saved but Im unable to catch the exception. Here is the log file
WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper: SQL Error: -104, SQLState: 23505
2014-08-27 14:52:06,843 ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper: integrity constraint violation: unique constraint or index violation; UK_NFU7LXMMDFVIR1WD08662085N table: SERVERENTITY
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [UK_NFU7LXMMDFVIR1WD08662085N]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:161)
at org.springframework.orm.hibernate4.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:681)
at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:563)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy38.execute(Unknown Source)
at com.opserver.simpleapp.MainApp.start(MainApp.java:60)
at com.opserver.simpleapp.MainApp.main(MainApp.java:37)
... 6 more
Both the service and dao implementations have #Transactional at class level. I've a component class that is calling the service class, this component class is not transactional! The component class prints a response, does the session need to be flushed here?
Need to figure out why the save method in the dao is not throwing the exception, I can actually see it an id being created and then rolled back.
J
My component class is very basic;
boolean isValid = opServerService.loadXMLFile("Server.xml");
try{
if (isValid) {
System.out.println("Entity has been added");
} else {
System.out.println("Entity has not been added");
}
}catch (Exception ex){
System.out.println("that was a focked up");
}
The problem is that "Entity has been added" gets printed to console and then I see the above error in console.
DAO looks like this
#Override
#Transactional
public final void save(final ServerEntity serverEntity) throws DataIntegrityViolationException {
LOGGER.debug(">>start(serverEntity=" + serverEntity + ")");
Preconditions.checkNotNull(serverEntity);
this.getCurrentSession().save(serverEntity);
}
Service method with #Transactional at class level, looks like this
#Override
public final void save(ServerEntity serverEntity) {
opServerDao.save(serverEntity);
}
And Component looks like this
#Component
public class AddCommand implements Command {
#Autowired
OpService opService;
public AddServerCommand() {
super();
}
#Override
public void execute(String[] options) {
try{
boolean isValid = opService.save("Server.xml");
if (isValid) {
System.out.println("Entity has been added");
} else {
System.out.println("Entity has not been added");
}
}catch (Exception ex){
System.out.println("Exception found");
}
}
}
You should catch your exception in the component that's calling the service.
Found the solution, was missing from the applicationContext.xml. The wrapped try/catch around the opService now catches the exception. Need to implement my own custom exception handler but for now at least I know the component class is handling the exception which is being thrown from the service class.
Thanks for your help.
J

Resources