In my application I want to have a live chat feature - in which multiple people (perhaps 5 or more) can chat together at the same time.
I am using a Java based Google App Engine - this is really the first time I've tried to use GAE Datastore, I'm so used to using Oracle/MySQL so I think my strategy is wrong.
Note: For simplicity, I am omitting any validation/security checks
In some servlet called WriteMessage
I have the following code
Entity entity = new Entity("ChatMessage");
entity.setProperty("userName", request.getParameter("userName"));
entity.setProperty("message", request.getParameter("message"));
entity.setProperty("time", new Date());
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
datastore.put(entity);
In some other servlet called ReadMessages
I have the following code
String id = request.getParameter("id");
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("ChatMessage");
if (id != null) {
// Client requested only messages with id greater than this id
Filter idFilter = new FilterPredicate(Entity.KEY_RESERVED_PROPERTY,
FilterOperator.GREATER_THAN,
KeyFactory.createKey("ChatMessage", Long.parseLong(id)));
query.setFilter(idFilter);
}
PreparedQuery pq = datastore.prepare(query);
JsonArray messages = new JsonArray();
for (Entity result : pq.asIterable()) {
JsonObject jmsg = new JsonObject();
// Client will use this id on the next request to read to poll only
// "new" messages
jmsg.addProperty("id", result.getKey().getId());
jmsg.addProperty("userName", (String) result.getProperty("userName"));
jmsg.addProperty("message", (String) result.getProperty("message"));
jmsg.addProperty("time", ((Date) result.getProperty("time")).getTime());
messages.add(jmsg);
}
PrintWriter out = response.getWriter();
out.print(messages.toString());
In the javascript client code - the WriteMessage
servlet is called any time the user submits a new message - and ReadMessages
servlet is called every second to get new messages.
In order to optimize, the javascript will send the id of the last message that it received (or possibly the highest id it has received so far) on subsequent requests to ReadMessage
, so that the response only contains messages that it hasn't seen before.
This all seems to work at first, but I think maybe there are a couple of things wrong with this code.
Here is what I think is wrong:
Some messages might not be read because I am relying on the id of the ChatMessage's key to filter out messages that the JS client has already seen before - I don't think that will be reliable right?
Some writes might fail because there might be 5 or 6 incoming writes at the same exact time - and my understanding is that this might result in
ConcurrentModificationException
if there are too many writes per second.The date passed on the entity is the current date of the JRE on the application server - maybe I should be using something like "sysdate()" in SQL? I don't know if this is actually an issue or not.
How can I fix the code so that:
All chat messages will be written - would it just be best to have a fail-over so that if the request fails the javascript will just re-attempt until successful?
All chat messages will be read (no exceptions)
Clean up old messages so that only 1000 or so messages are stored