/******************************************************************************* * Copyright (c) 2011 epyx SA. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package ch.windmobile.server.social.mongodb; import java.util.ArrayList; import java.util.List; import org.joda.time.DateTime; import org.joda.time.format.ISODateTimeFormat; import ch.windmobile.server.socialmodel.ChatService; import ch.windmobile.server.socialmodel.xml.Message; import ch.windmobile.server.socialmodel.xml.Messages; import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; public class ChatServiceImpl extends BaseMongoDBService implements ChatService { private static final String counter = "function counter(name) { return db." + MongoDBConstants.COLLECTION_CHATROOMS + ".findAndModify({query:{_id:name}, update:{$inc : {counter:1}}, new:true, upsert:true}).counter; }"; public ChatServiceImpl(DB database) { super(database); } private String computeCollectionName(final String chatRoomId) { return MongoDBConstants.COLLECTION_CHAT_ROOM_PREFIX + chatRoomId; } private DBCollection createChatCappedCollection(String collectionName) { DBObject options = BasicDBObjectBuilder.start("capped", true).add("size", 10000).get(); // create the capped chat room of 10K return db.createCollection(collectionName, options); } public DBCollection getOrCreateCappedCollection(String collectionName) { DBCollection col; try { // try to create the collection, it may raise an exception if already create, in this case just ignore it // and get the collection // Due to the "consistency" behavior of mongoDB trying to first check if the collection exist the create it // will not work, race condition will certainly occurs... col = createChatCappedCollection(collectionName); } catch (Exception ex) { col = db.getCollection(collectionName); } return col; } @Override public Message postMessage(String chatRoomId, String pseudo, String text, String emailHash) { String collectionName = computeCollectionName(chatRoomId); DBCollection col = getOrCreateCappedCollection(collectionName); DBObject chatItem = new BasicDBObject(); Double id = (Double) db.eval(counter, chatRoomId); chatItem.put("_id", id.longValue()); chatItem.put(MongoDBConstants.CHAT_PROP_TEXT, text); chatItem.put(MongoDBConstants.CHAT_PROP_USER, pseudo); DateTime date = new DateTime().toDateTimeISO(); chatItem.put(MongoDBConstants.CHAT_PROP_TIME, date.toString()); chatItem.put(MongoDBConstants.CHAT_PROP_EMAIL_HASH, emailHash); col.insert(chatItem); Message message = new Message(); message.setId(id.toString()); message.setDate(date); message.setPseudo(pseudo); message.setText(text); message.setEmailHash(emailHash); return message; } @Override public Messages findMessages(String chatRoomId, int maxCount) { if (maxCount <= 0) { throw new IllegalArgumentException("maxCount arg[1] must be greater than 0"); } final String collectionName = computeCollectionName(chatRoomId); final DBObject fields = BasicDBObjectBuilder.start("_id", 1).add(MongoDBConstants.CHAT_PROP_USER, 1) .add(MongoDBConstants.CHAT_PROP_TEXT, 1).add(MongoDBConstants.CHAT_PROP_TIME, 1).add(MongoDBConstants.CHAT_PROP_EMAIL_HASH, 1).get(); DBCursor result = db.getCollection(collectionName).find(null, fields).sort(new BasicDBObject("$natural", -1)).limit(maxCount); List<DBObject> all = result.toArray(); Messages messages = new Messages(); for (DBObject dbObject : all) { Message message = new Message(); message.setId(dbObject.get("_id").toString()); DateTime dateTime = ISODateTimeFormat.dateTime().parseDateTime((String) dbObject.get("time")); message.setDate(dateTime); message.setPseudo((String) dbObject.get(MongoDBConstants.CHAT_PROP_USER)); message.setText((String) dbObject.get(MongoDBConstants.CHAT_PROP_TEXT)); message.setEmailHash((String) dbObject.get(MongoDBConstants.CHAT_PROP_EMAIL_HASH)); messages.getMessages().add(message); } return messages; } @Override public Long getLastMessageId(String chatRoomId) { try { Double value = (Double) db.getCollection(MongoDBConstants.COLLECTION_CHATROOMS).findOne(new BasicDBObject("_id", chatRoomId)) .get("counter"); return value.longValue(); } catch (Exception e) { return -1L; } } @Override public List<Long> getLastMessageIds(List<String> chatRoomIds) { List<Long> returnValue = new ArrayList<Long>(); for (String chatRoomId : chatRoomIds) { returnValue.add(getLastMessageId(chatRoomId)); } return returnValue; } @Override public boolean allowAnonymousMessages(String chatRoomId) { try { Boolean refuseAnonymous = (Boolean) db.getCollection(MongoDBConstants.COLLECTION_CHATROOMS).findOne(new BasicDBObject("_id", chatRoomId)) .get(MongoDBConstants.CHATROOM_REFUSE_ANONYMOUS); return !refuseAnonymous; } catch (Exception e) { return true; } } }