I have a GWT application that has RPC service on its back end. I'm currently trying to implement users support and the only question that still remains is the way I should store session data.
I'm storing session id using
getThreadLocalRequest().getSession().setAttribute("sid", "randomSIDgoeshere");
So, the first question is more to Java servlets than to GWT. Does this code guarantee that next time I make a call like this:
getThreadLocalRequest().getSession().getAttribute("sid");
It would either be null (in case it gets called for the user that hasn't yet visited the piece of code where SID attribute is set) or it would exactly the same SID I've already save for that user. In other words, are these 2 pieces of code user-specific? (by user I mean single browser on a single computer)
The second question is about storing the mappings between SIDs and some extra data like user id. In case I have a code like this:
public class MyGwtServiceImpl extends RemoteServiceServlet implements MyGwtService {
// SID to User ID mappings
private final Map<String, String> sessions =
new HashMap<String, String>();
...
}
Is it guaranteed that sessions is always the same object for all requests and its data will remain "alive" unless the whole application is terminated? (Tomcat is stopped for instance) Is it normal approach or I should persist all these mappings on my DB?
First:
Yes, it does guarantee. The next time you call getThreadLocalRequest().getSession().getAttribute("sid"); on the next request, the attribute sid will stay there. But remember, that's the local request area, thus only requests from the same user (browser + ip) shall share the information. That means:
Fact:
User A connects with Firefox
You store a random value X with the ID Y
Case 1
User A connects with Firefox
You can retrieve the X
Case 2
User A connects with Google Chrome
You cannot retrieve the value X
Case 3
User B connects with Firefox
You cannot retrieve the value X
So yes, the session's content is user-specific. What exists in one session does not imply that will exist in other session.
Second:
No, it is not guaranteed. Although most of the times the same servelet isntance will be called, it is not guaranteed that will always exist. If you want to persist with attributes in your servelet, you must declare those attributes as Static, and by so, THAT static attribute won't be user-specific. Or you can store in the ServeletContext
I say this because different implementations (like Glassfish) can terminate instances if the servelet isn't being required for a long period of time (as far as I remember, I'm not sure of this (Glassfish terminating the instance)). But there is no documentation saying that it does guarantee that will be the same instance, so you cannot declare non-static attributes and share between diferent instances.
Related
I have the following code for an instant messenger style app.
public class MyEndpoint {
private Integer numberOfConvos=0;
...}
then I update it like this:
#ApiMethod (name="createGroup")
public myModel createGroup(#Named("profile") String profile){
numberOfConvos=numberOfConvos+1;
}
So everytime I make a new chat, I can make a unique and increasing ID.
If I redeploy the backend (bug fix for example) would the variable be reset? I do not want to store this one token in the datastore, because it doesnt seem needed and datastore charges for reads/writes.
If it would be reset each time I deploy, what is the correct way to keep track of this variable?
If is does not reset when I redeploy, how could i force it to reset?
A non-static variable is initialized for each thread. Several threads can be running on the same App Engine instance and your Cloud Endpoint service can be running on several instances in parallel.
So let's say that given the current load, you Cloud Endpoint service is served through 3 instances I1, I2, I3. Let's also say that each instances run 5 threads. In that case you would have 15 different versions of numberOfConvos with 15 different values.
Keep also in mind that instances can be turned on or off by Google at any time, in which case your service would be moved to a different instance. This would reset the numberOfConvos variable.
To put it more generally : your java code should be stateless, you should not store state between requests in a variable, even a static one.
You have two choices here :
If you do not want/need to keep track of the number of convos on the server and just want a way to uniquely identify each convo, then use the UUID class to generate a unique id for each convo, with very low risk of collision. The documentation is here but a typical code would be :
UUID.randomUUID().toString()
If you want to keep track of the number of convos, or persist the convo ids on the server, your only choice is to use a database such as App Engine's datastore to keep track of the variable. If you're new to this subject, I suggest you read a bit about transactions, otherwise you will not be able to manage the state correctly. Note that App Engine can generate ids automatically for you.
In my code, there is a websocket server that persists information to the database on behalf of the connected client.
I am using Jetty 9, Hibernate, and Postgres.
Essentially, when a client posts a data object via the websocket:
The server deserializes the data
Checks to see if the object already exists in the database, based on content
If a match is found
The server will update the database row, and indicate that the item already exists
Else
The server will create a new database row and indicate that the item was added.
In this system:
An endpoint (ie., URL) corresponds to a single user principal
Multiple connections to the same endpoint are allowed, meaning multiple connections from different clients to a single user.
Data objects in the database are specific to a user
(This means that the server will only check rows belonging to the user for already-existing items)
This is all working well, except when 2 clients post the same data at precisely the same time. Neither server instance knows about the other, and the database ends up with 2 rows that are the same, except for the server-generated ID.
How can I prevent the server from writing the same data at the same time?
I can't use a UNIQUE constraint in the database, because I actually do have to support having multiple rows with the same data, but posted at different times.
I can get all the other sessions attached to the same endpoint, using "session.getOpenSessions()".
Can I:
Synchronize on the endpoint, somehow?
Synchronize on something attached to the session?
Do something with threading configuration?
Thanks for any suggestions.
When you say:
Checks to see if the object already exists in the database, based on
content
You already define a unique constraint, meaning there's a column set combination that must be unique, so that when you match an existing row, you will update instead of inserting.
The database already offers a centralized concurrency control mechanism, so you should probably use an index on all the aforementioned columns.
Are there patterns for implementing a single user per page model in a J2EE environment?
By single user per page I mean, if I access:
mysite.com/foo/pageA
If someone else tries to access this page, they should not be able to until I:
-navigate away from the page
-close the browser
As it stands now, I have implemented this feature using a map that is a member variable in the servlet that handles requests for foo/*. The keys for the map are page names (enum), and the values for the maps are a boolean value that represents whether or not that particular page is locked.
Before the page is served, I look at the map value to determine if the page is occuiped, and if it is not, I send the user to the page and update the map entry (synchronizing on the map), if it is , I send the user to a page displaying the message that the page is occupied.
To implement the locking, I use the onbeforeunload javascript method, in which I use an ajax post call to update the map entry to false, again, synchronizing on the map.
I know that if the application expands to more than one instance, the locking/unlocking will have to be persisted, but as it stands now, the application only serves about 10 users.
It seems to be working, but I was wondering if there were established patterns for accomplishing this, or if the idea of locking a page to only allow a single user is bad practice in a web application.
I would like to get some advice on designing a count based access control. For example I want to restrict the number of users that a customer can create in my system based on their account. So by default a customer can create 2 users but if the upgrade their account they get to create 5 users and so on.
There are a few more features that I need to restrict on a similar basis.
The application follows a generic model so every feature exposed has a backing table and we have a class which handles the CRUD operation on that table. Also the application runs on multiple nodes and has a distributed cache.
The approach that I am taking to implement this is as follows
- I have a new table which captures the functionality to control and the allowed limit (stored per customer).
- I intercept the create method for all tables and check if the table in question needs to have access control applied. If so I fetch the count of created entities and compare against the limit to decide if I should allow the creation or not.
- I am using the database to handle synchronization in case of concurrent requests. So after the create method is called I update the table using the following where clause
where ( count_column + 1 ) = #countInMemory#
. i.e. the update will succeed only if the value stored in the DB + 1 = value in memory. This will ensure that even if two threads attempt a create at the same time, only one of them will be able to successfully update. The thread that successfully updates wins and the other one is rolled back. This way I do not need to synchronize any code in the application.
I would like to know if there is any other / better way of doing this. My application runs on Oracle and MySQL DB.
Thanks for the help.
When you roll back, do you retry (after fetching the new user count) or do you fail? I recommend the former, assuming that the new fetched user count would permit another user.
I've dealt with a similar system recently, and a few things to consider: do you want CustomerA to be able to transfer their users to CustomerB? (This assumes that customers are not independent, for example in our system CustomerA might be an IT manager and CustomerB might be an accounting manager working for the same company, and when one of CustomerA's employees moves to accounting he wants this to be reflected by CustomerB's account.) What happens to a customer's users when the customer is deleted? (In our case another customer/manager would need to adopt them, or else they would be deleted.) How are you storing the customer's user limit - in a separate table (e.g. a customer has type "Level2," and the customer-type table says that "Level2" customers can create 5 users), or in the customer's row (which is more error prone, but would also allow a per-customer override on their max user count), or a combination (a customer has a type column that says they can have 5 users, and an override column that says they can have an additional 3 users)?
But that's beside the point. Your DB synchronization is fine.
I have web application based on jsp and spring mvc where i need resolve this task :
The user must be able to add new instances of the main entity using wizard dialog. The wizard consists of 3 steps:
On the first step there must be a form which allows filling main entity’s fields, including association with the entity related as many-to one (it’s recommended to use drop-down field). The form should contain fields of different types: text, number, date, radio button, etc. Some fields should be required and some are not.
Example: input name, surname, birth date, phone, number of kids, select gender (radiobutton), department (drop-down), etc.
On the second step user fills additional attributes, including association with the entity related as many-to-many with the current one.
Example: associate employee with skills that (s)he has (checkboxes), add some note (textarea).
On the third step all the fields from previous 2 steps should be displayed as read-only fields. The user should confirm saving this data into database. After the user confirms saving, the data should be saved into database, and user should be redirected to the page with the list of objects.
How can i transfer and hold information without using sessions(Http session, session scope)?
You need to keep state across multiple server interactions. There are several possibilities, in general factors such as the size of the state data to be retained influence our decisions.
It sounds like you have some small number of hundreds of bytes here, so you're not particularly constrained by size - a few Megabytes would be more of a challenge.
First possibility, keep it all in the browser in JavaScript variables, no actual need to send anything to server. This is typical of a modern dynamic Web UI, where the server serves up data rather than pages. Sounds like you're in a multi-page world so discount this option.
Second, just put some data (possibly encrypted, in a cookie) effectively the browser is keeping the data for you, but it's shared across the pages.
Third use Http Session state - you case does sound very much like a typical candidate for a session. Why do you want to avoid it? Depending upon your server's capabilities this approach may not give great resilience behaviour (if the state is on one server instance then all requests for a session must be served by the same server). Note that HTTP Session and EJB Session Beans are not the same thing, HttpSessions are lighter weight.
Use a custom session "database" - maybe literally a SQL database maybe something lighter. For larger scale data entry cases, where a user may take 10s of minutes to complete many pages this may be the best option - the user's work is saved should they need to break off and resume later. It's more development work and you need to look at housekeeping too, but it's sometimes the best option.
In summary: be very clear why you reject the "obvious" HTTP session technique, in terms of simplicity it's where I'd start.