前两篇简单介绍了下Web Service。下面就将此项技术与开源项目androidpn结合起来,实现服务器向android手机端推送消息。
首先在eclipse中打开Androidpn服务器端,然后我们就准备将服务器端推送消息的方法暴露出来,在写代码前我们要将发布Web Service要用到的jar包拷贝到Androidpn工程中的WebRoot-->WEB-INF->lib目录下,要拷贝的几个jar包如下所示:
这些jar包都可以在D:\apache-cxf-2.4.0\lib目录下找到(这里是我的目录,也就是可以在解压得到的apache-cxf-2.4.0的lib目录下找到这些jar包)。
首先我们要将服务器端向android手机端发送消息的三个方法找到,通过之前我的跟踪调试发现androidpn是通过org.androidpn.server.xmpp.push.NotificationManager类里的sendBroadcast、sendAllBroadcast和sendNotifications三个方法来分别向所有在线用户、所有用户、指定用户发送信息的。找到方法后,我们现在要做的就是将其暴露出来。前面说过发布Web Service需要2个部分:接口和实现类。下面我们就在工程的Java Resources-->src目录下新建一个接口及其实现类,代码如下:
- package org.YL.cxf.ws;
-
- import javax.jws.WebService;
-
-
- @WebService
- public interface AndroidPushNotification {
- public void sendBroadcast(String apiKey, String title, String message);
- public void sendAllBroadcast(String apiKey, String title, String message);
- void sendNotifications(String apiKey, String username,
- String title, String message);
-
- }
注意上面的接口定义前有@WebService关键字。然后AndroidPushNotification接口的
实现
代码:
- package org.YL.cxf.ws.impl;
-
- import javax.jws.WebService;
-
- import org.YL.cxf.ws.AndroidPushNotification;
- import org.androidpn.server.console.controller.NotificationController;
- import org.androidpn.server.xmpp.push.NotificationManager;
-
-
- @WebService(endpointInterface="org.YL.cxf.ws.AndroidPushNotification",serviceName="AndroidPushNotificationWs")
- public class AndroidPushNotificationWs implements AndroidPushNotification {
-
- public void sendBroadcast(String apiKey, String title, String message) {
-
- String uri = NotificationController.myuri;
- NotificationManager nm = new NotificationManager();
- nm.sendBroadcast(apiKey, title, message, uri);
-
- }
-
- public void sendAllBroadcast(String apiKey, String title, String message) {
-
- String uri = NotificationController.myuri;
- NotificationManager nm = new NotificationManager();
- nm.sendAllBroadcast(apiKey, title, message, uri);
-
- }
-
- public void sendNotifications(String apiKey, String username, String title,
- String message) {
-
- String uri = NotificationController.myuri;
- NotificationManager nm = new NotificationManager();
- nm.sendNotifications(apiKey, username, title, message, uri);
-
- }
-
- }
在发送信息的过程中需要uri,而uri在org.androidpn.server.console.controller.NotificationController中设置。为了获得此uri我在此类中定义了一个public static的String 成员 myuri,然后将此类中将uri赋值给myuri。下面是我修改后org.androidpn.server.console.controller.NotificationController类的代码:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- package org.androidpn.server.console.controller;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
-
- import org.androidpn.server.util.Config;
- import org.androidpn.server.xmpp.push.NotificationManager;
- import org.springframework.web.bind.ServletRequestUtils;
- import org.springframework.web.servlet.ModelAndView;
- import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
-
-
-
-
-
-
- public class NotificationController extends MultiActionController {
- public static String myuri;
-
- private NotificationManager notificationManager;
-
- public NotificationController() {
- notificationManager = new NotificationManager();
- }
-
- public ModelAndView list(HttpServletRequest request,
- HttpServletResponse response) throws Exception {
- ModelAndView mav = new ModelAndView();
-
- mav.setViewName("notification/form");
- return mav;
- }
-
- public ModelAndView send(HttpServletRequest request,
- HttpServletResponse response) throws Exception {
- String broadcast = ServletRequestUtils.getStringParameter(request,
- "broadcast", "Y");
- String username = ServletRequestUtils.getStringParameter(request,
- "username");
- String title = ServletRequestUtils.getStringParameter(request, "title");
- String message = ServletRequestUtils.getStringParameter(request,
- "message");
- String uri = ServletRequestUtils.getStringParameter(request, "uri");
- myuri = uri;
-
- String apiKey = Config.getString("apiKey", "");
- logger.debug("apiKey=" + apiKey);
-
- if (broadcast.equalsIgnoreCase("Y")) {
- notificationManager.sendBroadcast(apiKey, title, message, uri);
-
- }else if (broadcast.equalsIgnoreCase("A")) {
- notificationManager.sendAllBroadcast(apiKey, title, message, uri);
-
- }else {
- notificationManager.sendNotifications(apiKey, username, title,
- message, uri);
- }
-
- ModelAndView mav = new ModelAndView();
- mav.setViewName("redirect:notification.do");
- return mav;
- }
-
- }
所做的处理就是在类的最前面定义一个public static String myuri,然后在public ModelAndView send(HttpServletRequest request,
HttpServletResponse response) throws Exception中将uri赋值给myuri。
接口代码写完后,我们还需要更改下WebRoot-->WEB-INF下的web.xml配置文件,更改后的配置文件如下:
- xml version="1.0" encoding="UTF-8"?>
- <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
-
-
-
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
- listener>
-
- <context-param>
-
- <param-name>contextConfigLocationparam-name>
-
- <param-value>/WEB-INF/applicationContext.xmlparam-value>
- context-param>
-
-
-
- <display-name>androidpn-serverdisplay-name>
-
- <filter>
- <filter-name>encodingFilterfilter-name>
- <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
- <init-param>
- <param-name>encodingparam-name>
- <param-value>UTF-8param-value>
- init-param>
- <init-param>
- <param-name>forceEncodingparam-name>
- <param-value>trueparam-value>
- init-param>
- filter>
- <filter>
- <filter-name>sitemeshfilter-name>
- <filter-class>com.opensymphony.module.sitemesh.filter.PageFilterfilter-class>
- filter>
-
- <filter-mapping>
- <filter-name>encodingFilterfilter-name>
- <url-pattern>/*url-pattern>
- filter-mapping>
- <filter-mapping>
- <filter-name>sitemeshfilter-name>
- <url-pattern>/*url-pattern>
- <dispatcher>REQUESTdispatcher>
- <dispatcher>FORWARDdispatcher>
- filter-mapping>
-
-
- <servlet>
- <servlet-name>cxfservlet-name>
- <servlet-class>org.apache.cxf.transport.servlet.CXFServletservlet-class>
- servlet>
- <servlet-mapping>
- <servlet-name>cxfservlet-name>
- <url-pattern>/androidpnservice/*url-pattern>
- servlet-mapping>
-
-
- <servlet>
- <servlet-name>dispatcherservlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
- <load-on-startup>1load-on-startup>
- servlet>
-
- <servlet-mapping>
- <servlet-name>dispatcherservlet-name>
- <url-pattern>*.dourl-pattern>
- servlet-mapping>
-
- <session-config>
- <session-timeout>30session-timeout>
- session-config>
-
- <welcome-file-list>
- <welcome-file>/index.htmlwelcome-file>
- <welcome-file>/index.jspwelcome-file>
- <welcome-file>/index.dowelcome-file>
- welcome-file-list>
-
- <error-page>
- <error-code>400error-code>
- <location>/index.jsplocation>
- error-page>
- <error-page>
- <error-code>403error-code>
- <location>/403.jsplocation>
- error-page>
- <error-page>
- <error-code>404error-code>
- <location>/404.jsplocation>
- error-page>
- <error-page>
- <error-code>500error-code>
- <location>/error.jsplocation>
- error-page>
-
- web-app>
其中
-
- <servlet>
- <servlet-name>cxfservlet-name>
- <servlet-class>org.apache.cxf.transport.servlet.CXFServletservlet-class>
- servlet>
- <servlet-mapping>
- <servlet-name>cxfservlet-name>
- <url-pattern>/androidpnservice/*url-pattern>
- servlet-mapping>
和
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
- listener>
-
- <context-param>
-
- <param-name>contextConfigLocationparam-name>
-
- <param-value>/WEB-INF/applicationContext.xmlparam-value>
- context-param>
是新添加的。
上面还指定了一个/WEB-INF/applicationContext.xml的配置文件,这个文件是我新添加进去的,是为了发布Web Service用的,此文件内容为:
- xml version="1.0" encoding="UTF-8"?>
-
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:jaxws="http://cxf.apache.org/jaxws"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://cxf.apache.org/jaxws
- http://cxf.apache.org/schemas/jaxws.xsd">
-
- <import resource="classpath:META-INF/cxf/cxf.xml"/>
- <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
- <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
-
- <jaxws:endpoint
- implementor = "org.YL.cxf.ws.impl.AndroidPushNotificationWs"
- address = "/androidpush">
- jaxws:endpoint>
-
- beans>
其中
- <jaxws:endpoint
- implementor = "org.YL.cxf.ws.impl.AndroidPushNotificationWs"
- address = "/androidpush">
- jaxws:endpoint>
指定发布的Web Service的
实现
类和在获得WSDL描述文档时在“?”前面的服务名。
好了现在一切就绪,我们运行Androidpn服务器端程序,成功运行起来后,我们在浏览器地址栏中输入:http://192.168.1.117:8080/androidpnservice/androidpush?wsdl(192.168.1.117:8080是我本机的IP地址和tomcat的端口) 可得到WSDL描述文档如下(部分截图):
这时表明我们的服务端推送消息的方法发布成功,我们在来看客户端。
首先我们在eclipse中建立一个名为PushClient的空的Java Project,初始时什么也没有:
然后我们用cxf的wsdl2java来生成客户端代码:
在回到PushClient工程中刷新下(F5),可得:
为了方便调用,我又写了一个org.yl.SendNotification类,代码如下:
- package org.yl;
-
- import org.yl.cxf.ws.AndroidPushNotification;
- import org.yl.cxf.ws.impl.AndroidPushNotificationWs;
-
- public class SendNotification {
-
- AndroidPushNotificationWs factory = new AndroidPushNotificationWs();
- AndroidPushNotification apn = factory.getAndroidPushNotificationWsPort();
-
- public void sendBroadcast(String apiKey, String title, String message)
- {
- apn.sendAllBroadcast(apiKey, title, message);
- }
- public void sendAllBroadcast(String apiKey, String title, String message)
- {
- apn.sendBroadcast(apiKey, title, message);
- }
- public void sendNotifications(String apiKey, String username,
- String title, String message)
- {
- apn.sendNotifications(apiKey,username,title,message);
- }
- }
以后调用发送
消息
的方法时,只需要定义一个SendNotification对象,通过此对象就可以方便的调用方法了,避免每次调用时都
- AndroidPushNotificationWs factory = new AndroidPushNotificationWs();
- AndroidPushNotification apn = factory.getAndroidPushNotificationWsPort();
然后写一个主函数类来测试我们的整个系统,代码如下:
- package org.main;
-
- import org.yl.SendNotification;
-
- public class CallMain {
-
-
-
-
- public static void main(String[] args) {
-
- SendNotification sn = new SendNotification();
-
-
- sn.sendAllBroadcast("1234567890", "hello", "How are you?");
-
-
-
-
-
- }
-
- }
在整个工程中,如下所示,红圈中是我手动添加的代码,其他的都cxf工具自动生成的。
好了,现在就来测试下我们的推送消息系统。
首先将Androidpn开源项目的客户端在模拟器重运行起来,然后回到PushClient工程,在主函数中将其他方法注释掉,只留下:
sn.sendAllBroadcast()方法,如下所示:
- package org.main;
-
- import org.yl.SendNotification;
-
- public class CallMain {
-
-
-
-
- public static void main(String[] args) {
-
- SendNotification sn = new SendNotification();
-
-
- sn.sendAllBroadcast("1234567890", "hello", "How are you?");
-
-
-
-
-
- }
-
- }
然后运行程序,可看到模拟器中接收到了推送主题为:hello,内容为:How are you?的
消息
。
同理注释掉其他方法直留下:sn.sendBroadcast()方法:
- package org.main;
-
- import org.yl.SendNotification;
-
- public class CallMain {
-
-
-
-
- public static void main(String[] args) {
-
- SendNotification sn = new SendNotification();
-
-
-
-
- sn.sendBroadcast("1234567890", "Hi", "OK");
-
-
-
- }
-
- }
然后运行程序,可看到模拟器中接收到了推送主题为:Hi,内容为:OK的
消息
。
测试最后一个给指定用户发送消息的方法:
- package org.main;
-
- import org.yl.SendNotification;
-
- public class CallMain {
-
-
-
-
- public static void main(String[] args) {
-
- SendNotification sn = new SendNotification();
-
-
-
-
-
-
- sn.sendNotifications("1234567890","f6ac2608faa94a65b5c2d94b72e968b0", "Hello Hi", "Hello World! How are you?");
-
- }
-
- }
其中第二个参数是从
服务器
端页面得来的:
运行程序得:
到此为止我们完成了根据某一事件触发,服务器可以主动向android手机端推送消息的这一需求。
另外为了使用上的方便,可以将上面PushClient工程中除了包含main函数的类之外的代码都统统打包成androidpn.jar文件。这样当其他的应用程序需要调用相应的方法发送信息时,只需要将此androidpn.jar文件加入到相应的工程中就可以调用其中的方法了。操作如下:
首先新建一个工程PushClient2,将PushClient工程中除了包含main函数的类外的其他文件文件拷贝到PushClient2工程中,
然后file-->Export-->Java-->JAR file-->选定输出路径和文件名-->finish即可。这里我取得文件名为androidp:
然后我们新建一个空的Java Project:PushClient3,并将androidp.jar引入此工程,主函数与PushClient中的主函数相同,
然后运行此工程可得到与工程PushClient相同的效果。
总结:服务器端向android手机端推送消息的实现逻辑是:当我们需要向用户推送消息时,就将消息发给AndroidPn的服务器端,由AndroidPn服务器端来替我们将消息发送给用户。那么怎么将我们要推送的信息发给AndroidPn服务器端呢?我们可以采用WebService技术,将AndroidPn服务器端发送消息的方法暴露出来,然后我们就可以在本地或远程来调用AndroidPn暴露出来的方法了。所以我们的系统要想正常运行,Androidpn服务器端必须事先开启。因为Androidpn服务器端是一个Web应用程序,可以将其经过Web Service技术处理后部署到tomcat上,以后我们只需要启动tomcat,Androidpn服务器端就会自动启动起来。
做法是:在eclipse中选中经过WebService处理后的AndroidPN服务器端,然后file-->Export-->Web-->WAR file-->将其导入到tomcat的webapps目录中。
以后只需要执行apache-tomcat-7.0.32\bin\startup.bat,启动tomcat,Androidpn服务器端也就随即启动了,不需要在eclipse中启动了。
转自:http://www.verydemo.com/demo_c131_i166053.html