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