com.microsoft.bingads.v13.internal.bulk.PollingBulkOperationTracker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of microsoft.bingads Show documentation
Show all versions of microsoft.bingads Show documentation
The Bing Ads Java SDK is a library improving developer experience when working with the Bing Ads services by providing high-level access to features such as Bulk API, OAuth Authorization and SOAP API.
package com.microsoft.bingads.v13.internal.bulk;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import com.microsoft.bingads.AsyncCallback;
import com.microsoft.bingads.ServiceClient;
import com.microsoft.bingads.internal.OperationStatusRetry;
import com.microsoft.bingads.internal.ResultFuture;
import com.microsoft.bingads.internal.functionalinterfaces.Consumer;
import com.microsoft.bingads.internal.functionalinterfaces.TriConsumer;
import com.microsoft.bingads.internal.utilities.ThreadPool;
import com.microsoft.bingads.v13.bulk.AdApiFaultDetail_Exception;
import com.microsoft.bingads.v13.bulk.BulkOperationProgressInfo;
import com.microsoft.bingads.v13.bulk.BulkOperationStatus;
import com.microsoft.bingads.v13.bulk.CouldNotGetBulkOperationStatusException;
import com.microsoft.bingads.v13.bulk.IBulkService;
import com.microsoft.bingads.v13.bulk.Progress;
public class PollingBulkOperationTracker implements BulkOperationTracker {
private static final int INITIAL_STATUS_CHECK_INTERVAL_IN_MS = 1000;
private static final int NUMBER_OF_INITIAL_STATUS_CHECKS = 5;
private int numberOfStatusChecks = 0;
private ScheduledExecutorService executorService;
private BulkOperationStatusProvider statusProvider;
private Progress progress;
private boolean stopTracking;
protected int lastProgressReported;
private BulkOperationStatus currentStatus;
private final int statusCheckIntervalInMs;
private ResultFuture> trackResultFuture;
private ServiceClient serviceClient;
private OperationStatusRetry, BulkOperationStatusProvider, IBulkService> operationStatusRetry;
private int numberOfStatusRetry = 4;
private final Runnable pollExecutorTask = new Runnable() {
@Override
public void run() {
pollOperationStatus();
}
};
public PollingBulkOperationTracker(BulkOperationStatusProvider statusProvider,
ServiceClient serviceClient, Progress progress,
int statusCheckIntervalInMs) {
this.statusCheckIntervalInMs = statusCheckIntervalInMs;
this.statusProvider = statusProvider;
this.serviceClient = serviceClient;
this.progress = progress;
this.operationStatusRetry = new OperationStatusRetry, BulkOperationStatusProvider, IBulkService>(
new Function() {
@Override
public Integer apply(Exception exception) {
Throwable cause = exception.getCause();
int errorCode = -1;
try {
errorCode = ((AdApiFaultDetail_Exception) cause).getFaultInfo().getErrors().getAdApiErrors()
.get(0).getCode();
} catch (Exception e) {
// Ignore
}
return errorCode;
}
});
}
AtomicBoolean statusUpdateInProgress = new AtomicBoolean(false);
public void pollOperationStatus() {
try {
if (!statusUpdateInProgress.compareAndSet(false, true)) {
return;
}
if (trackingWasStopped()) {
return;
}
if (cancelPollingIfRequestedByUser()) {
return;
}
refreshStatus(new AsyncCallback>() {
@Override
public void onCompleted(Future> result) {
try {
result.get();
numberOfStatusChecks++;
reportProgressIfNeeded();
completeTaskIfOperationIsComplete();
} catch (InterruptedException ex) {
stopTracking();
propagateExceptionToCallingThread(new CouldNotGetBulkOperationStatusException(ex));
} catch (ExecutionException ex) {
stopTracking();
propagateExceptionToCallingThread(new CouldNotGetBulkOperationStatusException(ex));
} finally {
statusUpdateInProgress.set(false);
if (!stopTracking) {
int interval = INITIAL_STATUS_CHECK_INTERVAL_IN_MS;
if (numberOfStatusChecks >= NUMBER_OF_INITIAL_STATUS_CHECKS) {
interval = statusCheckIntervalInMs;
}
executorService.schedule(pollExecutorTask, interval, TimeUnit.MILLISECONDS);
}
}
}
});
} catch (Throwable ex) {
stopTracking();
propagateExceptionToCallingThread(ex);
}
}
private boolean cancelPollingIfRequestedByUser() {
if (trackResultFuture.isCancelled()) {
stopTracking();
return true;
}
return false;
}
private void reportProgressIfNeeded() {
if (!userRequestedProgressReports()) {
return;
}
if (progressChangedSinceLastReport()) {
reportProgress();
}
}
private void reportProgress() {
final int percentage = currentStatus.getPercentComplete();
ThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
progress.report(new BulkOperationProgressInfo(percentage));
updateLastProgressReported(percentage);
} catch (Exception ex) {
// ignore exceptions from progress update thread
}
}
});
}
private boolean userRequestedProgressReports() {
return this.progress != null;
}
private boolean progressChangedSinceLastReport() {
return currentStatus.getPercentComplete() != lastProgressReported;
}
private void updateLastProgressReported(int per) {
this.lastProgressReported = per;
}
private void propagateExceptionToCallingThread(Throwable ex) {
trackResultFuture.setException(ex);
}
private void completeTaskIfOperationIsComplete() {
if (operationIsComplete()) {
stopTracking();
completeTaskWithResult();
}
}
private void completeTaskWithResult() {
trackResultFuture.setResult(currentStatus);
}
private boolean operationIsComplete() {
return statusProvider.isFinalStatus(currentStatus);
}
private void refreshStatus(AsyncCallback> callback) {
final ResultFuture> resultFuture = new ResultFuture>(
callback);
operationStatusRetry.executeWithRetry(
new TriConsumer, ServiceClient, AsyncCallback>>() {
@Override
public void accept(BulkOperationStatusProvider statusProvider,
ServiceClient serviceClient,
AsyncCallback> callback) {
statusProvider.getCurrentStatus(serviceClient, callback);
}
}, statusProvider, serviceClient, new Consumer>() {
@Override
public void accept(BulkOperationStatus status) {
currentStatus = status;
resultFuture.setResult(currentStatus);
}
}, new Consumer() {
@Override
public void accept(Exception exception) {
resultFuture.setException(exception);
}
}, numberOfStatusRetry);
}
private boolean trackingWasStopped() {
return this.stopTracking;
}
private void stopTracking() {
this.stopTracking = true;
this.executorService.shutdown();
}
/*
* (non-Javadoc)
*
* @see
* com.microsoft.bingads.api.internal.bulk.operations.BulkOperationTracker#
* trackResultFileAsync()
*/
@Override
public Future> trackResultFileAsync(
AsyncCallback> callback) {
this.trackResultFuture = new ResultFuture>(callback);
this.startTracking();
return trackResultFuture;
}
private void startTracking() {
executorService = Executors.newSingleThreadScheduledExecutor();
executorService.schedule(pollExecutorTask, INITIAL_STATUS_CHECK_INTERVAL_IN_MS, TimeUnit.MILLISECONDS);
}
}