/* * * This file is part of the Hesperides distribution. * * (https://github.com/voyages-sncf-technologies/hesperides) * * Copyright (c) 2016 VSCT. * * * * Hesperides is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as * * published by the Free Software Foundation, version 3. * * * * Hesperides is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package com.vsct.dt.hesperides.applications.properties.cache; import com.google.common.cache.CacheLoader; import com.vsct.dt.hesperides.applications.PlatformKey; import com.vsct.dt.hesperides.applications.cache.ApplicationStoragePrefixInterface; import com.vsct.dt.hesperides.applications.properties.event.PlatformContainer; import com.vsct.dt.hesperides.applications.virtual.VirtualApplicationsAggregate; import com.vsct.dt.hesperides.storage.EventStore; import com.vsct.dt.hesperides.storage.HesperidesSnapshotItem; import com.vsct.dt.hesperides.templating.platform.PlatformData; import org.slf4j.Logger; import java.util.*; /** * Created by emeric_martineau on 18/01/2016. */ public abstract class AbstractPropertiesCacheLoader<K> extends CacheLoader<K, PlatformContainer> implements ApplicationStoragePrefixInterface { /** * Event store. */ private final EventStore store; /** * Nb events before store cache. */ private final long nbEventBeforePersiste; public AbstractPropertiesCacheLoader(final EventStore store, final long nbEventBeforePersiste) { this.store = store; this.nbEventBeforePersiste = nbEventBeforePersiste; } /** * Return logger. * * @return */ protected abstract Logger getLogger(); /** * Load properties from database. * * @param key key * @param timestamp timestamp * * @return properties * * @throws Exception if not found. */ protected PlatformContainer loadProperties(final PlatformKey key, final long timestamp) throws Exception { if (timestamp == Long.MAX_VALUE) { getLogger().debug("Load properties with key '{}'.", key); } else { getLogger().debug("Load properties with key '{}' on timestamp '{}'.", key, timestamp); } // Redis key pattern to search all application platform final String redisKey = generateDbKey(key); // Properties builder PlatformContainer propertiesBuilder; HesperidesSnapshotItem hesperidesSnapshotItem; if (timestamp == Long.MAX_VALUE) { // In case of max value, we get last snapshot hesperidesSnapshotItem = store.findLastSnapshot(redisKey); } else { hesperidesSnapshotItem = store.findSnapshot(redisKey, nbEventBeforePersiste, e -> e.getTimestamp() > timestamp); } if (hesperidesSnapshotItem == null || hesperidesSnapshotItem.getCurrentNbEvents() < hesperidesSnapshotItem.getNbEvents()) { propertiesBuilder = new PlatformContainer(); final VirtualApplicationsAggregate virtualApplicationsAggregate = new VirtualApplicationsAggregate(store); virtualApplicationsAggregate.replay(redisKey); updatePropertiesContainer(propertiesBuilder, virtualApplicationsAggregate); } else { propertiesBuilder = (PlatformContainer) hesperidesSnapshotItem.getSnapshot(); final VirtualApplicationsAggregate virtualApplicationsAggregate = new VirtualApplicationsAggregate( store, propertiesBuilder.getPlatform(), propertiesBuilder.getProperties()); final long start = hesperidesSnapshotItem.getNbEvents(); final long stop = hesperidesSnapshotItem.getCurrentNbEvents(); if (timestamp == Long.MAX_VALUE) { virtualApplicationsAggregate.replay(redisKey, start, stop); } else if (hesperidesSnapshotItem.getCurrentNbEvents() > hesperidesSnapshotItem.getNbEvents()) { virtualApplicationsAggregate.replay(redisKey, start, stop, timestamp); } updatePropertiesContainer(propertiesBuilder, virtualApplicationsAggregate); } // Can't return null !!!! return propertiesBuilder; } /** * Update properties container. * * @param propertiesBuilder * @param virtualApplicationsAggregate */ private void updatePropertiesContainer( final PlatformContainer propertiesBuilder, final VirtualApplicationsAggregate virtualApplicationsAggregate) { final Optional<PlatformData> platform = virtualApplicationsAggregate.getPlatform(); if (platform.isPresent()) { propertiesBuilder.setPlatform( platform.get()); propertiesBuilder.addProperties( virtualApplicationsAggregate.getProperties()); } } /** * Return all plaform key for application. * * @param applicationName applicationName or null * * @return list of platform key */ public List<PlatformKey> getAllPlatformKeyFromApplication(final String applicationName) { final List<PlatformKey> listPlatformKey; // Redis key pattern to search all application platform final String redisKey; if (applicationName == null) { getLogger().debug("Load all platforms keys for all application."); redisKey = String.format("%s-*", getStreamPrefix()); } else { getLogger().debug("Load all platforms keys for application '{}' from store.", applicationName); redisKey = String.format("%s-%s-*", getStreamPrefix(), applicationName); } // All application platform redis key. final Set<String> platforms = this.store.getStreamsLike(redisKey); listPlatformKey = new ArrayList<>(platforms.size()); for (String platformRedisKey : platforms) { listPlatformKey.add( new PlatformKey( entityNameFormRedisKey(platformRedisKey))); } if (applicationName == null) { getLogger().debug("All platform keys for all application are loaded."); } else { getLogger().debug("All platform keys for application '{}' are loaded.", applicationName); } return listPlatformKey; } /** * Return list from application name. * * @param platformKey list of key * * @return list of application (never return null. Maybe return empty list) */ public Map<PlatformKey, PlatformContainer> getPlatformFromApplication(final List<PlatformKey> platformKey) { getLogger().debug("Load platform for {} keys.", platformKey.size()); // List of platform return by method final Map<PlatformKey, PlatformContainer> listPlatform = new HashMap<>(platformKey.size()); // Current platform PlatformContainer currentPlatform; // Key of redis String platformRedisKey; for (PlatformKey ptfKey : platformKey) { platformRedisKey = generateDbKey(ptfKey); getLogger().debug("Load platform from store associate with key '{}'.", platformRedisKey); try { currentPlatform = loadProperties(ptfKey, Long.MAX_VALUE); } catch (Exception e) { e.printStackTrace(); currentPlatform = null; } // Platform can be remove at last event if (currentPlatform != null) { listPlatform.put(ptfKey, currentPlatform); } } getLogger().debug("{} platforms loaded.", listPlatform.size()); return listPlatform; } /** * Generate name of entity. * * @param redisKey * @return */ private String entityNameFormRedisKey(final String redisKey) { return redisKey.substring(getStreamPrefix().length() + 1); } /** * Generate key to search in database. * * @param ptfKey namespace of module * * @return db key */ private String generateDbKey(final PlatformKey ptfKey) { return String.format("%s-%s", getStreamPrefix(), ptfKey.getEntityName()); } /** * Store object in snapshot. * * @param platformKey key of cache (same as cache.get(K)) * @param object object */ public void saveSnapshot(final PlatformKey platformKey, final PlatformContainer object) { final String redisKey = generateDbKey(platformKey); // Now store snapshot store.storeSnapshot(redisKey, object, nbEventBeforePersiste); } /** * Store object in snapshot. * * @param platformKey key of cache (same as cache.get(K)) * @param object object */ public void forceSaveSnapshot(final PlatformKey platformKey, final PlatformContainer object, final long nbEvent) { final String redisKey = generateDbKey(platformKey); // Now store snapshot store.createSnapshot(redisKey, object, nbEvent); } }