/**
   * You can make any number of invalidation requests, but you can have only three invalidation
   * requests in progress at one time. Each request can contain up to 1,000 objects to invalidate.
   * If you exceed these limits, you get an error message.
   *
   * <p>It usually takes 10 to 15 minutes to complete your invalidation request, depending on the
   * size of your request.
   *
   * @param origin Origin server
   * @param method Distribution method
   * @param files Files to purge
   * @param recursive Recursivly for folders
   */
  @Override
  public void invalidate(
      String origin, Distribution.Method method, List<Path> files, boolean recursive) {
    try {
      this.check();
      this.message(
          MessageFormat.format(
              Locale.localizedString("Writing CDN configuration of {0}", "Status"), origin));

      final long reference = System.currentTimeMillis();
      Distribution d = distributionStatus.get(method).get(origin);
      if (null == d) {
        log.error(String.format("No cached distribution for origin %s", origin));
        return;
      }
      List<String> keys = this.getInvalidationKeys(files, recursive);
      if (keys.isEmpty()) {
        log.warn("No keys selected for invalidation");
        return;
      }
      CloudFrontService cf = this.getClient();
      cf.invalidateObjects(
          d.getId(),
          keys.toArray(new String[keys.size()]), // objects
          new Date(reference).toString() // Comment
          );
    } catch (CloudFrontServiceException e) {
      this.error("Cannot write CDN configuration", e);
    } catch (IOException e) {
      this.error("Cannot write CDN configuration", e);
    } finally {
      distributionStatus.get(method).clear();
    }
  }
 /**
  * @param distribution Configuration
  * @return Status message from service
  * @throws IOException Service error
  */
 private String readInvalidationStatus(Distribution distribution) throws IOException {
   try {
     final CloudFrontService cf = this.getClient();
     boolean complete = false;
     int inprogress = 0;
     List<InvalidationSummary> summaries = cf.listInvalidations(distribution.getId());
     for (InvalidationSummary s : summaries) {
       if ("Completed".equals(s.getStatus())) {
         // No schema for status enumeration. Fail.
         complete = true;
       } else {
         // InProgress
         inprogress++;
       }
     }
     if (inprogress > 0) {
       return MessageFormat.format(
           Locale.localizedString("{0} invalidations in progress", "S3"), inprogress);
     }
     if (complete) {
       return MessageFormat.format(
           Locale.localizedString("{0} invalidations completed", "S3"), summaries.size());
     }
     return Locale.localizedString("None");
   } catch (CloudFrontServiceException e) {
     this.error("Cannot read CDN configuration", e);
   }
   return Locale.localizedString("Unknown");
 }
 private Distribution convert(
     final org.jets3t.service.model.cloudfront.Distribution d, Distribution.Method method)
     throws IOException, CloudFrontServiceException {
   // Retrieve distributions configuration to access current logging status settings.
   final DistributionConfig distributionConfig = this.getDistributionConfig(d);
   final String loggingTarget;
   if (null == distributionConfig.getLoggingStatus()) {
     // Default logging target to origin itself
     loggingTarget =
         ServiceUtils.findBucketNameInHostname(
             d.getConfig().getOrigin().getDomainName(), Protocol.S3_SSL.getDefaultHostname());
   } else {
     loggingTarget =
         ServiceUtils.findBucketNameInHostname(
             distributionConfig.getLoggingStatus().getBucket(),
             Protocol.S3_SSL.getDefaultHostname());
   }
   final Distribution distribution =
       new Distribution(
           d.getId(),
           distributionConfig.getEtag(),
           distributionConfig.getCallerReference(),
           d.getConfig().getOrigin().getDomainName(),
           method,
           d.getConfig().isEnabled(),
           d.isDeployed(),
           // CloudFront URL
           String.format("%s://%s%s", method.getScheme(), d.getDomainName(), method.getContext()),
           method.equals(Distribution.DOWNLOAD) || method.equals(Distribution.CUSTOM)
               ? String.format("https://%s%s", d.getDomainName(), method.getContext())
               : null, // No SSL
           null,
           Locale.localizedString(d.getStatus(), "S3"),
           distributionConfig.getCNAMEs(),
           distributionConfig.getLoggingStatus().isEnabled(),
           loggingTarget,
           distributionConfig.getDefaultRootObject());
   if (this.isInvalidationSupported(method)) {
     distribution.setInvalidationStatus(this.readInvalidationStatus(distribution));
   }
   if (this.isLoggingSupported(method)) {
     distribution.setContainers(this.getContainers());
   }
   return distribution;
 }
  @Override
  public void write(
      boolean enabled,
      String origin,
      Distribution.Method method,
      String[] cnames,
      boolean logging,
      String loggingBucket,
      String defaultRootObject) {
    try {
      this.check();

      // Configure CDN
      LoggingStatus loggingStatus = null;
      if (logging) {
        if (this.isLoggingSupported(method)) {
          final String loggingDestination =
              StringUtils.isNotBlank(loggingBucket)
                  ? ServiceUtils.generateS3HostnameForBucket(
                      loggingBucket, false, Protocol.S3_SSL.getDefaultHostname())
                  : origin;
          loggingStatus =
              new LoggingStatus(
                  loggingDestination,
                  Preferences.instance().getProperty("cloudfront.logging.prefix"));
        }
      }
      StringBuilder name =
          new StringBuilder(Locale.localizedString("Amazon CloudFront", "S3"))
              .append(" ")
              .append(method.toString());
      if (enabled) {
        this.message(
            MessageFormat.format(
                Locale.localizedString("Enable {0} Distribution", "Status"), name));
      } else {
        this.message(
            MessageFormat.format(
                Locale.localizedString("Disable {0} Distribution", "Status"), name));
      }
      Distribution d = distributionStatus.get(method).get(origin);
      if (null == d) {
        if (log.isDebugEnabled()) {
          log.debug(String.format("No existing distribution found for method %s", method));
        }
        this.createDistribution(enabled, method, origin, cnames, loggingStatus, defaultRootObject);
      } else {
        boolean modified = false;
        if (d.isEnabled() != enabled) {
          modified = true;
        }
        if (!Arrays.equals(d.getCNAMEs(), cnames)) {
          modified = true;
        }
        if (d.isLogging() != logging) {
          modified = true;
        }
        // Compare default root object for possible change
        if (!StringUtils.equals(d.getDefaultRootObject(), defaultRootObject)) {
          modified = true;
        }
        // Compare logging target for possible change
        if (!StringUtils.equals(d.getLoggingTarget(), loggingBucket)) {
          modified = true;
        }
        if (modified) {
          this.updateDistribution(
              enabled,
              method,
              origin,
              d.getId(),
              d.getEtag(),
              d.getReference(),
              cnames,
              loggingStatus,
              defaultRootObject);
        } else {
          log.info("Skip updating distribution not modified.");
        }
      }
    } catch (CloudFrontServiceException e) {
      this.error("Cannot write CDN configuration", e);
    } catch (IOException e) {
      this.error("Cannot write CDN configuration", e);
    } finally {
      distributionStatus.get(method).clear();
    }
  }