@Override
  public Distribution read(String origin, Distribution.Method method) {
    if (method.equals(Distribution.DOWNLOAD)
        || method.equals(Distribution.STREAMING)
        || method.equals(Distribution.CUSTOM)
        || method.equals(Distribution.WEBSITE_CDN)) {
      if (!distributionStatus.get(method).containsKey(origin)) {
        try {
          this.check();
          this.message(
              MessageFormat.format(
                  Locale.localizedString("Reading CDN configuration of {0}", "Status"), origin));

          this.cache(origin, method);
        } catch (CloudFrontServiceException e) {
          this.error("Cannot read CDN configuration", e);
        } catch (LoginCanceledException canceled) {
          // User canceled Cloudfront login. Possibly not enabled in Amazon configuration.
          distributionStatus
              .get(method)
              .put(
                  origin,
                  new Distribution(null, origin, method, false, null, canceled.getMessage()));
        } catch (IOException e) {
          this.error("Cannot read CDN configuration", e);
        }
      }
    }
    if (distributionStatus.get(method).containsKey(origin)) {
      return distributionStatus.get(method).get(origin);
    }
    return new Distribution(origin, method);
  }
  /**
   * Amazon CloudFront Extension used to list all configured distributions
   *
   * @param origin Name of the container
   * @param method Distribution method
   * @throws CloudFrontServiceException CloudFront failure details
   * @throws IOException Service error
   */
  private void cache(String origin, Distribution.Method method)
      throws IOException, CloudFrontServiceException {

    if (log.isDebugEnabled()) {
      log.debug(String.format("List distributions for origin %s", origin));
    }

    CloudFrontService cf = this.getClient();

    if (method.equals(Distribution.STREAMING)) {
      for (org.jets3t.service.model.cloudfront.Distribution d :
          cf.listStreamingDistributions(origin)) {
        for (Origin o : d.getConfig().getOrigins()) {
          if (o instanceof S3Origin) {
            // Write to cache
            distributionStatus.get(method).put(origin, this.convert(d, method));
            // We currently only support one distribution per bucket
            break;
          }
        }
      }
    } else if (method.equals(Distribution.DOWNLOAD)) {
      // List distributions restricting to bucket name origin
      for (org.jets3t.service.model.cloudfront.Distribution d : cf.listDistributions(origin)) {
        for (Origin o : d.getConfig().getOrigins()) {
          if (o instanceof S3Origin) {
            // Write to cache
            distributionStatus.get(method).put(origin, this.convert(d, method));
            // We currently only support one distribution per bucket
            break;
          }
        }
      }
    } else if (method.equals(Distribution.CUSTOM) || method.equals(Distribution.WEBSITE_CDN)) {
      for (org.jets3t.service.model.cloudfront.Distribution d : cf.listDistributions()) {
        for (Origin o : d.getConfig().getOrigins()) {
          // Listing all distributions and look for custom origin
          if (o instanceof CustomOrigin) {
            if (o.getDomainName().equals(origin)) {
              distributionStatus.get(method).put(origin, this.convert(d, method));
            }
          }
        }
      }
    }
  }
 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;
 }
  /**
   * Amazon CloudFront Extension used to enable or disable a distribution configuration and its
   * CNAMESs
   *
   * @param enabled Distribution status
   * @param method Distribution method
   * @param origin Name of the container
   * @param distributionId Distribution reference
   * @param cnames DNS CNAME aliases for distribution
   * @param logging Access log configuration
   * @param defaultRootObject Index file for distribution. Only supported for download and custom
   *     origins.
   * @throws CloudFrontServiceException CloudFront failure details
   * @throws IOException I/O error
   */
  private void updateDistribution(
      boolean enabled,
      Distribution.Method method,
      final String origin,
      final String distributionId,
      final String etag,
      final String reference,
      final String[] cnames,
      final LoggingStatus logging,
      final String defaultRootObject)
      throws CloudFrontServiceException, IOException {

    if (log.isDebugEnabled()) {
      log.debug(String.format("Update %s distribution with origin %s", method.toString(), origin));
    }

    final String originId = UUID.randomUUID().toString();
    final CacheBehavior cacheBehavior =
        new CacheBehavior(originId, false, null, CacheBehavior.ViewerProtocolPolicy.ALLOW_ALL, 0L);

    final CloudFrontService cf = this.getClient();
    if (method.equals(Distribution.STREAMING)) {
      StreamingDistributionConfig config =
          new StreamingDistributionConfig(
              new Origin[] {new S3Origin(originId, origin, null)},
              reference,
              cnames,
              null,
              enabled,
              logging,
              null);
      config.setEtag(etag);
      cf.updateDistributionConfig(distributionId, config);
    } else if (method.equals(Distribution.DOWNLOAD)) {
      DistributionConfig config =
          new DistributionConfig(
              new Origin[] {new S3Origin(originId, origin, null)},
              reference,
              cnames,
              null,
              enabled,
              logging,
              defaultRootObject,
              cacheBehavior,
              new CacheBehavior[] {});
      config.setEtag(etag);
      cf.updateDistributionConfig(distributionId, config);
    } else if (method.equals(Distribution.CUSTOM) || method.equals(Distribution.WEBSITE_CDN)) {
      DistributionConfig config =
          new DistributionConfig(
              new Origin[] {this.getCustomOriginConfiguration(originId, method, origin)},
              reference,
              cnames,
              null,
              enabled,
              logging,
              defaultRootObject,
              cacheBehavior,
              new CacheBehavior[] {});
      config.setEtag(etag);
      cf.updateDistributionConfig(distributionId, config);
    } else {
      throw new RuntimeException("Invalid distribution method:" + method);
    }
  }
  /**
   * Amazon CloudFront Extension to create a new distribution configuration *
   *
   * @param enabled Distribution status
   * @param method Distribution method
   * @param origin Name of the container
   * @param cnames DNS CNAME aliases for distribution
   * @param logging Access log configuration
   * @param defaultRootObject Index file for distribution. Only supported for download and custom
   *     origins.
   * @return Distribution configuration
   * @throws CloudFrontServiceException CloudFront failure details
   * @throws ConnectionCanceledException Authentication canceled
   */
  private org.jets3t.service.model.cloudfront.Distribution createDistribution(
      boolean enabled,
      Distribution.Method method,
      final String origin,
      String[] cnames,
      LoggingStatus logging,
      String defaultRootObject)
      throws ConnectionCanceledException, CloudFrontServiceException {

    final String reference = String.valueOf(System.currentTimeMillis());

    if (log.isDebugEnabled()) {
      log.debug(String.format("Create new %s distribution", method.toString()));
    }
    CloudFrontService cf = this.getClient();

    final String originId = UUID.randomUUID().toString();
    final CacheBehavior cacheBehavior =
        new CacheBehavior(originId, false, null, CacheBehavior.ViewerProtocolPolicy.ALLOW_ALL, 0L);

    if (method.equals(Distribution.STREAMING)) {
      final StreamingDistributionConfig config =
          new StreamingDistributionConfig(
              new S3Origin[] {new S3Origin(originId, origin, null)},
              reference,
              cnames,
              null,
              enabled,
              logging,
              null);
      return cf.createDistribution(config);
    }
    if (method.equals(Distribution.DOWNLOAD)) {
      DistributionConfig config =
          new DistributionConfig(
              new Origin[] {new S3Origin(originId, origin, null)},
              reference,
              cnames,
              null,
              enabled,
              logging,
              defaultRootObject,
              cacheBehavior,
              new CacheBehavior[] {});
      return cf.createDistribution(config);
    }
    if (method.equals(Distribution.CUSTOM) || method.equals(Distribution.WEBSITE_CDN)) {
      DistributionConfig config =
          new DistributionConfig(
              new Origin[] {
                new CustomOrigin(originId, origin, CustomOrigin.OriginProtocolPolicy.MATCH_VIEWER)
              },
              reference,
              cnames,
              null,
              enabled,
              logging,
              defaultRootObject,
              cacheBehavior,
              new CacheBehavior[] {});
      return cf.createDistribution(config);
    }
    throw new RuntimeException("Invalid distribution method:" + method);
  }
 @Override
 public boolean isLoggingSupported(Distribution.Method method) {
   return method.equals(Distribution.DOWNLOAD)
       || method.equals(Distribution.STREAMING)
       || method.equals(Distribution.CUSTOM);
 }
 @Override
 public boolean isInvalidationSupported(Distribution.Method method) {
   return method.equals(Distribution.DOWNLOAD)
       || method.equals(Distribution.WEBSITE_CDN)
       || method.equals(Distribution.CUSTOM);
 }