diff --git a/scm-webapp/src/main/java/sonia/scm/cache/GuavaBaseCache.java b/scm-webapp/src/main/java/sonia/scm/cache/GuavaBaseCache.java new file mode 100644 index 0000000000..24f7b05ea7 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/cache/GuavaBaseCache.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2014, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ +package sonia.scm.cache; + +import com.google.common.cache.Cache; +import com.google.common.collect.Sets; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.Filter; + +/** + * Implementation of basic cache methods for guava based caches. + * + * @author Sebastian Sdorra + * + * @since 1.52 + * + * @param key + * @param value + */ +public abstract class GuavaBaseCache { + + /** + * the logger for GuavaBaseCache + */ + private static final Logger logger = LoggerFactory.getLogger(GuavaCache.class); + + protected com.google.common.cache.Cache cache; + protected CopyStrategy copyStrategy = CopyStrategy.NONE; + private final String name; + + GuavaBaseCache(com.google.common.cache.Cache cache, CopyStrategy copyStrategy, String name) { + this.cache = cache; + this.name = name; + + if (copyStrategy != null) { + this.copyStrategy = copyStrategy; + } + } + + //~--- methods -------------------------------------------------------------- + + public void clear() { + logger.debug("clear cache {}", name); + cache.invalidateAll(); + } + + public boolean contains(K key) { + return cache.getIfPresent(key) != null; + } + + public boolean removeAll(Filter filter) { + Set keysToRemove = Sets.newHashSet(); + + for (K key : cache.asMap().keySet()) { + if (filter.accept(key)) { + keysToRemove.add(key); + } + } + + boolean result = false; + + if (!keysToRemove.isEmpty()) { + cache.invalidateAll(keysToRemove); + result = true; + } + + return result; + } + + public V get(K key) { + V value = cache.getIfPresent(key); + + if (value != null) { + value = copyStrategy.copyOnRead(value); + } + + return value; + } + + public Cache getWrappedCache() { + return cache; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/cache/GuavaCache.java b/scm-webapp/src/main/java/sonia/scm/cache/GuavaCache.java index 9c761fd620..6481936d65 100644 --- a/scm-webapp/src/main/java/sonia/scm/cache/GuavaCache.java +++ b/scm-webapp/src/main/java/sonia/scm/cache/GuavaCache.java @@ -34,18 +34,6 @@ package sonia.scm.cache; //~--- non-JDK imports -------------------------------------------------------- -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Sets; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sonia.scm.Filter; - -//~--- JDK imports ------------------------------------------------------------ - -import java.util.Set; - /** * * @author Sebastian Sdorra @@ -53,185 +41,22 @@ import java.util.Set; * @param * @param */ -public class GuavaCache implements Cache -{ +public class GuavaCache extends GuavaBaseCache implements Cache { - /** - * the logger for GuavaCache - */ - private static final Logger logger = - LoggerFactory.getLogger(GuavaCache.class); - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param configuration - */ - public GuavaCache(GuavaNamedCacheConfiguration configuration) - { - this(configuration, configuration.getName()); + GuavaCache(com.google.common.cache.Cache cache, CopyStrategy copyStrategy, String name) { + super(cache, copyStrategy, name); } - - /** - * Constructs ... - * - * - * @param configuration - * @param name - */ - public GuavaCache(GuavaCacheConfiguration configuration, String name) - { - this(GuavaCaches.create(configuration, name), - configuration.getCopyStrategy(), name); - } - - /** - * Constructs ... - * - * - * @param cache - * @param copyStrategy - * @param name - */ - @VisibleForTesting - protected GuavaCache(com.google.common.cache.Cache cache, - CopyStrategy copyStrategy, String name) - { - this.cache = cache; - this.name = name; - - if (copyStrategy != null) - { - this.copyStrategy = copyStrategy; - } - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - */ + @Override - public void clear() - { - if (logger.isDebugEnabled()) - { - logger.debug("clear cache {}", name); - } - - cache.invalidateAll(); - } - - /** - * Method description - * - * - * @param key - * - * @return - */ - @Override - public boolean contains(K key) - { - return cache.getIfPresent(key) != null; - } - - /** - * Method description - * - * - * @param key - * @param value - */ - @Override - public void put(K key, V value) - { + public void put(K key, V value) { cache.put(key, copyStrategy.copyOnWrite(value)); } - /** - * Method description - * - * - * @param key - * - * @return - */ @Override - public boolean remove(K key) - { + public boolean remove(K key) { cache.invalidate(key); return true; - } - - /** - * Method description - * - * - * @param filter - * - * @return - */ - @Override - public boolean removeAll(Filter filter) - { - Set keysToRemove = Sets.newHashSet(); - - for (K key : cache.asMap().keySet()) - { - if (filter.accept(key)) - { - keysToRemove.add(key); - } - } - - boolean result = false; - - if (!keysToRemove.isEmpty()) - { - cache.invalidateAll(keysToRemove); - result = true; - } - - return result; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param key - * - * @return - */ - @Override - public V get(K key) - { - V value = cache.getIfPresent(key); - - if (value != null) - { - value = copyStrategy.copyOnRead(value); - } - - return value; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private com.google.common.cache.Cache cache; - - /** Field description */ - private CopyStrategy copyStrategy = CopyStrategy.NONE; - - /** Field description */ - private String name; + } + } diff --git a/scm-webapp/src/main/java/sonia/scm/cache/GuavaCacheManager.java b/scm-webapp/src/main/java/sonia/scm/cache/GuavaCacheManager.java index 87fe2d9d15..fb112c0107 100644 --- a/scm-webapp/src/main/java/sonia/scm/cache/GuavaCacheManager.java +++ b/scm-webapp/src/main/java/sonia/scm/cache/GuavaCacheManager.java @@ -47,66 +47,52 @@ import java.io.IOException; import java.util.Map; +import org.apache.shiro.cache.CacheException; + /** - * + * Guava based implementation of {@link CacheManager} and {@link org.apache.shiro.cache.CacheManager}. + * * @author Sebastian Sdorra */ @Singleton -public class GuavaCacheManager implements CacheManager +public class GuavaCacheManager implements CacheManager, org.apache.shiro.cache.CacheManager { /** * the logger for GuavaCacheManager */ - private static final Logger logger = - LoggerFactory.getLogger(GuavaCacheManager.class); + private static final Logger logger = LoggerFactory.getLogger(GuavaCacheManager.class); + private volatile Map cacheMap = Maps.newHashMap(); + + private GuavaCacheConfiguration defaultConfiguration; + + //~--- constructors --------------------------------------------------------- - /** - * Constructs ... - * - */ - public GuavaCacheManager() - { + public GuavaCacheManager() { this(GuavaCacheConfigurationReader.read()); } - /** - * Constructs ... - * - * - * @param config - */ @VisibleForTesting - protected GuavaCacheManager(GuavaCacheManagerConfiguration config) - { + protected GuavaCacheManager(GuavaCacheManagerConfiguration config) { defaultConfiguration = config.getDefaultCache(); - for (GuavaNamedCacheConfiguration ncc : config.getCaches()) - { - logger.debug("create cache {} from configured configuration {}", - ncc.getName(), ncc); - cacheMap.put(ncc.getName(), new GuavaCache(ncc)); + for (GuavaNamedCacheConfiguration ncc : config.getCaches()) { + logger.debug("create cache {} from configured configuration {}", ncc.getName(), ncc); + cacheMap.put(ncc.getName(), new CacheWithConfiguration( + GuavaCaches.create(defaultConfiguration, ncc.getName()), + defaultConfiguration) + ); } } - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @throws IOException - */ @Override - public void close() throws IOException - { + public void close() throws IOException { logger.info("close guava cache manager"); - for (Cache c : cacheMap.values()) - { - c.clear(); + for (CacheWithConfiguration c : cacheMap.values()) { + c.cache.invalidateAll(); } cacheMap.clear(); @@ -114,43 +100,44 @@ public class GuavaCacheManager implements CacheManager //~--- get methods ---------------------------------------------------------- - /** - * Method description - * - * - * @param key - * @param value - * @param name - * @param - * @param - * - * @return - */ - @Override - public synchronized GuavaCache getCache(Class key, - Class value, String name) - { + private synchronized CacheWithConfiguration getCacheWithConfiguration(String name) { logger.trace("try to retrieve cache {}", name); - GuavaCache cache = cacheMap.get(name); + CacheWithConfiguration cache = cacheMap.get(name); - if (cache == null) - { + if (cache == null) { logger.debug( "cache {} does not exists, creating a new instance from default configuration: {}", - name, defaultConfiguration); - cache = new GuavaCache(defaultConfiguration, name); + name, defaultConfiguration + ); + cache = new CacheWithConfiguration(GuavaCaches.create(defaultConfiguration, name), defaultConfiguration); cacheMap.put(name, cache); } - + return cache; } + + @Override + public GuavaCache getCache(Class key, Class value, String name) { + CacheWithConfiguration cw = getCacheWithConfiguration(name); + return new GuavaCache<>(cw.cache, cw.configuration.getCopyStrategy(), name); + } + + @Override + public GuavaSecurityCache getCache(String name) throws CacheException { + CacheWithConfiguration cw = getCacheWithConfiguration(name); + return new GuavaSecurityCache<>(cw.cache, cw.configuration.getCopyStrategy(), name); + } - //~--- fields --------------------------------------------------------------- + private static class CacheWithConfiguration { + + private final com.google.common.cache.Cache cache; + private final GuavaCacheConfiguration configuration; - /** Field description */ - private volatile Map cacheMap = Maps.newHashMap(); - - /** Field description */ - private GuavaCacheConfiguration defaultConfiguration; + private CacheWithConfiguration(com.google.common.cache.Cache cache, GuavaCacheConfiguration configuration) { + this.cache = cache; + this.configuration = configuration; + } + + } } diff --git a/scm-webapp/src/main/java/sonia/scm/cache/GuavaSecurityCache.java b/scm-webapp/src/main/java/sonia/scm/cache/GuavaSecurityCache.java new file mode 100644 index 0000000000..679110eb65 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/cache/GuavaSecurityCache.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2014, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ +package sonia.scm.cache; + +import com.google.common.cache.Cache; +import java.util.Collection; +import java.util.Set; +import org.apache.shiro.cache.CacheException; + +/** + * Guava based implementation of {@link org.apache.shiro.cache.Cache}. + * + * @author Sebastian Sdorra + * + * @since 1.52 + * + * @param + * @param + */ +public class GuavaSecurityCache extends GuavaBaseCache implements org.apache.shiro.cache.Cache { + + GuavaSecurityCache(Cache cache, CopyStrategy copyStrategy, String name) { + super(cache, copyStrategy, name); + } + + @Override + public V put(K key, V value) throws CacheException { + V previousValue = cache.getIfPresent(key); + cache.put(key, value); + return previousValue; + } + + @Override + public V remove(K key) throws CacheException { + V previousValue = cache.getIfPresent(key); + cache.invalidate(key); + return previousValue; + } + + @Override + public int size() { + return (int) cache.size(); + } + + @Override + public Set keys() { + return cache.asMap().keySet(); + } + + @Override + public Collection values() { + return cache.asMap().values(); + } + +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 315f19d1e5..cbc22ee553 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -333,7 +333,7 @@ public class AuthorizationCollector * * @return */ - AuthorizationInfo collect(PrincipalCollection principals) + public AuthorizationInfo collect(PrincipalCollection principals) { Preconditions.checkNotNull(principals, "principals parameter is required"); diff --git a/scm-webapp/src/test/java/sonia/scm/cache/CacheManagerTestBase.java b/scm-webapp/src/test/java/sonia/scm/cache/CacheManagerTestBase.java index d2290c8143..7332ecb39b 100644 --- a/scm-webapp/src/test/java/sonia/scm/cache/CacheManagerTestBase.java +++ b/scm-webapp/src/test/java/sonia/scm/cache/CacheManagerTestBase.java @@ -159,8 +159,7 @@ public abstract class CacheManagerTestBase * @param c1 * @param c2 */ - protected void assertIsSame(Cache c1, - Cache c2) + protected void assertIsSame(Cache c1, Cache c2) { assertSame(c1, c2); } diff --git a/scm-webapp/src/test/java/sonia/scm/cache/GuavaCacheManagerTest.java b/scm-webapp/src/test/java/sonia/scm/cache/GuavaCacheManagerTest.java index 2c1e991c49..3d91a56f40 100644 --- a/scm-webapp/src/test/java/sonia/scm/cache/GuavaCacheManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/cache/GuavaCacheManagerTest.java @@ -32,6 +32,8 @@ package sonia.scm.cache; +import org.junit.Assert; + /** * * @author Sebastian Sdorra @@ -50,4 +52,14 @@ public class GuavaCacheManagerTest extends CacheManagerTestBase { return CacheTestUtil.createDefaultGuavaCacheManager(); } + + @Override + protected void assertIsSame(Cache c1, Cache c2) { + Assert.assertSame( + ((GuavaCache)c1).getWrappedCache(), + ((GuavaCache)c2).getWrappedCache() + ); + } + + } diff --git a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java new file mode 100644 index 0000000000..9fb78f5879 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java @@ -0,0 +1,223 @@ +/** + * Copyright (c) 2014, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ +package sonia.scm.repository; + +import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Provider; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.mgt.DefaultSecurityManager; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.subject.SimplePrincipalCollection; +import org.apache.shiro.util.ThreadContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.SCMContextProvider; +import sonia.scm.Type; +import sonia.scm.cache.GuavaCacheManager; +import sonia.scm.config.ScmConfiguration; +import sonia.scm.security.AuthorizationCollector; +import sonia.scm.security.DefaultKeyGenerator; +import sonia.scm.security.KeyGenerator; +import sonia.scm.security.RepositoryPermissionResolver; +import sonia.scm.security.SecuritySystem; +import sonia.scm.user.UserTestData; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class DefaultRepositoryManagerPerfTest { + + private static final int REPOSITORY_COUNT = 2000; + + private static final String REPOSITORY_TYPE = "perf"; + + @Mock + private SCMContextProvider contextProvider; + + @Mock + private RepositoryDAO repositoryDAO; + + @Mock + private PreProcessorUtil preProcessorUtil; + + private final ScmConfiguration configuration = new ScmConfiguration(); + + private final KeyGenerator keyGenerator = new DefaultKeyGenerator(); + + @Mock + private RepositoryHandler repositoryHandler; + + private DefaultRepositoryManager repositoryManager; + + @Mock + private SecuritySystem securitySystem; + + private AuthorizationCollector authzCollector; + + @Before + public void setUpObjectUnderTest(){ + when(repositoryHandler.getType()).thenReturn(new Type(REPOSITORY_TYPE, REPOSITORY_TYPE)); + Set handlerSet = ImmutableSet.of(repositoryHandler); + Provider> repositoryListenersProvider = new SetProvider(); + Provider> repositoryHooksProvider = new SetProvider(); + + repositoryManager = new DefaultRepositoryManager( + configuration, + contextProvider, + keyGenerator, + repositoryDAO, + handlerSet, + repositoryListenersProvider, + repositoryHooksProvider, + preProcessorUtil + ); + + setUpTestRepositories(); + + GuavaCacheManager cacheManager = new GuavaCacheManager(); + authzCollector = new AuthorizationCollector(cacheManager, repositoryDAO, securitySystem, new RepositoryPermissionResolver()); + DefaultSecurityManager securityManager = new DefaultSecurityManager(new DummyRealm(authzCollector, cacheManager)); + + ThreadContext.bind(securityManager); + } + + @After + public void tearDown(){ + ThreadContext.unbindSecurityManager(); + } + + @Test(timeout = 6000l) + public void perfTestGetAll(){ + SecurityUtils.getSubject().login(new UsernamePasswordToken("trillian", "secret")); + + List times = new ArrayList<>(); + for ( int i=0; i<3; i++ ) { + times.add(benchGetAll()); + } + + long average = calculateAverage(times); + double value = (double) average / TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS); + + // Too bad this functionality is not exposed as a regular method call + System.out.println( String.format("%.4g s", value) ); + } + +private long calculateAverage(List times) { + Long sum = 0l; + if(!times.isEmpty()) { + for (Long time : times) { + sum += time; + } + return Math.round(sum.doubleValue() / times.size()); + } + return sum; +} + + private long benchGetAll(){ + Stopwatch sw = Stopwatch.createStarted(); + System.out.append("found ").append(String.valueOf(repositoryManager.getAll().size())); + sw.stop(); + System.out.append(" in ").println(sw); + return sw.elapsed(TimeUnit.MILLISECONDS); + } + + private void setUpTestRepositories() { + Map repositories = new LinkedHashMap<>(); + for ( int i=0; i