package com.sissi.addressing.impl; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.WriteConcern; import com.sissi.addressing.Addressing; import com.sissi.config.Dictionary; import com.sissi.config.MongoConfig; import com.sissi.config.impl.MongoUtils; import com.sissi.context.JID; import com.sissi.context.JIDContext; import com.sissi.context.JIDContextBuilder; import com.sissi.context.JIDContextParam; import com.sissi.context.JIDs; import com.sissi.context.impl.JIDContexts; import com.sissi.context.impl.ShareJIDs; /** * 基于ConcurrentHashMap的寻址策略</p>索引策略1:{"jid":1},索引策略2:{"jid":1,"resource":1,"priority":-1},索引策略3:{"index":-1,"jid":1,"resource":1}</p> * * @author kim 2014年1月29日 */ public class MongoAddressing implements Addressing { private final String fieldAddress = "address"; /** * 过滤策略,{"index":1} */ private final DBObject filterIndex = BasicDBObjectBuilder.start(Dictionary.FIELD_INDEX, 1).get(); /** * 过滤策略,{"resource":1} */ private final DBObject filterResource = BasicDBObjectBuilder.start(Dictionary.FIELD_RESOURCE, 1).get(); /** * 排序策略,{"priority":-1} */ private final DBObject querySort = BasicDBObjectBuilder.start(Dictionary.FIELD_PRIORITY, -1).get(); private final JIDContextParam nothing = new NothingJIDContextParam(); private final Map<Long, JIDContext> contexts = new ConcurrentHashMap<Long, JIDContext>(); private final MongoConfig config; private final JIDContextBuilder offline; public MongoAddressing(MongoConfig config, JIDContextBuilder offline) { super(); this.offline = offline; this.config = config.reset(); } @Override public Addressing join(JIDContext context) { if (MongoUtils.success(this.config.collection().save(this.buildQueryWithNecessaryFields(context), WriteConcern.SAFE))) { this.contexts.put(context.index(), context); } return this; } @Override public Addressing leave(JIDContext context) { if (MongoUtils.success(this.config.collection().remove(this.buildQueryWithNecessaryFields(context), WriteConcern.SAFE))) { this.contexts.remove(context.index()); } return this; } /* * {"$set":{"priority":Xxx}} * * @see com.sissi.addressing.Addressing#priority(com.sissi.context.JIDContext) */ public Addressing priority(JIDContext context) { this.config.collection().update(this.buildQueryWithSmartResource(context.jid(), true), BasicDBObjectBuilder.start("$set", BasicDBObjectBuilder.start(Dictionary.FIELD_PRIORITY, context.priority()).get()).get()); return this; } @Override public JIDContexts find(JID jid) { return this.find(jid, false); } @Override public JIDContexts find(JID jid, boolean usingResource) { return this.find(jid, usingResource, true); } @Override public JIDContext findOne(JID jid) { return this.findOne(jid, false); } public JIDContext findOne(JID jid, boolean usingResource) { return this.findOne(jid, usingResource, false); } /* * 1,符合条件唯一JIDContext</p>2,不存在则</p>2.1,使用离线JIDContext</p>2.2,使用Find(All) * * @see com.sissi.addressing.Addressing#findOne(com.sissi.context.JID, boolean, boolean) */ public JIDContext findOne(JID jid, boolean usingResource, boolean offline) { DBObject entity = this.config.collection().findOne(this.buildQueryWithSmartResource(jid, usingResource), this.filterIndex); return entity != null ? this.contexts.get(MongoUtils.asLong(entity, Dictionary.FIELD_INDEX)) : offline ? this.offline.build(jid, this.nothing) : this.find(jid); } public JIDs resources(JID jid) { return this.resources(jid, false); } public JIDs resources(JID jid, boolean usingResource) { return new ShareJIDs(jid, this.config.collection().find(this.buildQueryWithSmartResource(jid, usingResource), this.filterResource)); } private JIDContexts find(JID jid, boolean usingResource, boolean usingOffline) { return new MongoJIDContexts(jid, usingOffline, this.config.collection().find(this.buildQueryWithSmartResource(jid, usingResource), this.filterIndex).sort(this.querySort)); } /** * For Join / Remove</p>{"index":Xxx,"jid":Xxx,"resource":Xxx,"address":Xxx} * * @param context * @return */ private DBObject buildQueryWithNecessaryFields(JIDContext context) { return BasicDBObjectBuilder.start().add(Dictionary.FIELD_INDEX, context.index()).add(Dictionary.FIELD_JID, context.jid().asStringWithBare()).add(Dictionary.FIELD_RESOURCE, context.jid().resource()).add(this.fieldAddress, context.address().toString()).get(); } /** * For FindXxx</p>{"jid",Xxx,"resource":Xxx(如果使用资源)} * * @param jid * @param usingResource * @return */ private DBObject buildQueryWithSmartResource(JID jid, boolean usingResource) { // JID,Resource BasicDBObjectBuilder query = BasicDBObjectBuilder.start(Dictionary.FIELD_JID, jid.asStringWithBare()); if (usingResource && !jid.isBare()) { query.add(Dictionary.FIELD_RESOURCE, jid.resource()); } return query.get(); } private class MongoJIDContexts extends JIDContexts { private final static long serialVersionUID = 1L; private MongoJIDContexts(JID jid, boolean usingOffline, DBCursor cursor) { this.read(cursor).usingOffline(jid, usingOffline); } private MongoJIDContexts read(DBCursor cursor) { try (DBCursor iterator = cursor) { while (iterator.hasNext()) { JIDContext context = MongoAddressing.this.contexts.get(MongoUtils.asLong(iterator.next(), Dictionary.FIELD_INDEX)); // Double check 4 multi thread if (context != null) { super.add(context); } } } return this; } private MongoJIDContexts usingOffline(JID jid, boolean usingOffline) { if (usingOffline && super.isEmpty()) { super.add(MongoAddressing.this.offline.build(jid, MongoAddressing.this.nothing)); } return this; } } private class NothingJIDContextParam implements JIDContextParam { @Override public <T> T find(String key, Class<T> clazz) { return null; } } }