(转)OpenFire源码学习之十八:IOS离线推送
转:http://blog.csdn.net/huwenfeng_2011/article/details/43458213
IOS离线推送
场景:
如果您有iOS端的APP,在会话聊天的时候,用户登陆了但可能会退出了界面。这时候其他终端给目标端发送消息时候,消息可以发送到ios的推送服务器。用过QQ的都知道,你会有哦一条消息在您的主屏上展示。这个就是利用了IOS的推送服务器呢。那么openfire只需要判断用户不在线的时候将消息推送给IOS端。
苹果服务器的消息推送都需要手机的唯一标志,也就是唯一的终端设备号。那么IOS端在登陆的时候需要将该手机的设备号传递给OF服务器。这个传递很简单,您可以自定义发送IQ消息。也可以在登陆后绑定资料的时候添加JID属性来绑定设备。(建议用绑定资源的形式,这样在服务端判断的时候可以很方便的根据JID的属性值来决定是都推送)
服务端实现ios消息推送所需2个证书(附件):测试推送证书.p12、正式推送正式.p12,密码都为123456.
2个证书的区别在于一个是用于开发测试的推送证书,一个是用于产品正式上线的推送证书。2个证书获取到的终端token是不一样的。这2个证书用于Java后台连接APNS的服务器地址也是不同的,测试推送证书对应服务器地址是:gateway.sandbox.push.apple.com , 正式推送证书对应的服务器地址是:gateway.push.apple.com .
具体怎么做呢:
1、安装IOS推送服务需要的证书到本地,这个在网上有很多中方法
2、IOS终端登陆发送设备消息给服务器,或者以绑定资源的形式。
3、OF服务端接收该设备ID,并保存起来。
4、当有消息推送时候,根据JID属性push消息。
接下来具体看看源码了。
源码
OfflinePushPlugin
- public class OfflinePushPlugin implements Component, Plugin, PropertyEventListener, PacketInterceptor{
- private static final Logger Log = LoggerFactory.getLogger(OfflinePushPlugin.class);
- public static final String NAMESPACE_JABBER_IQ_TOKEN_BIND= "jabber:iq:token:bind";
- public static final String NAMESPACE_JABBER_IQ_TOKEN_UNBUND= "jabber:iq:token:unbund";
- public static final String SERVICENAME = "plugin.offlinepush.serviceName";
- public static final String SERVICEENABLED = "plugin.offlinepush.serviceEnabled";
- private ComponentManager componentManager;
- private PluginManager pluginManager;
- private String serviceName;
- private boolean serviceEnabled;
- //证书安装的目录
- private static String dcpath = System.getProperty("openfireHome") + "\\conf\\";
- private String dcName;
- private String dcPassword;
- private boolean enabled;
- private static Map<String, String> map = new ConcurrentHashMap<String, String>(20);
- private static Map<String, Integer> count = new ConcurrentHashMap<String, Integer>(20);
- private static AppleNotificationServer appleServer = null;
- private static List<PayloadPerDevice> list ;
- public String getDcName() {
- return dcName;
- }
- public void setDcName(String dcName) {
- JiveGlobals.setProperty("plugin.offlinepush.dcName", dcName);
- this.dcName = dcName;
- }
- public String getDcPassword() {
- return dcPassword;
- }
- public void setDcPassword(String dcPassword) {
- JiveGlobals.setProperty("plugin.offlinepush.password", dcPassword);
- this.dcPassword = dcPassword;
- }
- public boolean getEnabled() {
- return enabled;
- }
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- JiveGlobals.setProperty("plugin.offlinepush.enabled", enabled ? "true" : "false");
- }
- public OfflinePushPlugin () {
- serviceName = JiveGlobals.getProperty(SERVICENAME, "offlinepush");
- serviceEnabled = JiveGlobals.getBooleanProperty(SERVICEENABLED, true);
- }
- @Override
- public void xmlPropertySet(String property, Map<String, Object> params) {
- }
- @Override
- public void xmlPropertyDeleted(String property, Map<String, Object> params) {
- }
- @Override
- public void initializePlugin(PluginManager manager, File pluginDirectory) {
- dcName = JiveGlobals.getProperty("plugin.offlinepush.dcName", "");
- // If no secret key has been assigned to the user service yet, assign a random one.
- if (dcName.equals("")){
- dcName = "delementtest.p12";
- setDcName(dcName);
- }
- dcpath += dcName;
- dcPassword = JiveGlobals.getProperty("plugin.offlinepush.password", "");
- if (dcPassword.equals("")){
- dcPassword = "123456";
- setDcPassword(dcPassword);
- }
- enabled = JiveGlobals.getBooleanProperty("plugin.offlinepush.enabled");
- setEnabled(enabled);
- Log.info("dcpath: " + dcpath);
- Log.info("dcPassword: " + dcPassword);
- Log.info("enabled: " + enabled);
- try {
- appleServer = new AppleNotificationServerBasicImpl(dcpath, dcPassword, enabled );
- if (list == null ) {
- list = new ArrayList<PayloadPerDevice>();
- }
- } catch (KeystoreException e1) {
- Log.error("KeystoreException: " + e1.getMessage());
- }
- pluginManager = manager;
- componentManager = ComponentManagerFactory.getComponentManager();
- try {
- componentManager.addComponent(serviceName, this);
- }
- catch (ComponentException e) {
- Log.error(e.getMessage(), e);
- }
- InterceptorManager.getInstance().addInterceptor(this);
- PropertyEventDispatcher.addListener(this);
- }
- @Override
- public void destroyPlugin() {
- InterceptorManager.getInstance().removeInterceptor(this);
- PropertyEventDispatcher.removeListener(this);
- pluginManager = null;
- try {
- componentManager.removeComponent(serviceName);
- componentManager = null;
- }
- catch (Exception e) {
- if (componentManager != null) {
- Log.error(e.getMessage(), e);
- }
- }
- serviceName = null;
- }
- @Override
- public String getName() {
- return pluginManager.getName(this);
- }
- @Override
- public String getDescription() {
- return pluginManager.getDescription(this);
- }
- @Override
- public void processPacket(Packet p) {
- if (!(p instanceof IQ)) {
- return;
- }
- final IQ packet = (IQ) p;
- if (packet.getType().equals(IQ.Type.error)
- || packet.getType().equals(IQ.Type.result)) {
- return;
- }
- final IQ replyPacket = handleIQRequest(packet);
- try {
- componentManager.sendPacket(this, replyPacket);
- } catch (ComponentException e) {
- Log.error(e.getMessage(), e);
- }
- }
- private IQ handleIQRequest(IQ iq) {
- final IQ replyPacket; // 'final' to ensure that it is set.
- if (iq == null) {
- throw new IllegalArgumentException("Argument 'iq' cannot be null.");
- }
- final IQ.Type type = iq.getType();
- if (type != IQ.Type.get && type != IQ.Type.set) {
- throw new IllegalArgumentException(
- "Argument 'iq' must be of type 'get' or 'set'");
- }
- final Element childElement = iq.getChildElement();
- if (childElement == null) {
- replyPacket = IQ.createResultIQ(iq);
- replyPacket
- .setError(new PacketError(
- Condition.bad_request,
- org.xmpp.packet.PacketError.Type.modify,
- "IQ stanzas of type 'get' and 'set' MUST contain one and only one child element (RFC 3920 section 9.2.3)."));
- return replyPacket;
- }
- final String namespace = childElement.getNamespaceURI();
- if (namespace == null) {
- replyPacket = IQ.createResultIQ(iq);
- replyPacket.setError(Condition.feature_not_implemented);
- return replyPacket;
- }
- if (namespace.equals(NAMESPACE_JABBER_IQ_TOKEN_BIND)) {
- replyPacket = processSetUUID(iq, true);
- }
- else if (namespace.equals(NAMESPACE_JABBER_IQ_TOKEN_UNBUND)) {
- replyPacket = processSetUUID(iq, false);
- }
- else if (namespace.equals(IQDiscoInfoHandler.NAMESPACE_DISCO_INFO)) {
- replyPacket = handleDiscoInfo(iq);
- }
- else {
- // don't known what to do with this.
- replyPacket = IQ.createResultIQ(iq);
- replyPacket.setError(Condition.feature_not_implemented);
- }
- return replyPacket;
- }
- private static IQ handleDiscoInfo(IQ iq) {
- if (iq == null) {
- throw new IllegalArgumentException("Argument 'iq' cannot be null.");
- }
- if (!iq.getChildElement().getNamespaceURI().equals(
- IQDiscoInfoHandler.NAMESPACE_DISCO_INFO)
- || iq.getType() != Type.get) {
- throw new IllegalArgumentException(
- "This is not a valid disco#info request.");
- }
- final IQ replyPacket = IQ.createResultIQ(iq);
- final Element responseElement = replyPacket.setChildElement("query",
- IQDiscoInfoHandler.NAMESPACE_DISCO_INFO);
- responseElement.addElement("identity").addAttribute("category",
- "directory").addAttribute("type", "user").addAttribute("name",
- "Offline Push");
- responseElement.addElement("feature").addAttribute("var",
- NAMESPACE_JABBER_IQ_TOKEN_BIND);
- responseElement.addElement("feature").addAttribute("var",
- IQDiscoInfoHandler.NAMESPACE_DISCO_INFO);
- responseElement.addElement("feature").addAttribute("var",
- ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT);
- return replyPacket;
- }
- private IQ processSetUUID(IQ packet, boolean isSet) {
- Element rsmElement = null;
- if (!packet.getType().equals(IQ.Type.set)) {
- throw new IllegalArgumentException(
- "This method only accepts 'set' typed IQ stanzas as an argument.");
- }
- final IQ resultIQ;
- final Element incomingForm = packet.getChildElement();
- rsmElement = incomingForm.element(QName.get("info",
- NAMESPACE_JABBER_IQ_TOKEN_UNBUND));
- if(rsmElement == null) {
- rsmElement = incomingForm.element(QName.get("info",
- NAMESPACE_JABBER_IQ_TOKEN_BIND));
- }
- resultIQ = IQ.createResultIQ(packet);
- if (rsmElement != null) {
- String osElement = rsmElement.attributeValue("os");
- String jidElement = rsmElement.attributeValue("jid");
- String username = new JID(jidElement).getNode();
- if (osElement == null || jidElement == null) {
- resultIQ.setError(Condition.bad_request);
- return resultIQ;
- }
- if (isSet) {
- String tokenElement = rsmElement.attributeValue("token");
- map.put(username, tokenElement);
- count.put(username, 0);
- Log.info("set token,username:" + username + " ,token:" + tokenElement);
- }
- else {
- map.remove(username);
- count.remove(username);
- Log.info("remove token,username:" + username );
- }
- }
- else{
- resultIQ.setError(Condition.bad_request);
- }
- return resultIQ;
- }
- public String getServiceName() {
- return serviceName;
- }
- public void setServiceName(String name) {
- JiveGlobals.setProperty(SERVICENAME, name);
- }
- public boolean getServiceEnabled() {
- return serviceEnabled;
- }
- public void setServiceEnabled(boolean enabled) {
- serviceEnabled = enabled;
- JiveGlobals.setProperty(SERVICEENABLED, enabled ? "true" : "false");
- }
- public void propertySet(String property, Map<String, Object> params) {
- if (property.equals(SERVICEENABLED)) {
- this.serviceEnabled = Boolean.parseBoolean((String)params.get("value"));
- }
- if (property.equals("plugin.offlinepush.dcName")) {
- this.dcName = (String)params.get("value");
- }
- else if (property.equals("plugin.offlinepush.enabled")) {
- this.enabled = Boolean.parseBoolean((String)params.get("value"));
- }
- else if (property.equals("plugin.offlinepush.password")) {
- this.dcPassword = (String)params.get("value");
- }
- }
- /*
- * (non-Javadoc)
- *
- * @see org.jivesoftware.util.PropertyEventListener#propertyDeleted(java.lang.String,
- * java.util.Map)
- */
- public void propertyDeleted(String property, Map<String, Object> params) {
- if (property.equals(SERVICEENABLED)) {
- this.serviceEnabled = true;
- }
- if (property.equals("plugin.offlinepush.dcName")) {
- this.dcName = "delementtest.p12";
- }
- else if (property.equals("plugin.offlinepush.enabled")) {
- this.enabled = false;
- }
- else if (property.equals("plugin.offlinepush.password")) {
- this.dcPassword = "123456";
- }
- }
- @Override
- public void initialize(JID jid, ComponentManager componentManager)
- throws ComponentException {
- // TODO Auto-generated method stub
- }
- @Override
- public void start() {
- // TODO Auto-generated method stub
- }
- @Override
- public void shutdown() {
- // TODO Auto-generated method stub
- }
- @Override
- public void interceptPacket(Packet packet, Session session,
- boolean incoming, boolean processed) throws PacketRejectedException {
- if (processed && incoming) {
- if (packet instanceof Message) {
- if (((Message) packet).getBody() == null) {
- return;
- }
- JID jid = packet.getTo();
- //获取用户的设备标志id
- String uuid = map.get(jid.getNode());
- if (uuid != null && !"".equals(uuid)) {
- User user = null;
- try {
- user = XMPPServer.getInstance().getUserManager().getUser(jid.getNode());
- } catch (UserNotFoundException e2) {
- e2.printStackTrace();
- }
- PresenceManager presenceManager = XMPPServer.getInstance().getPresenceManager();
- org.xmpp.packet.Presence presence = presenceManager.getPresence(user);
- if (presence == null) {
- String body = ((Message) packet).getBody();
- JSONObject jb = null;
- String msgType = "10015";
- try {
- jb = new JSONObject(body);
- msgType = jb.getString("msgType");
- if ("10012".equals(msgType) || "10001".equals(msgType) || "10002".equals(msgType)) {
- return;
- }
- } catch (JSONException e) {
- try {
- //根据不同的消息类型,发送不通的提示语
- msgType = jb.getInt("msgType")+"";
- if ("10012".equals(msgType) || "10001".equals(msgType) || "10002".equals(msgType)) {
- return;
- }
- } catch (JSONException e1) {
- msgType = "10015";
- }
- }
- if (msgType != null) {
- //msgType = "offlinepush." + msgType;
- String pushCont = LocaleUtils.getLocalizedString("offlinepush.10015", "offlinepush");
- if (!"10000".equals(msgType)) {
- msgType = "offlinepush." + msgType;
- pushCont = LocaleUtils.getLocalizedString(msgType, "offlinepush");
- }
- else {
- pushCont = LocaleUtils.getLocalizedString("offlinepush.10000", "offlinepush");
- String cont = LocaleUtils.getLocalizedString("offlinepush.other", "offlinepush");;
- String mtype = "";
- try {
- mtype = jb.getString("mtype");
- } catch (JSONException e) {
- try {
- mtype = jb.getInt("mtype") + "";
- } catch (JSONException e1) {
- msgType = "10015";
- }
- }
- if ("0".equals(mtype)) {
- try {
- cont = jb.getString("Cnt");
- if (cont.length() > 20) {
- cont = cont.substring(0, 20);
- cont += "...";
- }
- } catch (JSONException e) {
- }
- }
- else if ("1".equals(mtype)) {
- cont = LocaleUtils.getLocalizedString("offlinepush.image", "offlinepush");
- }
- else if ("2".equals(mtype)) {
- cont = LocaleUtils.getLocalizedString("offlinepush.audio", "offlinepush");
- }
- else if ("4".equals(mtype)) {
- cont = LocaleUtils.getLocalizedString("offlinepush.file", "offlinepush");
- }
- else if ("3".equals(mtype)) {
- cont = LocaleUtils.getLocalizedString("offlinepush.location", "offlinepush");
- }
- else if ("6".equals(mtype)) {
- cont = LocaleUtils.getLocalizedString("offlinepush.video", "offlinepush");
- }
- pushCont += cont;
- }
- pushOfflineMsg(uuid, pushCont, jid);
- }
- }
- }
- }
- }
- }
- private void pushOfflineMsg(String token, String pushCont, JID jid) {
- NotificationThreads work = null;
- try {
- Integer size = count.get(jid.getNode()) + 1;
- if (size <= 1000)
- count.put(jid.getNode(), size);
- List<PayloadPerDevice> list = new ArrayList<PayloadPerDevice>();
- PushNotificationPayload payload = new PushNotificationPayload();
- payload.addAlert(pushCont);
- payload.addSound("default");
- payload.addBadge(size);
- payload.addCustomDictionary("jid", jid.toString());
- PayloadPerDevice pay = new PayloadPerDevice(payload, token);
- list.add(pay);
- work = new NotificationThreads(appleServer,list,1);
- work.setListener(DEBUGGING_PROGRESS_LISTENER);
- work.start();
- } catch (JSONException e) {
- Log.error("JSONException:" + e.getMessage());
- } catch (InvalidDeviceTokenFormatException e) {
- Log.error("InvalidDeviceTokenFormatException:" + e.getMessage());
- }finally{
- work.destroy();
- Log.info("push to apple: username: " + jid.getNode() + " ,context" + pushCont);
- }
- }
- public Runnable createTask(final String token, final String msgType, final JID jid) {
- return new Runnable() {
- @Override
- public void run() {
- PushNotificationPayload payload = new PushNotificationPayload();
- try {
- String pushCont = LocaleUtils.getLocalizedString(msgType, "offlinepush");
- List<PayloadPerDevice> list = new ArrayList<PayloadPerDevice>();
- payload.addAlert(pushCont);
- payload.addSound("default");
- payload.addBadge(1);
- payload.addCustomDictionary("jid", jid.toString());
- PayloadPerDevice pay = new PayloadPerDevice(payload, token);
- list.add(pay);
- NotificationThreads work = new NotificationThreads(appleServer,list,1);
- work.setListener(DEBUGGING_PROGRESS_LISTENER);
- work.start();
- } catch (JSONException e) {
- Log.error("JSONException:" + e.getMessage());
- } catch (InvalidDeviceTokenFormatException e) {
- Log.error("InvalidDeviceTokenFormatException:" + e.getMessage());
- }
- }
- };
- }
- public static final NotificationProgressListener DEBUGGING_PROGRESS_LISTENER = new NotificationProgressListener() {
- public void eventThreadStarted(NotificationThread notificationThread) {
- System.out.println(" [EVENT]: thread #" + notificationThread.getThreadNumber() + " started with " + " devices beginning at message id #" + notificationThread.getFirstMessageIdentifier());
- }
- public void eventThreadFinished(NotificationThread thread) {
- System.out.println(" [EVENT]: thread #" + thread.getThreadNumber() + " finished: pushed messages #" + thread.getFirstMessageIdentifier() + " to " + thread.getLastMessageIdentifier() + " toward "+ " devices");
- }
- public void eventConnectionRestarted(NotificationThread thread) {
- System.out.println(" [EVENT]: connection restarted in thread #" + thread.getThreadNumber() + " because it reached " + thread.getMaxNotificationsPerConnection() + " notifications per connection");
- }
- public void eventAllThreadsStarted(NotificationThreads notificationThreads) {
- System.out.println(" [EVENT]: all threads started: " + notificationThreads.getThreads().size());
- }
- public void eventAllThreadsFinished(NotificationThreads notificationThreads) {
- System.out.println(" [EVENT]: all threads finished: " + notificationThreads.getThreads().size());
- }
- public void eventCriticalException(NotificationThread notificationThread, Exception exception) {
- System.out.println(" [EVENT]: critical exception occurred: " + exception);
- }
- };
- }
Plugin.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <plugin>
- <class>com.....offlinepush.plugin.OfflinePushPlugin</class>
- <name>offlinepush</name>
- <description>.......</description>
- <author>huwenfeng</author>
- <version>1.5.1</version>
- <date>1/2/2014</date>
- <minServerVersion>3.7.0</minServerVersion>
- </plugin>
资源文件:offlinepush_i18n_zh_CN.properties
- offlinepush.10000=\u65B0\u6D88\u606F\uFF1A
- offlinepush.10001=\u7528\u6237\u64CD\u4F5C
- offlinepush.image=[\u56FE\u7247]
- offlinepush.audio=[\u8BED\u97F3]
- offlinepush.file=[\u6587\u4EF6]
- offlinepush.other=[\u5176\u4ED6]
- offlinepush.location=[\u4F4D\u7F6E]
- offlinepush.video=[\u89C6\u9891]
- ......
需要的jar包。
OK啦。
注意:IOS的推送服务器有两种模式都是免费,一种是测试的还一种是正式使用的。
所以这里最好将推送服务的使用模式在OF的管理台做配置。
本人在控制台配置了三个属性值:
(转)OpenFire源码学习之十八:IOS离线推送的更多相关文章
- (转)OpenFire源码学习之十:连接管理(上)
转:http://blog.csdn.net/huwenfeng_2011/article/details/43415827 关于连接管理分为上下两部分 连接管理 在大并发环境下,连接资源 需要随着用 ...
- (转)OpenFire源码学习之十五:插件开发
转:http://blog.csdn.net/huwenfeng_2011/article/details/43418493 Plugin接口规范 插件是openfire功能的增强表现,它的主要任务: ...
- (转)OpenFire源码学习之十二:HttpBind&Script Syntax
转:http://blog.csdn.net/huwenfeng_2011/article/details/43417343 HttpSessionManager 该类管理所有通过httpbing连接 ...
- (转)OpenFire源码学习之十四:插件管理
转:http://blog.csdn.net/huwenfeng_2011/article/details/43418433 Plugin管理 Openfire把插件模块加入到容器分为以下步骤: l ...
- Spring源码分析(十八)创建bean
本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 目录 一.创建bean的实例 1. autowireConstructor 2 ...
- spark 源码分析之十八 -- Spark存储体系剖析
本篇文章主要剖析BlockManager相关的类以及总结Spark底层存储体系. 总述 先看 BlockManager相关类之间的关系如下: 我们从NettyRpcEnv 开始,做一下简单说明. Ne ...
- (转)OpenFire源码学习之七:组(用户群)与花名册(用户好友)
转:http://blog.csdn.net/huwenfeng_2011/article/details/43413651 Group 在openfire中的gorop——组,也可以理解为共享组.什 ...
- (转)OpenFire源码学习之二十七:Smack源码解析
转:http://blog.csdn.net/huwenfeng_2011/article/details/43484199 Smack Smack是一个用于和XMPP服务器通信的类库,由此可以实现即 ...
- (转)OpenFire源码学习之六:用户注册
转:http://blog.csdn.net/huwenfeng_2011/article/details/43413509 用户注册 注册流程: 1.客户端进行握手给服务端发送连接消息: <s ...
随机推荐
- 常用js校验
身份证 function isCardNo(idCard){ //15位和18位身份证号码的正则表达式 var regIdCard=/^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0 ...
- 【Flutter学习】之绘画实例(一)
一,概述 画布(Canvas) 画布是一个矩形区域,我们可以控制其每一像素来绘制我们想要的内容 Canvas 拥有多种绘制点.线.路径.矩形.圆形.以及添加图像等的方法,结合这些方法我们可以绘制出千变 ...
- 关闭禁用 Redis 危险命令
Redis 的危险命令主要有: flushdb,清空数据库 flushall,清空所有记录,数据库 config,客户端连接后可配置服务器 keys,客户端连接后可查看所有存在的键,当 redis k ...
- centos python3.7 报错 No module named ‘_ctypes’ make: ***
1.先装好python的依赖 2.yum install libffi-devel -y(解决)
- 用scp实现多服务器文件分发
需要安装expect环境 yum install expect -y vi ip.txt #主机地址池 192.168.1.1 192.168.1.2 192.168.3.3 #如果是同一网段也可以不 ...
- idea sevlet连sqlserver失败解决
1 普通java程序连sqlserver失败解决办法:https://www.cnblogs.com/Demonfeatuing/p/9208514.html 出现javax.xml.bind.J ...
- python学习笔记:sys、os模块
os模块:负责程序与操作系统的交互,提供了访问操作系统底层的接口; sys模块:负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控python的运行时环境. --os 常用方法-- ...
- vue实现轮播效果
vue实现轮播效果 效果如下:(不好意思,图有点大:) 功能:点击左侧图片,右侧出现相应的图片:同时左侧边框变颜色. 代码如下:(也可以直接下载文件) <!DOCTYPE html> &l ...
- Tomcat启动脚本(1)startup.bat
@echo off rem Licensed to the Apache Software Foundation (ASF) under one or more rem contributor lic ...
- Sql Server 表结构相关
1.库表列信息 --取所有库 SELECT Name FROM Master..SysDatabases ORDER BY Name --查询所有表 select name from 库名..syso ...