From aaea181784d9ec19820ac5493ad7a029fcba3ec4 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 24 Jul 2011 14:14:20 +0200 Subject: [PATCH] added caching for changesetviewer --- .../scm/NotSupportedFeatuerException.java | 65 ++++ .../scm/repository/ChangesetViewerUtil.java | 320 ++++++++++++++++++ .../RepositoryNotFoundException.java | 19 ++ .../main/java/sonia/scm/ScmServletModule.java | 2 + .../rest/resources/RepositoryResource.java | 42 +-- .../src/main/resources/config/ehcache.xml | 9 + 6 files changed, 437 insertions(+), 20 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/NotSupportedFeatuerException.java create mode 100644 scm-core/src/main/java/sonia/scm/repository/ChangesetViewerUtil.java diff --git a/scm-core/src/main/java/sonia/scm/NotSupportedFeatuerException.java b/scm-core/src/main/java/sonia/scm/NotSupportedFeatuerException.java new file mode 100644 index 0000000000..e6fea6cfa7 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/NotSupportedFeatuerException.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010, 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; + +/** + * + * @author Sebastian Sdorra + * @version 1.6 + */ +public class NotSupportedFeatuerException extends Exception +{ + + /** Field description */ + private static final long serialVersionUID = 256498734456613496L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + public NotSupportedFeatuerException() {} + + /** + * Constructs ... + * + * + * @param message + */ + public NotSupportedFeatuerException(String message) + { + super(message); + } +} diff --git a/scm-core/src/main/java/sonia/scm/repository/ChangesetViewerUtil.java b/scm-core/src/main/java/sonia/scm/repository/ChangesetViewerUtil.java new file mode 100644 index 0000000000..e313fce585 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/ChangesetViewerUtil.java @@ -0,0 +1,320 @@ +/** + * Copyright (c) 2010, 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.NotSupportedFeatuerException; +import sonia.scm.cache.Cache; +import sonia.scm.cache.CacheManager; +import sonia.scm.util.AssertUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Arrays; +import java.util.Collection; + +/** + * + * @author Sebastian Sdorra + * @since 1.6 + */ +public class ChangesetViewerUtil implements RepositoryHook +{ + + /** Field description */ + public static final String CACHE_NAME = "sonia.cache.repository.changesets"; + + /** the logger for ChangesetViewerUtil */ + private static final Logger logger = + LoggerFactory.getLogger(ChangesetViewerUtil.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param repositoryManager + * @param cacheManager + */ + @Inject + public ChangesetViewerUtil(RepositoryManager repositoryManager, + CacheManager cacheManager) + { + this.repositoryManager = repositoryManager; + this.repositoryManager.addHook(this); + this.cache = cacheManager.getCache(ChangesetViewerCacheKey.class, + ChangesetPagingResult.class, CACHE_NAME); + } + + //~--- methods -------------------------------------------------------------- + + /** + * TODO: only remove objects from the given repository + * + * + * @param event + */ + @Override + public void onEvent(RepositoryHookEvent event) + { + if (logger.isDebugEnabled()) + { + logger.debug("clear cache because repository {} has changed", + event.getRepository().getName()); + } + + cache.clear(); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param repositoryId + * @param start + * @param max + * + * @return + * + * @throws NotSupportedFeatuerException + * @throws RepositoryException + */ + public ChangesetPagingResult getChangesets(String repositoryId, int start, + int max) + throws RepositoryException, NotSupportedFeatuerException + { + AssertUtil.assertIsNotEmpty(repositoryId); + + Repository repository = repositoryManager.get(repositoryId); + + if (repository == null) + { + throw new RepositoryNotFoundException( + "could not find repository with id ".concat(repositoryId)); + } + + return getChangesets(repository, start, max); + } + + /** + * Method description + * + * + * @param repository + * @param start + * @param max + * + * @return + * + * @throws NotSupportedFeatuerException + * @throws RepositoryException + */ + public ChangesetPagingResult getChangesets(Repository repository, int start, + int max) + throws RepositoryException, NotSupportedFeatuerException + { + AssertUtil.assertIsNotNull(repository); + + ChangesetViewer viewer = repositoryManager.getChangesetViewer(repository); + + if (viewer == null) + { + throw new NotSupportedFeatuerException( + "ChangesetViewer is not supported for type ".concat( + repository.getType())); + } + + ChangesetViewerCacheKey key = + new ChangesetViewerCacheKey(repository.getId(), start, max); + ChangesetPagingResult result = cache.get(key); + + if (result == null) + { + result = viewer.getChangesets(start, max); + cache.put(key, result); + } + else if (logger.isDebugEnabled()) + { + logger.debug("fetch changesetviewer results from cache"); + } + + return result; + } + + /** + * Method description + * + * + * @return + */ + @Override + public Collection getTypes() + { + return Arrays.asList(RepositoryHookType.POST_RECEIVE); + } + + /** + * Method description + * + * + * @return + */ + @Override + public boolean isAsync() + { + return true; + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 11/07/24 + * @author Enter your name here... + */ + private class ChangesetViewerCacheKey + { + + /** + * Constructs ... + * + * + * @param repository + * @param start + * @param max + */ + public ChangesetViewerCacheKey(String repository, int start, int max) + { + this.repository = repository; + this.start = start; + this.max = max; + } + + //~--- methods ------------------------------------------------------------ + + /** + * Method description + * + * + * @param obj + * + * @return + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final ChangesetViewerCacheKey other = (ChangesetViewerCacheKey) obj; + + if ((this.repository == null) + ? (other.repository != null) + : !this.repository.equals(other.repository)) + { + return false; + } + + if (this.start != other.start) + { + return false; + } + + if (this.max != other.max) + { + return false; + } + + return true; + } + + /** + * Method description + * + * + * @return + */ + @Override + public int hashCode() + { + int hash = 3; + + hash = 67 * hash + ((this.repository != null) + ? this.repository.hashCode() + : 0); + hash = 67 * hash + this.start; + hash = 67 * hash + this.max; + + return hash; + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private int max; + + /** Field description */ + private String repository; + + /** Field description */ + private int start; + } + + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private Cache cache; + + /** Field description */ + private RepositoryManager repositoryManager; +} diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java index d1ed0501e5..130a19b3ef 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java @@ -43,4 +43,23 @@ public class RepositoryNotFoundException extends RepositoryException /** Field description */ private static final long serialVersionUID = -6583078808900520166L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + public RepositoryNotFoundException() {} + + /** + * Constructs ... + * + * + * @param message + */ + public RepositoryNotFoundException(String message) + { + super(message); + } } diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index efd5d60b43..b9d323231a 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -59,6 +59,7 @@ import sonia.scm.plugin.Plugin; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.ScriptResourceServlet; +import sonia.scm.repository.ChangesetViewerUtil; import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.xml.XmlRepositoryManager; import sonia.scm.security.EncryptionHandler; @@ -217,6 +218,7 @@ public class ScmServletModule extends ServletModule bind(UserManager.class).to(XmlUserManager.class); bind(GroupManager.class).to(XmlGroupManager.class); bind(CGIExecutorFactory.class).to(DefaultCGIExecutorFactory.class); + bind(ChangesetViewerUtil.class); // bind httpclient bind(HttpClient.class).to(URLHttpClient.class); diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java index 72d7ccaccf..16361af640 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java @@ -42,12 +42,14 @@ import com.google.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.NotSupportedFeatuerException; import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.ChangesetPreProcessor; import sonia.scm.repository.ChangesetViewer; +import sonia.scm.repository.ChangesetViewerUtil; import sonia.scm.repository.FileObject; import sonia.scm.repository.FileObjectNameComparator; import sonia.scm.repository.PathNotFoundException; @@ -59,6 +61,7 @@ import sonia.scm.repository.RepositoryBrowser; import sonia.scm.repository.RepositoryException; import sonia.scm.repository.RepositoryHandler; import sonia.scm.repository.RepositoryManager; +import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; @@ -114,18 +117,21 @@ public class RepositoryResource * @param repositoryManager * @param securityContextProvider * @param changesetPreProcessorSet + * @param changesetViewerUtil */ @Inject public RepositoryResource( ScmConfiguration configuration, RepositoryManager repositoryManager, Provider securityContextProvider, - Set changesetPreProcessorSet) + Set changesetPreProcessorSet, + ChangesetViewerUtil changesetViewerUtil) { super(repositoryManager); this.configuration = configuration; this.repositoryManager = repositoryManager; this.securityContextProvider = securityContextProvider; this.changesetPreProcessorSet = changesetPreProcessorSet; + this.changesetViewerUtil = changesetViewerUtil; setDisableCache(false); } @@ -213,37 +219,30 @@ public class RepositoryResource @QueryParam("limit") int limit) throws RepositoryException { Response response = null; - Repository repository = repositoryManager.get(id); - if (repository != null) + try { - ChangesetViewer changesetViewer = - repositoryManager.getChangesetViewer(repository); + ChangesetPagingResult changesets = changesetViewerUtil.getChangesets(id, + start, limit); - if (changesetViewer != null) + if (changesets != null) { - ChangesetPagingResult changesets = changesetViewer.getChangesets(start, - limit); - - if (changesets != null) - { - callPreProcessors(changesets); - response = Response.ok(changesets).build(); - } - else - { - response = Response.ok().build(); - } + callPreProcessors(changesets); + response = Response.ok(changesets).build(); } else { - response = Response.status(Response.Status.NOT_FOUND).build(); + response = Response.ok().build(); } } - else + catch (RepositoryNotFoundException ex) { response = Response.status(Response.Status.NOT_FOUND).build(); } + catch (NotSupportedFeatuerException ex) + { + response = Response.status(Response.Status.BAD_REQUEST).build(); + } return response; } @@ -566,6 +565,9 @@ public class RepositoryResource /** Field description */ private Set changesetPreProcessorSet; + /** Field description */ + private ChangesetViewerUtil changesetViewerUtil; + /** Field description */ private ScmConfiguration configuration; diff --git a/scm-webapp/src/main/resources/config/ehcache.xml b/scm-webapp/src/main/resources/config/ehcache.xml index c55a65acc1..cf62a98813 100644 --- a/scm-webapp/src/main/resources/config/ehcache.xml +++ b/scm-webapp/src/main/resources/config/ehcache.xml @@ -133,5 +133,14 @@ timeToLiveSeconds="5400" diskPersistent="false" /> + +