package alma.acs.monitoring.controller; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import alma.ACSErrTypeCommon.wrappers.AcsJIllegalArgumentEx; import alma.MonitorArchiver.BlobberOperations; /** * Ordered list of {@link BlobberInfo} objects. * Duplicate blobber names are not allowed. * Access is thread-safe. */ class BlobberList { /** * Blobber name, ref and some meta data. */ class BlobberInfo { /** * Name of the blobber component. */ final String blobberName; /** * Reference to the blobber component. * May be null if the blobber component has not been requested yet, * or if it is not even available in the system (see http://jira.alma.cl/browse/COMP-6572) */ BlobberOperations blobberRef; /** * Avoids requesting a blobber reference too soon after a failure, * see http://jira.alma.cl/browse/COMP-6572 */ long lastRefRequestFailedTimeMillis; /** * Number of calls to the blobber that failed, for example by throwing runtime exceptions. * <p> * @TODO: This information could be used to stop using that blobber, after all collectors are deregistered and re-assigned * to other blobbers. */ int numCallsFailed; /** * Private c'tor, to enforce construction via {@link BlobberList#createBlobberInfo(String)}. * @param blobberName */ private BlobberInfo(String blobberName) { this.blobberName = blobberName; } /** * @return True if the last failed request for the blobber reference was less than 30 seconds ago. */ boolean hadRefRequestFailureWithinLastSec(int seconds) { return ( System.currentTimeMillis() - lastRefRequestFailedTimeMillis < seconds * 1000 ); } } /** * We back this list with an insertion-ordered map, to find BlobberInfos fast by name. * @TODO: Use a PriorityQueue with custom sorting, so that the first blobber found has the * fewest collectors assigned. Then we don't need to keep an index, but just take the first blobber, * possibly skipping problematic blobbers. This will nicely implement load balancing based on number * of assigned collectors. */ protected final LinkedHashMap<String, BlobberInfo> blobberMap = new LinkedHashMap<String, BlobberInfo>(); /** * Zero-based index, used by {@link #getNextBlobberInfo()} to iterate over the list. */ protected int lastBlobberPosition; /** * Factory method for BlobberInfo objects. They automatically get stored in the list. * @param blobberName * @throws AcsJIllegalArgumentEx If the blobber name already exists in the list. */ void createBlobberInfo(String blobberName) throws AcsJIllegalArgumentEx { synchronized (blobberMap) { if (blobberName == null || blobberMap.containsKey(blobberName)) { throw new AcsJIllegalArgumentEx(); } blobberMap.put(blobberName, new BlobberInfo(blobberName)); } } BlobberInfo getBlobberInfo(String blobberName) { synchronized (blobberMap) { return blobberMap.get(blobberName); } } /** * @return All "live" BlobberInfo objects from the internal list. The returned list itself is a copy and safe to iterate over. */ List<BlobberInfo> getBlobberInfos() { synchronized (blobberMap) { return new ArrayList<BlobberInfo>(blobberMap.values()); } } /** * Returns all blobber names, or only those whose reference we hold. * @param referencedOnly If true, then consider only blobbers that have been activated * and that are referenced by this controller. * @return List of blobber names. */ List<String> getBlobberNames(boolean referencedOnly) { List<String> ret = new ArrayList<String>(); synchronized (blobberMap) { for (BlobberInfo blobberInfo : blobberMap.values()) { if (!referencedOnly || blobberInfo.blobberRef != null) { ret.add(blobberInfo.blobberName); } } } return ret; } /** * Number of BlobberInfos contained in this list. */ int size() { synchronized (blobberMap) { return blobberMap.size(); } } /** * Circularly iterates over the BlobberInfos. * Returns <code>null</code> if no BlobberInfos are available. * <p> * If blobbers get added or removed in between calls to this method, there is a simple and possibly "unfair" * algorithm that will find a valid blobber, but it might even be the same blobber as last time, * or a blobber may be skipped. */ BlobberInfo getNextBlobberInfo() { BlobberInfo ret = null; synchronized (blobberMap) { int maxPos = blobberMap.size() - 1; if (maxPos >= 0) { lastBlobberPosition++; // this becomes the next blobber position if (lastBlobberPosition > maxPos) { lastBlobberPosition = 0; } // iterate over the ordered map values to lastBlobberPosition Iterator<BlobberInfo> iterator = blobberMap.values().iterator(); for (int i = 0; i <= lastBlobberPosition; i++) { ret = iterator.next(); } } else { // no BlobberInfo available, just return null } } return ret; } }