Intro
I'm bulding a REST web service using Hibernate and Jersey to supply JSON data to mobile clients. I have a general question about how to deal with Hibernate sessions internally. There are two different approaches(A,B) I'd like to discuss.
Approaches
A. Someone told me I should open a new session per user, let it open for the whole web session of the user and finally close this session after the user stopped using my web service. I was told it would be a better approach from the view of security and performance.
Though I've read that: "Sessions are irrelevant" Common REST Mistakes, 6.
B. Right now I'm using a SessionFactory to open a session in my service classes and close this session immediately after a query is done. My web service is only using GET and POST requests. There is not PUT or DELETE. I don't need any user authentication (like oAuth) to request data. Therefore I don't think it's necessary to use Transactions
Here is an Example of my Service class:
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Query query = session.createQuery("from RoomEntity");
#SuppressWarnings("unchecked")
List<RoomEntity> list = (List<RoomEntity>) query.list();
session.close();
Questions
Would be great to hear your opinion about my following questions:
What would be the best practice? Which approach do you follow?
What do you think about the performance matter?
What do you think about the security matter?
I don't like A. It makes your service stateful and how do you know when a user has finished with your services anyway?
The general rule with Hibernate and web apps is to use a single session per http request. Most REST service GET requests map pretty simply to a single DB query as you have demonstrated with B, so this is the way to go.
You should be having one Session per request, its called Onit of Work pattern. There are frameworks out there that support this implementation one of them is Spring(I would recomend to use it and not try to reinvent a wheel). You can read about it here.
I am in a similar situation. Based on the JBOSS documentation a session per request within Jersey appears to be valid. You could use an implementation of the ContainerRequestFilter in Jersey to open up a hibernate session and then close it with commits in an implementation of the ContainerResponseFilter.
Related
In an server application I use bare websockets without the spring messaging layer and sockjs/stomp. I need my own messaging layer on top, but I want to use for example spring session to leverage the servlet HttpSession replacement to keep the http session alive which is beneath the websocket session, to prevent timeouts. The websocket is opened from a static HTML-page using JavaScript.
In the spring-framework documentation there is all the necessary information to setup bare websocket support with e.g. a basic BinaryWebSocketHandler or TextWebSocketHandler as well as how to
provide your own HandshakeInterceptor.
I've added spring session 1.1.0.RC1 with hazelcast for storing sessions and replacing the servlet container HttpSession implementation with the Session/ExpiringSession proivided by spring session. I just followed the given instructions to do this.
So far everything is fine, but as I'm serving a static HTML page from which I open the websocket using JavaScript without using spring security or any other mechanism to trigger a processing by the servlet container, no http session is intialized so far.
So the question is: How do I intitialze the custom HttpSession which is then stored in a the SessionRepository provided by spring session?
Note: I will provide an answer myself as I think I solved the issue for my case and it may be helpful for others facing a similar problem.
As can be seen from the spring-framework documentation, it is possible to add a custom HandshakeInterceptor. Spring session offers the
// example for reference:
org.springframework.session.web.socket.server.SessionRepositoryMessageInterceptor
which targets the spring messaging layer. It shows how to reset the timer of the HttpSession prior to sending a message within the method preSend(...) as well as how to optain the HttpSession from the request in the method beforeHandshake(...).
I implemented my own HandshakeInterceptor based on this with an almost identical beforeHandshake(...) method, but with one minor difference and of course leaving out the ChannelInterceptorAdapter stuff. The reset of the HttpSession time has to be done somewhere else, but that is out-of-scope.
Because in my case no HttpSession has been created so far, I changed the line obtaining the session from the request to look like this:
HttpSession session = servletRequest.getServletRequest().getSession(true);
Changing the boolean parameter from false to true in the method getSession(...). Now this call will trigger the creation of a new HttpSession (provided by spring session) if none is available so far.
This solution seems to do what is required in my case. Please correct me if I'm missing something or using anything the wrong way.
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.
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.
I have a web application in which we use JSF framework.
I have been diving deep into the security part for web application and hence I was looking to generate my own unique session ID(using encryption algorithm and assign it to every new session which gets created once user logs in.
Can anyone please guide me on how to set manual generated session id in session and ensure with each request that session id is transmitted.
Thanks.
I really doubt you'll generate session IDs that are more secure than the ones generated by the container, but here's what you could do, without using any container-specific extension.
Create a servlet filter which intercept every request to the server.
When a request comes in, check if a session already exists for this request (using getSession(false)). If one exists, then extract your specific cookie MY_SESSION_ID from the request, and compare its value to the one that is stored in the session. If they don't match, reject the request.
If the session doesn't exist, then create it (using getSession(true)), generate your super-secure session ID, store it as a session attribute and add the cookie MY_SESSION_ID to the response.
This has the disadvantage of creating a session automatically, even if it's not strictly needed. But that's the case most of the time when using JSPs of component frameworks.
Attempting to do this at the JSF application layer is unlikely to be successful; I would perform this task at a lower level API. I am assuming a servlet container.
I can think of two approaches:
do this at a container level via a server-specific SPI (if one even exists)
do this by rewriting requests/responses via a servlet Filter
There is insufficient information to comment on the viability of the first approach.
In the second, you would have to determine the name of the session cookie (it is usually JSESSIONID, but does not have to be). Your API would:
map the filter to all application requests
maintain a map of container session ids to "secure" ids
use the filter to rewrite any session cookie in the request with the session id
use the filter rewrite any session cookie in the response with the secure id
use a listener to remove invalid sessions from the map to avoid memory leaks
I'm using Spring for my web app. I have used several SimpleFormControllers. I've created a session in the first SimpleFormController for the login page using:
HttpSession session = request.getSession(true);
How can I protect other SimpleFormControllers using Sessions, i.e. so that other controllers won't load if the user is not loged in.
Thank you
You probably want to use Spring Security.
It's flexible and allows restrictions based on roles.
Without it, you will need to manually check in every controller whether the user logged in or not. Or you'll have to "reinvent" a security framework by adding filter to the webapp.
If you only want to protect the operation of getting the session, you need to write a filter that wraps the original request and overrides the getSession methods. There you can check for login data using the original request's getSession().
BTW, getSession() is equivalent to getSession(true)
To protect the Controller from access outside of the intended Session, you may want to compare the Scoping rules you need with this clearly written Guide.
How to get Session Object In Spring MVC
The author gives an example of creating a Controller annotated with #Scope("session")