| @@ -41,6 +41,7 @@ public abstract class Broadcast | |||||
| private String broadcastId; | private String broadcastId; | ||||
| private BroadcastState state = BroadcastState.ACCEPTED; | private BroadcastState state = BroadcastState.ACCEPTED; | ||||
| private Object stateSemaphore = new Object(); | |||||
| String haltReason; | String haltReason; | ||||
| String stateErrorText; | String stateErrorText; | ||||
| @@ -335,62 +336,62 @@ public abstract class Broadcast | |||||
| { | { | ||||
| myLogger.debug("Waiting for jobs"); | myLogger.debug("Waiting for jobs"); | ||||
| readyQueue.wait(); | readyQueue.wait(); | ||||
| // go back to look for jobs | |||||
| continue; | |||||
| } | } | ||||
| catch (Exception e) | catch (Exception e) | ||||
| { | { | ||||
| // Do nothing | |||||
| // go back to look for jobs | |||||
| continue; | |||||
| } | } | ||||
| } | } | ||||
| updateServiceActivityCount(batch.size()); | |||||
| } | } | ||||
| if (batch.size() > 0) | |||||
| // Process jobs. | |||||
| // Mark start time | |||||
| long now = System.currentTimeMillis(); | |||||
| for (Job job : batch) | |||||
| { | { | ||||
| // Process jobs. | |||||
| // Mark start time | |||||
| long now = System.currentTimeMillis(); | |||||
| for (Job job : batch) | |||||
| { | |||||
| job.startTime = now; | |||||
| } | |||||
| // Service the jobs | |||||
| // But first get dependent resource | |||||
| // which includes allocation from capacity. Only returns when the required allocation | |||||
| // is obtained. Example, RTP port allocation, limit due to total number of allowable calls. | |||||
| ServicePrerequisites prerequisites = secureServicePrerequisites(); | |||||
| try | |||||
| { | |||||
| updateServiceActivityCount(1); | |||||
| int transactions = processJobs(batch, serviceProviderPeer, prerequisites); | |||||
| incrementTransactions(transactions); | |||||
| } | |||||
| catch (EngineException e) | |||||
| { | |||||
| // Aborting | |||||
| setState(BroadcastState.ABORTING, e.errorCodeText, e.errorText); | |||||
| } | |||||
| catch (Throwable t) | |||||
| { | |||||
| // This is unexpected. Log stack trace | |||||
| myLogger.error("Caught unexpected Throwable", t); | |||||
| terminate(BroadcastState.ABORTED, t + ": " + t.getMessage()); | |||||
| } | |||||
| finally | |||||
| { | |||||
| updateServiceActivityCount(-1); | |||||
| } | |||||
| if (sleepBetweenJobs > 0) | |||||
| { | |||||
| try | |||||
| { | |||||
| Thread.sleep(sleepBetweenJobs); | |||||
| } | |||||
| catch (InterruptedException e1) | |||||
| { | |||||
| // Do nothing? | |||||
| } | |||||
| } | |||||
| job.startTime = now; | |||||
| } | |||||
| // Service the jobs | |||||
| // But first get dependent resource | |||||
| // which includes allocation from capacity. Only returns when the required allocation | |||||
| // is obtained. Example, RTP port allocation, limit due to total number of allowable calls. | |||||
| ServicePrerequisites prerequisites = secureServicePrerequisites(); | |||||
| try | |||||
| { | |||||
| int transactions = processJobs(batch, serviceProviderPeer, prerequisites); | |||||
| incrementTransactions(transactions); | |||||
| } | |||||
| catch (EngineException e) | |||||
| { | |||||
| // Aborting | |||||
| setState(BroadcastState.ABORTING, e.errorCodeText, e.errorText); | |||||
| } | |||||
| catch (Throwable t) | |||||
| { | |||||
| // This is unexpected. Log stack trace | |||||
| myLogger.error("Caught unexpected Throwable", t); | |||||
| terminate(BroadcastState.ABORTED, t + ": " + t.getMessage()); | |||||
| } | |||||
| finally | |||||
| { | |||||
| updateServiceActivityCount(-batch.size()); | |||||
| } | |||||
| if (sleepBetweenJobs > 0) | |||||
| { | |||||
| try | |||||
| { | |||||
| Thread.sleep(sleepBetweenJobs); | |||||
| } | |||||
| catch (InterruptedException e1) | |||||
| { | |||||
| // Do nothing? | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -429,10 +430,6 @@ public abstract class Broadcast | |||||
| transactions += delta; | transactions += delta; | ||||
| } | } | ||||
| } | } | ||||
| public int pendingJobs() | |||||
| { | |||||
| return readyQueue.size() + scheduledJobs; | |||||
| } | |||||
| /** | /** | ||||
| * Experimental formulation where it takes over directing | * Experimental formulation where it takes over directing | ||||
| @@ -493,7 +490,7 @@ public abstract class Broadcast | |||||
| { | { | ||||
| readyQueue.add(mkJob(recipient)); | readyQueue.add(mkJob(recipient)); | ||||
| } | } | ||||
| if (getState() == BroadcastState.ALLDONE) return; | |||||
| if (state == BroadcastState.ALLDONE) return; | |||||
| } | } | ||||
| catch (BroadcastException e) | catch (BroadcastException e) | ||||
| { | { | ||||
| @@ -641,34 +638,37 @@ public abstract class Broadcast | |||||
| boolean isLegal; | boolean isLegal; | ||||
| BroadcastState prev = null; | BroadcastState prev = null; | ||||
| if (state == newState) return new StateChangeResult(StateChangeStatus.NO_CHANGE, state, null); | if (state == newState) return new StateChangeResult(StateChangeStatus.NO_CHANGE, state, null); | ||||
| List<BroadcastState> to = toStates.get(state); | |||||
| isLegal = (to == null? false : to.contains(newState)); | |||||
| prev = state; | |||||
| if (isLegal) | |||||
| synchronized(stateSemaphore) | |||||
| { | { | ||||
| this.haltReason = haltReason; | |||||
| this.stateErrorText = stateErrorText; | |||||
| CommonLogger.activity.info(String.format("Broadcast %s: State transitioned from %s to %s", broadcastId, state, newState)); | |||||
| state = newState; | |||||
| changeStateTime = System.currentTimeMillis(); | |||||
| if (state == BroadcastState.RUNNING) serviceStartTime = changeStateTime; | |||||
| if (prev == BroadcastState.RUNNING) serviceEndTime = changeStateTime; | |||||
| if (postback != null) | |||||
| { | |||||
| if (state == BroadcastState.ALLDONE) postback.queueReport(mkStatusReport()); | |||||
| else postback.queueReportFirst(mkStatusReport()); | |||||
| } | |||||
| return new StateChangeResult(StateChangeStatus.SUCCESS, newState, prev); | |||||
| } | |||||
| else | |||||
| { | |||||
| // log illegal state transition with call trace | |||||
| Exception e = new Exception(String.format("Broadast %s ignored illegal transition from %s to %s", broadcastId, prev, newState)); | |||||
| myLogger.error(e.getMessage()); | |||||
| myLogger.debug("This exception is not thrown -- only for debugging information", e); | |||||
| return new StateChangeResult(StateChangeStatus.FORBIDDEN, prev, null); | |||||
| List<BroadcastState> to = toStates.get(state); | |||||
| isLegal = (to == null? false : to.contains(newState)); | |||||
| prev = state; | |||||
| if (isLegal) | |||||
| { | |||||
| this.haltReason = haltReason; | |||||
| this.stateErrorText = stateErrorText; | |||||
| CommonLogger.activity.info(String.format("Broadcast %s: State transitioned from %s to %s", broadcastId, state, newState)); | |||||
| state = newState; | |||||
| changeStateTime = System.currentTimeMillis(); | |||||
| if (state == BroadcastState.RUNNING) serviceStartTime = changeStateTime; | |||||
| if (prev == BroadcastState.RUNNING) serviceEndTime = changeStateTime; | |||||
| if (postback != null) | |||||
| { | |||||
| if (state == BroadcastState.ALLDONE) postback.queueReport(mkStatusReport()); | |||||
| else postback.queueReportFirst(mkStatusReport()); | |||||
| } | |||||
| return new StateChangeResult(StateChangeStatus.SUCCESS, newState, prev); | |||||
| } | |||||
| else | |||||
| { | |||||
| // log illegal state transition with call trace | |||||
| Exception e = new Exception(String.format("Broadast %s ignored illegal transition from %s to %s", broadcastId, prev, newState)); | |||||
| myLogger.error(e.getMessage()); | |||||
| myLogger.debug("This exception is not thrown -- only for debugging information", e); | |||||
| return new StateChangeResult(StateChangeStatus.FORBIDDEN, prev, null); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -720,7 +720,7 @@ public abstract class Broadcast | |||||
| responseXML.append("\""); | responseXML.append("\""); | ||||
| } | } | ||||
| responseXML.append(" accepted='"); | responseXML.append(" accepted='"); | ||||
| responseXML.append(e != null || getState() == BroadcastState.COMPLETED ? "FALSE" : "TRUE"); | |||||
| responseXML.append(e != null || state == BroadcastState.COMPLETED ? "FALSE" : "TRUE"); | |||||
| responseXML.append("'"); | responseXML.append("'"); | ||||
| if (e == null) | if (e == null) | ||||
| { | { | ||||
| @@ -927,10 +927,8 @@ public abstract class Broadcast | |||||
| */ | */ | ||||
| protected int getActiveJobCount() | protected int getActiveJobCount() | ||||
| { | { | ||||
| return serviceActivityCount; | |||||
| return serviceActivityCount; | |||||
| } | } | ||||
| /** | /** | ||||
| * Parses broadcastId and return if notInService is true. | * Parses broadcastId and return if notInService is true. | ||||
| @@ -1128,7 +1126,7 @@ public abstract class Broadcast | |||||
| { | { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (pendingJobs() == 0) | |||||
| if (getRemainingJobCount() == 0) | |||||
| { | { | ||||
| wakeUpServiceThreads(); | wakeUpServiceThreads(); | ||||
| return true; | return true; | ||||
| @@ -1309,8 +1307,16 @@ public abstract class Broadcast | |||||
| public ScheduledFuture<?> rescheduleJob(final Job job, long rescheduleTimeMS) | public ScheduledFuture<?> rescheduleJob(final Job job, long rescheduleTimeMS) | ||||
| { | { | ||||
| scheduledJobs++; | |||||
| Runnable r = new Runnable() { public void run() { scheduledJobs--; addJob(job);}}; | |||||
| synchronized(readyQueue) { | |||||
| scheduledJobs++; | |||||
| } | |||||
| Runnable r = new Runnable() { public void run() { | |||||
| synchronized(readyQueue) { | |||||
| scheduledJobs--; | |||||
| } | |||||
| addJob(job); | |||||
| } | |||||
| }; | |||||
| return scheduler.schedule(r, rescheduleTimeMS, TimeUnit.MILLISECONDS); | return scheduler.schedule(r, rescheduleTimeMS, TimeUnit.MILLISECONDS); | ||||
| } | } | ||||
| @@ -1326,7 +1332,7 @@ public abstract class Broadcast | |||||
| case RUNNING: | case RUNNING: | ||||
| case PAUSING: | case PAUSING: | ||||
| case PAUSED: | case PAUSED: | ||||
| return pendingJobs(); | |||||
| return getRemainingJobCount(); | |||||
| default: | default: | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -1334,7 +1340,7 @@ public abstract class Broadcast | |||||
| public int getCompletedJobCount() | public int getCompletedJobCount() | ||||
| { | { | ||||
| return jobsTotal - pendingJobs() - serviceActivityCount; | |||||
| return jobsTotal - getRemainingJobCount() - serviceActivityCount; | |||||
| } | } | ||||
| public String getBroadcastType() { | public String getBroadcastType() { | ||||
| @@ -1342,26 +1348,34 @@ public abstract class Broadcast | |||||
| } | } | ||||
| public PostbackThreadActionOnEmpty getPostbackThreadActionOnEmpty() { | public PostbackThreadActionOnEmpty getPostbackThreadActionOnEmpty() { | ||||
| myLogger.debug("getPostbackThreadActionOnEmpty(): broadcast state " + state); | |||||
| if (state.isFinal) return PostbackThreadActionOnEmpty.STOP; | if (state.isFinal) return PostbackThreadActionOnEmpty.STOP; | ||||
| if (getActiveJobCount() == 0) { | |||||
| if (state == BroadcastState.PAUSING) { | |||||
| setState(BroadcastState.PAUSED); | |||||
| return PostbackThreadActionOnEmpty.WAIT; | |||||
| } | |||||
| if (state == BroadcastState.CANCELING) { | |||||
| setState(BroadcastState.CANCELED); | |||||
| return PostbackThreadActionOnEmpty.STOP; | |||||
| } | |||||
| else if (state == BroadcastState.ABORTING) { | |||||
| setState(BroadcastState.ABORTED); | |||||
| return PostbackThreadActionOnEmpty.STOP; | |||||
| } | |||||
| else if (state == BroadcastState.COMPLETED) { | |||||
| setState(BroadcastState.ALLDONE); | |||||
| return PostbackThreadActionOnEmpty.STOP; | |||||
| } | |||||
| } | |||||
| return PostbackThreadActionOnEmpty.WAIT; | |||||
| int activeJobCount = getActiveJobCount(); | |||||
| myLogger.debug("getPostbackThreadActionOnEmpty(): activeJobCount = " + activeJobCount); | |||||
| if (activeJobCount > 0) { | |||||
| return PostbackThreadActionOnEmpty.WAIT; | |||||
| } | |||||
| if (state == BroadcastState.PAUSING) { | |||||
| return setState(BroadcastState.PAUSED).stateChangeStatus == StateChangeStatus.SUCCESS? | |||||
| PostbackThreadActionOnEmpty.CONTINUE : PostbackThreadActionOnEmpty.WAIT; | |||||
| } | |||||
| if (state == BroadcastState.CANCELING) { | |||||
| return setState(BroadcastState.CANCELED).stateChangeStatus == StateChangeStatus.SUCCESS? | |||||
| PostbackThreadActionOnEmpty.CONTINUE : PostbackThreadActionOnEmpty.STOP; | |||||
| } | |||||
| else if (state == BroadcastState.ABORTING) { | |||||
| return setState(BroadcastState.ABORTED).stateChangeStatus == StateChangeStatus.SUCCESS? | |||||
| PostbackThreadActionOnEmpty.CONTINUE : PostbackThreadActionOnEmpty.STOP; | |||||
| } | |||||
| else if (state == BroadcastState.COMPLETED) { | |||||
| return setState(BroadcastState.ALLDONE).stateChangeStatus == StateChangeStatus.SUCCESS? | |||||
| PostbackThreadActionOnEmpty.CONTINUE : PostbackThreadActionOnEmpty.STOP; | |||||
| } | |||||
| else { | |||||
| return PostbackThreadActionOnEmpty.WAIT; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||