/**
* $RCSfile: MessageRouter.java,v $
* $Revision: 3007 $
* $Date: 2005-10-31 13:29:25 -0300 (Mon, 31 Oct 2005) $
*
* Copyright (C) 2005-2008 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.jivesoftware.openfire; import java.util.Date;
import java.util.List;
import java.util.StringTokenizer; import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.QName;
import org.jivesoftware.openfire.carbons.Sent;
import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.forward.Forwarded;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.session.LocalClientSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError; /**
* <p>Route message packets throughout the server.</p>
* <p>Routing is based on the recipient and sender addresses. The typical
* packet will often be routed twice, once from the sender to some internal
* server component for handling or processing, and then back to the router
* to be delivered to it's final destination.</p>
*
* @author Iain Shigeoka
*/
public class MessageRouter extends BasicModule { private static Logger log = LoggerFactory.getLogger(MessageRouter.class); private OfflineMessageStrategy messageStrategy;
private RoutingTable routingTable;
private SessionManager sessionManager;
private MulticastRouter multicastRouter;
private UserManager userManager; private String serverName; /**
* Constructs a message router.
*/
public MessageRouter() {
super("XMPP Message Router");
} /**
* <p>Performs the actual packet routing.</p>
* <p>You routing is considered 'quick' and implementations may not take
* excessive amounts of time to complete the routing. If routing will take
* a long amount of time, the actual routing should be done in another thread
* so this method returns quickly.</p>
* <h2>Warning</h2>
* <p>Be careful to enforce concurrency DbC of concurrent by synchronizing
* any accesses to class resources.</p>
*
* @param packet The packet to route
* @throws NullPointerException If the packet is null
*/
public void route(Message packet) {
if (packet == null) {
throw new NullPointerException();
}
ClientSession session = sessionManager.getSession(packet.getFrom()); try {
// Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, false);
if (session == null || session.getStatus() == Session.STATUS_AUTHENTICATED) {
JID recipientJID = packet.getTo(); // If the server receives a message stanza with no 'to' attribute, it MUST treat the message as if the 'to' address were the bare JID <localpart@domainpart> of the sending entity.
if (recipientJID == null) {
recipientJID = packet.getFrom().asBareJID();
} // Check if the message was sent to the server hostname
if (recipientJID.getNode() == null && recipientJID.getResource() == null &&
serverName.equals(recipientJID.getDomain())) {
if (packet.getElement().element("addresses") != null) {
// Message includes multicast processing instructions. Ask the multicastRouter
// to route this packet
multicastRouter.route(packet);
}
else {
// Message was sent to the server hostname so forward it to a configurable
// set of JID's (probably admin users)
sendMessageToAdmins(packet);
}
return;
} boolean isAcceptable = true;
if (session instanceof LocalClientSession) {
// Check if we could process messages from the recipient.
// If not, return a not-acceptable error as per XEP-0016:
// If the user attempts to send an outbound stanza to a contact and that stanza type is blocked, the user's server MUST NOT route the stanza to the contact but instead MUST return a <not-acceptable/> error
Message dummyMessage = packet.createCopy();
dummyMessage.setFrom(packet.getTo());
dummyMessage.setTo(packet.getFrom());
if (!((LocalClientSession) session).canProcess(dummyMessage)) {
packet.setTo(session.getAddress());
packet.setFrom((JID)null);
packet.setError(PacketError.Condition.not_acceptable);
session.process(packet);
isAcceptable = false;
}
}
if (isAcceptable) {
boolean isPrivate = packet.getElement().element(QName.get("private", "urn:xmpp:carbons:2")) != null;
try {
// Deliver stanza to requested route
//增加发送时间
if (packet instanceof Message) {
Message message = (Message) packet;
java.sql.Timestamp date = new java.sql.Timestamp(new Date().getTime());
Element chatInfoElement = message.getElement().element("chatinfo");
if(chatInfoElement != null && !"".equals(chatInfoElement)){
Element timeElement = DocumentFactory.getInstance().createDocument().addElement("sendtime");
timeElement.setText(String.valueOf(date.getTime()));
chatInfoElement.add(timeElement);
message.addChildElement("sendtime", String.valueOf(date.getTime()));
}
}
routingTable.routePacket(recipientJID, packet, false);
} catch (Exception e) {
log.error("Failed to route packet: " + packet.toXML(), e);
routingFailed(recipientJID, packet);
} // Sent carbon copies to other resources of the sender:
// When a client sends a <message/> of type "chat"
if (packet.getType() == Message.Type.chat && !isPrivate && session != null) { // && session.isMessageCarbonsEnabled() ??? // must the own session also be carbon enabled?
List<JID> routes = routingTable.getRoutes(packet.getFrom().asBareJID(), null);
for (JID route : routes) {
// The sending server SHOULD NOT send a forwarded copy to the sending full JID if it is a Carbons-enabled resource.
if (!route.equals(session.getAddress())) {
ClientSession clientSession = sessionManager.getSession(route);
if (clientSession != null && clientSession.isMessageCarbonsEnabled()) {
Message message = new Message();
// The wrapping message SHOULD maintain the same 'type' attribute value
message.setType(packet.getType());
// the 'from' attribute MUST be the Carbons-enabled user's bare JID
message.setFrom(packet.getFrom().asBareJID());
// and the 'to' attribute SHOULD be the full JID of the resource receiving the copy
message.setTo(route);
// The content of the wrapping message MUST contain a <sent/> element qualified by the namespace "urn:xmpp:carbons:2", which itself contains a <forwarded/> qualified by the namespace "urn:xmpp:forward:0" that contains the original <message/> stanza.
message.addExtension(new Sent(new Forwarded(packet)));
clientSession.process(message);
}
}
}
}
}
}
else {
packet.setTo(session.getAddress());
packet.setFrom((JID)null);
packet.setError(PacketError.Condition.not_authorized);
session.process(packet);
}
// Invoke the interceptors after we have processed the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, true);
} catch (PacketRejectedException e) {
// An interceptor rejected this packet
if (session != null && e.getRejectionMessage() != null && e.getRejectionMessage().trim().length() > 0) {
// A message for the rejection will be sent to the sender of the rejected packet
Message reply = new Message();
reply.setID(packet.getID());
reply.setTo(session.getAddress());
reply.setFrom(packet.getTo());
reply.setType(packet.getType());
reply.setThread(packet.getThread());
reply.setBody(e.getRejectionMessage());
session.process(reply);
}
}
} /**
* Forwards the received message to the list of users defined in the property
* <b>xmpp.forward.admins</b>. The property may include bare JIDs or just usernames separated
* by commas or white spaces. When using bare JIDs the target user may belong to a remote
* server.<p>
*
* If the property <b>xmpp.forward.admins</b> was not defined then the message will be sent
* to all the users allowed to enter the admin console.
*
* @param packet the message to forward.
*/
private void sendMessageToAdmins(Message packet) {
String jids = JiveGlobals.getProperty("xmpp.forward.admins");
if (jids != null && jids.trim().length() > 0) {
// Forward the message to the users specified in the "xmpp.forward.admins" property
StringTokenizer tokenizer = new StringTokenizer(jids, ", ");
while (tokenizer.hasMoreTokens()) {
String username = tokenizer.nextToken();
Message forward = packet.createCopy();
if (username.contains("@")) {
// Use the specified bare JID address as the target address
forward.setTo(username);
}
else {
forward.setTo(username + "@" + serverName);
}
route(forward);
}
}
else {
// Forward the message to the users allowed to log into the admin console
for (JID jid : XMPPServer.getInstance().getAdmins()) {
Message forward = packet.createCopy();
forward.setTo(jid);
route(forward);
}
}
} @Override
public void initialize(XMPPServer server) {
super.initialize(server);
messageStrategy = server.getOfflineMessageStrategy();
routingTable = server.getRoutingTable();
sessionManager = server.getSessionManager();
multicastRouter = server.getMulticastRouter();
userManager = server.getUserManager();
serverName = server.getServerInfo().getXMPPDomain();
} /**
* 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 )
{
log.debug( "Message sent to unreachable address: " + packet.toXML() );
final Message msg = (Message) packet;
boolean storeOffline = true; if ( msg.getType().equals( Message.Type.chat ) && serverName.equals( recipient.getDomain() ) && recipient.getResource() != null ) {
// Find an existing AVAILABLE session with non-negative priority.
for (JID address : routingTable.getRoutes(recipient.asBareJID(), packet.getFrom())) {
ClientSession session = routingTable.getClientRoute(address);
if (session != null && session.isInitialized()) {
if (session.getPresence().getPriority() >= 1) {
storeOffline = false;
}
}
}
} if ( !storeOffline )
{
// If message was sent to an unavailable full JID of a user then retry using the bare JID.
routingTable.routePacket( recipient.asBareJID(), packet, false );
}
else
{
// Delegate to offline message strategy, which will either bounce or ignore the message depending on user settings.
messageStrategy.storeOffline( (Message) packet );
}
}
}

openfire源码修改聊天消息发送内容的更多相关文章

  1. openfire源码修改后如何打包部署到linux服务器上

    原文:http://blog.csdn.net/jinzhencs/article/details/50457152 1.linux版本的3.10.3解压部署启动(过程略,参考我的另一篇博文http: ...

  2. 源码分析 Kafka 消息发送流程(文末附流程图)

    温馨提示:本文基于 Kafka 2.2.1 版本.本文主要是以源码的手段一步一步探究消息发送流程,如果对源码不感兴趣,可以直接跳到文末查看消息发送流程图与消息发送本地缓存存储结构. 从上文 初识 Ka ...

  3. 源码分析 Kafka 消息发送流程

    Futuresend(ProducerRecord<K, V> record) Futuresend(ProducerRecord<K, V> record, Callback ...

  4. 源码分析RocketMQ消息轨迹

    目录 1.发送消息轨迹流程 1.1 DefaultMQProducer构造函数 1.2 SendMessageTraceHookImpl钩子函数 1.3 TraceDispatcher实现原理 2. ...

  5. Android6.0 源码修改之屏蔽系统短信功能和来电功能

    一.屏蔽系统短信功能 1.屏蔽所有短信 android 4.2 短信发送流程分析可参考这篇 戳这 源码位置 vendor\mediatek\proprietary\packages\apps\Mms\ ...

  6. Openfire源码阅读(一)

    本篇先分析openfire源码的主要流程,模块细节后续再继续分析: 一.简介: Openfire是开源的实时协作服务器(RTC),它是基于公开协议XMPP(RFC-3920),并在此基础上实现了XMP ...

  7. java开源即时通讯软件服务端openfire源码构建

    java开源即时通讯软件服务端openfire源码构建 本文使用最新的openfire主干代码为例,讲解了如何搭建一个openfire开源开发环境,正在实现自己写java聊天软件: 编译环境搭建 调试 ...

  8. Java学习-039-源码 jar 包的二次开发扩展实例(源码修改)

    最近在使用已有的一些 jar 包时,发现有些 jar 包中的一些方法无法满足自己的一些需求,例如返回固定的格式,字符串处理等等,因而需要对原有 jar 文件中对应的 class 文件进行二次开发扩展, ...

  9. Android6.0 源码修改之 Contacts应用

    一.Contacts应用的主界面和联系人详情界面增加顶部菜单添加退出按钮 通过Hierarchy View 工具可以发现 主界面对应的类为 PeopleActivity 联系人详情界面对应的类为 Qu ...

随机推荐

  1. youtube视频下载

    开你的电脑,然后打开你的浏览器,浏览器可以是IE.Chrome.Firefox等等   在浏览器中输入这个网址:en.savefrom.net,点击Enter键,进入这个网页:   打开你需要下载的y ...

  2. 小试牛刀3之JavaScript基础题

    JavaScript基础题 1.让用户输入两个数字,然后输出相加的结果. *prompt() 方法用于显示可提示用户进行输入的对话框. 语法: prompt(text,defaultText) 说明: ...

  3. OnScrollListener回调分析

    new AbsListView.OnScrollListener() {       @Override   public void onScrollStateChanged(AbsListView ...

  4. JS的URL编码

    背景 URL只能使用英文字母.阿拉伯数字和某些标点符号,不能使用其他文字和符号,这是网络标准: 只有字母和数字[-9a-zA-Z].一些特殊符号"$-_.+!*'(),"[不包括双 ...

  5. 关键字static/const的作用

    static关键字的作用:(1)设置变量的存储域,函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值:(2)限制变量的作用域 ...

  6. js match() 方法

    方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配.

  7. Velocity(5)——#set指令

    引用可以让模板设计者生成动态内容,而指令允许设计者真正的负责页面的展现和内容. 指令是以#开头,后面紧跟一个关键字,比如if,else,foreach等.而这个关键字,是可以被放在{}中间的.这是规范 ...

  8. Java集合容器简介

    Java集合容器主要有以下几类: 1,内置容器:数组 2,list容器:Vetor,Stack,ArrayList,LinkedList, CopyOnWriteArrayList(1.5),Attr ...

  9. SignalR实时推送

    SignalR 的实现机制与 .NET WCF 或 Remoting 是相似的,都是使用远程代理来实现.在具体使用上,有两种不同目的的接口:PersistentConnection 和 Hubs,其中 ...

  10. STL之优先队列(1)

    优先队列用法 在优先队列中,优先级高的元素先出队列. 标准库默认使用元素类型的<操作符来确定它们之间的优先级关系. 优先队列的第一种用法: 也是最常用的用法 priority_queue< ...