openfire 保存离线消息

文章参考 http://www.cnblogs.com/yjl49/archive/2011/07/22/2371969.html

 

OfflineMessagetrategy —— 离线消息的处理策略类。

 

1.静态成员变量type 用来设置消息的处理类型,主要包括:

          打回        

          丢弃

          存储,在超限情况下打回

          存储,在超限情况下丢弃

 

2.静态成员变量quota 标识最大所能存储的message总和大小默认为100k。

 

3.支持OfflineMessageListener事件监听器,只要实现此接口并加入到监听器列表,则在消息被打回或存储时可扑捉到相应消息。        

备注:在OfflineMessagetrategy类中的下面两个方法会执行添加的监听器

private static List<OfflineMessageListener> listeners = new CopyOnWriteArrayList<OfflineMessageListener>();
/**
 * Registers a listener to receive events.
 *
 * @param listener the listener.
 */
public static void addListener(OfflineMessageListener listener) {
	if (listener == null) {
		throw new NullPointerException();
	}
	listeners.add(listener);
}

/**
 * Unregisters a listener to receive events.
 *
 * @param listener the listener.
 */
public static void removeListener(OfflineMessageListener listener) {
	listeners.remove(listener);
}
private void store(Message message) {
	messageStore.addMessage(message);
	// Inform listeners that an offline message was stored
	if (!listeners.isEmpty()) {
		for (OfflineMessageListener listener : listeners) {
			listener.messageStored(message);
		}
	}
}

private void bounce(Message message) {
	// Do nothing if the sender was the server itself
	if (message.getFrom() == null) {
		return;
	}
	try {
		// Generate a rejection response to the sender
		Message errorResponse = message.createCopy();
		errorResponse.setError(new PacketError(PacketError.Condition.item_not_found,
				PacketError.Type.continue_processing));
		errorResponse.setFrom(message.getTo());
		errorResponse.setTo(message.getFrom());
		// Send the response
		router.route(errorResponse);
		// Inform listeners that an offline message was bounced
		if (!listeners.isEmpty()) {
			for (OfflineMessageListener listener : listeners) {
				listener.messageBounced(message);
			}
		}
	}
	catch (Exception e) {
		Log.error(e.getMessage(), e);
	}
}

 

4.所有离线消息都存储在表ofoffline中。

 

 

OfflineMessageStore:

用来具体处理离线消息的类。

 

1.用addMessage(Message message)来存储一条消息。

 

2.用getMessages(String username,boolean delete)来提供指定用户的所有离线消息。delete参数则是指定提取后是否要从数据库中删除。

 

3.用deleteMessages(String username)来删除某个用户所有的离线消息。

 

4.此类实现了UserEventListener接口,当用户被删除时掉用deleteMessage()来删除此用户所有离线消息。

 

离线消息的存储时机:

1.routingFailed()在进行消息路由失败的情况下。比如目的用户不在线。

 

2.由ConnectonManager转发过来的消息找不到路由或无法处理。

 

离线消息的提取时机:

1.用户状态变为可用,session被初始化时。比如用户上线。

 

2.用户发送了特定的IQ消息,要求递送自己发送的离线消息。

 

3.用户的session权限级别发生了变化。

  

我查看代码的入口是从routingFailed()在进行消息路由失败的情况下开始的

 

RoutingTableImpl类中的
/*
 * (non-Javadoc)
 * @see org.jivesoftware.openfire.RoutingTable#routePacket(org.xmpp.packet.JID, org.xmpp.packet.Packet, boolean)
 * 
 * @param jid the recipient of the packet to route.
 * @param packet the packet to route.
 * @param fromServer true if the packet was created by the server. This packets should
 *        always be delivered
 * @throws PacketException thrown if the packet is malformed (results in the sender's
 *      session being shutdown).
 */
public void routePacket(JID jid, Packet packet, boolean fromServer) throws PacketException {
	boolean routed = false;
	if (serverName.equals(jid.getDomain())) {
		// Packet sent to our domain.
		routed = routeToLocalDomain(jid, packet, fromServer);
	}
	else if (jid.getDomain().contains(serverName)) {
		// Packet sent to component hosted in this server
		routed = routeToComponent(jid, packet, routed);
	}
	else {
		// Packet sent to remote server
		routed = routeToRemoteDomain(jid, packet, routed);
	}

	if (!routed) {
		if (Log.isDebugEnabled()) {
			Log.debug("RoutingTableImpl: Failed to route packet to JID: {} packet: {}", jid, packet.toXML());
		}
		if (packet instanceof IQ) {
			iqRouter.routingFailed(jid, packet);
		}
		else if (packet instanceof Message) {
		//查看routingFailedf()方法里面的内容
			messageRouter.routingFailed(jid, packet);
		}
		else if (packet instanceof Presence) {
			presenceRouter.routingFailed(jid, packet);
		}
	}
}



/**
 * Notification message indicating that a packet has failed to be routed to the recipient.
 *
 * @param recipient address of the entity that failed to receive the packet.
 * @param packet Message packet that failed to be sent to the recipient.
 */
public void routingFailed(JID recipient, Packet packet) {
	// If message was sent to an unavailable full JID of a user then retry using the bare JID
	if (serverName.equals(recipient.getDomain()) && recipient.getResource() != null &&
			userManager.isRegisteredUser(recipient.getNode())) {
		routingTable.routePacket(recipient.asBareJID(), packet, false);
	} else {
		// Just store the message offline
		//将消息保存到数据库中ofoffline表中
		messageStrategy.storeOffline((Message) packet);
	}
}
备注:userManager.isRegisteredUser(recipient.getNode())这个类会判断用户是否是注册用户,



/**
 * Returns true if the specified local username belongs to a registered local user.
 *
 * @param username to username of the user to check it it's a registered user.
 * @return true if the specified JID belongs to a local registered user.
 */
public boolean isRegisteredUser(String username) {
	if (username == null || "".equals(username)) {
		return false;
	}
	try {
		getUser(username);
		return true;
	}
	catch (UserNotFoundException e) {
		return false;
	}
}
备注:getUser(username)就是从缓存或者数据库中查询用户


/**
 * Returns the User specified by username.
 *
 * @param username the username of the user.
 * @return the User that matches <tt>username</tt>.
 * @throws UserNotFoundException if the user does not exist.
 */
public User getUser(String username) throws UserNotFoundException {
	if (username == null) {
		throw new UserNotFoundException("Username cannot be null");
	}
	// Make sure that the username is valid.
	username = username.trim().toLowerCase();
	//判断用户是否在缓存中
	User user = userCache.get(username);
	if (user == null) {
		synchronized (username.intern()) {
			user = userCache.get(username);
			if (user == null) {
			//用户不在缓存中则从数据库中查询这个用户是否在用户表中,如果存在则放入缓存中
				user = provider.loadUser(username);
				userCache.put(username, user);
			}
		}
	}
	return user;
}
备注:provider是UserProvider接口的实例,这个接口就是访问用户表的接口,具体的实现类是根据具体的配置实现的,可以在ofproperty中查询这个接口

 

猜你喜欢

转载自hbiao68.iteye.com/blog/1996646