/**
* $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. 3DTouch开发 (基础)

    一.3DTouch开发准备工作(让模拟器也支持 3DTouch 的解决办法) 需要支持3DTouch的设备,如iPhone6s或以上.iOS9或以上.Xcode7或以上,估计很多和我一样的屌丝还没有i ...

  2. Wordpress制作文章页面single.php

    可以调用的文章内容: 调用文章标题:<?php the_title(); ?> 调用文章内容:<?php the_content(); ?> 调用文章摘要:<?php t ...

  3. NET 框架基本原理透析⑵

    生成.打包.部署及管理应用程序与类型 要生成就离不开程,序集,程序集是包含一个或多个类型定义文件和资源文件的集合.在程序集包含的所有文件中,有一个文件用于保存清单.清单是另外一组元数据表的集合,其中主 ...

  4. HIVE 时间操作函数

    转自http://www.oratea.net/?p=944 日期函数UNIX时间戳转日期函数: from_unixtime语法:   from_unixtime(bigint unixtime[,  ...

  5. Java学习-047-数值格式化及小数位数四舍五入

    此小工具类主要用于数值四舍五入.数值格式化输出,很简单,若想深入研究,敬请自行查阅 BigDecimal 或 DecimalFormat 的 API,BigDecimal.setScale(位数,四舍 ...

  6. CentOS6修改主机名(hostname)及 修改/etc/hosts 文件,增加ip和hostname的映射关系(转)

    CentOS修改主机名(hostname)  需要修改两处:一处是/etc/sysconfig/network,另一处是/etc/hosts,只修改任一处会导致系统启动异常.首先切换到root用户. ...

  7. [Android Tips] 12. How to Create a Dash Line Shape

    <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http: ...

  8. Matlab的XTickLabel中数值带下标

    %axis为'x'或'y',分别表示更改x或y刻度 %ticks是字符cell function settick(axis,ticks) n=length(ticks); tkx=get(gca,'X ...

  9. ASP.NET MVC controller 之间传JS值

    在ASP.NET MVC中有东西叫TempData,它的类型是TempDataDictionary,它与ViewData以及ViewBag的不同之处在于 它的内部是使用session来保存信息的,可以 ...

  10. C++学习笔记(1)——数据类型占空间大小

    boolean bool 1 byte   character char 1 byte May be signed or unsigned   wchar_t 1 byte     char16_t ...