/* * Data Hub Service (DHuS) - For Space data distribution. * Copyright (C) 2013,2014,2015,2016 GAEL Systems * * This file is part of DHuS software sources. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.gael.dhus.service; import fr.gael.dhus.database.object.MetadataIndex; import fr.gael.dhus.database.object.Product; import fr.gael.dhus.database.object.Role; import fr.gael.dhus.util.TestContextLoader; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.RandomAccess; import java.util.Set; import java.util.SortedSet; @ContextConfiguration ( locations = { "classpath:fr/gael/dhus/spring/context-test.xml", "classpath:fr/gael/dhus/spring/context-security-test.xml" }, loader = TestContextLoader.class) @DirtiesContext (classMode = DirtiesContext.ClassMode.AFTER_CLASS) public class TestCacheProductService extends AbstractTransactionalTestNGSpringContextTests { private static final Logger LOGGER = LogManager.getLogger(TestCacheProductService.class); @Autowired private ProductService productService; @Autowired private CacheManager cacheManager; private Product productTest; @BeforeClass public void setUp () throws MalformedURLException { initProductTest (); authenticate (); } private void authenticate () { String name = "userTest"; Set<GrantedAuthority> roles = new HashSet<> (); roles.add (new SimpleGrantedAuthority (Role.DOWNLOAD.getAuthority ())); roles.add (new SimpleGrantedAuthority (Role.SEARCH.getAuthority ())); roles.add ( new SimpleGrantedAuthority (Role.DATA_MANAGER.getAuthority ())); SandBoxUser user = new SandBoxUser (name, name, true, 0, roles); Authentication auth = new UsernamePasswordAuthenticationToken ( user, user.getPassword (), roles); SecurityContextHolder.getContext ().setAuthentication (auth); logger.info ("userTest roles: " + auth.getAuthorities ()); } @Test public void testIndexesCache () { // initialize variables Long productId1 = 1L; String uuid1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1"; Long productId2 = 2L; String uuid2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2"; String productsCacheName = "products"; String productIndexCacheName = "indexes"; MetadataIndex mi = new MetadataIndex ("a", "b", "c", "d", "e"); List<MetadataIndex> index = new ArrayList<> (5); index.add (mi); // validate cache Cache pCache = cacheManager.getCache (productsCacheName); Cache iCache = cacheManager.getCache (productIndexCacheName); Assert.assertNull (pCache.get (uuid1, Product.class)); Assert.assertNull (pCache.get (uuid2, Product.class)); Assert.assertNull (iCache.get (productId1, List.class)); Assert.assertNull (iCache.get (productId2, List.class)); // Cacheable Product product1 = productService.getProduct (uuid1); Assert.assertEquals (pCache.get (uuid1, Product.class), product1); Assert.assertNull (pCache.get (uuid2)); Product product2 = productService.getProduct (uuid2); Assert.assertEquals (pCache.get (uuid2, Product.class), product2); List<MetadataIndex> index1 = productService.getIndexes (productId1); Assert.assertTrue ( equalCollection (iCache.get (productId1, List.class), index1)); Assert.assertNull (iCache.get (productId2)); List<MetadataIndex> index2 = productService.getIndexes (productId2); Assert.assertTrue ( equalCollection (iCache.get (productId2, List.class), index2)); // CacheEvict productService.setIndexes (productId1, index); Assert.assertNull (iCache.get (productId1)); Assert.assertNotNull (iCache.get (productId2)); productService.getIndexes (productId1); // re-cached productService.systemDeleteProduct (productId2); Assert.assertNull (iCache.get (productId2)); Assert.assertNotNull (iCache.get (productId1)); Assert.assertNull (pCache.get (uuid1)); Assert.assertNull (pCache.get (uuid2)); } @Test public void testProductCountCache () { fr.gael.dhus.database.object.Collection c = new fr.gael.dhus.database.object.Collection (); c.setUUID ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1"); c.setName ("Asia"); String cache_name = "product_count"; String filter = "upper(p.identifier) LIKE upper('%prod_%')"; Object filter_collection_key = Arrays.asList (filter, c.getUUID ()); Object filter_key = Arrays.asList (filter, null); Object all_key = "all"; Cache cache; // count (Collection, String) Integer expected = productService.count (c, filter); cache = cacheManager.getCache (cache_name); Assert.assertEquals ( cache.get (filter_collection_key, Integer.class), expected); expected = productService.count ((fr.gael.dhus.database.object.Collection)null, filter); Assert.assertEquals (cache.get (filter_key, Integer.class), expected); // count (String) clearCache (); expected = productService.count (filter); Assert.assertEquals (cache.get (filter_key, Integer.class), expected); // count (String, Long) clearCache (); expected = productService.count (filter, c.getUUID ()); Assert.assertEquals ( cache.get (filter_collection_key, Integer.class), expected); // countAuthorizedProducts () expected = productService.count (); Assert.assertEquals (cache.get (all_key, Integer.class), expected); // addProduct (Product) expected = expected + 1; productService.addProduct (productTest); Assert.assertNull (cache.get (filter_collection_key, Integer.class)); Assert.assertNull (cache.get (filter_key, Integer.class)); Assert.assertEquals (cache.get (all_key, Integer.class), expected); // addProduct (URL, User, List, String, Scanner, FileScannerWrapper) // TODO too hard to simulate the call ProductService.processProduct // deleteProduct (Long) expected = productService.count () - 1; productService.deleteProduct (7L); Assert.assertEquals (cache.get (all_key, Integer.class), expected); // systemDeleteProduct (Long) expected = productService.count () - 1; productService.systemDeleteProduct (6L); Assert.assertEquals (cache.get (all_key, Integer.class), expected); } @Test public void testProductCache () throws MalformedURLException { String cache_name = "product"; String uuid; Long pid; Product product; // systemGetProduct (Long) pid = 0L; product = productService.systemGetProduct (pid); Cache cache = cacheManager.getCache (cache_name); Assert.assertNotNull (cache); Assert.assertEquals (cache.get (pid, Product.class), product); // getProduct (Long) pid = 1L; product = productService.getProduct (pid); Assert.assertEquals (cache.get (pid, Product.class), product); // getProductToDownload (Long) product = productService.getProductToDownload (pid); Assert.assertEquals (product, cache.get (pid, Product.class)); // systemGetProduct (String) uuid = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4"; product = productService.systemGetProduct (uuid); Assert.assertEquals (cache.get (uuid, Product.class), product); // getProduct (String) product = productService.getProduct (uuid); Assert.assertEquals (product, cache.get (uuid, Product.class)); // getProduct (String, User) product = productService.getProduct (uuid); Assert.assertEquals (product, cache.get (uuid, Product.class)); // addProduct (Product) productService.addProduct (productTest); Assert.assertNull (cache.get (pid, Product.class)); Assert.assertNull (cache.get (uuid, Product.class)); // addProduct (URL, User, List, String, Scanner, FileScannerWrapper) // TODO too hard to simulate the call ProductService.processProduct // systemDeleteProduct (Long) product = productService.getProduct (0L); productService.systemDeleteProduct (product.getId ()); Assert.assertNull (cache.get (product.getId (), Product.class)); Assert.assertNull (cache.get (product.getUuid (), Product.class)); Assert.assertNull (cache.get (product.getPath (), Product.class)); // deleteProduct (Long) product = productService.getProduct (5L); productService.systemDeleteProduct (product.getId ()); Assert.assertNull (cache.get (product.getId (), Product.class)); Assert.assertNull (cache.get (product.getUuid (), Product.class)); Assert.assertNull (cache.get (product.getPath (), Product.class)); } @Test public void testProductsCache () { String cache_name = "products"; Cache cache = cacheManager.getCache (cache_name); List<Product> products; // getProducts (List<Long>) Object key2 = Arrays.asList (0L, 5L, 6L, 7L); products = productService.getProducts ((List<Long>) key2); Assert.assertEquals (cache.get (key2, List.class), products); // addProduct (Product) productService.addProduct (productTest); Assert.assertNull (cache.get (key2, List.class)); // addProduct (URL, User, List, String, Scanner, FileScannerWrapper) // TODO too hard to simulate the call ProductService.processProduct // deleteProduct (Long) productService.getProducts ((List<Long>) key2); productService.deleteProduct (0L); Assert.assertNull (cache.get (key2, List.class)); // systemDeleteProduct (Long) productService.getProducts ((List<Long>) key2); productService.systemDeleteProduct (5L); Assert.assertNull (cache.get (key2, List.class)); } @BeforeMethod private void clearCache () { LOGGER.info ("### clearing cache for test."); for (String cache_name : cacheManager.getCacheNames ()) { cacheManager.getCache (cache_name).clear (); } } /** * Returns {@code true} if the two specified collections have all * elements in common. * <p/> * <p>Care must be exercised if this method is used on collections that * do not comply with the general contract for {@code Collection}. * Implementations may elect to iterate over either collection and test * for containment in the other collection (or to perform any equivalent * computation). If either collection uses a nonstandard equality test * (as does a {@link SortedSet} whose ordering is not <em>compatible with * equals</em>, or the key set of an {@link IdentityHashMap}), both * collections must use the same nonstandard equality test, or the * result of this method is undefined. * <p/> * <p>Care must also be exercised when using collections that have * restrictions on the elements that they may contain. Collection * implementations are allowed to throw exceptions for any operation * involving elements they deem ineligible. For absolute safety the * specified collections should contain only elements which are * eligible elements for both collections. * <p/> * <p>Note that it is permissible to pass the same collection in both * parameters, in which case the method will return {@code true}. * * @param c1 a collection * @param c2 a collection * @return {@code true} if the two specified collections have all * elements in common. * @throws NullPointerException if either collection is {@code null}. * @throws NullPointerException if one collection contains a {@code null} * element and {@code null} is not an eligible element for the other collection. * (<a href="Collection.html#optional-restrictions">optional</a>) * @throws ClassCastException if one collection contains an element that is * of a type which is ineligible for the other collection. * (<a href="Collection.html#optional-restrictions">optional</a>) */ private boolean equalCollection (final Collection<?> c1, final Collection<?> c2) { // The collection to be used for contains(). Preference is given to // the collection who's contains() has lower O() complexity. Collection<?> contains = c2; // The collection to be iterated. If the collections' contains() impl // are of different O() complexity, the collection with slower // contains() will be used for iteration. For collections who's // contains() are of the same complexity then best performance is // achieved by iterating the smaller collection. Collection<?> iterate = c1; int c1size = c1.size (); int c2size = c2.size (); if (c1size == 0 && c2size == 0) { // Both collections are empty. return true; } if (c1size != c2size) { return false; } // Performance optimization cases. The heuristics: // 1. Generally iterate over c1. // 2. If c1 is a Set then iterate over c2. // 3. If either collection is empty then result is always true. // 4. Iterate over the smaller Collection. if (c1 instanceof Set || c1 instanceof RandomAccess) { // Use c1 for contains as a Set's contains() is expected to perform // better than O(N/2) iterate = c2; contains = c1; } else if (!(c2 instanceof Set || c2 instanceof RandomAccess)) { iterate = c2; contains = c1; } for (Object e : iterate) { if (!contains.contains (e)) { // Found an uncommon element. return false; } } // No uncommon elements were found. return true; } private void initProductTest () throws MalformedURLException { productTest = new Product (); productTest.setUuid ("testaaaaaaaaaaaaaaaaaaaaaaaaaaa0"); productTest.setIdentifier ("test"); productTest.setPath ( new URL ("file://home/lambert/dhus/productTest.zip")); productTest.setLocked (false); productTest.setProcessed (true); productTest.setOrigin ("space"); } }