content: /**
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.aurora.scheduler.cron;
  
  import java.util.Objects;
  import java.util.Optional;
  
  import javax.annotation.Nullable;
  
  import com.google.common.annotations.VisibleForTesting;
  import com.google.common.base.MoreObjects;
  import com.google.common.base.Strings;
  
  import org.apache.aurora.gen.CronCollisionPolicy;
  import org.apache.aurora.scheduler.configuration.ConfigurationManager;
  import org.apache.aurora.scheduler.configuration.SanitizedConfiguration;
  import org.apache.aurora.scheduler.storage.entities.IJobConfiguration;
  
  import static java.util.Objects.requireNonNull;
  
  /**
   * Used by functions that expect field validation before being called.
   */
  public final class SanitizedCronJob {
    @VisibleForTesting
    public static final String NO_CRON_SCHEDULE = "Job has an empty cron schedule.";
  
    private final SanitizedConfiguration config;
    private final CrontabEntry crontabEntry;
  
    private SanitizedCronJob(ConfigurationManager configurationManager, IJobConfiguration unsanitized)
        throws CronException, ConfigurationManager.TaskDescriptionException {
  
      this(SanitizedConfiguration.fromUnsanitized(configurationManager, unsanitized));
    }
  
    private SanitizedCronJob(SanitizedConfiguration config) throws CronException {
      final IJobConfiguration job = config.getJobConfig();
      if (!hasCronSchedule(job)) {
        throw new CronException(NO_CRON_SCHEDULE);
      }
  
      Optional<CrontabEntry> entry = CrontabEntry.tryParse(job.getCronSchedule());
      if (!entry.isPresent()) {
        throw new CronException("Invalid cron schedule: " + job.getCronSchedule());
      }
  
      this.config = config;
      this.crontabEntry = entry.get();
    }
  
    /**
     * Get the default cron collision policy.
     *
     * @param policy A (possibly null) policy.
     * @return The given policy or a default if the policy was null.
     */
    public static CronCollisionPolicy orDefault(@Nullable CronCollisionPolicy policy) {
      return Optional.ofNullable(policy).orElse(CronCollisionPolicy.KILL_EXISTING);
    }
  
    /**
     * Create a SanitizedCronJob from a SanitizedConfiguration. SanitizedCronJob performs additional
     * validation to ensure that the provided job contains all properties needed to run it on a
     * cron schedule.
     *
     * @param config Config to validate.
     * @return Config wrapped in defaults.
     * @throws CronException If a cron-specific validation error occured.
     */
    public static SanitizedCronJob from(SanitizedConfiguration config)
        throws CronException {
  
      return new SanitizedCronJob(config);
    }
  
    /**
     * Create a cron job from an unsanitized input job. Suitable for RPC input validation.
     *
     * @param unsanitized Unsanitized input job.
     * @return A sanitized job if all validation succeeds.
     * @throws CronException If validation fails with a cron-specific error.
     * @throws ConfigurationManager.TaskDescriptionException If validation fails with a non
     * cron-specific error.
     */
    public static SanitizedCronJob fromUnsanitized(
        ConfigurationManager configurationManager,
        IJobConfiguration unsanitized)
        throws CronException, ConfigurationManager.TaskDescriptionException {
  
      return new SanitizedCronJob(configurationManager, unsanitized);
    }
  
    /**
     * Get this job's cron collision policy.
     *
     * @return This job's cron collision policy.
     */
    public CronCollisionPolicy getCronCollisionPolicy() {
      return orDefault(config.getJobConfig().getCronCollisionPolicy());
    }
  
    private static boolean hasCronSchedule(IJobConfiguration job) {
      requireNonNull(job);
      return !Strings.isNullOrEmpty(job.getCronSchedule());
    }
  
    /**
     * Returns the cron schedule associated with this job.
     *
     * @return The cron schedule associated with this job.
     */
    public CrontabEntry getCrontabEntry() {
      return crontabEntry;
    }
  
    /**
     * Returns the sanitized job configuration associated with the cron job.
     *
     * @return This cron job's sanitized job configuration.
     */
    public SanitizedConfiguration getSanitizedConfig() {
      return config;
    }
  
    @Override
    public boolean equals(Object o) {
      if (!(o instanceof SanitizedCronJob)) {
        return false;
      }
  
      SanitizedCronJob other = (SanitizedCronJob) o;
  
      return Objects.equals(config, other.config) && Objects.equals(crontabEntry, other.crontabEntry);
    }
  
    @Override
    public int hashCode() {
      return Objects.hash(config, crontabEntry);
    }
  
    @Override
    public String toString() {
      return MoreObjects.toStringHelper(this)
          .add("config", config)
          .add("crontabEntry", crontabEntry)
          .toString();
    }
  }
  