package org.commcare.heartbeat; import org.commcare.CommCareApp; import org.commcare.CommCareApplication; import org.commcare.logging.AndroidLogger; import org.commcare.preferences.CommCareServerPreferences; import org.commcare.services.CommCareSessionService; import org.javarosa.core.services.Logger; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** * While active, this class is responsible controlling a TimerTask that periodically pings the * server with a "heartbeat" request. The lifecycle of this object is tied directly to that of * the CommCareSessionService; it should be started whenever a session service is started, * and ended whenever a session service is ended for any reason. * * Created by amstone326 on 4/13/17. */ public class HeartbeatLifecycleManager { private static final long ONE_HOUR_IN_MS = 60 * 60 * 1000; private TimerTask heartbeatRequestTask; private HeartbeatRequester requester = new HeartbeatRequester(); private CommCareSessionService enclosingSessionService; public HeartbeatLifecycleManager(CommCareSessionService sessionService) { this.enclosingSessionService = sessionService; } public void startHeartbeatCommunications() { if (shouldStartHeartbeatRequests()) { this.heartbeatRequestTask = new TimerTask() { @Override public void run() { if (shouldStopHeartbeatRequests()) { HeartbeatLifecycleManager.this.endCurrentHeartbeatTask(); } else { try { requester.requestHeartbeat(); //requester.parseTestHeartbeatResponse(); } catch (Exception e) { // Encountered an unexpected issue, should just bail on this thread HeartbeatLifecycleManager.this.endCurrentHeartbeatTask(); Logger.log(AndroidLogger.TYPE_ERROR_SERVER_COMMS, "Encountered unexpected exception during heartbeat communications: " + e.getMessage() + ". Stopping the heartbeat thread."); } } } }; (new Timer()).schedule(heartbeatRequestTask, new Date(), ONE_HOUR_IN_MS); } } private boolean shouldStartHeartbeatRequests() { return appHasHeartbeatUrl() && !hasSucceededOnThisLogin() && endCurrentHeartbeatTask(); } private boolean shouldStopHeartbeatRequests() { return sessionHasDied() || hasSucceededOnThisLogin(); } private boolean appHasHeartbeatUrl() { CommCareApp currentApp = CommCareApplication.instance().getCurrentApp(); String urlString = currentApp.getAppPreferences().getString( CommCareServerPreferences.PREFS_HEARTBEAT_URL_KEY, null); return urlString != null; } private boolean hasSucceededOnThisLogin() { return enclosingSessionService.heartbeatSucceededForSession(); } private boolean sessionHasDied() { return !enclosingSessionService.isActive(); } public void endHeartbeatCommunications() { endCurrentHeartbeatTask(); this.enclosingSessionService = null; } /** * * @return true if we have successfully canceled the current heartbeat task, or there is no * current heartbeat task */ private boolean endCurrentHeartbeatTask() { if (heartbeatRequestTask == null) { return true; } if (heartbeatRequestTask.cancel()) { heartbeatRequestTask = null; return true; } return false; } }