/* * * * 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; import com.google.common.cache.LoadingCache; import com.vsct.dt.hesperides.HesperidesCacheParameter; import com.vsct.dt.hesperides.applications.properties.PropertiesRegistryInterface; import com.vsct.dt.hesperides.applications.properties.cache.PropertiesCacheLoader; import com.vsct.dt.hesperides.applications.properties.cache.PropertiesTimelineCacheLoader; import com.vsct.dt.hesperides.applications.properties.event.PlatformContainer; import com.vsct.dt.hesperides.storage.EventStore; import com.vsct.dt.hesperides.templating.platform.PlatformData; import com.vsct.dt.hesperides.templating.platform.PropertiesData; import com.vsct.dt.hesperides.util.HesperidesCacheBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; /** * This platform registry holds everything inmemory. * It might be updated for further development in order to manage memory in a better and safier way */ class PlatformRegistry implements PropertiesRegistryInterface, PlatformRegistryInterface { /** * Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(PlatformRegistry.class); /** * Little cache to retain platform loaded at a specific point in time * It is very likely for users to perform read operations on that platform once they loaded it */ private final LoadingCache<PlatformTimelineKey, PlatformContainer> cacheTimelineProperties; /** * Cache contain applicaiton or load it. */ private final LoadingCache<PlatformKey, PlatformContainer> cache; /** * Cache loader. */ private final PropertiesCacheLoader propertiesCacheLoader; /** * Constructor. * * @param store store of event for lazy load * @param nbEventBeforePersiste nb event before save * @param config config of cache for platform * @param configTimeLine config of cache for platform in the past */ public PlatformRegistry(final EventStore store, final long nbEventBeforePersiste, final HesperidesCacheParameter config, final HesperidesCacheParameter configTimeLine) { this.propertiesCacheLoader = new PropertiesCacheLoader(store, nbEventBeforePersiste); this.cacheTimelineProperties = HesperidesCacheBuilder.newBuilder(configTimeLine, (key, value) -> ((PlatformContainer) value).getProperties().size()) .build(new PropertiesTimelineCacheLoader(store, nbEventBeforePersiste)); this.cache = HesperidesCacheBuilder.newBuilder(config) .build(this.propertiesCacheLoader); } @Override public Optional<PlatformData> getPlatform(final PlatformKey key) { try { LOGGER.debug("Search platform by key '{}'.", key); return Optional.ofNullable(this.cache.get(key).getPlatform()); } catch (final ExecutionException e) { LOGGER.debug("Platform (key '{}' not found !", key); // Platform not found return Optional.empty(); } } @Override public Optional<PlatformData> getPlatform(final PlatformTimelineKey key) { try { LOGGER.debug("Search in past platform by key '{}'.", key); return Optional.ofNullable(this.cacheTimelineProperties.get(key).getPlatform()); } catch (final ExecutionException e) { LOGGER.debug("Platform in past (key '{}' not found !", key); // Platform not found return Optional.empty(); } } @Override public void createOrUpdatePlatform(final PlatformData platform) { LOGGER.debug("Add platform '{}'", platform); PlatformContainer platformContainer; try { platformContainer = this.cache.get(platform.getKey()); } catch (final ExecutionException e) { // When not found in database -> create platformContainer = new PlatformContainer(); } // Update module platformContainer.setPlatform(platform); // Write snapshot this.propertiesCacheLoader.saveSnapshot(platform.getKey(), platformContainer); } @Override public List<PlatformData> getPlatformsForApplication(final String applicationName) { LOGGER.debug("Get all platforms for application '{}'", applicationName == null ? "all" : applicationName); // Get all key of platform final List<PlatformKey> platformKey = this.propertiesCacheLoader.getAllPlatformKeyFromApplication(applicationName); // Create result list to return to this method final List<PlatformData> listCachePlatform = new ArrayList<>(platformKey.size()); // Filter key if platform is loaded final List<PlatformKey> platformKeyToLoad = platformKey.stream().filter(key -> { final PlatformContainer platformContainer = this.cache.getIfPresent(key); final boolean notExists = platformContainer == null; if (!notExists) { listCachePlatform.add(platformContainer.getPlatform()); } return notExists; }).collect(Collectors.toList()); // Get platform final Map<PlatformKey, PlatformContainer> list = this.propertiesCacheLoader.getPlatformFromApplication(platformKeyToLoad); for (Map.Entry<PlatformKey, PlatformContainer> entryPlatform : list.entrySet()) { LOGGER.debug("Platform '{}' is missing in cache, put it.", entryPlatform.getKey()); this.cache.put(entryPlatform.getKey(), entryPlatform.getValue()); } // Convert Map to list final List<PlatformData> missingCachePlatform = list.entrySet().stream() .map(platformKeyPlatformContainerEntry -> platformKeyPlatformContainerEntry.getValue().getPlatform()) .collect(Collectors.toList()); listCachePlatform.addAll(missingCachePlatform); return listCachePlatform.stream().filter(p -> p != null) .collect(Collectors.toList()); } @Override public void deletePlatform(final PlatformKey key) { try { PlatformContainer platformContainer; // Load snapshot platformContainer = this.cache.get(key); // Update module platformContainer.setPlatform(null); // Write snapshot this.propertiesCacheLoader.saveSnapshot(key, platformContainer); } catch (final ExecutionException e) { // ??? normaly not ! } } @Override public Collection<PlatformData> getAllPlatforms() { LOGGER.debug("Get all platform."); // Return all platform return getPlatformsForApplication(null); } @Override public void removeFromCache(final PlatformKey key) { this.cache.invalidate(key); } /** * @param applicationName * @param platformName * @param path * @return The properties entity or empty */ @Override public Optional<PropertiesData> getProperties(final String applicationName, final String platformName, final String path) { try { LOGGER.debug("Search properties for application '{}' on platform '{}' with path '{}'.", applicationName, platformName, path); final PlatformKey platformKey = new PlatformKey(applicationName, platformName); final PlatformContainer platformContainer = this.cache.get(platformKey); final PropertiesData prop = platformContainer.getProperties().get(path); return Optional.ofNullable(prop); } catch (final ExecutionException e) { LOGGER.debug("Properties for application '{}' on platform '{}' with path '{}' not found !", applicationName, platformName, path); // Platform not found return Optional.empty(); } } /** * @param applicationName * @param platformName * @param path * @param timestamp * * @return The properties entity or empty */ @Override public Optional<PropertiesData> getProperties(final String applicationName, final String platformName, final String path, final long timestamp) { try { if (LOGGER.isDebugEnabled()) { final SimpleDateFormat sdf = new SimpleDateFormat("MMM dd,yyyy HH:mm"); final Date resultdate = new Date(timestamp); LOGGER.debug("Search properties for application '{}' on platform '{}' with path '{}' at date {}.", applicationName, platformName, path, sdf.format(resultdate)); } final PlatformTimelineKey platformKey = new PlatformTimelineKey(new PlatformKey(applicationName, platformName), timestamp); final PlatformContainer platformContainer = this.cacheTimelineProperties.get(platformKey); final PropertiesData prop = platformContainer.getProperties().get(path); return Optional.ofNullable(prop); } catch (final ExecutionException e) { if (LOGGER.isDebugEnabled()) { final SimpleDateFormat sdf = new SimpleDateFormat("MMM dd,yyyy HH:mm"); final Date resultdate = new Date(timestamp); LOGGER.debug("Properties for application '{}' on platform '{}' with path '{}' at date {} not found !.", applicationName, platformName, path, sdf.format(resultdate)); } // Platform not found return Optional.empty(); } } /** * @param fromApplication * @param fromPlatform * @return */ @Override public Map<String, PropertiesData> getProperties(String fromApplication, String fromPlatform) { try { LOGGER.debug("Get all properties for application '{}' on platform '{}'.", fromApplication, fromPlatform); final PlatformKey platformKey = new PlatformKey(fromApplication, fromPlatform); final PlatformContainer platformContainer = this.cache.get(platformKey); final Map<String, PropertiesData> prop = platformContainer.getProperties(); final Map<String, PropertiesData> newHashMap = new HashMap<>(); newHashMap.putAll(prop); return newHashMap; } catch (final ExecutionException e) { LOGGER.debug("All properties for application '{}' on platform '{}' not found.", fromApplication, fromPlatform); // Platform not found return new HashMap<>(); } } /** * @param applicationName * @param platformName * @param path * @param entity */ @Override public void createOrUpdateProperties(final String applicationName, final String platformName, final String path, final PropertiesData entity) { try { LOGGER.debug("Add or update properties for application '{}' on platform '{}' with path '{}'.", applicationName, platformName, path); final PlatformKey platformKey = new PlatformKey(applicationName, platformName); final PlatformContainer platformContainer = this.cache.get(platformKey); platformContainer.getProperties().put(path, entity); this.propertiesCacheLoader.saveSnapshot(platformKey, platformContainer); } catch (final ExecutionException e) { LOGGER.debug("Can't add or update properties for application '{}' on platform '{}' with path '{}'.", applicationName, platformName, path); // Platform not found } } @Override public void removeFromCache(final String applicationName, final String platformName) { this.cache.invalidate(new PlatformKey(applicationName, platformName)); } @Override public void removeAllCache() { this.cache.invalidateAll(); } }