package azkaban.jobcallback; import static azkaban.jobcallback.JobCallbackConstants.DEFAULT_POST_BODY_LENGTH; import static azkaban.jobcallback.JobCallbackConstants.HTTP_GET; import static azkaban.jobcallback.JobCallbackConstants.HTTP_POST; import static azkaban.jobcallback.JobCallbackConstants.JOB_CALLBACK_BODY_TEMPLATE; import static azkaban.jobcallback.JobCallbackConstants.JOB_CALLBACK_REQUEST_METHOD_TEMPLATE; import static azkaban.jobcallback.JobCallbackConstants.JOB_CALLBACK_URL_TEMPLATE; import static azkaban.jobcallback.JobCallbackConstants.MAX_POST_BODY_LENGTH_PROPERTY_KEY; import static azkaban.jobcallback.JobCallbackConstants.SEQUENCE_TOKEN; import static azkaban.jobcallback.JobCallbackConstants.STATUS_TOKEN; import java.util.Collection; import org.apache.log4j.Logger; import azkaban.utils.Props; /** * Responsible for validating the job callback related properties at project * upload time * * @author hluu * */ public class JobCallbackValidator { private static final Logger logger = Logger .getLogger(JobCallbackValidator.class); /** * Make sure all the job callback related properties are valid * * @param jobProps * @param error * @return number of valid job callback properties. Mainly for testing * purpose. */ public static int validate(String jobName, Props serverProps, Props jobProps, Collection<String> errors) { int maxNumCallback = serverProps.getInt( JobCallbackConstants.MAX_CALLBACK_COUNT_PROPERTY_KEY, JobCallbackConstants.DEFAULT_MAX_CALLBACK_COUNT); int maxPostBodyLength = serverProps.getInt(MAX_POST_BODY_LENGTH_PROPERTY_KEY, DEFAULT_POST_BODY_LENGTH); int totalCallbackCount = 0; for (JobCallbackStatusEnum jobStatus : JobCallbackStatusEnum.values()) { totalCallbackCount += validateBasedOnStatus(jobProps, errors, jobStatus, maxNumCallback, maxPostBodyLength); } if (logger.isDebugEnabled()) { logger.debug("Found " + totalCallbackCount + " job callbacks for job " + jobName); } return totalCallbackCount; } private static int validateBasedOnStatus(Props jobProps, Collection<String> errors, JobCallbackStatusEnum jobStatus, int maxNumCallback, int maxPostBodyLength) { int callbackCount = 0; // replace property templates with status String jobCallBackUrl = JOB_CALLBACK_URL_TEMPLATE.replaceFirst(STATUS_TOKEN, jobStatus.name() .toLowerCase()); String requestMethod = JOB_CALLBACK_REQUEST_METHOD_TEMPLATE.replaceFirst(STATUS_TOKEN, jobStatus.name().toLowerCase()); String httpBody = JOB_CALLBACK_BODY_TEMPLATE.replaceFirst(STATUS_TOKEN, jobStatus.name() .toLowerCase()); for (int i = 0; i <= maxNumCallback; i++) { // callback url String callbackUrlKey = jobCallBackUrl.replaceFirst(SEQUENCE_TOKEN, Integer.toString(i)); String callbackUrlValue = jobProps.get(callbackUrlKey); // sequence number should start at 1, this is to check for sequence // number that starts a 0 if (i == 0) { if (callbackUrlValue != null) { errors.add("Sequence number starts at 1, not 0"); } continue; } if (callbackUrlValue == null || callbackUrlValue.length() == 0) { break; } else { String requestMethodKey = requestMethod.replaceFirst(SEQUENCE_TOKEN, Integer.toString(i)); String methodValue = jobProps.getString(requestMethodKey, HTTP_GET); if (HTTP_POST.equals(methodValue)) { // now try to get the post body String postBodyKey = httpBody.replaceFirst(SEQUENCE_TOKEN, Integer.toString(i)); String postBodyValue = jobProps.get(postBodyKey); if (postBodyValue == null || postBodyValue.length() == 0) { errors.add("No POST body was specified for job callback '" + callbackUrlValue + "'"); } else if (postBodyValue.length() > maxPostBodyLength) { errors.add("POST body length is : " + postBodyValue.length() + " which is larger than supported length of " + maxPostBodyLength); } else { callbackCount++; } } else if (HTTP_GET.equals(methodValue)) { // that's cool callbackCount++; } else { errors.add("Unsupported request method: " + methodValue + " Only POST and GET are supported"); } } } return callbackCount; } }