[Yanel-dev] User management search implementation
Cedric Staub
cedric.staub at wyona.com
Thu Oct 21 16:18:55 CEST 2010
Hello everyone
In short: I built a search feature for the user management page.
The current implementation of Yanel's user management resource does not
support searching for users, which can be a bit annoying if you have a
realm with a lot of users. Since Yarep already has an interface that
allows to search for nodes, I built a search feature for the user
management page on top of that.
There is one limitation to this approach: The search feature is subject
to the limitations of the underlying implementation, e.g. if your users
repository does not support searching it won't work. I suggest the
VirtualFileSystemRepository implementation, which can be configured to
use a Lucene search index.
Attached are a series of patches for review. If anyone has suggestions
or ideas for improvements just reply to this email ;-). I plan to
improve the code and do some more testing and then file a bug report
later in order to get it commited.
Have a nice day
Cedric
-------------- next part --------------
Index: src/impl/java/org/wyona/security/impl/yarep/YarepUserManager.java
===================================================================
--- src/impl/java/org/wyona/security/impl/yarep/YarepUserManager.java (revision 54048)
+++ src/impl/java/org/wyona/security/impl/yarep/YarepUserManager.java (working copy)
@@ -346,8 +346,7 @@
* @see org.wyona.security.core.api.UserManager#getUsers(String)
*/
public java.util.Iterator<User> getUsers(String query) throws AccessManagementException {
- log.error("TODO: Not implemented yet!");
- return null;
+ return new YarepUsersIterator(identityManager, identitiesRepository, cacheEnabled, resolveGroupsAtCreation, query);
}
/**
Index: src/impl/java/org/wyona/security/impl/yarep/YarepUsersIterator.java
===================================================================
--- src/impl/java/org/wyona/security/impl/yarep/YarepUsersIterator.java (revision 54048)
+++ src/impl/java/org/wyona/security/impl/yarep/YarepUsersIterator.java (working copy)
@@ -15,6 +15,7 @@
import org.wyona.yarep.core.Node;
import org.wyona.yarep.core.Repository;
+import org.wyona.yarep.core.search.Searcher;
/**
* Yarep users iterator.
@@ -72,6 +73,42 @@
}
/**
+ * Constructor with search query.
+ */
+ public YarepUsersIterator(IdentityManager identityManager,
+ Repository identitiesRepository,
+ boolean cacheEnabled,
+ boolean resolveGroupsAtCreation,
+ String query)
+ throws AccessManagementException {
+ // Call parent
+ super(identityManager, identitiesRepository, cacheEnabled, resolveGroupsAtCreation);
+ log.info("Load users from repository '" + identitiesRepository.getConfigFile() + "'");
+
+ // TODO: This is inefficient! If Yarep were able to
+ // return an iterator instead of an array, this could
+ // be done in a more effective manner, but until Yarep
+ // gains that capability we're currently stuck like this.
+ try {
+ Searcher s = identitiesRepository.getSearcher();
+ Node[] userNodes = s.search(query);
+
+ userNodeList = new LinkedList<Node>();
+ for(Node n : userNodes) {
+ if(n.isResource()) {
+ userNodeList.add(n);
+ }
+ }
+
+ listIterator = userNodeList.listIterator();
+ } catch(Exception e) {
+ String errorMsg = "Could not read users from repository: " + e.getMessage();
+ log.error(errorMsg, e);
+ throw new AccessManagementException(errorMsg, e);
+ }
+ }
+
+ /**
* @see java.util.Iterator#hasNext()
*/
@Override
-------------- next part --------------
Index: src/resources/user-mgmt/htdocs/list-users.jelly
===================================================================
--- src/resources/user-mgmt/htdocs/list-users.jelly (revision 54048)
+++ src/resources/user-mgmt/htdocs/list-users.jelly (working copy)
@@ -7,36 +7,86 @@
Showing users ${resource.getLowerBound()} through ${resource.getUpperBound()}
out of a total of ${resource.getTotalUsers()}.
</p>
- <!-- Table of users -->
- <table border="1">
+ <!-- Jump to certain page, search for user -->
+ <table>
+ <j:if test="${resource.getTotalPages() > 1}">
+ <tr>
+ <td style="border:none;">
+ <label for="pageinput">Jump to page:</label>
+ </td>
+ <td style="border:none;">
+ <form method="GET">
+ <input style="display:inline;" type="text" size="10" name="page" id="pageinput"/>
+ <input style="display:inline;" type="submit" value="Go"/>
+ </form>
+ </td>
+ </tr>
+ </j:if>
<tr>
- <th>ID</th>
- <th>Name</th>
- <th>Email</th>
- <th colspan="2">Actions</th>
+ <td style="border:none;">
+ <label for="queryinput">Search for users:</label>
+ </td>
+ <td style="border:none;">
+ <form method="GET">
+ <input style="display:inline;" type="text" size="10" name="query" id="queryinput"/>
+ <input style="display:inline;" type="submit" value="Go"/>
+ </form>
+ </td>
</tr>
- <j:forEach var="user" items="${resource.getUsers()}">
+ </table>
+ <!-- Table of users -->
+ <j:choose>
+ <j:when test="${resource.getTotalUsers() < 1}">
+ <p>
+ <strong>No users found.</strong>
+ </p>
+ </j:when>
+ <j:otherwise>
+ <br/>
+ <table border="1">
<tr>
- <td>${user.getID()}</td>
- <td>${user.getName()}</td>
- <td>${user.getEmail()}</td>
- <td><a href="update-user-admin.html?userID=${user.getID()}">Update/Edit</a></td>
- <td><a href="delete-user.html?userID=${user.getID()}">Delete</a></td>
+ <th>ID</th>
+ <th>Name</th>
+ <th>Email</th>
+ <th colspan="2">Actions</th>
</tr>
- </j:forEach>
- </table>
- <!-- Pagination -->
- <p>
- <j:if test="${resource.getCurrentPage() > 1}">
- <a href="?page=${resource.getCurrentPage()-1}">? Prev</a>
-  
- </j:if>
- Page ${resource.getCurrentPage()}/${resource.getTotalPages()}
- <j:if test="${resource.hasNext()}">
-  
- <a href="?page=${resource.getCurrentPage()+1}">Next ?</a>
- </j:if>
- </p>
+ <j:forEach var="user" items="${resource.getUsers()}">
+ <j:choose>
+ <j:when test="${user != null}">
+ <tr>
+ <td>${user.getID()}</td>
+ <td>${user.getName()}</td>
+ <td>${user.getEmail()}</td>
+ <td><a href="update-user-admin.html?userID=${user.getID()}">Update/Edit</a></td>
+ <td><a href="delete-user.html?userID=${user.getID()}">Delete</a></td>
+ </tr>
+ </j:when>
+ <j:otherwise>
+ <tr>
+ <td>???</td>
+ <td>Invalid user</td>
+ <td>(none)</td>
+ <td>Update/Edit</td>
+ <td>Delete</td>
+ </tr>
+ </j:otherwise>
+ </j:choose>
+ </j:forEach>
+ </table>
+ <!-- Pagination -->
+ <p>
+ <j:if test="${resource.getCurrentPage() > 1}">
+ <a href="?page=${resource.getCurrentPage()-1}">? Prev</a>
+  
+ </j:if>
+ Page ${resource.getCurrentPage()}/${resource.getTotalPages()}
+ <j:if test="${resource.hasNext()}">
+  
+ <a href="?page=${resource.getCurrentPage()+1}">Next ?</a>
+ </j:if>
+ </p>
+ </j:otherwise>
+ </j:choose>
<!-- Links -->
<a href="create-user.html">Create new user</a>
</body>
Index: src/resources/user-mgmt/src/java/org/wyona/yanel/impl/resources/ListUsersResource.java
===================================================================
--- src/resources/user-mgmt/src/java/org/wyona/yanel/impl/resources/ListUsersResource.java (revision 54048)
+++ src/resources/user-mgmt/src/java/org/wyona/yanel/impl/resources/ListUsersResource.java (working copy)
@@ -22,6 +22,8 @@
import org.wyona.yanel.impl.resources.usecase.UsecaseException;
import org.wyona.yanel.impl.resources.usecase.UsecaseResource;
+import org.apache.log4j.Logger;
+
import java.lang.System;
import java.lang.Integer;
import java.lang.Boolean;
@@ -37,6 +39,7 @@
// Constants
private static final int DEFAULT_ITEMS_PER_PAGE = 10;
//private static final int DEFAULT_ITEMS_PER_PAGE = 100;
+ private static final Logger log = Logger.getLogger(ListUsersResource.class);
// Variables
private int currentPage = 1;
@@ -82,11 +85,26 @@
// All users matching search term,
// or all users overall if search term is empty
Iterator<User> allUsers;
- allUsers = userManager.getAllUsers();
+ String query = getParameterAsString("query");
+ if(query != null && !"".equals(query)) {
+ try {
+ // TODO: What if getUsers() returns garbage?
+ allUsers = userManager.getUsers(query);
+ } catch(Exception e) {
+ log.warn(e, e);
+ lowerBound = 0;
+ upperBound = 0;
+ totalUsers = 0;
+ return;
+ }
+ } else {
+ allUsers = userManager.getAllUsers();
+ }
+
// Boundaries...
lowerBound = (currentPage-1)*itemsPerPage;
- upperBound = lowerBound+itemsPerPage-1; // TODO: On the very last page this doesn't have to be correct
+ upperBound = lowerBound+itemsPerPage-1;
totalUsers = userManager.getUserCount();
totalPages = totalUsers/itemsPerPage;
if(totalUsers%itemsPerPage != 0) totalPages++;
-------------- next part --------------
Index: src/realms/from-scratch-realm-template/tika-config.xml
===================================================================
--- src/realms/from-scratch-realm-template/tika-config.xml (revision 0)
+++ src/realms/from-scratch-realm-template/tika-config.xml (revision 0)
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- The original version is located within the Tika jar file at org/apache/tika/tika-config.xml -->
+<properties>
+
+ <mimeTypeRepository resource="/org/apache/tika/mime/tika-mimetypes.xml" magic="false"/>
+
+ <parsers>
+
+ <parser name="text-xml" class="org.apache.tika.parser.xml.XMLParser">
+ <mime>application/xml</mime>
+ <mime>application/xhtml+xml</mime>
+ </parser>
+
+ <parser name="parse-msword" class="org.apache.tika.parser.microsoft.WordParser">
+ <mime>application/msword</mime>
+ </parser>
+
+ <parser name="parse-msexcel" class="org.apache.tika.parser.microsoft.ExcelParser">
+ <mime>application/vnd.ms-excel</mime>
+ </parser>
+
+ <parser name="parse-mspowerpoint" class="org.apache.tika.parser.microsoft.PowerPointParser">
+ <mime>application/vnd.ms-powerpoint</mime>
+ </parser>
+
+ <parser name="parse-html" class="org.apache.tika.parser.html.HtmlParser">
+ <mime>text/html</mime>
+ <mime>application/x-asp</mime>
+ </parser>
+
+ <parser mame="parse-rtf" class="org.apache.tika.parser.rtf.RTFParser">
+ <mime>application/rtf</mime>
+ </parser>
+
+ <parser name="parse-pdf" class="org.apache.tika.parser.pdf.PDFParser">
+ <mime>application/pdf</mime>
+ </parser>
+
+ <parser name="parse-txt" class="org.apache.tika.parser.txt.TXTParser">
+ <mime>text/plain</mime>
+ </parser>
+
+ <parser name="parse-openoffice" class="org.apache.tika.parser.opendocument.OpenOfficeParser">
+ <mime>application/vnd.sun.xml.writer</mime>
+ <mime>application/vnd.oasis.opendocument.text</mime>
+ <mime>application/vnd.oasis.opendocument.graphics</mime>
+ <mime>application/vnd.oasis.opendocument.presentation</mime>
+ <mime>application/vnd.oasis.opendocument.spreadsheet</mime>
+ <mime>application/vnd.oasis.opendocument.chart</mime>
+ <mime>application/vnd.oasis.opendocument.image</mime>
+ <mime>application/vnd.oasis.opendocument.formula</mime>
+ <mime>application/vnd.oasis.opendocument.text-master</mime>
+ <mime>application/vnd.oasis.opendocument.text-web</mime>
+ <mime>application/vnd.oasis.opendocument.text-template</mime>
+ <mime>application/vnd.oasis.opendocument.graphics-template</mime>
+ <mime>application/vnd.oasis.opendocument.presentation-template</mime>
+ <mime>application/vnd.oasis.opendocument.spreadsheet-template</mime>
+ <mime>application/vnd.oasis.opendocument.chart-template</mime>
+ <mime>application/vnd.oasis.opendocument.image-template</mime>
+ <mime>application/vnd.oasis.opendocument.formula-template</mime>
+ <mime>application/x-vnd.oasis.opendocument.text</mime>
+ <mime>application/x-vnd.oasis.opendocument.graphics</mime>
+ <mime>application/x-vnd.oasis.opendocument.presentation</mime>
+ <mime>application/x-vnd.oasis.opendocument.spreadsheet</mime>
+ <mime>application/x-vnd.oasis.opendocument.chart</mime>
+ <mime>application/x-vnd.oasis.opendocument.image</mime>
+ <mime>application/x-vnd.oasis.opendocument.formula</mime>
+ <mime>application/x-vnd.oasis.opendocument.text-master</mime>
+ <mime>application/x-vnd.oasis.opendocument.text-web</mime>
+ <mime>application/x-vnd.oasis.opendocument.text-template</mime>
+ <mime>application/x-vnd.oasis.opendocument.graphics-template</mime>
+ <mime>application/x-vnd.oasis.opendocument.presentation-template</mime>
+ <mime>application/x-vnd.oasis.opendocument.spreadsheet-template</mime>
+ <mime>application/x-vnd.oasis.opendocument.chart-template</mime>
+ <mime>application/x-vnd.oasis.opendocument.image-template</mime>
+ <mime>application/x-vnd.oasis.opendocument.formula-template</mime>
+ </parser>
+
+ </parsers>
+
+</properties>
Index: src/realms/from-scratch-realm-template/config/ac-identities-repository.xml
===================================================================
--- src/realms/from-scratch-realm-template/config/ac-identities-repository.xml (revision 54048)
+++ src/realms/from-scratch-realm-template/config/ac-identities-repository.xml (working copy)
@@ -1,11 +1,37 @@
<?xml version="1.0"?>
-<repository class="org.wyona.yarep.impl.DefaultRepository">
+<repository class="org.wyona.yarep.impl.repo.vfs.VirtualFileSystemRepository">
<name>Yanel from Scratch Access Control Identities</name>
- <paths class="org.wyona.yarep.impl.VFileSystemMapImpl" src="../ac-identities"/>
+ <content src="../ac-identities">
+ <!-- Ignore patterns are optional -->
+ <ignore pattern=".*\.svn"/>
+ <ignore pattern=".*\.yarep"/>
+ <ignore pattern=".*\.sh"/>
+ <ignore pattern="search-index"/>
+ </content>
- <storage class="org.wyona.yarep.core.impl.vfs.VFileSystemStorage">
- <content src="../ac-identities"/>
- </storage>
+ <!-- Search index config -->
+ <s:search-index
+ xmlns:s="http://www.wyona.org/yarep/search/2.0"
+ indexer-class="org.wyona.yarep.impl.search.lucene.LuceneIndexer"
+ searcher-class="org.wyona.yarep.impl.search.lucene.LuceneSearcher">
+ <index-location file="../ac-identities/search-index"/>
+ <repo-auto-index-fulltext boolean="true"/>
+ <repo-auto-index-properties boolean="true"/>
+ <lucene>
+ <local-tika-config file="tika-config.xml"/>
+ <fulltext-analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer"/>
+ <property-analyzer class="org.apache.lucene.analysis.WhitespaceAnalyzer"/>
+ <write-lock-timeout ms="3000"/>
+ </lucene>
+ </s:search-index>
+
+ <!--
+ <splitpath depth="1" length="2" escape="+">
+ <include path="/users/"/>
+ <include path="/groups/"/>
+ </splitpath>
+ -->
+
</repository>
More information about the Yanel-development
mailing list