diff --git a/scm-core/src/main/java/sonia/scm/group/GroupManager.java b/scm-core/src/main/java/sonia/scm/group/GroupManager.java index 93c2795a33..36aeb5969b 100644 --- a/scm-core/src/main/java/sonia/scm/group/GroupManager.java +++ b/scm-core/src/main/java/sonia/scm/group/GroupManager.java @@ -37,6 +37,7 @@ package sonia.scm.group; import sonia.scm.ListenerSupport; import sonia.scm.Manager; +import sonia.scm.search.Searchable; //~--- JDK imports ------------------------------------------------------------ @@ -47,7 +48,8 @@ import java.util.Collection; * @author Sebastian Sdorra */ public interface GroupManager - extends Manager, ListenerSupport + extends Manager, Searchable, + ListenerSupport { /** diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/SearchResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/SearchResource.java index 757361cf43..838fcbcff4 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/SearchResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/SearchResource.java @@ -36,36 +36,29 @@ package sonia.scm.api.rest.resources; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.base.Function; -import com.google.common.collect.Collections2; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sonia.scm.HandlerEvent; -import sonia.scm.api.rest.SearchResult; -import sonia.scm.api.rest.SearchResults; import sonia.scm.cache.Cache; import sonia.scm.cache.CacheManager; -import sonia.scm.search.SearchRequest; +import sonia.scm.group.Group; +import sonia.scm.group.GroupListener; +import sonia.scm.group.GroupManager; +import sonia.scm.search.SearchHandler; +import sonia.scm.search.SearchResult; +import sonia.scm.search.SearchResults; import sonia.scm.user.User; import sonia.scm.user.UserListener; import sonia.scm.user.UserManager; -import sonia.scm.util.SecurityUtil; -import sonia.scm.util.Util; import sonia.scm.web.security.WebSecurityContext; //~--- JDK imports ------------------------------------------------------------ -import java.util.Collection; - import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response.Status; /** * @@ -73,15 +66,14 @@ import javax.ws.rs.core.Response.Status; */ @Singleton @Path("search") -public class SearchResource implements UserListener +public class SearchResource implements UserListener, GroupListener { /** Field description */ - public static final String CACHE_USER = "sonia.cache.search.users"; + public static final String CACHE_GROUP = "sonia.cache.search.groups"; - /** the logger for SearchResource */ - private static final Logger logger = - LoggerFactory.getLogger(SearchResource.class); + /** Field description */ + public static final String CACHE_USER = "sonia.cache.search.users"; //~--- constructors --------------------------------------------------------- @@ -91,17 +83,32 @@ public class SearchResource implements UserListener * * @param securityContextProvider * @param userManager + * @param groupManager * @param cacheManager */ @Inject public SearchResource(Provider securityContextProvider, - UserManager userManager, CacheManager cacheManager) + UserManager userManager, GroupManager groupManager, + CacheManager cacheManager) { - this.securityContextProvider = securityContextProvider; - this.userManager = userManager; - this.userManager.addListener(this); - this.userSearchCache = cacheManager.getCache(String.class, - SearchResults.class, CACHE_USER); + + // create user searchhandler + userManager.addListener(this); + + Cache userCache = + cacheManager.getCache(String.class, SearchResults.class, CACHE_USER); + + this.userSearchHandler = new SearchHandler(securityContextProvider, + userCache, userManager); + + // create group searchhandler + groupManager.addListener(this); + + Cache groupCache = + cacheManager.getCache(String.class, SearchResults.class, CACHE_GROUP); + + this.groupSearchHandler = new SearchHandler(securityContextProvider, + groupCache, groupManager); } //~--- methods -------------------------------------------------------------- @@ -116,7 +123,51 @@ public class SearchResource implements UserListener @Override public void onEvent(User user, HandlerEvent event) { - userSearchCache.clear(); + userSearchHandler.clearCache(); + } + + /** + * Method description + * + * + * @param group + * @param event + */ + @Override + public void onEvent(Group group, HandlerEvent event) + { + throw new UnsupportedOperationException("Not supported yet."); + } + + /** + * Method description + * + * + * @param queryString + * + * @return + */ + @GET + @Path("groups") + public SearchResults searchGroups(@QueryParam("query") String queryString) + { + return groupSearchHandler.search(queryString, + new Function() + { + @Override + public SearchResult apply(Group group) + { + String label = group.getName(); + String description = group.getDescription(); + + if (description != null) + { + label = label.concat(" (").concat(description).concat(")"); + } + + return new SearchResult(group.getName(), label); + } + }); } /** @@ -131,62 +182,26 @@ public class SearchResource implements UserListener @Path("users") public SearchResults searchUsers(@QueryParam("query") String queryString) { - SecurityUtil.assertIsNotAnonymous(securityContextProvider); - - if (Util.isEmpty(queryString)) + return userSearchHandler.search(queryString, + new Function() { - throw new WebApplicationException(Status.BAD_REQUEST); - } - - SearchResults result = userSearchCache.get(queryString); - - if (result == null) - { - SearchRequest request = new SearchRequest(queryString, true); - - request.setMaxResults(5); - - Collection users = userManager.search(request); - - result = new SearchResults(); - - if (Util.isNotEmpty(users)) + @Override + public SearchResult apply(User user) { - Collection resultCollection = - Collections2.transform(users, new Function() - { - @Override - public SearchResult apply(User user) - { - StringBuilder label = new StringBuilder(user.getName()); + StringBuilder label = new StringBuilder(user.getName()); - label.append(" (").append(user.getDisplayName()).append(")"); + label.append(" (").append(user.getDisplayName()).append(")"); - return new SearchResult(user.getName(), label.toString()); - } - }); - - result.setSuccess(true); - result.setResults(resultCollection); - userSearchCache.put(queryString, result); + return new SearchResult(user.getName(), label.toString()); } - } - else if (logger.isDebugEnabled()) - { - logger.debug("return searchresults for {} from cache", queryString); - } - - return result; + }); } //~--- fields --------------------------------------------------------------- /** Field description */ - private Provider securityContextProvider; + private SearchHandler groupSearchHandler; /** Field description */ - private UserManager userManager; - - /** Field description */ - private Cache userSearchCache; + private SearchHandler userSearchHandler; } diff --git a/scm-webapp/src/main/java/sonia/scm/group/xml/XmlGroupManager.java b/scm-webapp/src/main/java/sonia/scm/group/xml/XmlGroupManager.java index ddacdd8a5b..b19cd2e0f0 100644 --- a/scm-webapp/src/main/java/sonia/scm/group/xml/XmlGroupManager.java +++ b/scm-webapp/src/main/java/sonia/scm/group/xml/XmlGroupManager.java @@ -44,10 +44,13 @@ import org.slf4j.LoggerFactory; import sonia.scm.HandlerEvent; import sonia.scm.SCMContextProvider; +import sonia.scm.TransformFilter; import sonia.scm.group.AbstractGroupManager; import sonia.scm.group.Group; import sonia.scm.group.GroupAllreadyExistExeption; import sonia.scm.group.GroupException; +import sonia.scm.search.SearchRequest; +import sonia.scm.search.SearchUtil; import sonia.scm.security.SecurityContext; import sonia.scm.store.Store; import sonia.scm.store.StoreFactory; @@ -281,6 +284,41 @@ public class XmlGroupManager extends AbstractGroupManager fresh.copyProperties(group); } + /** + * Method description + * + * + * @param searchRequest + * + * @return + */ + @Override + public Collection search(final SearchRequest searchRequest) + { + if (logger.isDebugEnabled()) + { + logger.debug("search group with query {}", searchRequest.getQuery()); + } + + return SearchUtil.search(searchRequest, groupDB.values(), + new TransformFilter() + { + @Override + public Group accept(Group group) + { + Group result = null; + + if (SearchUtil.matchesOne(searchRequest, group.getName(), + group.getDescription())) + { + result = group.clone(); + } + + return result; + } + }); + } + //~--- get methods ---------------------------------------------------------- /** diff --git a/scm-webapp/src/main/java/sonia/scm/search/SearchHandler.java b/scm-webapp/src/main/java/sonia/scm/search/SearchHandler.java new file mode 100644 index 0000000000..4ecbbf33e0 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/search/SearchHandler.java @@ -0,0 +1,213 @@ +/** + * 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.search; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.google.inject.Provider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.cache.Cache; +import sonia.scm.util.SecurityUtil; +import sonia.scm.util.Util; +import sonia.scm.web.security.WebSecurityContext; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Collection; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response.Status; + +/** + * + * @author Sebastian Sdorra + * + * @param + */ +public class SearchHandler +{ + + /** the logger for SearchHandler */ + private static final Logger logger = + LoggerFactory.getLogger(SearchHandler.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param securityContextProvider + * @param cache + * @param searchable + */ + public SearchHandler(Provider securityContextProvider, + Cache cache, + Searchable searchable) + { + this.securityContextProvider = securityContextProvider; + this.cache = cache; + this.searchable = searchable; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + */ + public void clearCache() + { + this.cache.clear(); + } + + /** + * Method description + * + * + * @param queryString + * @param function + * + * @return + */ + public SearchResults search(String queryString, + Function function) + { + SecurityUtil.assertIsNotAnonymous(securityContextProvider); + + if (Util.isEmpty(queryString)) + { + throw new WebApplicationException(Status.BAD_REQUEST); + } + + SearchResults result = cache.get(queryString); + + if (result == null) + { + SearchRequest request = new SearchRequest(queryString, ignoreCase); + + request.setMaxResults(maxResults); + + Collection users = searchable.search(request); + + result = new SearchResults(); + + if (Util.isNotEmpty(users)) + { + Collection resultCollection = + Collections2.transform(users, function); + + result.setSuccess(true); + result.setResults(resultCollection); + cache.put(queryString, result); + } + } + else if (logger.isDebugEnabled()) + { + logger.debug("return searchresults for {} from cache", queryString); + } + + return result; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public int getMaxResults() + { + return maxResults; + } + + /** + * Method description + * + * + * @return + */ + public boolean isIgnoreCase() + { + return ignoreCase; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param ignoreCase + */ + public void setIgnoreCase(boolean ignoreCase) + { + this.ignoreCase = ignoreCase; + } + + /** + * Method description + * + * + * @param maxResults + */ + public void setMaxResults(int maxResults) + { + this.maxResults = maxResults; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + protected Cache cache; + + /** Field description */ + protected Searchable searchable; + + /** Field description */ + protected Provider securityContextProvider; + + /** Field description */ + private int maxResults = 5; + + /** Field description */ + private boolean ignoreCase = true; +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/SearchResult.java b/scm-webapp/src/main/java/sonia/scm/search/SearchResult.java similarity index 99% rename from scm-webapp/src/main/java/sonia/scm/api/rest/SearchResult.java rename to scm-webapp/src/main/java/sonia/scm/search/SearchResult.java index b26fdee547..3862fafe8b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/SearchResult.java +++ b/scm-webapp/src/main/java/sonia/scm/search/SearchResult.java @@ -31,7 +31,7 @@ -package sonia.scm.api.rest; +package sonia.scm.search; /** * diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/SearchResults.java b/scm-webapp/src/main/java/sonia/scm/search/SearchResults.java similarity index 97% rename from scm-webapp/src/main/java/sonia/scm/api/rest/SearchResults.java rename to scm-webapp/src/main/java/sonia/scm/search/SearchResults.java index 4a6e1dfa68..a26982052a 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/SearchResults.java +++ b/scm-webapp/src/main/java/sonia/scm/search/SearchResults.java @@ -31,13 +31,14 @@ -package sonia.scm.api.rest; +package sonia.scm.search; //~--- JDK imports ------------------------------------------------------------ import java.util.Collection; import javax.xml.bind.annotation.XmlRootElement; +import sonia.scm.api.rest.RestActionResult; /** * diff --git a/scm-webapp/src/main/resources/config/ehcache.xml b/scm-webapp/src/main/resources/config/ehcache.xml index 34562f56ff..c55a65acc1 100644 --- a/scm-webapp/src/main/resources/config/ehcache.xml +++ b/scm-webapp/src/main/resources/config/ehcache.xml @@ -125,4 +125,13 @@ diskPersistent="false" /> + +