All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jclouds.compute.domain.internal.TemplateBuilderImpl Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.jclouds.compute.domain.internal;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.and;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.find;
import static com.google.common.collect.Iterables.size;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Iterables.tryFind;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
import static org.jclouds.compute.util.ComputeServiceUtils.getCoresAndSpeed;
import static org.jclouds.compute.util.ComputeServiceUtils.getSpace;

import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;

import org.jclouds.collect.Memoized;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.TemplateBuilderSpec;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.domain.Location;
import org.jclouds.logging.Logger;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Doubles;

/**
 * 
 * @author Adrian Cole
 */
public class TemplateBuilderImpl implements TemplateBuilder {
   @Resource
   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
   protected Logger logger = Logger.NULL;

   protected final Supplier> images;
   protected final Supplier> hardwares;
   protected final Supplier> locations;
   protected final Supplier defaultLocation;
   protected final Provider optionsProvider;
   protected final Provider defaultTemplateProvider;

   @VisibleForTesting
   protected Location location;
   @VisibleForTesting
   protected String imageId;
   @VisibleForTesting
   protected String hardwareId;
   @VisibleForTesting
   protected String hypervisor;
   @VisibleForTesting
   protected String imageVersion;
   @VisibleForTesting
   protected OsFamily osFamily;
   @VisibleForTesting
   protected String osVersion;
   @VisibleForTesting
   protected Boolean os64Bit;
   @VisibleForTesting
   protected String osName;
   @VisibleForTesting
   protected String osDescription;
   @VisibleForTesting
   protected String osArch;
   @VisibleForTesting
   protected String imageName;
   @VisibleForTesting
   protected String imageDescription;
   @VisibleForTesting
   protected Predicate imagePredicate;
   @VisibleForTesting
   protected Function, Image> imageChooser;
   @VisibleForTesting
   protected double minCores;
   @VisibleForTesting
   protected int minRam;
   @VisibleForTesting
   protected double minDisk;
   @VisibleForTesting
   protected boolean biggest;
   @VisibleForTesting
   protected boolean fastest;
   @VisibleForTesting
   protected TemplateOptions options;

   @Inject
   protected TemplateBuilderImpl(@Memoized Supplier> locations,
         @Memoized Supplier> images, @Memoized Supplier> hardwares,
         Supplier defaultLocation2, @Named("DEFAULT") Provider optionsProvider,
         @Named("DEFAULT") Provider defaultTemplateProvider) {
      this.locations = locations;
      this.images = images;
      this.hardwares = hardwares;
      this.defaultLocation = defaultLocation2;
      this.optionsProvider = optionsProvider;
      this.defaultTemplateProvider = defaultTemplateProvider;
   }

   static Predicate supportsImagesPredicate(final Iterable images) {
      return new Predicate() {
         @Override
         public boolean apply(final Hardware hardware) {
            return Iterables.any(images, new Predicate() {

               @Override
               public boolean apply(Image input) {
                  return hardware.supportsImage().apply(input);
               }

               @Override
               public String toString() {
                  return "hardware(" + hardware + ").supportsImage()";
               }

            });

         }

      };
   } 

   final Predicate locationPredicate = new NullEqualToIsParentOrIsGrandparentOfCurrentLocation(new Supplier() {

      @Override
      public Location get() {
         return location;
      }
      
   });

   private final Predicate idPredicate = new Predicate() {
      @Override
      public boolean apply(Image input) {
         boolean returnVal = true;
         if (imageId != null) {
            returnVal = imageId.equals(input.getId());
            // match our input params so that the later predicates pass.
            if (returnVal) {
               fromImage(input);
            }
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "imageId(" + imageId + ")";
      }
   };

   private final Predicate osFamilyPredicate = new Predicate() {

      @Override
      public boolean apply(OperatingSystem input) {
         boolean returnVal = true;
         if (osFamily != null)
            returnVal = osFamily.equals(input.getFamily());
         return returnVal;
      }

      @Override
      public String toString() {
         return "osFamily(" + osFamily + ")";
      }
   };

   private final Predicate osNamePredicate = new Predicate() {
      @Override
      public boolean apply(OperatingSystem input) {
         boolean returnVal = true;
         if (osName != null) {
            if (input.getName() == null)
               returnVal = false;
            else
               returnVal = input.getName().contains(osName) || input.getName().matches(osName);
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "osName(" + osName + ")";
      }
   };

   private final Predicate osDescriptionPredicate = new Predicate() {
      @Override
      public boolean apply(OperatingSystem input) {
         boolean returnVal = true;
         if (osDescription != null) {
            if (input.getDescription() == null)
               returnVal = false;
            else
               returnVal = input.getDescription().contains(osDescription)
                     || input.getDescription().matches(osDescription);
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "osDescription(" + osDescription + ")";
      }
   };

   private final Predicate osVersionPredicate = new Predicate() {
      @Override
      public boolean apply(OperatingSystem input) {
         boolean returnVal = true;
         if (osVersion != null) {
            if (input.getVersion() == null)
               returnVal = false;
            else
               returnVal = input.getVersion().contains(osVersion) || input.getVersion().matches(osVersion);
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "osVersion(" + osVersion + ")";
      }
   };

   private final Predicate os64BitPredicate = new Predicate() {
      @Override
      public boolean apply(OperatingSystem input) {
         boolean returnVal = true;
         if (os64Bit != null) {
            if (os64Bit)
               return input.is64Bit();
            else
               return !input.is64Bit();
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "os64Bit(" + os64Bit + ")";
      }
   };

   private final Predicate osArchPredicate = new Predicate() {
      @Override
      public boolean apply(OperatingSystem input) {
         boolean returnVal = true;
         if (osArch != null) {
            if (input.getArch() == null)
               returnVal = false;
            else
               returnVal = input.getArch().contains(osArch) || input.getArch().matches(osArch);
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "osArch(" + osArch + ")";
      }
   };

   private final Predicate imageVersionPredicate = new Predicate() {
      @Override
      public boolean apply(Image input) {
         boolean returnVal = true;
         if (imageVersion != null) {
            if (input.getVersion() == null)
               returnVal = false;
            else
               returnVal = input.getVersion().contains(imageVersion) || input.getVersion().matches(imageVersion);
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "imageVersion(" + imageVersion + ")";
      }
   };

   private final Predicate imageNamePredicate = new Predicate() {
      @Override
      public boolean apply(Image input) {
         boolean returnVal = true;
         if (imageName != null) {
            if (input.getName() == null)
               returnVal = false;
            else
               returnVal = input.getName().equals(imageName) || input.getName().contains(imageName)
                        || input.getName().matches(imageName);
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "imageName(" + imageName + ")";
      }
   };
   
   private final Predicate imageDescriptionPredicate = new Predicate() {
      @Override
      public boolean apply(Image input) {
         boolean returnVal = true;
         if (imageDescription != null) {
            if (input.getDescription() == null)
               returnVal = false;
            else
               returnVal = input.getDescription().equals(imageDescription)
                     || input.getDescription().contains(imageDescription)
                     || input.getDescription().matches(imageDescription);
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "imageDescription(" + imageDescription + ")";
      }
   };
   
   private final Predicate hardwareIdPredicate = new Predicate() {
      @Override
      public boolean apply(Hardware input) {
         boolean returnVal = true;
         if (hardwareId != null) {
            returnVal = hardwareId.equals(input.getId());
            // match our input params so that the later predicates pass.
            if (returnVal) {
               fromHardware(input);
            }
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "hardwareId(" + hardwareId + ")";
      }
   };
   
   private final Predicate hypervisorPredicate = new Predicate() {
      @Override
      public boolean apply(Hardware input) {
         boolean returnVal = true;
         if (hypervisor != null) {
            if (input.getHypervisor() == null)
               returnVal = false;
            else
               returnVal = input.getHypervisor().contains(hypervisor)
                     || input.getHypervisor().matches(hypervisor);
         }
         return returnVal;
      }

      @Override
      public String toString() {
         return "hypervisorMatches(" + hypervisor + ")";
      }
   };

   private final Predicate hardwareCoresPredicate = new Predicate() {
      @Override
      public boolean apply(Hardware input) {
         double cores = getCores(input);
         return cores >= TemplateBuilderImpl.this.minCores;
      }

      @Override
      public String toString() {
         return "minCores(" + minCores + ")";
      }
   };

   private final Predicate hardwareDiskPredicate = new Predicate() {
      @Override
      public boolean apply(Hardware input) {
         return getSpace(input) >= TemplateBuilderImpl.this.minDisk;
      }

      @Override
      public String toString() {
         return "minDisk(" + minDisk + ")";
      }
   };

   private final Predicate hardwareRamPredicate = new Predicate() {
      @Override
      public boolean apply(Hardware input) {
         return input.getRam() >= TemplateBuilderImpl.this.minRam;
      }

      @Override
      public String toString() {
         return "minRam(" + minRam + ")";
      }
   };

   private Predicate buildHardwarePredicate() {
      List> predicates = newArrayList();
      if (location != null)
         predicates.add(new Predicate() {

            @Override
            public boolean apply(Hardware input) {
               return locationPredicate.apply(input);
            }

            @Override
            public String toString() {
               return locationPredicate.toString();
            }
         });
      if (hypervisor != null)
         predicates.add(hypervisorPredicate);
      predicates.add(hardwareCoresPredicate);
      predicates.add(hardwareRamPredicate);
      predicates.add(hardwareDiskPredicate);

      // looks verbose, but explicit  type needed for this to compile
      // properly
      Predicate hardwarePredicate = predicates.size() == 1 ? Iterables.> get(predicates, 0)
            : Predicates. and(predicates);
      return hardwarePredicate;
   }
   
   static final Ordering DEFAULT_SIZE_ORDERING = new Ordering() {
      public int compare(Hardware left, Hardware right) {
         return ComparisonChain.start().compare(getCores(left), getCores(right)).compare(left.getRam(), right.getRam())
               .compare(getSpace(left), getSpace(right)).result();
      }
   };
   static final Ordering BY_CORES_ORDERING = new Ordering() {
      public int compare(Hardware left, Hardware right) {
         return Doubles.compare(getCoresAndSpeed(left), getCoresAndSpeed(right));
      }
   };
   static final Ordering DEFAULT_IMAGE_ORDERING = new Ordering() {
      public int compare(Image left, Image right) {
         /* This currently, and for some time, has *preferred* images whose fields are null,
          * and prefers those which come last alphabetically.
          * It seems preferable to take images whose fields are *not* null, ie nullsFirst;
          * and to use something like the AlphaNum Algorithm then take the last
          * (so "Ubuntu 13.04" would be preferred over "Ubuntu 9.10").
          * However not changing it now as people may be surprised if the images they get back start changing.
          */
         return ComparisonChain.start()
               .compare(left.getName(), right.getName(), Ordering. natural().nullsLast())
               .compare(left.getVersion(), right.getVersion(), Ordering. natural().nullsLast())
               .compare(left.getDescription(), right.getDescription(), Ordering. natural().nullsLast())
               .compare(left.getOperatingSystem().getName(), right.getOperatingSystem().getName(),//
                     Ordering. natural().nullsLast())
               .compare(left.getOperatingSystem().getVersion(), right.getOperatingSystem().getVersion(),//
                     Ordering. natural().nullsLast())
               .compare(left.getOperatingSystem().getDescription(), right.getOperatingSystem().getDescription(),//
                     Ordering. natural().nullsLast())
               .compare(left.getOperatingSystem().getArch(), right.getOperatingSystem().getArch(),//
                     Ordering. natural().nullsLast()).result();
      }
   };

   @VisibleForTesting
   // non-static for logging
   final Function, Image> imageChooserFromOrdering(final Ordering ordering) {
      return new Function, Image>() {
         @Override
         public Image apply(Iterable input) {
            List maxImages = multiMax(ordering, input);
            if (logger.isTraceEnabled())
               logger.trace("<<   best images(%s)", transform(maxImages, imageToId));
            return maxImages.get(maxImages.size() - 1);
         }
      };
   }

   @VisibleForTesting
   Function, Image> defaultImageChooser() {
       return imageChooserFromOrdering(DEFAULT_IMAGE_ORDERING);
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder fromTemplate(Template template) {
      location = template.getLocation();
      fromHardware(template.getHardware());
      fromImage(template.getImage());
      options(template.getOptions());
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder fromHardware(Hardware hardware) {
      if (currentLocationWiderThan(hardware.getLocation()))
         this.location = hardware.getLocation();
      this.minCores = getCores(hardware);
      this.minRam = hardware.getRam();
      this.minDisk = getSpace(hardware);
      this.hypervisor = hardware.getHypervisor();
      return this;
   }

   private boolean currentLocationWiderThan(Location location) {
      return this.location == null || (location != null && this.location.getScope().compareTo(location.getScope()) < 0);
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder fromImage(Image image) {
      if (currentLocationWiderThan(image.getLocation()))
         this.location = image.getLocation();
      if (image.getOperatingSystem().getFamily() != null)
         this.osFamily = image.getOperatingSystem().getFamily();
      if (image.getName() != null)
         this.imageName = image.getName();
      if (image.getDescription() != null)
         this.imageDescription = String.format("^%s$", Pattern.quote(image.getDescription()));
      if (image.getOperatingSystem().getName() != null)
         this.osName = image.getOperatingSystem().getName();
      if (image.getOperatingSystem().getDescription() != null)
         this.osDescription = image.getOperatingSystem().getDescription();
      if (image.getVersion() != null)
         this.imageVersion = String.format("^%s$", Pattern.quote(image.getVersion()));
      if (image.getOperatingSystem().getVersion() != null)
         this.osVersion = image.getOperatingSystem().getVersion();
      this.os64Bit = image.getOperatingSystem().is64Bit();
      if (image.getOperatingSystem().getArch() != null)
         this.osArch = image.getOperatingSystem().getArch();
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder smallest() {
      this.biggest = false;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder biggest() {
      this.biggest = true;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder fastest() {
      this.fastest = true;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder locationId(final String locationId) {
      Set locations = this.locations.get();
      try {
         this.location = find(locations, new Predicate() {

            @Override
            public boolean apply(Location input) {
               return input.getId().equals(locationId);
            }

            @Override
            public String toString() {
               return "locationId(" + locationId + ")";
            }

         });
      } catch (NoSuchElementException e) {
         throw new NoSuchElementException(format("location id %s not found in: %s", locationId, locations));
      }
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder osFamily(OsFamily os) {
      this.osFamily = os;
      return this;
   }

   private static final Function imageToId = new Function() {

      @Override
      public String apply(Image arg0) {
         return arg0.getId();
      }
      
   };
   

   private static final Function hardwareToId = new Function() {

      @Override
      public String apply(Hardware arg0) {
         return arg0.getId();
      }
      
   };
   /**
    * {@inheritDoc}
    */
   @Override
   public Template build() {
      if (nothingChangedExceptOptions()) {
         TemplateBuilder defaultTemplate = defaultTemplateProvider.get();
         if (options != null)
            defaultTemplate.options(options);
         return defaultTemplate.build();
      }

      if (options == null)
         options = optionsProvider.get();
      logger.debug(">> searching params(%s)", this);
      Set images = getImages();
      checkState(images.size() > 0, "no images present!");
      Set hardwaresToSearch = hardwares.get();
      checkState(hardwaresToSearch.size() > 0, "no hardware profiles present!");

      Image image = null;
      if (imageId != null) {
         image = findImageWithId(images);
         if (currentLocationWiderThan(image.getLocation()))
            this.location = image.getLocation();
      }
      
      Hardware hardware = null;
      if (hardwareId != null) {
         hardware = findHardwareWithId(hardwaresToSearch);
         if (currentLocationWiderThan(hardware.getLocation()))
            this.location = hardware.getLocation();
      }
      
      // if the user hasn't specified a location id, or an image or hardware
      // with location, let's search scoped to the implicit one
      if (location == null)
         location = defaultLocation.get();
      
      if (image == null) {
         Iterable supportedImages = findSupportedImages(images);
         if (hardware == null)
            hardware = resolveHardware(hardwaresToSearch, supportedImages);
         image = resolveImage(hardware, supportedImages);
      } else {
         if (hardware == null)
            hardware = resolveHardware(hardwaresToSearch, ImmutableSet.of(image));
      }

      logger.debug("<<   matched image(%s) hardware(%s) location(%s)", image.getId(), hardware.getId(),
            location.getId());
      return new TemplateImpl(image, hardware, location, options);
   }

   private Iterable findSupportedImages(Set images) {
      Predicate imagePredicate = buildImagePredicate();
      Iterable supportedImages = filter(images, imagePredicate);
      if (size(supportedImages) == 0) {
         throw throwNoSuchElementExceptionAfterLoggingImageIds(
               format("no image matched predicate: %s", imagePredicate), images);
      }
      return supportedImages;
   }

   private Image findImageWithId(Set images) {
      Image image;
      // TODO: switch to GetImageStrategy in version 1.5
      image = tryFind(images, idPredicate).orNull();
      if (image == null)
         throwNoSuchElementExceptionAfterLoggingImageIds(format("%s not found", idPredicate), images);
      return image;
   }

   private Hardware findHardwareWithId(Set hardwaresToSearch) {
      Hardware hardware;
      // TODO: switch to GetHardwareStrategy in version 1.5
      hardware = tryFind(hardwaresToSearch, hardwareIdPredicate).orNull();
      if (hardware == null)
         throw throwNoSuchElementExceptionAfterLoggingHardwareIds(format("%s not found", hardwareIdPredicate),
               hardwaresToSearch);
      return hardware;
   }

   protected NoSuchElementException throwNoSuchElementExceptionAfterLoggingImageIds(String message, Iterable images) {
      NoSuchElementException exception = new NoSuchElementException(message);
      if (logger.isTraceEnabled())
         logger.warn(exception, "image ids that didn't match: %s", transform(images, imageToId));
      throw exception;
   }

   protected NoSuchElementException throwNoSuchElementExceptionAfterLoggingHardwareIds(String message, Iterable hardwares) {
      NoSuchElementException exception = new NoSuchElementException(message);
      if (logger.isTraceEnabled())
         logger.warn(exception, "hardware ids that didn't match: %s", transform(hardwares, hardwareToId));
      throw exception;
   }

   protected Hardware resolveHardware(Set hardwarel, final Iterable images) {
      Ordering hardwareOrdering = hardwareSorter();
      
      Iterable> supportsImagePredicates = Iterables.transform(hardwarel,
               new Function>() {

                  @Override
                  public Predicate apply(Hardware input) {
                     return input.supportsImage();
                  }

               });
      
      Predicate supportsImagePredicate = Iterables.size(supportsImagePredicates) == 1 ? Iterables
               .getOnlyElement(supportsImagePredicates) : Predicates.or(supportsImagePredicates);

      if (!Iterables.any(images, supportsImagePredicate)) {
         String message = format("no hardware profiles support images matching params: %s", supportsImagePredicate);
         throw throwNoSuchElementExceptionAfterLoggingHardwareIds(message, hardwarel);
      }

      Iterable hardwareCompatibleWithOurImages = filter(hardwarel, supportsImagesPredicate(images));
      Predicate hardwarePredicate = buildHardwarePredicate();
      Hardware hardware;
      try {
         hardware = hardwareOrdering.max(filter(hardwareCompatibleWithOurImages, hardwarePredicate));
      } catch (NoSuchElementException exception) {
         String message = format("no hardware profiles match params: %s", hardwarePredicate);
         throw throwNoSuchElementExceptionAfterLoggingHardwareIds(message, hardwareCompatibleWithOurImages);
      }
      logger.trace("<<   matched hardware(%s)", hardware.getId());
      return hardware;
   }

   protected Function, Image> imageChooser() {
      if (imageChooser != null) return imageChooser;
      return defaultImageChooser();
   }

   protected Ordering hardwareSorter() {
      Ordering hardwareOrdering = DEFAULT_SIZE_ORDERING;
      if (!biggest)
         hardwareOrdering = hardwareOrdering.reverse();
      if (fastest)
         hardwareOrdering = Ordering.compound(ImmutableList.of(BY_CORES_ORDERING, hardwareOrdering));
      return hardwareOrdering;
   }

   /**
    * 
    * @param hardware
    * @param supportedImages
    * @throws NoSuchElementException
    *            if there's no image that matches the predicate
    */
   protected Image resolveImage(final Hardware hardware, Iterable supportedImages) {
      Predicate imagePredicate = new Predicate() {

         @Override
         public boolean apply(Image arg0) {
            return hardware.supportsImage().apply(arg0);
         }

         @Override
         public String toString() {
            return "hardware(" + hardware + ").supportsImage()";
         }
      };

      try {
         Iterable matchingImages = filter(supportedImages, imagePredicate);
         if (logger.isTraceEnabled())
            logger.trace("<<   matched images(%s)", transform(matchingImages, imageToId));
         return imageChooser().apply(matchingImages);
      } catch (NoSuchElementException exception) {
         throwNoSuchElementExceptionAfterLoggingImageIds(format("no image matched params: %s", toString()),
                  supportedImages);
         assert false;
         return null;
      }
   }
   
   /**
    * Like Ordering, but handle the case where there are multiple valid maximums
    */
   @SuppressWarnings("unchecked")
   @VisibleForTesting
   static  List multiMax(Comparator ordering, Iterable iterable) {
      Iterator iterator = iterable.iterator();
      List maxes = newArrayList(iterator.next());
      E maxSoFar = maxes.get(0);
      while (iterator.hasNext()) {
         E current = iterator.next();
         int comparison = ordering.compare(maxSoFar, current);
         if (comparison == 0) {
            maxes.add(current);
         } else if (comparison < 0) {
            maxes = newArrayList(current);
            maxSoFar = current;
         }
      }
      return maxes;
   }
   protected Set getImages() {
      return images.get();
   }

   private Predicate buildImagePredicate() {
      List> predicates = newArrayList();
      if (location != null)
         predicates.add(new Predicate() {

            @Override
            public boolean apply(Image input) {
               return locationPredicate.apply(input);
            }

            @Override
            public String toString() {
               return locationPredicate.toString();
            }
         });

      final List> osPredicates = newArrayList();
      if (osFamily != null)
         osPredicates.add(osFamilyPredicate);
      if (osName != null)
         osPredicates.add(osNamePredicate);
      if (osDescription != null)
         osPredicates.add(osDescriptionPredicate);
      if (osVersion != null)
         osPredicates.add(osVersionPredicate);
      if (os64Bit != null)
         osPredicates.add(os64BitPredicate);
      if (osArch != null)
         osPredicates.add(osArchPredicate);
      if (osPredicates.size() > 0)
         predicates.add(new Predicate() {

            @Override
            public boolean apply(Image input) {
               return and(osPredicates).apply(input.getOperatingSystem());
            }

            @Override
            public String toString() {
               return and(osPredicates).toString();
            }

         });
      if (imageVersion != null)
         predicates.add(imageVersionPredicate);
      if (imageName != null)
         predicates.add(imageNamePredicate);
      if (imageDescription != null)
         predicates.add(imageDescriptionPredicate);
      if (imagePredicate != null)
         predicates.add(imagePredicate);

      // looks verbose, but explicit  type needed for this to compile
      // properly
      Predicate imagePredicate = predicates.size() == 1 ? Iterables.> get(predicates, 0)
            : Predicates. and(predicates);
      return imagePredicate;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder imageId(String imageId) {
      this.imageId = imageId;
      this.imageName = null;
      this.imageDescription = null;
      this.imagePredicate = null;
      this.imageVersion = null;
      this.osFamily = null;
      this.osName = null;
      this.osDescription = null;
      this.osVersion = null;
      this.os64Bit = null;
      this.osArch = null;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder imageNameMatches(String nameRegex) {
      this.imageName = nameRegex;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder imageDescriptionMatches(String descriptionRegex) {
      this.imageDescription = descriptionRegex;
      return this;
   }
   
   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder imageMatches(Predicate condition) {
      this.imagePredicate = condition;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilderImpl imageChooser(Function, Image> imageChooser) {
      this.imageChooser = imageChooser;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder imageVersionMatches(String imageVersionRegex) {
      this.imageVersion = imageVersionRegex;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder osVersionMatches(String osVersionRegex) {
      this.osVersion = osVersionRegex;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder osArchMatches(String osArchitectureRegex) {
      this.osArch = osArchitectureRegex;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder minCores(double minCores) {
      this.minCores = minCores;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder minRam(int megabytes) {
      this.minRam = megabytes;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder minDisk(double gigabytes) {
      this.minDisk = gigabytes;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder osNameMatches(String osNameRegex) {
      this.osName = osNameRegex;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder osDescriptionMatches(String osDescriptionRegex) {
      this.osDescription = osDescriptionRegex;
      return this;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder hardwareId(String hardwareId) {
      this.hardwareId = hardwareId;
      this.hypervisor = null;
      return this;
   }
   
   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder hypervisorMatches(String hypervisor) {
      this.hypervisor = hypervisor;
      return this;
   }
   
   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder options(TemplateOptions options) {
      this.options = optionsProvider.get();
      checkNotNull(options, "options").copyTo(this.options);
      return this;
   }

   @VisibleForTesting
   boolean nothingChangedExceptOptions() {
      return osFamily == null && location == null && imageId == null && hardwareId == null && hypervisor == null
            && osName == null && imagePredicate == null && imageChooser == null && osDescription == null 
            && imageVersion == null && osVersion == null && osArch == null && os64Bit == null && imageName == null 
            && imageDescription == null && minCores == 0 && minRam == 0 && minDisk == 0 && !biggest && !fastest;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public TemplateBuilder any() {
      return defaultTemplateProvider.get();
   }

   @Override
   public String toString() {
      return string().toString();
   }

   /**
    * @since 1.5
    */
   protected ToStringHelper string() {
      ToStringHelper toString = Objects.toStringHelper("").omitNullValues();
      if (biggest)
         toString.add("biggest", biggest);
      if (fastest)
         toString.add("fastest", fastest);
      toString.add("imageName", imageName);
      toString.add("imageDescription", imageDescription);
      toString.add("imageId", imageId);
      toString.add("imagePredicate", imagePredicate);
      toString.add("imageChooser", imageChooser);
      toString.add("imageVersion", imageVersion);
      if (location != null)
         toString.add("locationId", location.getId());
      if (minCores > 0) //TODO: make non-primitive
         toString.add("minCores", minCores);
      if (minRam > 0) //TODO: make non-primitive
         toString.add("minRam", minRam);
      if (minRam > 0) //TODO: make non-primitive
         toString.add("minRam", minRam);
      if (minDisk > 0) //TODO: make non-primitive
         toString.add("minDisk", minDisk);
      toString.add("osFamily", osFamily);
      toString.add("osName", osName);
      toString.add("osDescription", osDescription);
      toString.add("osVersion", osVersion);
      toString.add("osArch", osArch);
      toString.add("os64Bit", os64Bit);
      toString.add("hardwareId", hardwareId);
      toString.add("hypervisor", hypervisor);
      return toString;
   }

   @Override
   public TemplateBuilder os64Bit(boolean is64Bit) {
      this.os64Bit = is64Bit;
      return this;
   }

   @Override
   public TemplateBuilder from(TemplateBuilderSpec spec) {
      return spec.copyTo(this, options != null ? options : (options = optionsProvider.get()));
   }

   @Override
   public TemplateBuilder from(String spec) {
      return from(TemplateBuilderSpec.parse(spec));
   }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy