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

dev.galasa.framework.spi.FrameworkResourcePoolingService Maven / Gradle / Ivy

There is a newer version: 0.37.0
Show newest version
/*
 * Copyright contributors to the Galasa project
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package dev.galasa.framework.spi;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;

import javax.validation.constraints.NotNull;

import dev.galasa.framework.internal.rps.ResourceString;

/**
 * This class provides the Resource pooling service to the framework. It can
 * generate resources to a number of specifications. When params are not given,
 * defaults are used.
 * 
 *  
 */
public class FrameworkResourcePoolingService implements IResourcePoolingService {
    private static final int           DEFAULTNUMBEROFRESOURCES    = 10;
    private static final int           DEFAULTCONSECUTIVERESOURCES = 1;
    private IDynamicStatusStoreService defaultDss                  = new StubbedDss();
    private String                     defaultKeyPrefix            = "";
    private Random                     random                      = new Random();

    /**
     * This method obtaines resources from given definitions, but allowing rejected
     * resources to be disgarded from any returned list.
     * 
     * The default return list length is 10.
     * 
     * @param resourceStrings   - this is a string list of all the definitions for
     *                          resources to select from.
     * @param rejectedResources - this is a list of resource names NOT to be
     *                          included in the returned list.
     * @return - a list of available reosurces from the defined acceptable
     *         resources.
     */
    public List obtainResources(@NotNull List resourceStrings, List rejectedResources)
            throws InsufficientResourcesAvailableException {
        List resourceDefinitions;
        try {
            resourceDefinitions = createResourceDefintions(resourceStrings);
            return generateResources(resourceDefinitions, rejectedResources, DEFAULTNUMBEROFRESOURCES, defaultDss,
                    defaultKeyPrefix, DEFAULTCONSECUTIVERESOURCES);
        } catch (ResourcePoolingServiceException | DynamicStatusStoreException e) {
            throw new InsufficientResourcesAvailableException(
                    "Could not generate resource Strings from the definitions provided. ", e);
        }
    }

    /**
     * This method obtaines resources from given definitions, but allowing rejected
     * resources to be disgarded from any returned list. Additionally this method
     * checks against a Dynamic status store for the resources to ensure they are
     * not in use.
     * 
     * The default return list length is 10.
     * 
     * @param resourceStrings   - this is a string list of all the definitions for
     *                          resources to select from.
     * @param rejectedResources - this is a list of resource names NOT to be
     *                          included in the returned list.
     * @param dss               - the dynamic status store to check for the
     *                          resources.
     * @param keyPrefix         - the keyprefix for the resource for it to be found
     *                          in the DSS.
     * @return - a list of available reosurces from the defined acceptable
     *         resources.
     */
    public List obtainResources(@NotNull List resourceStrings, List rejectedResources,
            IDynamicStatusStoreService dss, String keyPrefix) throws InsufficientResourcesAvailableException {
        List resourceDefinitions;
        try {
            resourceDefinitions = createResourceDefintions(resourceStrings);
            return generateResources(resourceDefinitions, rejectedResources, DEFAULTNUMBEROFRESOURCES, dss, keyPrefix,
                    DEFAULTCONSECUTIVERESOURCES);
        } catch (ResourcePoolingServiceException | DynamicStatusStoreException e) {
            throw new InsufficientResourcesAvailableException(
                    "Could not generate resource Strings from the definitions provided. ", e);
        }
    }

    /**
     * This method obtaines resources from given definitions, but allowing rejected
     * resources to be disgarded from any returned list. Additonally this method
     * allows for the number of resources returned in the list to be defined.
     * 
     * @param resourceStrings   - this is a string list of all the definitions for
     *                          resources to select from.
     * @param rejectedResources - this is a list of resource names NOT to be
     *                          included in the returned list.
     * @param returnMinimum     - the number of resources to return.
     * @return - a list of available reosurces from the defined acceptable
     *         resources.
     */
    public List obtainResources(@NotNull List resourceStrings, List rejectedResources,
            int returnMinimum) throws InsufficientResourcesAvailableException {
        List resourceDefinitions;
        try {
            resourceDefinitions = createResourceDefintions(resourceStrings);
            return generateResources(resourceDefinitions, rejectedResources, returnMinimum, defaultDss,
                    defaultKeyPrefix, DEFAULTCONSECUTIVERESOURCES);
        } catch (ResourcePoolingServiceException | DynamicStatusStoreException e) {
            throw new InsufficientResourcesAvailableException(
                    "Could not generate resource Strings from the definitions provided. ", e);
        }
    }

    /**
     * This method obtaines resources from given definitions, but allowing rejected
     * resources to be disgarded from any returned list. Additionally this method
     * checks against a Dynamic status store for the resources to ensure they are
     * not in use. As well, this method allows the number of resources returned to
     * be defined.
     * 
     * 
     * @param resourceStrings   - this is a string list of all the definitions for
     *                          resources to select from.
     * @param rejectedResources - this is a list of resource names NOT to be
     *                          included in the returned list.
     * @param dss               - the dynamic status store to check for the
     *                          resources.
     * @param keyPrefix         - the keyprefix for the resource for it to be found
     *                          in the DSS.
     * @param returnMinimum     - the number of resources to return.
     * @return - a list of available reosurces from the defined acceptable
     *         resources.
     */
    public List obtainResources(@NotNull List resourceStrings, List rejectedResources,
            int returnMinimum, IDynamicStatusStoreService dss, String keyPrefix)
            throws InsufficientResourcesAvailableException {
        List resourceDefinitions;
        try {
            resourceDefinitions = createResourceDefintions(resourceStrings);
            return generateResources(resourceDefinitions, rejectedResources, returnMinimum, dss, keyPrefix,
                    DEFAULTCONSECUTIVERESOURCES);
        } catch (ResourcePoolingServiceException | DynamicStatusStoreException e) {
            throw new InsufficientResourcesAvailableException(
                    "Could not generate resource Strings from the definitions provided. ", e);
        }
    }

    /**
     * This method obtaines resources from given definitions, but allowing rejected
     * resources to be disgarded from any returned list. Additonally this method
     * allows for the number of resources returned in the list to be defined, in
     * sections of consecutive resources. For example if i asked for 30 resources
     * with a returnConsecutive of 10, then 3 lots of 10 resources would be in the
     * list. Each 10 would be in a continuous chain.
     * 
     * @param resourceStrings   - this is a string list of all the definitions for
     *                          resources to select from.
     * @param rejectedResources - this is a list of resource names NOT to be
     *                          included in the returned list.
     * @param returnMinimum     - the number of resources to return.
     * @param returnConsecutive - the size of the "chunks" to find consectutive
     *                          reosources in.
     * @return - a list of available reosurces from the defined acceptable
     *         resources.
     */
    public List obtainResources(@NotNull List resourceStrings, List rejectedResources,
            int returnMinimum, int returnConsecutive) throws InsufficientResourcesAvailableException {
        List resourceDefinitions;
        if ((returnMinimum % returnConsecutive) != 0) {
            throw new InsufficientResourcesAvailableException(
                    "The number of consecutive resources required needs to be a multiple of the total number of resources required.");
        }
        try {
            resourceDefinitions = createResourceDefintions(resourceStrings);
            return generateResources(resourceDefinitions, rejectedResources, returnMinimum, defaultDss,
                    defaultKeyPrefix, returnConsecutive);
        } catch (ResourcePoolingServiceException | DynamicStatusStoreException e) {
            throw new InsufficientResourcesAvailableException(
                    "Could not generate resource Strings from the definitions provided. ", e);
        }
    }

    /**
     * This method obtaines resources from given definitions, but allowing rejected
     * resources to be disgarded from any returned list. Additonally this method
     * allows for the number of resources returned in the list to be defined, in
     * sections of consecutive resources. For example if i asked for 30 resources
     * with a returnConsecutive of 10, then 3 lots of 10 resources would be in the
     * list. Each 10 would be in a continuous chain.
     * 
     * This method also checks the DSS for the resources to ensure they are not in
     * use.
     * 
     * @param resourceStrings   - this is a string list of all the definitions for
     *                          resources to select from.
     * @param rejectedResources - this is a list of resource names NOT to be
     *                          included in the returned list.
     * @param returnMinimum     - the number of resources to return.
     * @param dss               - the dynamic status store to check for the
     *                          resources.
     * @param keyPrefix         - the keyprefix for the resource for it to be found
     *                          in the DSS.
     * @param returnConsecutive - the size of the "chunks" to find consectutive
     *                          reosources in.
     * @return - a list of available reosurces from the defined acceptable
     *         resources.
     */
    public List obtainResources(@NotNull List resourceStrings, List rejectedResources,
            int returnMinimum, int returnConsecutive, IDynamicStatusStoreService dss, String keyPrefix)
            throws InsufficientResourcesAvailableException {
        List resourceDefinitions;
        if ((returnMinimum % returnConsecutive) != 0) {
            throw new InsufficientResourcesAvailableException(
                    "The number of consecutive resources required needs to be a multiple of the total number of resources required.");
        }
        try {
            resourceDefinitions = createResourceDefintions(resourceStrings);
            return generateResources(resourceDefinitions, rejectedResources, returnMinimum, dss, keyPrefix,
                    returnConsecutive);
        } catch (ResourcePoolingServiceException | DynamicStatusStoreException e) {
            throw new InsufficientResourcesAvailableException(
                    "Could not generate resource Strings from the definitions provided. ", e);
        }
    }

    /**
     * This method creates all the resource string defintions from the basic strings
     * passed which explain them.
     * 
     * @param resourceStrings - list of string like: APPLID{9}{4-7}{F}{z}, etc
     * @return - returns a list of the resource strings which can be used to gather
     *         reosources.
     *
     * @throws ResourcePoolingServiceException
     */
    private List createResourceDefintions(List resourceStrings)
            throws ResourcePoolingServiceException {
        List list = new ArrayList<>();
        for (String input : resourceStrings) {
            list.add(new ResourceString(input));
        }
        return list;
    }

    /**
     * This method generates the resources from the given restrictions. On the first
     * pass random generation is attempted to make sure load is equally spread
     * across resources. If random generation fails, sequential generation of
     * resources is then attempted. The InsufficentResourceException is thrown if
     * this too cannot generate the defined resources.
     * 
     * @param resourceDefinitions - the resource strings passed that define the
     *                            availabble resources to generate.
     * @param rejectedResources   - the resource strings which are to be rejected if
     *                            generated.
     * @param numberOfResources   - the number of resources required to be
     *                            generated.
     * @param dss                 - the dynamic status store to check against.
     * @param keyPrefix           - the prefix for the resource if it was to be
     *                            found in the DSS.
     * @param returnConsecutive   - the "chunk" size to generate consecutive
     *                            resources too.
     * @return - a list of generated resource within the constraits passed on this
     *         method.
     * @throws DynamicStatusStoreException
     * @throws InsufficientResourcesAvailableException
     */
    private List generateResources(List resourceDefinitions, List rejectedResources,
            int numberOfResources, IDynamicStatusStoreService dss, String keyPrefix, int returnConsecutive)
            throws DynamicStatusStoreException, InsufficientResourcesAvailableException {
        List generatedResources = new ArrayList<>();
        List bannedResources = new ArrayList<>();

        if (rejectedResources == null) {
            rejectedResources = new ArrayList<>();
        }

        bannedResources.addAll(rejectedResources);

        try {
            for (int i = 0; i < numberOfResources; i += returnConsecutive) {
                ResourceString randomDefinition = resourceDefinitions.get(random.nextInt(resourceDefinitions.size()));

                List newResources = generateRandomResources(randomDefinition, bannedResources, dss, keyPrefix,
                        returnConsecutive);
                generatedResources.addAll(newResources);
                bannedResources.addAll(newResources);
            }
            return generatedResources;
        } catch (InsufficientResourcesAvailableException | DynamicStatusStoreException e) {
            bannedResources.clear();
            bannedResources.addAll(rejectedResources);
            generatedResources.clear();
        }

        try {
            for (ResourceString definition : resourceDefinitions) {
                for (int i = 0; i < numberOfResources; i += returnConsecutive) {
                    if (generatedResources.size() == numberOfResources) {
                        break;
                    } else {
                        List newResources = generateSequentialResources(definition, bannedResources, dss,
                                keyPrefix, returnConsecutive);
                        generatedResources.addAll(newResources);
                        bannedResources.addAll(newResources);
                    }
                }
                if (generatedResources.size() == numberOfResources) {
                    break;
                }
            }
            if (generatedResources.size() != numberOfResources) {
                throw new InsufficientResourcesAvailableException("There is not enough available resource.");
            }
            return generatedResources;
        } catch (InsufficientResourcesAvailableException | DynamicStatusStoreException e) {
            throw new InsufficientResourcesAvailableException("There is not enough resource available", e);
        }
    }

    /**
     * This method is used for the random generation of the resources.
     * 
     * @param definition            -the resource string passed that define the
     *                              available resources to generate.
     * @param bannedReosurceStrings - the resources that are not allowed to be added
     *                              to the return list
     * @param dss                   - the dynamic status store to check against.
     * @param keyPrefix             - the prefix for the resource if it was to be
     *                              found in the DSS.
     * @param returnConsecutive     - the "chunk" size to generate consecutive
     *                              resources too.
     * @return - return list of generated resources that are randomly distributed.
     * @throws DynamicStatusStoreException
     * @throws InsufficientResourcesAvailableException
     */
    private List generateRandomResources(ResourceString definition, List bannedReosurceStrings,
            IDynamicStatusStoreService dss, String keyPrefix, int returnConsecutive)
            throws DynamicStatusStoreException, InsufficientResourcesAvailableException {
        String randomResource = definition.getRandomResource();
        List resources = new ArrayList<>();
        int attempts = 0;

        while (resources.size() < returnConsecutive) {
            if (!(bannedReosurceStrings.contains(randomResource)) && ((dss.get(keyPrefix + randomResource)) == null)) {
                resources.add(randomResource);
            } else {
                resources.clear();
                attempts++;
                if (attempts / definition.getNumberOfCombinations() > 0.4) {
                    throw new InsufficientResourcesAvailableException(
                            "Random generation is hitting too many banned resoruces");
                }
            }
            if (resources.size() < returnConsecutive) {
                try {
                    randomResource = definition.getNextResource();
                } catch (InsufficientResourcesAvailableException e) {
                    resources.clear();
                    randomResource = definition.getNextResource();
                }
            }

        }
        return resources;
    }

    /**
     * This method is used for sequential generation of resources if the random
     * generation fails. This should only trigger is the attempted number of random
     * generation number is 40% of the available reosurces.
     * 
     * @param definition            -the resource string passed that define the
     *                              available resources to generate.
     * @param bannedReosurceStrings - the resources that are not allowed to be added
     *                              to the return list
     * @param dss                   - the dynamic status store to check against.
     * @param keyPrefix             - the prefix for the resource if it was to be
     *                              found in the DSS.
     * @param returnConsecutive     - the "chunk" size to generate consecutive
     *                              resources too.
     * @return - return list of generated resources that are sequentially
     *         distributed.
     * @throws DynamicStatusStoreException
     */
    private List generateSequentialResources(ResourceString definition, List bannedReosurceStrings,
            IDynamicStatusStoreService dss, String keyPrefix, int returnConsecutive)
            throws DynamicStatusStoreException, InsufficientResourcesAvailableException {
        String resource = definition.getFirstResource();
        List resources = new ArrayList<>();
        int attempt = 0;

        while (resources.size() < returnConsecutive) {
            if (!(bannedReosurceStrings.contains(resource)) && ((dss.get(keyPrefix + resource)) == null)) {
                resources.add(resource);
            } else {
                resources.clear();
                attempt++;
                if (attempt > definition.getNumberOfCombinations()) {
                    return new ArrayList<>();
                }
            }
            if (resources.size() < returnConsecutive) {
                try {
                    resource = definition.getNextResource();
                } catch (InsufficientResourcesAvailableException e) {
                    resources.clear();
                    resource = definition.getNextResource();
                    attempt++;
                }
            }
        }
        return resources;
    }

    /**
     * This class is used when a dss is not provided. When checking resources this
     * stubbed class returns null for the DSS checks.
     */
    private class StubbedDss implements IDynamicStatusStoreService {

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public IDynamicResource getDynamicResource(String input) {
            return null;
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public IDynamicRun getDynamicRun() throws DynamicStatusStoreException {
            return null;
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public void deletePrefix(@NotNull String keyPrefix) throws DynamicStatusStoreException {
            // EMPTY METHOD
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public void delete(@NotNull Set keys) throws DynamicStatusStoreException {
            // EMPTY METHOD
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public void delete(@NotNull String key) throws DynamicStatusStoreException {
            // EMPTY METHOD
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public @NotNull Map getPrefix(@NotNull String keyPrefix) throws DynamicStatusStoreException {
            return new HashMap<>();
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public String get(@NotNull String key) throws DynamicStatusStoreException {
            return null;
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public boolean putSwap(@NotNull String key, String oldValue, @NotNull String newValue,
                @NotNull Map others) throws DynamicStatusStoreException {
            return true;
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public boolean putSwap(@NotNull String key, String oldValue, @NotNull String newValue)
                throws DynamicStatusStoreException {
            return true;
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public void put(@NotNull Map keyValues) throws DynamicStatusStoreException {
            // EMPTY METHOD
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        public void put(@NotNull String key, @NotNull String value) throws DynamicStatusStoreException {
            // EMPTY METHOD
        }

        /**
         * Commenting as unused, but required from IDynamicStatusStore implementation.
         */
        @Override
        public UUID watch(IDynamicStatusStoreWatcher watcher, String key) throws DynamicStatusStoreException {
            return null;
        }

        @Override
        public UUID watchPrefix(IDynamicStatusStoreWatcher watcher, String keyPrefix)
                throws DynamicStatusStoreException {
            return null;
        }

        @Override
        public void unwatch(UUID watchId) throws DynamicStatusStoreException {

        }

        @Override
        public void performActions(IDssAction... actions) throws DynamicStatusStoreException {
            
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy