/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.ow2.choreos.selectors; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * An hybrid between Always Create and Round Robin, with a limit on the number * of objects created. The initial behavior is Always Create. When the limit of * objects is reached, it acts as Round Robin. * * @author leonardo * */ public class LimitedRoundRobinSelector<T, R> implements Selector<T, R> { private int limit = 1; private ObjectRetriever<T> objectRetriever; private AtomicInteger objectsBeenCreated = new AtomicInteger(); // TODO // replace // by // Semaphore private Selector<T, R> alwaysCreatorSelector, roundRobinSelector; private enum State { CREATING, ROUND_ROBIN }; public LimitedRoundRobinSelector(int limit, ObjectRetriever<T> objectRetriever, ObjectFactory<T, R> objectFactory) { ObjectFilters<T, R> filters = new ObjectFilters<T, R>(); ObjectFilter<T, R> filter = filters.getNoFilter(); this.limit = limit; this.alwaysCreatorSelector = new AlwaysCreateSelector<T, R>(objectFactory); this.roundRobinSelector = new RoundRobinSelector<T, R>(objectRetriever, filter); this.objectRetriever = objectRetriever; } public LimitedRoundRobinSelector(int limit, ObjectRetriever<T> objectRetriever, ObjectFactory<T, R> objectFactory, ObjectFilter<T, R> objectFilter) { this.limit = limit; this.alwaysCreatorSelector = new AlwaysCreateSelector<T, R>(objectFactory); this.roundRobinSelector = new RoundRobinSelector<T, R>(objectRetriever); this.objectRetriever = objectRetriever; } @Override public List<T> select(R requirements, int objectsQuantity) throws NotSelectedException { List<T> objects = this.objectRetriever.retrieveObjects(); int newQty = 0; State state = State.ROUND_ROBIN; synchronized (this) { state = this.defineState(objects); if (state == State.CREATING) { newQty = this.defineNewQty(objects, objectsQuantity); objectsBeenCreated.addAndGet(newQty); } } switch (state) { case CREATING: return selectInCreatingState(objects, requirements, objectsQuantity, newQty); case ROUND_ROBIN: return selectInRoundRobinState(objects, requirements, objectsQuantity); default: throw new IllegalStateException("Invalid state " + state); } } private State defineState(List<T> objects) { if (objects.size() + objectsBeenCreated.get() < limit) return State.CREATING; else return State.ROUND_ROBIN; } private int defineNewQty(List<T> objects, int objectsQuantity) { int maximumNewAllowed = limit - objects.size(); int newQty = objectsQuantity; if (newQty > maximumNewAllowed) { newQty = maximumNewAllowed; } return newQty; } private List<T> selectInCreatingState(List<T> objects, R requirements, int objectsQuantity, int newQty) throws NotSelectedException { List<T> result = this.alwaysCreatorSelector.select(requirements, newQty); synchronized (this) { objectsBeenCreated.set(objectsBeenCreated.get() - newQty); } if (result.size() < objectsQuantity) { int diff = objectsQuantity = result.size(); List<T> moreNodes = this.roundRobinSelector.select(requirements, diff); result.addAll(moreNodes); } return result; } private List<T> selectInRoundRobinState(List<T> objects, R requirements, int objectsQuantity) throws NotSelectedException { this.waitNodesCreation(); return this.roundRobinSelector.select(requirements, objectsQuantity); } private void waitNodesCreation() { int c = 0; while (objectsBeenCreated.get() > 0) { try { if (c < 10) Thread.sleep(100); else Thread.sleep(1000); } catch (InterruptedException e) { } c++; } } }