情景:
客户端需要实时提醒服务,比如某个日程已过期的提醒,并且是刚刚过期的时间(所以是实时),
1. 日程对象放在数据库,由webservice调用得到
2. webservice在拿到数据后必须主动把数据给我的主程序端
3. 主程序端在拿到数据后开始推送数据到客户端
代码:
public class CheckUnsignedBookingJob { private static final Logger log = LoggerFactory.getLogger(CheckUnsignedBookingJob.class); private VisitBookingService visitBookingService; private TourBookingService tourBookingService; private SignoutNoticeService signoutNoticeService; public void execute() { log.debug("CheckUnsignedBookingJob start"); List<VisitBooking> visitBookings = visitBookingService.getUnsignoutedBookingInfo(); log.debug("unsignouted visit booking size: {}", visitBookings.size()); if (!visitBookings.isEmpty()) { for (VisitBooking visitBooking : visitBookings) { log.debug("visit booking number: {}", visitBooking.getBookingNumber()); signoutNoticeService.visitBookingNotSignouted(visitBooking); } } List<TourBooking> tourBookings = tourBookingService.getUnsignoutedBookingInfo(); log.debug("unsignouted tour booking size: {}", tourBookings.size()); if (!tourBookings.isEmpty()) { for (TourBooking tourBooking : tourBookings) { log.debug("tour booking number: {}", tourBooking.getBookingNumber()); signoutNoticeService.tourBookingNotSignouted(tourBooking); } } log.debug("CheckUnsignedBookingJob done"); } public void setVisitBookingService(VisitBookingService visitBookingService) { this.visitBookingService = visitBookingService; } public void setTourBookingService(TourBookingService tourBookingService) { this.tourBookingService = tourBookingService; } public void setSignoutNoticeService(SignoutNoticeService signoutNoticeService) { this.signoutNoticeService = signoutNoticeService; } }
这是webservice端的一个Job, 由spring定时触发,看配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" default-lazy-init="true"> <bean id="checkUnsignedBookingJobCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail"> <bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <bean class="com.XXX.tourbooking.job.CheckUnsignedBookingJob"> <property name="visitBookingService" ref="visitBookingServiceImpl" /> <property name="tourBookingService" ref="tourBookingServiceImpl" /> <property name="signoutNoticeService" ref="signoutNoticeService" /> </bean> </property> <property name="targetMethod"> <value>execute</value> </property> </bean> </property> <property name="cronExpression"> <value>0 0/30 * * * ?</value> </property> </bean> <!-- startQuertz --> <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="checkUnsignedBookingJobCronTrigger" /> </list> </property> </bean> </beans>
这个任务会定时触发刚那个Job的execute方法,在这个方法中,会调用webservice端的接口
SignoutNoticeService 中定义的方法,该接口的实现类在我们的主程序端,而不是webservice端。
@WebService public interface SignoutNoticeService { void visitBookingNotSignouted(VisitBooking visitBooking); void tourBookingNotSignouted(TourBooking tourBooking); }
看主程序端是如何实现webservice这个SignoutNoticeService 接口中的方法并实现推送的
@Service("signoutNoticeService") @WebService(endpointInterface = "com.XXX.tourbooking.service.SignoutNoticeService") public class SignoutNoticeServiceImpl implements SignoutNoticeService { private static final Logger log = LoggerFactory.getLogger(SignoutNoticeServiceImpl.class); public void visitBookingNotSignouted(final VisitBooking visitBooking) { log.info("signoutTimeoutVisitBookingPopup call.."); log.info("SignoutNotice size: {}", SignoutNotice.signoutNoticeList.size()); if (SignoutNotice.signoutNoticeList.size() > 0) { SignoutNotice notice = SignoutNotice.signoutNoticeList .get(SignoutNotice.signoutNoticeList.size() - 1); log.info("SignoutNotice: {}", notice); if (notice != null) { notice.signoutTimeoutVisitBookingPopup(visitBooking.getId()); } } } public void tourBookingNotSignouted(final TourBooking tourBooking) { log.info("signoutTimeoutTourBookingPopup call.."); log.info("signoutNoticeList size: {}", SignoutNotice.signoutNoticeList.size()); if (SignoutNotice.signoutNoticeList.size() > 0) { SignoutNotice notice = SignoutNotice.signoutNoticeList .get(SignoutNotice.signoutNoticeList.size() - 1); log.info("SignoutNotice: {}", notice); if (notice != null) { notice.signoutTimeoutTourBookingPopup(tourBooking.getId()); } } } }
可以看到这个实现类的头部注解,这个地方是关键,webservice端的数据给到了主程序端,虽然我还不明白,哈哈(可能是个长连接)。。。
实现类的方法中,调用了主程序端定义的服务器推送类SignoutNotice,这个类实现了dwr的服务器推送所需要的操作。
public class SignoutNotice { private static final Logger log = Logger.getLogger(SignoutNotice.class); public static List<SignoutNotice> signoutNoticeList = new ArrayList<SignoutNotice>(); private String[] pushRoleId = new String[] { "1", "2" }; private Collection<ScriptSession> sessions = new ArrayList<ScriptSession>(); public SignoutNotice() { log.info("new SignoutNotice..."); signoutNoticeList.add(this); } public void signoutTimeoutVisitBookingPopup(String bookId) { log.info("signoutTimeoutVisitBookingPopup ajax call....."); Collection filterSessions = filterScriptSessionByRoleId(sessions); log.info("signoutTimeoutVisitBookingPopup sessions.size(): " + sessions.size()+" filterSessions.size():"+filterSessions.size()); Util util = new Util(filterSessions); util.addFunctionCall("AlertSignoutNoticeVisitBooking", bookId); } public void signoutTimeoutTourBookingPopup(String bookId) { log.info("signoutTimeoutTourBookingPopup ajax call....."); Collection filterSessions = filterScriptSessionByRoleId(sessions); log.info("signoutTimeoutTourBookingPopup sessions.size(): " + sessions.size()+" filterSessions.size():"+filterSessions.size()); Util util = new Util(filterSessions); util.addFunctionCall("AlertSignoutNoticeTourBooking", bookId); } public void init(String roleId) { log.info("init ....."); WebContext wctx = WebContextFactory.get(); ScriptSession scriptSession = wctx.getScriptSession(); log.info("roleId: " + roleId); scriptSession.setAttribute("roleId", roleId); sessions.add(scriptSession); } public Collection filterScriptSessionByRoleId(Collection<ScriptSession> sessions) { Collection filterSessions = new ArrayList(); for (ScriptSession scriptSession : sessions) { String roleId = (String) scriptSession.getAttribute("roleId"); log.info("roleId:" + roleId); for (String id : pushRoleId) { if (id.equals(roleId)) { filterSessions.add(scriptSession); break; } } } return filterSessions; } }
这个类需要在dwr.xml配置文件中进行配置,作为一个ajax类,dwr的ajax就不讲解了。
要实现dwr的reverseAjax,在上面这个类中已经用到了方法util.addFunctionCall("AlertSignoutNoticeTourBooking", bookId);其中AlertSignoutNoticeTourBooking就是在页面上定义的一个js方法,这个方法实现提醒服务,它是由服务器端调用的,就是说,服务器端调用客户端的方法。
在客户端,我们需要进行如下的初始化才能实现dwr的服务器推送功能:
dwr.engine.setActiveReverseAjax(true); SignoutNotice.init(roleId);
dwr.engine.setActiveReverseAjax(true); 这句是dwr实现服务器推送必不可少的设置。
以前我有写过一个简单的dwr推送demo,可以去参考。
至此,webservice结合dwr实现服务器推送已经完成的差不多了,在客户端只要有过期的日程,就会被提醒。