I'm having a problem with Dropwizard where I can't catch the exception thrown by the Hibernate DAO object within my resource.
I have the following DAO object
public class ApplicantDAO extends AbstractDAO<Applicant>
{
public ApplicantDAO(SessionFactory factory)
{
super(factory);
}
public long create(Applicant person)
{
return persist(person).getApplicantId();
}
}
I am calling the create method from inside my Dropwizard resource to which I'm passing on my managed DAO from my Application's run method. The following doesn't work:
try
{
long id = dao.create(applicant);
message += "[Stored: " + id + "] ";
}catch (HibernateException ex)
{
message +="Could't store: " + exptionToString(ex);
}
Instead I get Dropwizard's/Jersey's message:
{"code":500,"message":"There was an error processing your request. It has been logged (ID a785167e05024c69)."}
Is there a way to get around that?
I am not familiar with Drop Wizard.
But my best guest is that it has a JAX-RS ExcepionMapper registered that writes its own error when an exception is thrown
see : javax.ws.rs.ext.ExceptionMapper
I figured it out. The problem was happening because of an exception throw inside of a transaction.
So instead of having #UnitOfWork on my resource method, I added #UnitOfWork(transactional = false)
Then I was able to manage my own transactions by passing in the SessionFactory to my resource and that did the trick!
It might be related to the following issue: https://github.com/dropwizard/dropwizard/issues/949
Related
I use an external rest api in my spring application, I can send json post requests to create objects but when a field is incorrect or if there is a duplicate it returns a 400 bad request error, and a body saying what the problem is.
I use Spring 5 with #PostExchange in the following code:
This is used to point spring into the right direction of the external api
public interface HardwareClient {
#PostExchange("/assetmgmt/assets/templateId/C04DBCC3-5FD3-45A2-BD34-8A84CE2EAC20")
String addMonitor(#RequestBody Monitor monitor);
}
This is the helper that is autowired into the class where I have the data that needs to be sent.
#Component
public class HardwareHelper {
private Logger logger = Logger.getLogger(getClass().getName());
#Autowired
HardwareClient hardwareClient;
#Async
public Future<String> addMonitor(MonitorForm monitorForm){
try {
Monitor monitor = new Monitor(monitorForm.objectID(), monitorForm.model(), monitorForm.make(),monitorForm.serialNumber(), monitorForm.orderNumber(),monitorForm.budgetholder(),monitorForm.ownership());
hardwareClient.addMonitor(monitor);
return new AsyncResult<String>("Success");
} catch (Exception e){
logger.info("HardwareHelper.addMonitor error: " + e.getMessage());
//todo error handling
}
return null;
}
}
When an error occurs the logger will print the error but I need to be able to control what happens after based on the response. So I need to see the body of the post request that is returned after. If everything goes well an ID is returned that I can read by printing the results of the addMonitor() method, but this is obviously not possible when it throws an exception as it skips to the catch part. How do I scan the request body when an error is thrown and handle this appropriately
So I would like to wrap a PessimisticLockingFailureException that gets thrown in a jpa repo when trying to get a lock for an entity that is already locked. And handle the wrapped exception in my exception handlers.
But it seems that when spring tries to end the transaction the connection is already closed and spring throws a new exception that overwrites the exception I would like to see.
In the logs I get "Application exception overridden by rollback exception" and it is this I would like to avoid. (Cause of rollback ex is that "Connection is closed")
Is there a solution to this? Or am I doing something wrong?
(Here's some pseudo code of what I'm doing)
String restControllerMethod(String args) {
try {
return service.serviceMethod(args);
} catch (Exception e1) {
throw e1; // org.springframework.orm.jpa.JpaSystemException caused by org.hibernate.TransactionException caused by java.sql.SQLException
}
}
#Transactional
String serviceMethod(String args) {
Entity entity;
try {
entity = repo.repoFindMethod(args);
} catch (Exception e2) {
throw new WrappingException(e2); // org.springframework.dao.PessimisticLockingFailureException caused by org.hibernate.PessimisticLockException
}
// do some processing with entity
return result;
}
#Lock(LockModeType.PESSIMISTIC_READ)
String repoFindMethod(String args);
I'm using spring-boot-starter-parent 2.3.2.RELEASE with spring-boot-starter-web spring-boot-starter-data-jpa and an emmbedded h2 db
Fixed this by adding a com.zaxxer.hikari.SQLExceptionOverride implementation and pointing the
spring.datasource.hikari.exception-override-class-name to it.
This causes hikari to not close the connection when the db throws an exception with the specified error code.
I've also added #QueryHints({#QueryHint(name = "javax.persistence.lock.timeout", value = "0")}) to the locking query since default lock wait times can be vendor specific
The issue with this solution is that it is vendor specific (both for h2 and hikari). And not all vendors support a custom timeout for obtaining locks (h2 for example does not support this but it matters less since it's timeout is very short anyway)
Example of my solution (for h2):
spring.datasource.hikari.exception-override-class-name=com.example.H2SQLExceptionOverride
public class H2SQLExceptionOverride implements SQLExceptionOverride {
private static final Logger logger = LoggerFactory.getLogger(H2SQLExceptionOverride.class);
public static final int LOCK_TIMOUT_ERROR_CODE = 50200;
#java.lang.Override
public Override adjudicate(SQLException sqlException) {
if (sqlException.getErrorCode() == LOCK_TIMOUT_ERROR_CODE) {
logger.debug("Diverting from default hikari impl and continuing transaction with errorCode: "
+ sqlException.getErrorCode() + " and sqlState: " + sqlException.getSQLState());
return Override.DO_NOT_EVICT;
}
return Override.CONTINUE_EVICT;
}
}
I try to develop a scenario here my code must be asynchronous using quarkus framework, bellow a snippet of my code:
#Inject
ThreadContext threadContext;
#Inject
ManagedExecutor managedExecutor
#Transactional
#ActivateRequestContext
private void asyncMethod(DataAccessAuthorisationEntity dataAccess) {
dataAccess.setStatus(IN_PROGRESS);//!!!!!!!!!
dataAccessAuthorisationRepository.persist(dataAccess);
threadContext.withContextCapture(CompletableFuture.completedFuture("T")).runAsync(()->{
logger.info("[][][] for dataAccess id we begin the treatement "+dataAccess.getId());
boolean exit = false;
PortfolioEntity portfolioEntity = portfolioRepository.findById(dataAccess.getPortfolioId());
System.out.println("");
try {
logger.info("[BEGIN][copyFileAfterSharing] for data access id= "+dataAccess.getId());
String portfolioId = portfolioEntity.getExternalId() + "_" + portfolioEntity.getExternalIdType().getCode();
fileService.copyFileOnAnotherServer(new CopyObject(portfolioId, dataAccess.getStartPoint().toString(),
dataAccess.getEmitterOrganisationId(), dataAccess.getRecipientOrganisationId()));
} catch (Exception e) {
dataAccess.setStatus(PENDING);//!!!!!!!!!
dataAccessAuthorisationRepository.persist(dataAccess);
logger.info("[ERROR][copyFileAfterSharing][BELLOW STACKTRACE] for data access id= "+dataAccess.getId());
e.printStackTrace();
exit= true;
}
},managedExecutor);
}
but I get always when I my execution pass by the exception catch and When I call dataAccessAuthorisationRepository.persist(dataAccess) I get:
Transaction is not active, consider adding #Transactional to your
method to automatically activate one.
because I update my entity dataAccess twice time in the same transaction
Quarkus creates a proxy wrapper around your instance that is injected. When you call a method of a manged bean you call actually this proxy object, that hanldes annotations. If you call a mehtod via "this." the Bean-container/proxy will not detect this call as the call does not go thorugh it. You can't use annotations on calls with "this.".
Below is what I did, I need to implement rollback, using #transactional annotation, but not working as expected, what else need to be done for proper rollback to happen ?? I want that when the code is executed result in db should be "testingOne" , currently it is set to "notRollBacked". Can you please point my mistake.
public Response deleteUser(Request argVO)throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setUserName("testingOne");
UsersLocalServiceUtil.updateUsers(users);
try
{
testRollbackFunction();
}
catch (Exception ex)
{
}
return new Response();
}
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
private void testRollbackFunction() throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setUserName("notRollbacked");
UsersLocalServiceUtil.updateUsers(users);
throw new PortalException();
}
****************Edit 1*************
I did what was mentioned in answers:
I did taken bean from context
and written a class/bean as
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
public class RollBack
{
#Transactional(isolation = Isolation.PORTAL, rollbackFor =
{PortalException.class, SystemException.class})
public void thisWillRollBack() throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setBarringReason("notRollbacked");
UsersLocalServiceUtil.updateUsers(users);
throw new PortalException();
}
}
spring xml file bean refrence set as :
<bean id="rollBackBean" class="com.alepo.RollBack">
</bean>
public Response myMethod(Request argVO)throws Exception
{
Users users = UsersLocalServiceUtil.getUsers("sagar");
users.setBarringReason("testingOne");
UsersLocalServiceUtil.updateUsers(users);
try
{
Test test = new Test();
Object obj = test.getBean();
RollBack rollBack = (RollBack)obj;
rollBack.thisWillRollBack();
}
catch (Exception ex)
{
ex.printStackTrace();
}
return new Response();
}
#################EDIT 4
now calling rollback function as :
RollBack rollBack = (RollBack)PortalBeanLocatorUtil.getBeanLocator().locate("rollBackBean");
rollBack.thisWillRollBack();
No Test class in picture now ...no new anywhere ...
still NOT WORKING .......
If you have a #Transactional annotation on method, Spring wraps the call to this method with aspect handling the transaction.
So:
1) Only public methodes can be wrapped in aspect
2) You call wrapped code only if you call the method on a bean taken from / injected by Spring container.
In your case:
1) The code isn't wrapped in transactional aspect because it is not public method
2) Event if it was, it is called directly from within the class, so you wouldn't call wrapped version anyway.
So the solution is to make separate bean with #Transactional method, inject it into and call it from Response class.
Of course you need <tx:annotation-driven/> in your spring-xml or instruct Spring otherwise to process #Transactional annotations (see the reference).
The issue is you are outside the application context. You are creating a new instance of a class, NEW is bad in Spring, very bad. Get an instance of Test from the application context, not by creating a new instance unless you start your application context in Test. Try to Autowire test in your class you mention above or inject it from Spring and then let me know, but the code you are showing above will never work with transaction management.
Sometimes my beans are not able to initialized properly due to external factors. Such as the MongoDB instance not being online. Is there a graceful way of handling the failed bean initializations? The following is the bean in question:
#Bean
public MorphiaDataSource morphiaDataSource() {
try {
MorphiaDataSource bean = new MorphiaDataSource();
Mongo mongo = new Mongo(mongoHost, mongoPort);
bean.setMongo(mongo);
bean.setMorphia(new Morphia());
bean.setDatabase(mongoDatabase);
bean.setUsername(mongoUsername);
bean.setPassword(mongoPassword);
return bean;
} catch(Exception e) {
logger.error("Error creating MorphiaDataSource: " + e.getMessage());
// Tell the context it's screwed?
}
return null;
}
If you rethrow the exception the context will stop loading and your application will be effectively dead. Or if you really want the JVM to completely stop call System.exit(1)