Let's say you have a method to add keywords as below:
@Override
public boolean addKeywords(List<BiddableKeyword> keywords) {
logger.debug("Adding {} keywords...", keywords.size());
List<AdGroupCriterionOperation> operations = new ArrayList<AdGroupCriterionOperation>();
for (BiddableKeyword keyword : keywords) {
// create keyword
com.google.api.ads.adwords.axis.v201309.cm.Keyword newKeyword = new com.google.api.ads.adwords.axis.v201309.cm.Keyword();
newKeyword.setText(keyword.getKeywordText());
newKeyword.setMatchType(KeywordMatchType.fromValue(keyword.getApiMatchTypeValue()));
// create ad group criterion
BiddableAdGroupCriterion adGroupCriterion = new BiddableAdGroupCriterion();
adGroupCriterion.setAdGroupId(keyword.getAdGroup().getApiId());
adGroupCriterion.setCriterion(newKeyword);
// update keyword status
if (keyword.getUserStatus() != null) {
UserStatus userStatus = UserStatus.fromValue(keyword.getUserStatusValue());
adGroupCriterion.setUserStatus(userStatus);
}
// update destination url
if (keyword.getDestinationUrl() != null) {
adGroupCriterion.setDestinationUrl(keyword.getDestinationUrl());
}
// update bid amount/max cpc
adGroupCriterion.setBiddingStrategyConfiguration(getBiddingStrategyConfiguration(keyword));
// create operation
AdGroupCriterionOperation operation = new AdGroupCriterionOperation();
operation.setOperand(adGroupCriterion);
operation.setOperator(Operator.ADD);
// add operation
operations.add(operation);
}
// submit changes now!
int triedTimes = 0;
while (triedTimes < Constants.API_CALL_TRY_TIMES) { // maximum 3 times
try {
AdGroupCriterionServiceInterface service = getAdWordsService(AdGroupCriterionServiceInterface.class);
AdGroupCriterionReturnValue response = service.mutate(operations.toArray(new AdGroupCriterionOperation[0]));
populateApiErrors(keywords, response.getPartialFailureErrors());
assignReturnedCriterionIds(keywords, response);
return response != null && updateAdParams(keywords);
} catch (Exception exception) {
triedTimes++;
if (handleApiCallException(keywords, exception, triedTimes))
break;
}
}
return false;
}
The maximum triedTimes is 3, populateApiErrors and handleApiCallException are the centralized methods to handle errors from both partial failures or thrown exceptions, the triedTimes to be passed into handleApiCallException which decides whether to abort or retry. Please check out following details:
protected void populateApiErrors(List<? extends ApiEntity> apiEntities, com.google.api.ads.adwords.axis.v201309.cm.ApiError[] apiErrors) {
if (apiErrors != null) {
for (com.google.api.ads.adwords.axis.v201309.cm.ApiError apiError : apiErrors) {
Matcher matcher = operationIndexPattern.matcher(apiError.getFieldPath());
if (matcher.matches()) {
int operationIndex = Integer.parseInt(matcher.group(1));
if (apiEntities.size() > operationIndex) { // fix a potential issue!!!
ApiEntity apiEntity = apiEntities.get(operationIndex);
apiEntity.getApiError(true).setItemIndex(operationIndex);
apiEntity.getApiError(true).setErrorType(apiError.getApiErrorType());
apiEntity.getApiError(true).appendErrorString(apiError.getErrorString());
}
} else {
for (ApiEntity apiEntity : apiEntities) {
apiEntity.getApiError(true).setItemIndex(ApiError.ALL_FAILED_INDEX);
apiEntity.getApiError(true).setErrorType(apiError.getApiErrorType());
apiEntity.getApiError(true).appendErrorString(apiError.getErrorString());
}
}
}
}
}
protected boolean handleApiCallException(List<? extends ApiEntity> apiEntities, Exception exception, int triedTimes) {
logger.debug("Got {} exception, tried {} times.", exception.getClass().getSimpleName(), triedTimes);
if (exception instanceof ApiException) {
ApiException apiException = (ApiException) exception;
// Try everything possible to recover from the error
for (com.google.api.ads.adwords.axis.v201309.cm.ApiError apiError : apiException.getErrors()) {
if (apiError instanceof AuthenticationError)
handleAuthenticationError((AuthenticationError) apiError);
else if (apiError instanceof RateExceededError)
handleRateExceededError((RateExceededError) apiError);
else if (apiError instanceof DatabaseError)
handleDatabaseError((DatabaseError) apiError);
else if (apiError instanceof CustomerSyncError)
triedTimes = handleCustomerSyncError((CustomerSyncError) apiError);
else if (apiError instanceof InternalApiError)
triedTimes = Constants.API_CALL_TRY_TIMES;
}
// Still failed, need to populate the errors
if (triedTimes == Constants.API_CALL_TRY_TIMES) {
populateApiErrors(apiEntities, apiException.getErrors());
return true; // no need to try again!
}
} else if (exception instanceof NullPointerException) {
populateDefaultException(apiEntities, exception);
return true; // no need to try again!
} else {
if (triedTimes == Constants.API_CALL_TRY_TIMES)
populateDefaultException(apiEntities, exception);
}
return false;
}
protected void handleAuthenticationError(AuthenticationError apiError) {
if (apiError.getReason() == AuthenticationErrorReason.GOOGLE_ACCOUNT_COOKIE_INVALID) {
try {
adWordsSession.getOAuth2Credential().refreshToken();
} catch (Exception e) {
// do nothing
}
} else {
logger.error("Service call failed for authentication reason: " + apiError.getReason());
}
}
protected void handleRateExceededError(RateExceededError error) {
logger.warn("A api call failed due to rate exceeded, will retry after {} seconds", Constants.API_CALL_WAIT_INTERVAL / 1000);
waitForNextAPICall();
}
protected void handleDatabaseError(DatabaseError error) {
if (error.getReason() == DatabaseErrorReason.CONCURRENT_MODIFICATION) {
logger.warn("A api call failed due to concurrent modification, will retry after {} seconds", Constants.API_CALL_WAIT_INTERVAL / 1000);
waitForNextAPICall();
}
}
protected int handleCustomerSyncError(CustomerSyncError error) {
return Constants.API_CALL_TRY_TIMES;
}
No comments:
Post a Comment