This is an automated email from the git hooks/post-receive script. New commit to branch feature/8145-2 in repository tutti. See https://gitlab.nuiton.org/codelutin/tutti.git commit 36c52ba34da0bab55db9f853213a7a482486147f Author: Tony CHEMIT <chemit@codelutin.com> Date: Sun Mar 20 21:41:47 2016 +0100 Ajout d'un chargeur de cache + amélioration de l'API de cache sur les échantillons --- .../fr/ifremer/tutti/service/TuttiDataContext.java | 78 ++++++-- .../service/samplingCache/CruiseSamplingCache.java | 222 ++++++++++++++++++--- .../samplingCache/CruiseSamplingCacheLoader.java | 84 ++++++++ .../samplingCache/CruiseSamplingInternalCache.java | 74 ++++++- 4 files changed, 400 insertions(+), 58 deletions(-) diff --git a/tutti-service/src/main/java/fr/ifremer/tutti/service/TuttiDataContext.java b/tutti-service/src/main/java/fr/ifremer/tutti/service/TuttiDataContext.java index 1306dae..5b59786 100644 --- a/tutti-service/src/main/java/fr/ifremer/tutti/service/TuttiDataContext.java +++ b/tutti-service/src/main/java/fr/ifremer/tutti/service/TuttiDataContext.java @@ -26,7 +26,6 @@ import com.google.common.base.Preconditions; import fr.ifremer.tutti.TuttiConfiguration; import fr.ifremer.tutti.persistence.entities.data.Cruise; import fr.ifremer.tutti.persistence.entities.data.FishingOperation; -import fr.ifremer.tutti.persistence.entities.data.IndividualObservationBatch; import fr.ifremer.tutti.persistence.entities.data.Program; import fr.ifremer.tutti.persistence.entities.data.SampleCategoryModel; import fr.ifremer.tutti.persistence.entities.data.SampleCategoryModelEntry; @@ -42,6 +41,7 @@ import fr.ifremer.tutti.persistence.entities.referential.TaxonCache; import fr.ifremer.tutti.persistence.entities.referential.TaxonCaches; import fr.ifremer.tutti.persistence.entities.referential.Vessel; import fr.ifremer.tutti.service.samplingCache.CruiseSamplingCache; +import fr.ifremer.tutti.service.samplingCache.CruiseSamplingCacheLoader; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -51,6 +51,7 @@ import java.io.Closeable; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -183,6 +184,16 @@ public class TuttiDataContext extends AbstractBean implements Closeable { fishingOperation = null; getValidationContext().resetExistingFishingOperations(); }); + // Pour supprimer le cache d'échantillons + addPropertyChangeListener(evt -> { + String propertyName = evt.getPropertyName(); + if (PROPERTY_CRUISE_ID.equals(propertyName) || PROPERTY_PROGRAM_ID.equals(propertyName)) { + + if (getOptionalCruiseSamplingCache().isPresent()) { + closeCruiseSamplingCache(); + } + } + }); } public void setPersistenceService(PersistenceService service) { @@ -422,10 +433,22 @@ public class TuttiDataContext extends AbstractBean implements Closeable { return fishingOperationId != null; } + public boolean isCanUseCruiseSamplingCache() { + return isProtocolFilled() + && isCruiseFilled() + && getProtocol().isUseCalcifiedPieceSampling(); + } + public boolean isCruiseSamplingCacheLoaded() { return cruiseSamplingCache != null; } + public boolean isCruiseSamplingCacheUpToDate() { + return isCruiseSamplingCacheLoaded() + && Objects.equals(getCruiseId(), cruiseSamplingCache.getCruiseId()) + && Objects.equals(getProtocolId(), cruiseSamplingCache.getProtocolId()); + } + public void setProgramId(String programId) { boolean oldProgramFilled = isProgramFilled(); boolean oldCruiseFilled = isCruiseFilled(); @@ -536,41 +559,52 @@ public class TuttiDataContext extends AbstractBean implements Closeable { return service.getProtocol(); } + public Optional<CruiseSamplingCache> getOptionalCruiseSamplingCache() { - return Optional.ofNullable(isCruiseSamplingCacheLoaded() ? cruiseSamplingCache : null); + return Optional.ofNullable(isCruiseSamplingCacheUpToDate() ? cruiseSamplingCache : null); } - public void loadCruiseSamplingCache() { + public void loadCruiseSamplingCache(CruiseSamplingCacheLoader cruiseSamplingCacheLoader) { checkOpened(); - if (!isCruiseSamplingCacheLoaded() && isProtocolFilled() && isCruiseFilled()) { - TuttiProtocol protocol = getProtocol(); + if (!isCanUseCruiseSamplingCache()) { + throw new IllegalStateException("Pas autorisé à charger le cache d'échantillons"); + } + if (isCruiseSamplingCacheLoaded() && !isCruiseSamplingCacheUpToDate()) { + closeCruiseSamplingCache(); + } - if (protocol.isUseCalcifiedPieceSampling()) { + if (log.isInfoEnabled()) { + log.info("Loading cruise sampling cache: {cruiseId:" + getCruiseId() + ", protocolId: " + getProtocolId() + "}"); + } - cruiseSamplingCache = new CruiseSamplingCache(protocol); + cruiseSamplingCache = cruiseSamplingCacheLoader.loadCruiseSamplingCache(getProtocol(), getCruiseId()); - service.getAllFishingOperationIds(getCruiseId()).stream().forEach(operationId -> { + if (log.isInfoEnabled()) { + log.info("cruise sampling cache loaded: " + cruiseSamplingCache); + } - FishingOperation operation = service.getFishingOperation(operationId); + } - List<IndividualObservationBatch> allIndividualObservationBatchsForFishingOperation = - service.getAllIndividualObservationBatchsForFishingOperation(operationId); + protected void closeCruiseSamplingCache() { + Objects.requireNonNull(cruiseSamplingCache); - allIndividualObservationBatchsForFishingOperation.forEach( - obs -> cruiseSamplingCache.increment(operation, - obs.getSpecies(), - (CaracteristicQualitativeValue) obs.getCaracteristics().get(service.getSexCaracteristic()), - null, - obs.getSize())); - }); + // on détruit le cache obsolète + if (log.isInfoEnabled()) { + log.info("Destroy obsolete cruise sampling cache: " + cruiseSamplingCache); + } - if (log.isInfoEnabled()) { - log.info("cruise sampling cache loaded"); - } - } + cruiseSamplingCache.close(); + ; + cruiseSamplingCache = null; + } + public List<Integer> getCruiseFishingOperationIds() { + checkOpened(); + if (!isCruiseFilled()) { + throw new IllegalStateException("Aucune campagne chargée, impossible de récupérer les identifiants de traits"); } + return service.getAllFishingOperationIds(getCruiseId()); } public FishingOperation getFishingOperation() { diff --git a/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingCache.java b/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingCache.java index 0d67031..c5ed6fc 100644 --- a/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingCache.java +++ b/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingCache.java @@ -24,13 +24,16 @@ package fr.ifremer.tutti.service.samplingCache; * #L% */ +import com.google.common.base.MoreObjects; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import fr.ifremer.tutti.persistence.entities.data.FishingOperation; +import fr.ifremer.tutti.persistence.entities.data.IndividualObservationBatch; import fr.ifremer.tutti.persistence.entities.protocol.CalcifiedPiecesSamplingDefinition; import fr.ifremer.tutti.persistence.entities.protocol.SubStrata; import fr.ifremer.tutti.persistence.entities.protocol.TuttiProtocol; import fr.ifremer.tutti.persistence.entities.protocol.Zone; +import fr.ifremer.tutti.persistence.entities.referential.Caracteristic; import fr.ifremer.tutti.persistence.entities.referential.CaracteristicQualitativeValue; import fr.ifremer.tutti.persistence.entities.referential.Species; import fr.ifremer.tutti.persistence.entities.referential.TuttiLocation; @@ -45,6 +48,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; /** @@ -56,16 +60,56 @@ public class CruiseSamplingCache implements Closeable { /** Logger. */ private static final Log log = LogFactory.getLog(CruiseSamplingCache.class); - protected final CruiseSamplingInternalCache totalCruiseCache = new CruiseSamplingInternalCache(); - protected final CruiseSamplingInternalCache zoneCache = new CruiseSamplingInternalCache(); - protected final CruiseSamplingInternalCache operationCache = new CruiseSamplingInternalCache(); - - protected final Map<TuttiLocation, Zone> zonesByLocations = new HashMap<>(); - protected final Multimap<Integer, CalcifiedPiecesSamplingDefinition> cpsDefinitionsBySpecies = HashMultimap.create(); - - protected final EventListenerList listeners = new EventListenerList(); - - public CruiseSamplingCache(TuttiProtocol protocol) { + /** + * Le cache des échantillons au niveau de la campagne. + */ + private final CruiseSamplingInternalCache totalCruiseCache = new CruiseSamplingInternalCache(); + /** + * Le cache des échantillons au niveau de chaque zone. + */ + private final CruiseSamplingInternalCache zoneCache = new CruiseSamplingInternalCache(); + /** + * Le cache des échantillons au niveau de chaque opération. + */ + private final CruiseSamplingInternalCache operationCache = new CruiseSamplingInternalCache(); + + /** + * Le dictionnaire des zones du protocole indexés par location. + */ + private final Map<TuttiLocation, Zone> zonesByLocations = new HashMap<>(); + /** + * Le dictionnaire des définitions de l'algorithme de prélèvement des piècess calcifiées indexés par identifiant de taxon. + */ + private final Multimap<Integer, CalcifiedPiecesSamplingDefinition> cpsDefinitionsBySpecies = HashMultimap.create(); + + /** + * La caractéristic qui définie le sexe dans une observation individuelle. + */ + private final Caracteristic sexCaracteristic; + + /** + * La liste des listeners. + */ + private final EventListenerList listeners = new EventListenerList(); + + /** + * L'identifiant de la campagne associée à ce cache. + */ + private final Integer cruiseId; + /** + * L'identificant du protocol associé à ce cache. + */ + private final String protocolId; + + /** + * Un drapeau pour indiquer qu'on est en train de construire le cache et donc ne déclancher aucun listener. + */ + private boolean loading; + + public CruiseSamplingCache(Integer cruiseId, TuttiProtocol protocol, Caracteristic sexCaracteristic) { + this.cruiseId = cruiseId; + this.sexCaracteristic = sexCaracteristic; + this.protocolId = protocol.getId(); // fill the zones by location protocol.getZone().forEach(zone -> { @@ -89,6 +133,22 @@ public class CruiseSamplingCache implements Closeable { speciesProtocol.getCalcifiedPiecesSamplingDefinition())); } + public Integer getCruiseId() { + return cruiseId; + } + + public String getProtocolId() { + return protocolId; + } + + public boolean isLoading() { + return loading; + } + + public void setLoading(boolean loading) { + this.loading = loading; + } + @Override public void close() { if (log.isInfoEnabled()) { @@ -105,13 +165,56 @@ public class CruiseSamplingCache implements Closeable { } } - public void increment(FishingOperation operation, - Species species, - CaracteristicQualitativeValue gender, - Boolean maturity, - float lengthStep) { + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("cruiseId", cruiseId) + .add("protocolId", protocolId) + .add("totalCruiseCache", totalCruiseCache.size()) + .add("zoneCache", zoneCache.size()) + .add("operationCache", operationCache.size()) + .toString(); + } + + /** + * Ajout d'un échantillon dans le cache. + * + * @param fishingOperation l'opération de pêche contenant l'observation individuelle + * @param individualObservationBatch l'observation individuelle à ajouter au cache + */ + public void addIndividualObservation(FishingOperation fishingOperation, IndividualObservationBatch individualObservationBatch) { + + Objects.requireNonNull(fishingOperation); + Objects.requireNonNull(individualObservationBatch); + + Species species = individualObservationBatch.getSpecies(); + Objects.requireNonNull(species); + + Boolean maturity = null; + Float lengthStep = individualObservationBatch.getSize(); + + CaracteristicQualitativeValue gender = individualObservationBatch.getCaracteristics().getQualitativeValue(sexCaracteristic); + + addIndividualObservation(fishingOperation, species, gender, maturity, lengthStep); + + } - Objects.requireNonNull(operation); + /** + * Ajout d'un échantillon dans le cache. + * + * @param fishingOperation l'opération de pêche concernée + * @param species l'espèces concernée + * @param gender le sexe de l'échantillon (peut-être null) + * @param maturity la maturité de l'échantillon (peut-être null) + * @param lengthStep la classe de taille de l'échantillon + */ + public void addIndividualObservation(FishingOperation fishingOperation, + Species species, + CaracteristicQualitativeValue gender, + Boolean maturity, + float lengthStep) { + + Objects.requireNonNull(fishingOperation); Objects.requireNonNull(species); Optional<CalcifiedPiecesSamplingDefinition> cpsDefinitionOpt = getCalcifiedPiecesSamplingDefinition(species, maturity, lengthStep); @@ -133,7 +236,7 @@ public class CruiseSamplingCache implements Closeable { } CruiseSamplingInternalCache.CruiseSamplingInternalCacheKey samplingKey = CruiseSamplingInternalCache.newCruiseSamplingInternalCacheKey(species, gender, maturity, lengthStep); - Optional<Zone> zone = findZone(operation); + Optional<Zone> zone = findZone(fishingOperation); int totalValue = totalCruiseCache.increment(samplingKey); int zoneValue = 0; @@ -141,14 +244,14 @@ public class CruiseSamplingCache implements Closeable { CruiseSamplingInternalCache.ZoneCruiseSamplingInternalCacheKey zoneKey = samplingKey.toZoneKey(zone.get()); zoneValue = zoneCache.increment(zoneKey); } - CruiseSamplingInternalCache.FishingOperationCruiseSamplingInternalCacheKey operationKey = samplingKey.toFishingOperationKey(operation); + CruiseSamplingInternalCache.FishingOperationCruiseSamplingInternalCacheKey operationKey = samplingKey.toFishingOperationKey(fishingOperation); int operationValue = operationCache.increment(operationKey); if (log.isInfoEnabled()) { - log.info("increment " + samplingKey + " => op " + operationValue + " / zone " + zoneValue + " / cruise " + totalValue); + log.info("addIndividualObservation " + samplingKey + " => op " + operationValue + " / zone " + zoneValue + " / cruise " + totalValue); } - if (totalValue == 1 || totalValue % samplingInterval == 1) { + if (!isLoading() && (totalValue == 1 || totalValue % samplingInterval == 1)) { if (log.isInfoEnabled()) { log.info("-> needs sampling"); } @@ -159,13 +262,50 @@ public class CruiseSamplingCache implements Closeable { } } - public void decrement(FishingOperation operation, - Species species, - CaracteristicQualitativeValue gender, - Boolean maturity, - Float lengthStep) { + /** + * Suppression d'un échantillon du cache. + * + * @param fishingOperation l'opération de pêche concernée + * @param individualObservationBatches les observations individuelles à supprimer du cache + */ + public void removeIndividualObservations(FishingOperation fishingOperation, Collection<IndividualObservationBatch> individualObservationBatches) { + + Objects.requireNonNull(fishingOperation); + Objects.requireNonNull(individualObservationBatches); + + individualObservationBatches.forEach(individualObservationBatch -> { + + + Species species = individualObservationBatch.getSpecies(); + Objects.requireNonNull(species); + + Boolean maturity = null; + Float lengthStep = individualObservationBatch.getSize(); + CaracteristicQualitativeValue gender = individualObservationBatch.getCaracteristics().getQualitativeValue(sexCaracteristic); - Objects.requireNonNull(operation); + removeIndividualObservation(fishingOperation, species, gender, maturity, lengthStep); + + }); + + + } + + /** + * Suppression d'un échantillon du cache. + * + * @param fishingOperation l'opération de pêche concernée + * @param species l'espèces concernée + * @param gender le sexe de l'échantillon (peut-être null) + * @param maturity la maturité de l'échantillon (peut-être null) + * @param lengthStep la classe de taille de l'échantillon + */ + public void removeIndividualObservation(FishingOperation fishingOperation, + Species species, + CaracteristicQualitativeValue gender, + Boolean maturity, + Float lengthStep) { + + Objects.requireNonNull(fishingOperation); Objects.requireNonNull(species); Objects.requireNonNull(lengthStep); @@ -188,7 +328,7 @@ public class CruiseSamplingCache implements Closeable { CruiseSamplingInternalCache.CruiseSamplingInternalCacheKey samplingKey = CruiseSamplingInternalCache.newCruiseSamplingInternalCacheKey(species, gender, maturity, lengthStep); - Optional<Zone> zone = findZone(operation); + Optional<Zone> zone = findZone(fishingOperation); int totalValue = totalCruiseCache.decrement(samplingKey); int zoneValue = 0; @@ -196,16 +336,39 @@ public class CruiseSamplingCache implements Closeable { CruiseSamplingInternalCache.ZoneCruiseSamplingInternalCacheKey zoneKey = samplingKey.toZoneKey(zone.get()); zoneValue = zoneCache.decrement(zoneKey); } - CruiseSamplingInternalCache.FishingOperationCruiseSamplingInternalCacheKey operationKey = samplingKey.toFishingOperationKey(operation); + CruiseSamplingInternalCache.FishingOperationCruiseSamplingInternalCacheKey operationKey = samplingKey.toFishingOperationKey(fishingOperation); int operationValue = operationCache.decrement(operationKey); if (log.isInfoEnabled()) { - log.info("decrement " + samplingKey + " => op " + operationValue + " / zone " + zoneValue + " / cruise " + totalValue); + log.info("removeIndividualObservation " + samplingKey + " => op " + operationValue + " / zone " + zoneValue + " / cruise " + totalValue); } } } } + /** + * Suppression de toutes les observations individuelles du cache. + * + * @param fishingOperation l'opération de pêche concernée + */ + public void removeFishingOperation(FishingOperation fishingOperation) { + + Objects.requireNonNull(fishingOperation); + + // suppression de toutes les entrées du cache des opérations (et récupération des clefs) + Set<String> removedKeys = operationCache.removeAllWhereKeyStartingWith(fishingOperation.getId()); + + removedKeys.forEach(totalCruiseCache::decrement); + + Optional<Zone> zone = findZone(fishingOperation); + + if (zone.isPresent()) { + String id = zone.get().getId(); + removedKeys.forEach(key -> zoneCache.decrementIfExist(CruiseSamplingInternalCache.addPrefixKey(id, key))); + } + + } + public void addSamplingListener(SamplingListener listener) { listeners.add(SamplingListener.class, listener); } @@ -266,5 +429,4 @@ public class CruiseSamplingCache implements Closeable { } } - } diff --git a/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingCacheLoader.java b/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingCacheLoader.java new file mode 100644 index 0000000..b7edd93 --- /dev/null +++ b/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingCacheLoader.java @@ -0,0 +1,84 @@ +package fr.ifremer.tutti.service.samplingCache; + +import fr.ifremer.tutti.persistence.ProgressionModel; +import fr.ifremer.tutti.persistence.entities.data.FishingOperation; +import fr.ifremer.tutti.persistence.entities.data.IndividualObservationBatch; +import fr.ifremer.tutti.persistence.entities.protocol.TuttiProtocol; +import fr.ifremer.tutti.persistence.entities.referential.Caracteristic; +import fr.ifremer.tutti.service.DecoratorService; +import fr.ifremer.tutti.service.PersistenceService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.decorator.Decorator; + +import java.util.List; + +/** + * Pour charger le cache. + * + * Created on 20/03/16. + * + * @author Tony Chemit - chemit@codelutin.com + * @since 4.5 + */ +public class CruiseSamplingCacheLoader { + + /** Logger. */ + private static final Log log = LogFactory.getLog(CruiseSamplingCacheLoader.class); + + private final PersistenceService persistenceService; + private final DecoratorService decoratorService; + private final ProgressionModel progressionModel; + + public CruiseSamplingCacheLoader(PersistenceService persistenceService, + DecoratorService decoratorService, + ProgressionModel progressionModel) { + this.persistenceService = persistenceService; + this.decoratorService = decoratorService; + this.progressionModel = progressionModel; + } + + public CruiseSamplingCache loadCruiseSamplingCache(TuttiProtocol protocol, Integer cruiseId) { + + Caracteristic sexCaracteristic = persistenceService.getSexCaracteristic(); + + CruiseSamplingCache cruiseSamplingCache = new CruiseSamplingCache(cruiseId, protocol, sexCaracteristic); + + cruiseSamplingCache.setLoading(true); + + try { + if (log.isInfoEnabled()) { + log.info("Loading cruise sampling cache: " + cruiseSamplingCache); + } + + + Decorator<FishingOperation> fishingOperationDecorator = decoratorService.getDecoratorByType(FishingOperation.class); + + persistenceService.getAllFishingOperationIds(cruiseId).forEach(fishingOperationId -> { + + FishingOperation fishingOperation = persistenceService.getFishingOperation(fishingOperationId); + + progressionModel.increments("Chargement du cache d'échantillons pour le trait : " + fishingOperationDecorator.toString(fishingOperation)); + + List<IndividualObservationBatch> allIndividualObservationBatchsForFishingOperation = + persistenceService.getAllIndividualObservationBatchsForFishingOperation(fishingOperationId); + + allIndividualObservationBatchsForFishingOperation.forEach( + individualObservationBatch -> cruiseSamplingCache.addIndividualObservation(fishingOperation, individualObservationBatch)); + + }); + + if (log.isInfoEnabled()) { + log.info("cruise sampling cache loaded: " + cruiseSamplingCache); + } + + return cruiseSamplingCache; + + } finally { + + cruiseSamplingCache.setLoading(false); + + } + + } +} diff --git a/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingInternalCache.java b/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingInternalCache.java index 97f51c9..c12dda1 100644 --- a/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingInternalCache.java +++ b/tutti-service/src/main/java/fr/ifremer/tutti/service/samplingCache/CruiseSamplingInternalCache.java @@ -5,10 +5,15 @@ import fr.ifremer.tutti.persistence.entities.protocol.Zone; import fr.ifremer.tutti.persistence.entities.referential.CaracteristicQualitativeValue; import fr.ifremer.tutti.persistence.entities.referential.Species; import org.apache.commons.lang3.mutable.MutableInt; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import java.io.Closeable; +import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.TreeMap; /** @@ -19,7 +24,20 @@ import java.util.TreeMap; */ class CruiseSamplingInternalCache implements Closeable { - protected final Map<String, MutableInt> data = new TreeMap<>(); + /** Logger. */ + private static final Log log = LogFactory.getLog(CruiseSamplingInternalCache.class); + + public static String addPrefixKey(String prefix, String key) { + return prefix + CruiseSamplingInternalCacheKey.KEY_SEPARATOR + key; + } + + private final Map<String, MutableInt> data = new TreeMap<>(); + + public static CruiseSamplingInternalCacheKey newCruiseSamplingInternalCacheKey(Species species, CaracteristicQualitativeValue gender, Boolean maturity, Float lengthStep) { + Objects.requireNonNull(species); + Objects.requireNonNull(lengthStep); + return new CruiseSamplingInternalCacheKey(species, gender, maturity, lengthStep); + } public int increment(CruiseSamplingInternalCacheKey samplingKey) { MutableInt value = data.computeIfAbsent(samplingKey.toString(), s -> new MutableInt(0)); @@ -28,20 +46,64 @@ class CruiseSamplingInternalCache implements Closeable { } public int decrement(CruiseSamplingInternalCacheKey samplingKey) { - MutableInt value = data.get(samplingKey.toString()); + return decrement(samplingKey.toString()); + } + + public int decrement(String samplingKey) { + MutableInt value = data.get(samplingKey); value.decrement(); return value.intValue(); } + public Integer decrementIfExist(String samplingKey) { + Integer result = null; + MutableInt value = data.get(samplingKey); + if (value != null) { + value.decrement(); + result = value.intValue(); + } + return result; + } + @Override public void close() { data.clear(); } - public static CruiseSamplingInternalCacheKey newCruiseSamplingInternalCacheKey(Species species, CaracteristicQualitativeValue gender, Boolean maturity, Float lengthStep) { - Objects.requireNonNull(species); - Objects.requireNonNull(lengthStep); - return new CruiseSamplingInternalCacheKey(species, gender, maturity, lengthStep); + public int size() { + return data.size(); + } + + /** + * Supprime du cache toutes les entrées dont la clef commence par l'identifiant donné (suivant du séparateur de clef) + * et retourne l'ensemble des clefs supprimées du cache. + * + * À noter que sur les clefs retournées, on a retirer le préfixe de concordance. + * + * @param id l'identifiant de début de clef à supprimer + * @return l'ensemble des clefs supprimées du cache (amputées du préfixe de concordance). + */ + public Set<String> removeAllWhereKeyStartingWith(String id) { + String keyPrefix = id + CruiseSamplingInternalCacheKey.KEY_SEPARATOR; + if (log.isDebugEnabled()) { + log.debug("Ask to remove all keys starting with: " + keyPrefix); + } + Set<String> result = new LinkedHashSet<>(); + Iterator<Map.Entry<String, MutableInt>> iterator = data.entrySet().iterator(); + int keyPrefixLength = keyPrefix.length(); + while (iterator.hasNext()) { + Map.Entry<String, MutableInt> entry = iterator.next(); + String key = entry.getKey(); + + if (key.startsWith(keyPrefix)) { + iterator.remove(); + if (log.isDebugEnabled()) { + log.debug("Removing key: " + key); + } + result.add(key.substring(keyPrefixLength)); + } + } + return result; } /** -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.