IOS离线推送

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);}@Overridepublic void xmlPropertySet(String property, Map<String, Object> params) {}@Overridepublic void xmlPropertyDeleted(String property, Map<String, Object> params) {}@Overridepublic 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);}@Overridepublic 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;}@Overridepublic String getName() {return pluginManager.getName(this);}@Overridepublic String getDescription() {return pluginManager.getDescription(this);}@Overridepublic 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";}}@Overridepublic void initialize(JID jid, ComponentManager componentManager)throws ComponentException {// TODO Auto-generated method stub}@Overridepublic void start() {// TODO Auto-generated method stub}@Overridepublic void shutdown() {// TODO Auto-generated method stub}@Overridepublic 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();//获取用户的设备标志idString 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() {@Overridepublic 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

“过去酒逢知已千杯少,现在酒逢千杯知已少”。

IOS离线推送

相关文章:

你感兴趣的文章:

标签云: