前两篇简单介绍了下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;
- /*endpointInterface="org.YL.cxf.ws.AndroidPushNotification"为暴露的接口,
- serviceName="AndroidPushNotificationWs" 为服务名随便取*/
- @WebService(endpointInterface="org.YL.cxf.ws.AndroidPushNotification",serviceName="AndroidPushNotificationWs")
- public class AndroidPushNotificationWs implements AndroidPushNotification {
- public void sendBroadcast(String apiKey, String title, String message) {
- // TODO Auto-generated method stub
- String uri = NotificationController.myuri;
- NotificationManager nm = new NotificationManager();
- nm.sendBroadcast(apiKey, title, message, uri);
- }
- public void sendAllBroadcast(String apiKey, String title, String message) {
- // TODO Auto-generated method stub
- 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) {
- // TODO Auto-generated method stub
- 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类的代码:
- /*
- * Copyright (C) 2010 Moduad Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- 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;
- /**
- * A controller class to process the notification related requests.
- *
- * @author Sehwan Noh (devnoh@gmail.com)
- */
- 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.addObject("list", null);
- 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.ContextLoaderListener</listener-class>
- </listener>
- <context-param>
- <!-- 指定Spring配置文件 -->
- <param-name>contextConfigLocation</param-name>
- <!-- 指定Spring配置文件位置 -->
- <param-value>/WEB-INF/applicationContext.xml</param-value>
- </context-param>
- <display-name>androidpn-server</display-name>
- <filter>
- <filter-name>encodingFilter</filter-name>
- <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
- <init-param>
- <param-name>encoding</param-name>
- <param-value>UTF-8</param-value>
- </init-param>
- <init-param>
- <param-name>forceEncoding</param-name>
- <param-value>true</param-value>
- </init-param>
- </filter>
- <filter>
- <filter-name>sitemesh</filter-name>
- <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>encodingFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>sitemesh</filter-name>
- <url-pattern>/*</url-pattern>
- <dispatcher>REQUEST</dispatcher>
- <dispatcher>FORWARD</dispatcher>
- </filter-mapping>
- <!-- 下面配置表明所有来自/>androidpnservice/*请求,都交给CXFServlet来处理 -->
- <servlet>
- <servlet-name>cxf</servlet-name>
- <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>cxf</servlet-name>
- <url-pattern>/androidpnservice/*</url-pattern>
- </servlet-mapping>
- <servlet>
- <servlet-name>dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcher</servlet-name>
- <url-pattern>*.do</url-pattern>
- </servlet-mapping>
- <session-config>
- <session-timeout>30</session-timeout>
- </session-config>
- <welcome-file-list>
- <welcome-file>/index.html</welcome-file>
- <welcome-file>/index.jsp</welcome-file>
- <welcome-file>/index.do</welcome-file>
- </welcome-file-list>
- <error-page>
- <error-code>400</error-code>
- <location>/index.jsp</location>
- </error-page>
- <error-page>
- <error-code>403</error-code>
- <location>/403.jsp</location>
- </error-page>
- <error-page>
- <error-code>404</error-code>
- <location>/404.jsp</location>
- </error-page>
- <error-page>
- <error-code>500</error-code>
- <location>/error.jsp</location>
- </error-page>
- </web-app>
其中
- <!-- 下面配置表明所有来自/>androidpnservice/*请求,都交给CXFServlet来处理 -->
- <servlet>
- <servlet-name>cxf</servlet-name>
- <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>cxf</servlet-name>
- <url-pattern>/androidpnservice/*</url-pattern>
- </servlet-mapping>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <context-param>
- <!-- 指定Spring配置文件 -->
- <param-name>contextConfigLocation</param-name>
- <!-- 指定Spring配置文件位置 -->
- <param-value>/WEB-INF/applicationContext.xml</param-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 {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- SendNotification sn = new SendNotification();
- //给所有人发信息,这里第一个参数"1234567890"是androidpn中的apikey,在androidpn中是写死了的,第二个参数为<strong>消息</strong>标题(title),第三个参数为<strong>消息</strong>内容(message)
- sn.sendAllBroadcast("1234567890", "hello", "How are you?");
- //给在线人发信息,参数同上
- //sn.sendBroadcast("1234567890", "Hi", "OK");
- //给指定人发信息, 第一、三、四个<strong>消息</strong>的意义同上,第二个参数"4ed224a3fa8c4560a27e313ab24f597d"是用户名,可以在<strong>服务器</strong>端页面看到。
- // sn.sendNotifications("1234567890","4ed224a3fa8c4560a27e313ab24f597d", "Hello Hi", "Hello World! How are you?");
- }
- }
在整个工程中,如下所示,红圈中是我手动添加的代码,其他的都cxf工具自动生成的。
好了,现在就来测试下我们的推送消息系统。
首先将Androidpn开源项目的客户端在模拟器重运行起来,然后回到PushClient工程,在主函数中将其他方法注释掉,只留下:
sn.sendAllBroadcast()方法,如下所示:
- package org.main;
- import org.yl.SendNotification;
- public class CallMain {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- SendNotification sn = new SendNotification();
- //给所有人发信息,这里第一个参数"1234567890"是androidpn中的apikey,在androidpn中是写死了的,第二个参数为<strong>消息</strong>标题(title),第三个参数为<strong>消息</strong>内容(message)
- sn.sendAllBroadcast("1234567890", "hello", "How are you?");
- //给在线人发信息,参数同上
- //sn.sendBroadcast("1234567890", "Hi", "OK");
- //给指定人发信息, 第一、三、四个<strong>消息</strong>的意义同上,第二个参数"4ed224a3fa8c4560a27e313ab24f597d"是用户名,可以在<strong>服务器</strong>端页面看到。
- // sn.sendNotifications("1234567890","4ed224a3fa8c4560a27e313ab24f597d", "Hello Hi", "Hello World! How are you?");
- }
- }
然后运行程序,可看到模拟器中接收到了推送主题为:hello,内容为:How are you?的 消息 。
同理注释掉其他方法直留下:sn.sendBroadcast()方法:
- package org.main;
- import org.yl.SendNotification;
- public class CallMain {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- SendNotification sn = new SendNotification();
- //给所有人发信息,这里第一个参数"1234567890"是androidpn中的apikey,在androidpn中是写死了的,第二个参数为<strong>消息</strong>标题(title),第三个参数为<strong>消息</strong>内容(message)
- //sn.sendAllBroadcast("1234567890", "hello", "How are you?");
- //给在线人发信息,参数同上
- sn.sendBroadcast("1234567890", "Hi", "OK");
- //给指定人发信息, 第一、三、四个<strong>消息</strong>的意义同上,第二个参数"4ed224a3fa8c4560a27e313ab24f597d"是用户名,可以在<strong>服务器</strong>端页面看到。
- // sn.sendNotifications("1234567890","4ed224a3fa8c4560a27e313ab24f597d", "Hello Hi", "Hello World! How are you?");
- }
- }
然后运行程序,可看到模拟器中接收到了推送主题为:Hi,内容为:OK的 消息 。
测试最后一个给指定用户发送消息的方法:
- package org.main;
- import org.yl.SendNotification;
- public class CallMain {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- SendNotification sn = new SendNotification();
- //给所有人发信息,这里第一个参数"1234567890"是androidpn中的apikey,在androidpn中是写死了的,第二个参数为<strong>消息</strong>标题(title),第三个参数为<strong>消息</strong>内容(message)
- //sn.sendAllBroadcast("1234567890", "hello", "How are you?");
- //给在线人发信息,参数同上
- //sn.sendBroadcast("1234567890", "Hi", "OK");
- //给指定人发信息, 第一、三、四个<strong>消息</strong>的意义同上,第二个参数"f6ac2608faa94a65b5c2d94b72e968b0"是用户名,可以在<strong>服务器</strong>端页面看到。
- 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