One transaction per request - servlet response is committed too early - java

i'm trying to implement one transaction per request in my spring boot application.
I have a filter with HIGHEST_PRECEDENCE order that opens a transaction, lets other filters and the app's services do their job and commits the transaction. However, if commit fails, the servlet response has already been written and committed (with a success code), i can't modify it's status and message body to provide info about the error.
I need the servlet response to be modifiable in my filter, but something commits it.
I've disabled jackson commits, but right after the last filter (WsFilter) the message is commited anyway. The buffer size is as big as possible. I guess the embedded tomcat commits by default after WsFilter?
I've also tried using a wrapper and forbidding flush until a Boolean flag is set to true in my transaction filter, but null pointer exception occurres - coyoteResponse is null.
How do i keep a response uncommitted?

I ended up using ContentCachingResponseWrapper. This way all filters after my transaction filter use the wrapper instead of the original response. Since the original response is untouched it is not commited. After my filter close the transaction i call responseWrapper.copyBodyToResponse() to "flush" all data from the wrapper to the original response.
in general the code looks like this:
// create a transaction
// create a wrapper
chain.doFilter(request, responseWrapper);
// end the transaction
responseWrapper.copyBodyToResponse();

Related

Hibernate not flushing out the results even after calling session.flush method

I'am adding some record in db using hibernate, but when i try to get the same record in some millis then it is returning 0 results.
This is the flow:
Create a put request.
Put result in db.
Response received 202 accepted.
Then same controller sends the request to another controller which then tries to update that record.
It returns result as failure.
Environment:
JDK 8
Spring boot 1.2.5
Hibernate 4.3.11.Final
I tried following ways:
Set session flush mode to ALWAYS and COMMIT.
Manually did session.flush() and session.clear()
Please provide the solution as soon as possible.

Spring Data Rest transaction boundaries

I've been implementing some business logic/validation with the Spring Data Rest and JPA repositories using repository validators as described in:
http://docs.spring.io/spring-data/rest/docs/current/reference/html/#events.
After digging deeper into the SDR code I noticed that the validators (or more generally, repository listeners) are not invoked within a transaction.
From the source code of org.springframework.data.rest.webmvc.RepositoryEntityController:
private ResponseEntity<ResourceSupport> createAndReturn(Object domainObject, RepositoryInvoker invoker,
PersistentEntityResourceAssembler assembler, boolean returnBody) {
// validation logic is implemented in the listener, no transaction yet
publisher.publishEvent(new BeforeCreateEvent(domainObject));
// invoker calls repository, which is wrapped in the transactional proxy,
// only then transaction begins
Object savedObject = invoker.invokeSave(domainObject);
publisher.publishEvent(new AfterCreateEvent(savedObject));
PersistentEntityResource resource = returnBody ? assembler.toFullResource(savedObject) : null;
HttpHeaders headers = prepareHeaders(resource);
addLocationHeader(headers, assembler, savedObject);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, headers, resource);
}
As seen in the code, the listeners are not called within a transaction, which could lead to eventual data consistency issues.
Am I missing something? Or the framework simply sets the transactional boundary incorrectly?
In spring data rest the repository method would run its own transaction. I also think that this is problematic in some cases. At least the event handler should be run in the same transaction as the repository method.
There was a similar question here:
Handle spring-data-rest application events within the transaction
Especially this answer provides a workaround that allows you to wrap the whole RepositoryEntityController method in a transaction - I think this is what you need most of the time:
https://stackoverflow.com/a/30713264/5371736

Maintaining a transaction flow using Jersey REST

I am pretty new to the REST world and we are trying our hands at migrating our application to a REST based architecture. We are working on a proof of concept and we need to come up with a working proof that what we are set to achieve can be done using REST.
In our architecture, the front end screens would use Angular and would call REST services to open up a customer session and perform/maintain transactions within that session and when the session is complete, the session details (i.e. all the customer transactions within the customer session) would be sent over for commit to the DB. We only want to write to the DB after customer completes all transactions within a session. In other words, a commit to the DB happens only when the session ends.
I am hoping to get some directions on how best to perform the below steps in our Java classes.
1) Browser initiates a request to open a new customer session. A session POST service is used to generate a new session ID. The session ID is sent back as response to the browser.
Question --> What is the best way to maintain this session ID value in my Java classes?
2) Customer transactions are performed within the session. For each transaction processed a transaction POST service is used to save the transaction information along with the session information.
Question --> In my Java classes what is the best way to maintain this transaction information and how best do I associate this transaction information with the session information that was created by the previous session POST information? The client would maintain the session ID but on the service side I need to be able to map the transaction with the session ID so that I can send back a combined session payload that includes session information and the transaction within that session.
3) A customer can perform some more transactions and each transaction performed would be a transaction POST request which would have to get associated with the session id created before. Each additional transaction performed would have to be associated to the session id on the service side such that when I do a GET on the session id, I need to get the session details along with all transactions within that session.
4) Finally when the session is complete the information from the session and the session payload on the service side (along with all the transactions) will commit to the DB.
I am just looking for some general guidance on how best to do this using my Java classes and Jersey REST.
Any pointers would be appreciated.
Thanks
Ali.
Basically this question isn't easy and requires a lot of writing, however I'll try to reply.
First of all remember that REST is stateless - it means that there's no session and client needs to be authorized with every request. This is a separate topic but a nice authorization method in REST is JSON Web Token.
Secondly REST is about nouns - not verbs. Thus you should avoid URLs like /session/{sessionId}/close/ but try to model the domain using nouns and default HTTP operations: POST (create), PUT (update), GET (read), DELETE (remove).
I guess that session and transactions is just an abstraction I will show you how to model it on an example of shopping cart. In all examples I doubled the URLs - with /users/{userId}/ prefix to show you can refer to resources in many different ways
Create a shopping cart (creating a session)
POST /shopping-carts/
POST /users/{userID}/shopping-carts/
Request: may be empty or should contain necessary details about the cart
Response: must contain a newly-created shoppingCartID
{
"shoppingCartID": "1qaz2wsx"
...
}
Add an item to a shopping cart (create a transaction)
POST /shopping-carts/{shoppingCartID}/items/
POST /users/{userID}/shopping-carts/{shoppingCartID}/items/
Request: contains details about an item being added
Response: returns a newly-added item along with its unique ID
Pay for the shopping cart (commit the transactions)
POST /payments/
POST /users/{userID}/payments/
Request: must contain a shoppingCartID
{
"shoppingCartID": "1qaz2wsx"
...
}
Response: Contains details about newly-created payment
{
"paymentId": "3edc4rfv"
...
}
I know that this is a general answer but it's difficult to give a precise answer for such a wide question.
EDIT (after a discussion in comments)
In my opinion the DB should be used to keep the transactions in a temporary table before they are approved. Even if you don't want to use a DB any other persistent store is highly recommended - imagine what could happen in case of a server restart when transactions are not approved, you will lose all the data.
The options I see:
In memory. You can write a simple in-memory structure with a synchronized access. In the most simple case just plain old HashMap will be enough. Mind the fact that keeping data this way is risky, the can be erased very easily.
Use file system. If you don't want to use DB you can use file system to keep the transactions data while they're uncommitted. After adding a new transaction it's written to a file. On commit file is read and all transactions are saved to DB. A synchronized file access is also very important here. When it comes to data format you can use JSON, XML, even plain java serialization.
The last idea that comes to my head is to use an in memory DB, such as Redis. The data will be erased on a system reboot so they're less likely to be deleted, however this is not safe in my opinion as well. Such DB is much easier to use/maintain than traditional DB.
It all depends what are you trying to implement. I can't imagine a scenario where uncommitted transactions can be simply removed and nothing happens - it seems that there's a must for persistent storage. However the ideas above might be useful as well.

Difference between HibernateTransactionManager and OpenSessionInViewFilter

From the docs - http://docs.spring.io/spring-framework/docs/2.0.x/api/org/springframework/orm/hibernate/HibernateTransactionManager.html
HibernateTransactionManager - Binds a Hibernate Session from the specified factory to the thread, potentially allowing for one thread-bound Session per factory
OpenSessionInViewFilter - This filter makes Hibernate Sessions available via the current thread, which will be autodetected by transaction managers.
What is the difference between both of them and at what scenarios should they be used ?
OpenSessionInViewFilter
Now when you are using OpenSessionInViewFilter, by default the session's flush mode is set to NEVER. So when you try to save something in your action using hibenate and commit it, it wont be reflected in your database. To solve this you need to flush the session in your action class or extend OpenSessionInViewFilter and override closeSession(Session session, SessionFactory sessionFactory).
Now it is also possible that you are maintaining a single transaction for per request. In your action you edit the attributes of a object and update it using session.update(object). But it is not yet commited as some other processing is remaining. At the same time, some other request is invoking a action which tries to retrieve the object which you were updating. Since the object is not yet commited the other request will get the old object. To solve this you need to begin a transaction before you load object and commit the transaction after you update the object. So that as soon as the object is saved/updated it is commited. With this there can be many transaction in single user request but only one session.
The OpenSessionInView pattern only guarantees that the session is open during one single thread execution.
When the page has been rendered and has been returned to the browser, the session gets closed by the filter.
So subsequent requests (e.g. navigation request) require another new session which will be opened by the OpenSessionInViewFilter. But as the "old" person object is not connected to the "new" session, it is considered as disconnected object which's references cannot be loaded lazily.

Could not initialize proxy - no Session

I've got an error that looks like this:
Could not initialize proxy - no Session
I'm working with java, hibernate and spring. This error comes up when trying to generate a PDF document, and I'm following the next steps to generate it on the fly and store in the database.
I sent a request to the app through a POST method. This generates the PDF on the fly and shows to the user.
Just after that request I send another, but through an ajax a request. This will generate the same PDF but will save it in the DB.
The error shows that a query could not be executed due to "could not initialize proxy - no Session" error.
Is there something that am I doing wrong, calling the same methods twice from the same user session? Could it be that the session is closed before both requests have finished?
Hope someone can help me to understand what is happening.
Your problem is that the hibernate Session lives only for one request. It opens in the start of the request and closes at the end. You guessed the answer: Hibernate session is closed before both requests are finished.
Exactly what is happening? Your entity objects live during both requests. How? They are stored in the HTTP session (which is a different thing called session) You don't give much information about the framework you are using, so I can't give you more details, but it is certain that the framework you are using somehow keeps your entities in the HTTP session. This is how the framework makes it easy for you to work with the same objects for more than one requests.
When the processing of the second request starts, the code is trying to access some entity (usually an element of a collection) that is lazily initialized by hibernate. The entity is not attached to a hibernate session, and so hibernate can't initialize the hibernate proxy before reading it. You should open a session and re-attach your entity to it at the beginning of the ajax request processing.
EDIT:
I will try to give a brief explanation of what is happening behind the scene. All java web frameworks have one or more servlets that handle the requests. The servlet handles each request (HttpRequest) by creating a new thread that will finally produce the response (HttpResponse). The method that processes each request is executed inside this thread.
At the beginning of the request processing your application should allocate the resources that it needs for processing (Transaction, Hibernate session etc). At the end of the processing cycle these resources are released (Transaction is committed, hibernate session is closed, JDBC connections are released etc). Lifecycle of these resources could be managed by your framework, or could be done by your code.
In order to support application state in a stateless protocol as HTTP, we have the HttpSession object. We (or the frameworks) put on HttpSession the information that remains relevant between different request cycles of the same client.
During the processing of the first request hibernate reads (lazily) an entity from the database. Due to lazy initialization some parts of this object's structure are hibernate proxy objects. These objects are associated with the hibernate session that created them.
The framework finds the entity from the previous request in the HttpSession object when you try to process the second request. Then it is trying to access a property from a child entity that was lazily initialized and now is a hibernate proxy object. The hibernate proxy object is an imitation of the real object that will ask its hibernate session to fill it with information from the database when someone tries to access one of its properties. This what your hibernate proxy is trying to do. But its session was closed at the end of the previous request processing, so now it doesn't have a hibernate session to use in order to be hydrated (filled with real info).
Note that it is possible that you have already opened a hibernate session at the beginning of the second request, but it isn't aware of the entity that contains the proxy object because this entity was read by a different hibernate sesion. You should re-attach the entity to the new hibernate session.
There is a lot of discussion about how to re-attach a detached entity, but the simplest approach right now is session.update(entity).
Hope it helps.

Categories