/** * Copyright (c) 2011 Martin M Reed * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.hardisonbrewing.signingserver; import java.util.Hashtable; import java.util.Timer; import java.util.TimerTask; import javax.microedition.io.StreamConnection; import net.hardisonbrewing.signingserver.model.OptionProperties; import net.hardisonbrewing.signingserver.model.PushNotification; import net.hardisonbrewing.signingserver.service.icon.IconService; import net.hardisonbrewing.signingserver.service.network.AbstractRadioStatusListener; import net.hardisonbrewing.signingserver.service.push.PushPPGService; import net.hardisonbrewing.signingserver.service.push.PushSIGService; import net.hardisonbrewing.signingserver.service.store.OptionsStore; import net.hardisonbrewing.signingserver.service.store.push.PushPPGStatusChangeListener; import net.hardisonbrewing.signingserver.service.store.push.PushPPGStatusChangeListenerStore; import net.rim.blackberry.api.push.PushApplication; import net.rim.blackberry.api.push.PushApplicationDescriptor; import net.rim.blackberry.api.push.PushApplicationRegistry; import net.rim.blackberry.api.push.PushApplicationStatus; import net.rim.device.api.io.http.PushInputStream; import net.rim.device.api.system.Application; import net.rim.device.api.system.DeviceInfo; import net.rim.device.api.system.RadioInfo; import net.rim.device.api.system.WLANInfo; import org.apache.commons.threadpool.DefaultThreadPool; import org.apache.commons.threadpool.ThreadPool; import org.metova.mobile.util.io.IOUtility; import org.metova.mobile.util.time.Dates; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SigservPushApplication extends Application implements PushApplication, PushPPGStatusChangeListener { private static final Logger log = LoggerFactory.getLogger( SigservPushApplication.class ); private final ThreadPool threadPool = new DefaultThreadPool( 1 ); private AbstractRadioStatusListener radioStatusListener; private TimerTask networkRequiredTimerTask; private TimerTask registerServ; public SigservPushApplication() { PushPPGStatusChangeListenerStore.put( this ); radioStatusListener = new MyRadioStatusListener(); addRadioListener( radioStatusListener ); WLANInfo.addListener( radioStatusListener ); } public static boolean isSupported() { return !DeviceInfo.isSimulator(); } public static void mainAutoRegister( String[] args ) { if ( !isSupported() ) { log.info( "PUSH not supported. Skipping registration." ); return; } log.info( "Running auto register PUSH application" ); if ( !OptionsStore.getBoolean( OptionProperties.PUSH_ENABLED ) ) { log.info( "Push notifications disabled. Skipping registration." ); return; } mainRegister( args ); } public static void mainRegister( String[] args ) { if ( !isSupported() ) { log.info( "PUSH not supported. Skipping registration." ); return; } log.info( "Registering PUSH application" ); PushApplicationDescriptor pushApplicationDescriptor = PushPPGService.getPushApplicationDescriptor(); PushApplicationStatus status = PushApplicationRegistry.getStatus( pushApplicationDescriptor ); SigservApplication.logEvent( "Status before registration: " + PushSIGService.getStatusText( status.getStatus() ) ); switch (status.getStatus()) { case PushApplicationStatus.STATUS_ACTIVE: { SigservApplication.logEvent( "Push status active. Skipping registration." ); PushPPGService.updateStoredStatus( status.getStatus() ); break; } case PushApplicationStatus.STATUS_PENDING: { SigservApplication.logEvent( "Push status pending. Skipping registration." ); PushPPGService.updateStoredStatus( status.getStatus() ); break; } case PushApplicationStatus.STATUS_FAILED: case PushApplicationStatus.STATUS_NOT_REGISTERED: default: { SigservApplication.logEvent( "Registering push application" ); PushApplicationRegistry.registerApplication( pushApplicationDescriptor ); status = PushApplicationRegistry.getStatus( pushApplicationDescriptor ); SigservApplication.logEvent( "Status after registration: " + PushSIGService.getStatusText( status.getStatus() ) ); break; } } } public static void mainUnregister( String[] args ) { log.info( "Unregistering PUSH application" ); PushPPGService.unregister(); } public static void mainStartup( String[] args ) { log.info( "Running PUSH application" ); try { SigservPushApplication application = new SigservPushApplication(); application.enterEventDispatcher(); } catch (Exception e) { mainAutoRegister( new String[] { SigservApplication.PUSH_REGISTER } ); } } public void onMessage( final PushInputStream inputStream, final StreamConnection conn ) { log.info( "Recieved new PUSH message" ); threadPool.invokeLater( new Runnable() { public void run() { handleMessage( inputStream, conn ); } } ); } public static void handleMessage( PushInputStream inputStream, StreamConnection conn ) { log.info( "Reading new PUSH message" ); PushNotification pushNotification = null; try { pushNotification = PushSIGService.parse( inputStream ); } catch (Throwable t) { log.error( "Exception while parsing new push message", t ); } finally { try { inputStream.accept(); } catch (Throwable t) { // do nothing } IOUtility.safeClose( inputStream ); IOUtility.safeClose( conn ); } Hashtable latest = pushNotification == null ? null : (Hashtable) pushNotification.message; SigservApplication.updateStoredStatus( latest ); IconService.updateIcon(); PushSIGService.confirm( pushNotification == null ? null : pushNotification.pid ); } public void onStatusChange( PushApplicationStatus status ) { String errorText = status.getError(); String statusText = PushSIGService.getStatusText( status.getStatus() ); String reasonText = PushSIGService.getReasonText( status.getReason() ); SigservApplication.logEvent( "PushApplicationStatus status[" + statusText + "], error[" + errorText + "], reason[" + reasonText + "]" ); PushPPGService.updateStoredStatus( status.getStatus() ); } public void onStatusChange( byte status ) { switch (status) { case PushApplicationStatus.STATUS_ACTIVE: { startRegisterServTask(); break; } case PushApplicationStatus.STATUS_FAILED: case PushApplicationStatus.STATUS_NOT_REGISTERED: { cancelRegisterServTask(); PushSIGService.register( false ); break; } case PushApplicationStatus.STATUS_PENDING: default: { break; } } } private void cancelNetworkRequiredTask() { if ( networkRequiredTimerTask != null ) { networkRequiredTimerTask.cancel(); networkRequiredTimerTask = null; } } private void startNetworkRequiredTask() { cancelNetworkRequiredTask(); Timer timer = new Timer(); networkRequiredTimerTask = new NetworkRequiredTimerTask(); timer.schedule( networkRequiredTimerTask, 0, Dates.SECOND * 10 ); } private void cancelRegisterServTask() { if ( registerServ != null ) { registerServ.cancel(); registerServ = null; } } private void startRegisterServTask() { cancelRegisterServTask(); Timer timer = new Timer(); registerServ = new RegisterServTimerTask(); timer.schedule( registerServ, 0, Dates.DAY ); } public boolean requestClose() { log.info( "Closing PUSH application" ); if ( radioStatusListener != null ) { WLANInfo.removeListener( radioStatusListener ); removeRadioListener( radioStatusListener ); radioStatusListener = null; } cancelNetworkRequiredTask(); cancelRegisterServTask(); System.exit( 0 ); return true; } private final class RegisterServTimerTask extends TimerTask { public void run() { PushSIGService.register( true ); } } private final class NetworkRequiredTimerTask extends TimerTask { public void run() { log.info( "Checking PUSH registration from task" ); if ( !PushPPGService.shouldRegister() ) { synchronized (radioStatusListener) { log.info( "PUSH registration no longer required, canceling task" ); cancelNetworkRequiredTask(); return; } } log.info( "Checking coverage for PUSH registration" ); if ( SigservApplication.hasSufficientCoverage() ) { log.info( "Sufficient coverage, running auto PUSH registration" ); mainAutoRegister( new String[] { SigservApplication.PUSH_REGISTER } ); } else { log.info( "Coverage not sufficient, skipping auto PUSH registration" ); } } } private final class MyRadioStatusListener extends AbstractRadioStatusListener { private void checkRegistration() { log.info( "Checking PUSH registration" ); if ( !PushPPGService.shouldRegister() || networkRequiredTimerTask != null ) { synchronized (radioStatusListener) { if ( !PushPPGService.shouldRegister() || networkRequiredTimerTask != null ) { log.info( "PUSH registration not required or task already running" ); return; } } } log.info( "PUSH registration required, running task" ); startNetworkRequiredTask(); } public void networkStarted( int networkId, int service ) { super.networkStarted( networkId, service ); if ( ( service & RadioInfo.NETWORK_SERVICE_DATA ) == RadioInfo.NETWORK_SERVICE_DATA ) { checkRegistration(); } } public void networkServiceChange( int networkId, int service ) { super.networkServiceChange( networkId, service ); if ( ( service & RadioInfo.NETWORK_SERVICE_DATA ) == RadioInfo.NETWORK_SERVICE_DATA ) { checkRegistration(); } } public void networkStateChange( int state ) { super.networkStateChange( state ); checkRegistration(); } public void networkConnected() { super.networkConnected(); checkRegistration(); } public void networkDisconnected( int reason ) { super.networkDisconnected( reason ); checkRegistration(); } } }