首页 > 代码库 > OpenFire源码学习之二十二:openfie对用户的优化(下)

OpenFire源码学习之二十二:openfie对用户的优化(下)

用户名片

在预初始化中,贴出来用户名片的程序。这里也一样不在重复。

首先同样先修改系统属性:

provider.vcard.className

org.jivesoftware.util.redis.expand.RedisVCardProvider

然后需要修改VCardManager名片管理这个类。

RedisVCardProvider:

import redis.clients.jedis.Jedis;
public class RedisVCardProvider implements VCardProvider {

	private static final Logger Log = LoggerFactory.getLogger(RedisVCardProvider.class);
	
    private static final String DELETE_PROPERTIES =
        "DELETE FROM ofVCard WHERE username=?";
    private static final String UPDATE_PROPERTIES =
        "UPDATE ofVCard SET vcard=? WHERE username=?";
    private static final String INSERT_PROPERTY =
        "INSERT INTO ofVCard (username, vcard) VALUES (?, ?)";
	
	private static final int POOL_SIZE = 10;
	
	private BlockingQueue<SAXReader> xmlReaders = new LinkedBlockingQueue<SAXReader>(POOL_SIZE);
	
	public RedisVCardProvider() {
        super();
        // Initialize the pool of sax readers
        for (int i=0; i<POOL_SIZE; i++) {
            SAXReader xmlReader = new SAXReader();
            xmlReader.setEncoding("UTF-8");
            xmlReaders.add(xmlReader);
        }
    }
	
	@Override
	public Element loadVCard(String username) {
		synchronized (username.intern()) {
			Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();
            Element vCardElement = null;
            SAXReader xmlReader = null;
            try {
                // Get a sax reader from the pool
                xmlReader = xmlReaders.take();
                String usercard = jedis.get("OFVCARD:" + username);
                if (usercard == null || "".equals(usercard)) {
                	return XMPPServer.getInstance().getJedisConfDao().getVCardProvider().loadVCard(username);
                }
                vCardElement = xmlReader.read(new StringReader(usercard)).getRootElement();
            }
            catch (Exception e) {
                Log.error("Error loading vCard of username: " + username, e);
            }
            finally {
            	if (xmlReader != null) {
                    xmlReaders.add(xmlReader);
                }
            	XMPPServer.getInstance().getUserJedis().returnRes(jedis);
            }
            return vCardElement;
        }
	}

	@Override
	public Element createVCard(String username, Element vCardElement)
			throws AlreadyExistsException {
		......
	}

	@Override
	public Element updateVCard(String username, Element vCardElement)
			throws NotFoundException {
		......
	}

	@Override
	public void deleteVCard(String username) {
		......
	}
	@Override
	public boolean isReadOnly() {
		// TODO Auto-generated method stub
		return false;
	}
}

VCardManager

/**
 * Manages VCard information for users.
 *
 * @author Matt Tucker
 */
public class VCardManager extends BasicModule implements ServerFeaturesProvider {

	private static final Logger Log = LoggerFactory.getLogger(VCardManager.class);
	
	private VCardProvider provider;
	
    private static VCardManager instance;
    
    private EventHandler eventHandler;
    
    private static HmThreadPool threadPool = new HmThreadPool(5);
    
    public static VCardManager getInstance() {
        return instance;
    }
    
    public static VCardProvider getProvider() {
        return instance.provider;
    }
    
	public VCardManager() {
		super("VCard Manager");
        //String cacheName = "VCard";
        //vcardCache = CacheFactory.createCache(cacheName);
        this.eventHandler = new EventHandler();

        // Keeps the cache updated in case the vCard action was not performed by VCardManager
        VCardEventDispatcher.addListener(new VCardListener() {
            public void vCardCreated(String username, Element vCard) {
                // Since the vCard could be created by the provider, add it to the cache.
                //vcardCache.put(username, vCard);
            	Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();
				try {
					jedis.set("OFVCARD:" + username, vCard.asXML());
				} finally {
					XMPPServer.getInstance().getUserJedis().returnRes(jedis);
				}
            }

            public void vCardUpdated(String username, Element vCard) {
                // Since the vCard could be updated by the provider, update it to the cache.
                //vcardCache.put(username, vCard);
            	vCardCreated(username, vCard);
            }

            public void vCardDeleted(String username, Element vCard) {
                // Since the vCard could be delated by the provider, remove it to the cache.
                //vcardCache.remove(username);
            	Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();
            	
            	try {
            		jedis.del("OFVCARD:" + username);
				} finally {
					XMPPServer.getInstance().getUserJedis().returnRes(jedis);
				}
            }
        });
	}

    public String getVCardProperty(String username, String name) {
        ......
    }
	
    public void setVCard(String username, Element vCardElement) throws Exception {
        boolean created = false;
        boolean updated = false;

        if (provider.isReadOnly()) {
            throw new UnsupportedOperationException("VCard provider is read-only.");
        }
        
        //Element newvCard = null;
    	Jedis jedis = XMPPServer.getInstance().getUserJedis().getJedis();
    	try {
    		boolean exists = jedis.exists("OFVCARD:" + username);
        	
        	if (exists) {
        		threadPool.execute(createTaskByUpdateVCard(provider, username, vCardElement));
        		updated = true;
        	}
        	else {
        		threadPool.execute(createTaskByCreateVCard(provider, username, vCardElement));
        		created = true;
        	}
		}finally {
			XMPPServer.getInstance().getUserJedis().returnRes(jedis);
		}
        // Dispatch vCard events
        if (created) { 
            // Alert listeners that a new vCard has been created
            VCardEventDispatcher.dispatchVCardCreated(username, vCardElement);
        } else if (updated) {
            // Alert listeners that a vCard has been updated
            VCardEventDispatcher.dispatchVCardUpdated(username, vCardElement);
        }
    }
    
	private Runnable createTaskByCreateVCard(final VCardProvider provider, final String username, 
			final Element vCardElement) {   
		return new Runnable() {   
			public void run() {
				try {
					provider.createVCard(username, vCardElement);
				} catch (AlreadyExistsException e) {
					Log.error("AlreadyExistsException: username=" + username + ", vCardElement=" + vCardElement);
				}
			}   
		};   
	} 
	
	private Runnable createTaskByUpdateVCard(final VCardProvider provider, final String username, 
			final Element vCardElement) {   
		return new Runnable() {   
			public void run() {
				try {
					provider.updateVCard(username, vCardElement);
				} catch (NotFoundException e) {
					Log.error("NotFoundException: username=" + username + ", vCardElement=" + vCardElement);
				}
			}   
		};   
	}
    
	public void deleteVCard(String username) {
        if (provider.isReadOnly()) {
            throw new UnsupportedOperationException("VCard provider is read-only.");
        }
        
        final String vusername = username;
        threadPool.execute(new Runnable() {
			@Override
			public void run() {
				provider.deleteVCard(vusername);
			}
		});
        VCardEventDispatcher.dispatchVCardDeleted(username, null);
    }
	
	
	public Element getVCard(String username) {
        Element vCardElement = getOrLoadVCard(username);
        return vCardElement == null ? null : vCardElement.createCopy();
    }
	
	private Element getOrLoadVCard(String username) {
		return provider.loadVCard(username);
    }
	
    @Override
	public void initialize(XMPPServer server) {
        ......
    }
	
    @Override
	public void start() {
        ......
    }
    
    @Override
	public void stop() {
        // Remove this module as a user event listener
        UserEventDispatcher.removeListener(eventHandler);
    }
    
    public void reset() {
        //vcardCache.clear();
    }
    
	@Override
	public Iterator<String> getFeatures() {
		ArrayList<String> features = new ArrayList<String>();
        features.add("redis-vcard-temp");
        return features.iterator();
	}

	private class EventHandler extends UserEventAdapter {
        @Override
		public void userDeleting(User user, Map params) {
            try {
                deleteVCard(user.getUsername());
            } catch (UnsupportedOperationException ue) { /* Do Nothing */ }
        }
    }
	
    public UserCardEnity getUserCardByUserName (String username) {
    	Element element = getVCard(username);
    	UserCardEnity uce = new UserCardEnity();
    	
    	if (element != null) {
    		String myName = element.elementText("MYNAME");
    		String sex = element.elementText("SEX");
    		String oname = element.elementText("ONAME");
    		String moblie = element.elementText("MOBILE");
    		String landline = element.elementText("LANDLINE");
    		String address = element.elementText("ADDRESS");
    		String workUnit = element.elementText("WORKUNIT");
    		String birthday = element.elementText("BIRTHDAY");
    		String photo = element.elementText("PHOTO");
    		String userType = element.elementText("USERTYPE");
    		
    		uce.setMyName( myName);
    		uce.setSex(sex);
    		uce.setOname(oname);
    		uce.setMoblie( moblie) ;
    		uce.setLandline(landline);
    		uce.setAddress(address);
    		uce.setWorkUnit(workUnit) ;
    		uce.setBirthday(birthday);
    		uce.setPhoto(photo);
    		
    		if (userType == null) {
    			uce.setUserType("");
    		}
    		else if (1 == Integer.valueOf(userType)) {
    			uce.setUserType("student");
    		}
    		else if (2 == Integer.valueOf(userType)) {
    			uce.setUserType("teacher");
    		}
    		else if (3 == Integer.valueOf(userType)) {
    			uce.setUserType("Guardian");
    		}
    		else if (4 == Integer.valueOf(userType)) {
    			uce.setUserType("edusun admin");
    		}
    		else if (5 == Integer.valueOf(userType)) {
    			uce.setUserType("Agents");
    		}
    		else {
    			uce.setUserType("Other locations");
    		}
    	}
    	return uce;
    }
}

用户名片就到这了。

用户搜索

搜索用户的时候,在openfire中都是重新查找关系数据库的。因为我们已经将用户预加载到了redis中。那么这里只需要对用户做一些分词存储检索即可。

本人在这里做了比较简单的分词处理。比如用户名,手机号码等。

首先要做的就是制作用户分词了。然后要做的就是需要修修改搜索的handler处理类。Openfire上提供了一个search搜索插件。查询消息最后提交给SearchPlugin这个类的handler方法。本人这里就不做描述。重点要说的就是如何在redis中分词存储。

客户端发送用户查找请求如下:

<iq id="E6l1b-43" to="test@search.hytest240" type="get"><query xmlns="jabber:iq:search"></query></iq>

请看代码清单:

RedisSearchManager:

public class RedisSearchManager extends BasicModule{

	private static final Logger LOG = LoggerFactory.getLogger(RedisSearchManager.class);

	private static final Integer timeout = 1000*10;
	private static final int maxActive = 5000 * 10;
	private static final int maxIdle = 5;
	private static final long maxWait = (1000 * 100);
	
	private static JedisPool pool;
	private static XMPPServer loaclserver;
	private static JedisPoolConfig config;
	
	public RedisSearchManager() {
		super("Redis search manager");
	}
	
	private static JedisPoolConfig createConfig() {
           ......
	}
	
	private static void createJedisPool() {
		......//创建连接池
	}

	private static synchronized void poolInit() {
		boolean enabled = JiveGlobals.getBooleanProperty("plugin.userservice.enabled");
		if (enabled) {
			if (pool == null)
				createJedisPool();
		}
	}

	public Jedis getSearchJedis() {
		if (pool == null)
			poolInit();
		Jedis jedis = pool.getResource();
		jedis.select(3);
		return jedis;
	}

	public void returnSearchJedis(Jedis jedis) {
		pool.returnResource(jedis);
	}
	
	@Override
	public void initialize(XMPPServer server) {
.....
    }
	
	//创建用户的搜索关键词数据
	public void createSearchAllUserData() {
		Collection<User> userList = XMPPServer.getInstance().getUserJedis().getAllUser();
		for (User user : userList) {
			createSearchUserData(user);
		}
		System.out.println("Initialize the user search data is completed...(the end)");
	}
	
   //这里根据string长度切分。按照“|”分割
	public void createSearchUserData(User user) {
		Jedis jedis = getSearchJedis();
		IndexWriter iw = new IndexWriter(jedis);
		String username = user.getUsername();
		String keyword = "";
		if (username.length() >= 4) {
			int index = 4;
			for (int i=0; i<=username.length()-4; i++) {
				String usernameKey = username.substring(0, index++) + "|";
				keyword += usernameKey;
			}
		}
		else {
			keyword = username + "|";
		}
		
		if (user.getName() != null && !"".equals(user.getName())) {
			keyword += user.getName() + "|";
		}
		if (user.getEmail() != null && !"".equals(user.getEmail())) {
			keyword += user.getEmail() + "|";
		}
		if (user.getMoblie() != null && !"".equals(user.getMoblie())) {
			keyword += user.getMoblie();
		}
		

		if ("|".equals(keyword.substring(keyword.length()-1))) {
			keyword = keyword.substring(0, keyword.length()-1);
		}
		
		iw.addIdAndIndexItem(username, keyword);
		iw.addNeedSortItem("USERNAME",username);
        iw.addNeedSortItem("CREATIONDATE",user.getCreationDate() != null ? user.getCreationDate() : StringUtils.dateToMillis(new Date()));
        iw.addNeedSortItem("MODIFICATIONDATE",user.getModificationDate() != null ? user.getModificationDate() : StringUtils.dateToMillis(new Date()));
        iw.writer();
        System.out.println("create user search data, id:" + username + ", keyword:" + keyword);
        LOG.info("create user search data, id:" + username + ", keyword:" + keyword);
        returnSearchJedis(jedis);
	} 
}

IndexWriter写入索引

public class IndexWriter {
	private Jedis jedis;
	private String id;
	private Map<String, String> items = new HashMap<String, String>();
	private String contentItems[];

	public IndexWriter(Jedis jedis) {
		if (!jedis.isConnected()) {
			jedis.connect();
		}
		this.jedis = jedis;
	}
	/**
	 * @param id
	 *            必须有
	 * @param content
	 *            是分词程序切分后的内容,每个词中间必须用用“|”分隔,如:中国|中国人|2012
	 */
	public void addIdAndIndexItem(String id, String content) {
		this.id = id;
		contentItems = content.split("\\|");
	}
	public void addNeedSortItem(String name, String value) {
		items.put(name, value);
	}
	public void writer() {
		indexWriter();
		itemWriter();
	}
	private void indexWriter() {
		if (!id.equals("") && contentItems.length != 0) {
			for (int i = 0; i < contentItems.length; i++) {
				jedis.sadd(contentItems[i].trim(), id);
			}
		}
	}
	private void itemWriter() {
		if (items.size() != 0) {
			Iterator<Entry<String, String>> it = items.entrySet().iterator();
			while (it.hasNext()) {
				Entry<String, String> entry = (Entry<String, String>) it.next();
				jedis.set(entry.getKey().toString() + ":" + id, entry
						.getValue().toString());
			}
		}
	}
}

IndexSearch:查找索引

public class IndexSearch {
	public static int ALPHA = 0;
	public static int DESC = 1;
	public static int ASC = 2;

	private Jedis jedis;
	private int limit = 100;
	private String itemName = null;
	private int pager = 0;

	public IndexSearch(Jedis jedis) {
		if (!jedis.isConnected()) {
			jedis.connect();
		}
		this.jedis = jedis;
	}

	private SortingParams getSP(String item, int sort) {
		SortingParams sp = new SortingParams();
		sp.limit(pager, limit);

		if (null == item || "".equals(item)) {
			switch (sort) {
			case 1:
				sp.desc();
				break;
			case 2:
				sp.asc();
				break;
			case 0:
			default:
				sp.alpha();
				break;
			}
		} else {
			switch (sort) {
			case 1:
				sp.by(itemName + ":*").desc();
				break;
			case 2:
				sp.by(itemName + ":*").asc();
				break;
			case 0:
			default:
				sp.by(itemName + ":*").alpha();
				break;
			}
		}
		return sp;
	}

	private List<String> isearch(int sort, String... query) {
		jedis.sinterstore("tempKey", query);
		return jedis.sort("tempKey", this.getSP(itemName, sort));
	}

	public List<String> search(String... query) {
		return this.isearch(0, query);
	}

	public List<String> search(int sort, String... query) {
		return this.isearch(sort, query);
	}

	public List<String> search(String itemName, int sort, String... query) {
		this.itemName = itemName;
		return this.isearch(sort, query);
	}

	public List<String> search(String itemName, int sort, int limit,
			String... query) {
		this.itemName = itemName;
		this.limit = limit;
		return this.isearch(sort, query);
	}

	public List<String> search(String itemName, int sort, int pager, int limit,
			String... query) {
		this.itemName = itemName;
		this.limit = limit;
		this.pager = pager;
		return this.isearch(sort, query);
	}
}

Ok用户搜索这就只贴出一些比较关键性的代码。提供思路。代码贴多了搞了篇幅很长。


OpenFire源码学习之二十二:openfie对用户的优化(下)