diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/CachedViewableResource.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/CachedViewableResource.java new file mode 100644 index 0000000000..6abbda75d2 --- /dev/null +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/CachedViewableResource.java @@ -0,0 +1,162 @@ +/** + * 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.plugin.rest; + +//~--- non-JDK imports -------------------------------------------------------- + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Element; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.plugin.BackendConfiguration; +import sonia.scm.plugin.PluginBackend; +import sonia.scm.plugin.PluginBackendListener; +import sonia.scm.plugin.PluginInformation; + +//~--- JDK imports ------------------------------------------------------------ + +import com.sun.jersey.api.view.Viewable; + +import java.util.Collection; + +import javax.servlet.ServletContext; + +/** + * + * @author Sebastian Sdorra + */ +public class CachedViewableResource extends ViewableResource + implements PluginBackendListener +{ + + /** the logger for CachedViewableResource */ + private static final Logger logger = + LoggerFactory.getLogger(CachedViewableResource.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param context + * @param configuration + * @param backend + * @param cacheManager + * @param cacheName + */ + public CachedViewableResource(ServletContext context, PluginBackend backend, + BackendConfiguration configuration, + CacheManager cacheManager, String cacheName) + { + super(context, configuration); + this.cacheName = cacheName; + this.cache = cacheManager.getCache(cacheName); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param plugins + */ + @Override + public void addedNewPlugins(Collection plugins) + { + if (logger.isDebugEnabled()) + { + logger.debug("clear cache {}", cacheName); + } + + cache.removeAll(); + } + + /** + * Method description + * + * + * @param key + * @param viewable + */ + protected void putToCache(String key, Viewable viewable) + { + if (logger.isTraceEnabled()) + { + logger.trace("put viewable to cache with key {}", key); + } + + cache.put(new Element(key, viewable)); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param key + * + * @return + */ + protected Viewable getFromCache(String key) + { + if (logger.isTraceEnabled()) + { + logger.trace("retrive viewable from cache with key {}", key); + } + + Viewable viewable = null; + Element el = cache.get(key); + + if (el != null) + { + viewable = (Viewable) el.getObjectValue(); + } + + return viewable; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private Cache cache; + + /** Field description */ + private String cacheName; +} diff --git a/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/DetailResource.java b/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/DetailResource.java index 631e9d31e6..721ea9af14 100644 --- a/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/DetailResource.java +++ b/scm-plugin-backend/src/main/java/sonia/scm/plugin/rest/DetailResource.java @@ -36,6 +36,12 @@ package sonia.scm.plugin.rest; //~--- non-JDK imports -------------------------------------------------------- import com.google.inject.Inject; +import com.google.inject.Singleton; + +import net.sf.ehcache.CacheManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import sonia.scm.plugin.BackendConfiguration; import sonia.scm.plugin.PluginBackend; @@ -54,7 +60,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.Map; import javax.servlet.ServletContext; @@ -71,10 +76,20 @@ import javax.ws.rs.core.Response.Status; * * @author Sebastian Sdorra */ +@Singleton @Path("/detail/{groupId}/{artifactId}.html") -public class DetailResource extends ViewableResource +public class DetailResource extends CachedViewableResource { + /** Field description */ + public static final String CACHE = "sonia.cache.plugin-detail"; + + /** the logger for DetailResource */ + private static final Logger logger = + LoggerFactory.getLogger(DetailResource.class); + + //~--- constructors --------------------------------------------------------- + /** * Constructs ... * @@ -84,13 +99,14 @@ public class DetailResource extends ViewableResource * @param backend * @param configuration * @param urlFactory + * @param cacheManager */ @Inject public DetailResource(ServletContext context, PluginBackend backend, BackendConfiguration configuration, - UrlBuilderFactory urlFactory) + UrlBuilderFactory urlFactory, CacheManager cacheManager) { - super(context, configuration); + super(context, backend, configuration, cacheManager, CACHE); this.backend = backend; this.urlFactory = urlFactory; } @@ -113,33 +129,67 @@ public class DetailResource extends ViewableResource @DefaultValue("false") @QueryParam("snapshot") boolean snapshot) { - List pluginVersions = - PluginUtil.getFilteredPluginVersions(backend, groupId, artifactId); + String cacheKey = createCacheKey(groupId, artifactId, snapshot); + Viewable viewable = getFromCache(cacheKey); - if (Util.isEmpty(pluginVersions)) + if (viewable == null) { - throw new WebApplicationException(Status.NOT_FOUND); + if (logger.isDebugEnabled()) + { + logger.debug("create viewable from backend"); + } + + List pluginVersions = + PluginUtil.getFilteredPluginVersions(backend, groupId, artifactId); + + if (Util.isEmpty(pluginVersions)) + { + throw new WebApplicationException(Status.NOT_FOUND); + } + + PluginInformation latest = pluginVersions.get(0); + + if (!snapshot) + { + pluginVersions = PluginUtil.filterSnapshots(pluginVersions); + } + + List detailList = createDetailList(latest, + pluginVersions); + Map vars = createVarMap(latest.getName()); + + vars.put("latest", latest); + vars.put("versions", detailList); + viewable = new Viewable("/detail", vars); + putToCache(cacheKey, viewable); + } + else if (logger.isDebugEnabled()) + { + logger.debug("create viewable from cache"); } - PluginInformation latest = pluginVersions.get(0); - - if (!snapshot) - { - pluginVersions = PluginUtil.filterSnapshots(pluginVersions); - } - - List detailList = createDetailList(latest, - pluginVersions); - Map vars = createVarMap(latest.getName()); - - vars.put("latest", latest); - vars.put("versions", detailList); - - return new Viewable("/detail", vars); + return viewable; } //~--- methods -------------------------------------------------------------- + /** + * Method description + * + * + * @param groupId + * @param artifactId + * @param snapshot + * + * @return + */ + private String createCacheKey(String groupId, String artifactId, + boolean snapshot) + { + return new StringBuilder(Util.nonNull(groupId)).append( + Util.nonNull(artifactId)).append(Boolean.toString(snapshot)).toString(); + } + /** * Method description * diff --git a/scm-plugin-backend/src/main/resources/config/ehcache.xml b/scm-plugin-backend/src/main/resources/config/ehcache.xml index ecf0f5323f..5596a25dc8 100644 --- a/scm-plugin-backend/src/main/resources/config/ehcache.xml +++ b/scm-plugin-backend/src/main/resources/config/ehcache.xml @@ -106,5 +106,15 @@ timeToLiveSeconds="3600" diskPersistent="false" /> + +