You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by no...@apache.org on 2010/08/12 12:10:56 UTC

svn commit: r984711 - in /james/imap/trunk: jcr/src/main/java/org/apache/james/imap/jcr/mail/ jpa/src/main/java/org/apache/james/imap/jpa/mail/ memory/src/main/java/org/apache/james/imap/inmemory/mail/ store/src/main/java/org/apache/james/imap/store/ s...

Author: norman
Date: Thu Aug 12 10:10:56 2010
New Revision: 984711

URL: http://svn.apache.org/viewvc?rev=984711&view=rev
Log:
Introduce a SearchQueryIterator which can handle the SearchQuery matching lazy. I also changed the Mapper to return only an Iterator with the uids of the found messages so its easier to write an implementation specific implementation whith an optimized performance (IMAP-190)

Added:
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/SearchQueryIterator.java
Modified:
    james/imap/trunk/jcr/src/main/java/org/apache/james/imap/jcr/mail/JCRMessageMapper.java
    james/imap/trunk/jpa/src/main/java/org/apache/james/imap/jpa/mail/JPAMessageMapper.java
    james/imap/trunk/memory/src/main/java/org/apache/james/imap/inmemory/mail/InMemoryMessageMapper.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/StoreMessageManager.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/MessageMapper.java

Modified: james/imap/trunk/jcr/src/main/java/org/apache/james/imap/jcr/mail/JCRMessageMapper.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/jcr/src/main/java/org/apache/james/imap/jcr/mail/JCRMessageMapper.java?rev=984711&r1=984710&r2=984711&view=diff
==============================================================================
--- james/imap/trunk/jcr/src/main/java/org/apache/james/imap/jcr/mail/JCRMessageMapper.java (original)
+++ james/imap/trunk/jcr/src/main/java/org/apache/james/imap/jcr/mail/JCRMessageMapper.java Thu Aug 12 10:10:56 2010
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.Iterator;
 import java.util.List;
 
 import javax.jcr.ItemNotFoundException;
@@ -51,6 +52,7 @@ import org.apache.james.imap.mailbox.Sto
 import org.apache.james.imap.mailbox.MessageRange.Type;
 import org.apache.james.imap.mailbox.SearchQuery.Criterion;
 import org.apache.james.imap.mailbox.SearchQuery.NumericRange;
+import org.apache.james.imap.store.SearchQueryIterator;
 import org.apache.james.imap.store.mail.MessageMapper;
 import org.apache.james.imap.store.mail.model.Mailbox;
 import org.apache.james.imap.store.mail.model.MailboxMembership;
@@ -604,25 +606,37 @@ public class JCRMessageMapper extends Ab
             return String.valueOf(value);
         }
     }
+
     /*
      * (non-Javadoc)
-     * 
-     * @see
-     * org.apache.james.imap.store.mail.MessageMapper#searchMailbox(org.apache
-     * .james.imap.mailbox.SearchQuery)
+     * @see org.apache.james.imap.store.mail.MessageMapper#searchMailbox(org.apache.james.imap.store.mail.model.Mailbox, org.apache.james.imap.mailbox.SearchQuery)
      */
-    public List<MailboxMembership<String>> searchMailbox(Mailbox<String> mailbox, SearchQuery query) throws StorageException {
+    public Iterator<Long> searchMailbox(Mailbox<String> mailbox, SearchQuery query) throws StorageException {
         try {
-            List<MailboxMembership<String>> list = new ArrayList<MailboxMembership<String>>();
             final Query xQuery = formulateXPath(mailbox, query);
             
             QueryResult result = xQuery.execute();
             
-            NodeIterator it = result.getNodes();
-            while (it.hasNext()) {
-                list.add(new JCRMessage(it.nextNode(), getLogger()));
-            }
-            return list;
+            final NodeIterator it = result.getNodes();
+
+            
+            // Lazy build the JCRMessage instances
+            return new SearchQueryIterator(new Iterator<MailboxMembership<?>>() {
+
+                public boolean hasNext() {
+                    return it.hasNext();
+                }
+
+                public MailboxMembership<?> next() {
+                    return new JCRMessage(it.nextNode(), getLogger());
+                    
+                }
+
+                public void remove() {
+                    it.remove();
+                }
+                
+            }, query, getLogger());
         } catch (RepositoryException e) {
             throw new StorageException(HumanReadableText.SEARCH_FAILED, e);
         }

Modified: james/imap/trunk/jpa/src/main/java/org/apache/james/imap/jpa/mail/JPAMessageMapper.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/jpa/src/main/java/org/apache/james/imap/jpa/mail/JPAMessageMapper.java?rev=984711&r1=984710&r2=984711&view=diff
==============================================================================
--- james/imap/trunk/jpa/src/main/java/org/apache/james/imap/jpa/mail/JPAMessageMapper.java (original)
+++ james/imap/trunk/jpa/src/main/java/org/apache/james/imap/jpa/mail/JPAMessageMapper.java Thu Aug 12 10:10:56 2010
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.james.imap.jpa.mail;
 
+import java.util.Iterator;
 import java.util.List;
 
 import javax.persistence.EntityManager;
@@ -39,6 +40,7 @@ import org.apache.james.imap.mailbox.Sto
 import org.apache.james.imap.mailbox.MessageRange.Type;
 import org.apache.james.imap.mailbox.SearchQuery.Criterion;
 import org.apache.james.imap.mailbox.SearchQuery.NumericRange;
+import org.apache.james.imap.store.SearchQueryIterator;
 import org.apache.james.imap.store.mail.MessageMapper;
 import org.apache.james.imap.store.mail.model.Mailbox;
 import org.apache.james.imap.store.mail.model.MailboxMembership;
@@ -189,61 +191,61 @@ public class JPAMessageMapper extends JP
         }
     }
 
-    /**
-     * @see org.apache.james.imap.store.mail.MessageMapper#searchMailbox(org.apache.james.imap.mailbox.SearchQuery)
+
+
+    /*
+     * 
+     * (non-Javadoc)
+     * @see org.apache.james.imap.store.mail.MessageMapper#searchMailbox(org.apache.james.imap.store.mail.model.Mailbox, org.apache.james.imap.mailbox.SearchQuery)
      */
     @SuppressWarnings("unchecked")
-    public List<MailboxMembership<Long>> searchMailbox(Mailbox<Long> mailbox, SearchQuery query) throws StorageException {
+    public Iterator<Long> searchMailbox(Mailbox<Long> mailbox, SearchQuery query) throws StorageException {
         try {
-            final Query jQuery = formulateJQL(mailbox.getMailboxId(), query);
-            return jQuery.getResultList();
-        } catch (PersistenceException e) {
-            throw new StorageException(HumanReadableText.SEARCH_FAILED, e);
-        }
-    }
-
-    private Query formulateJQL(Long mailboxId, SearchQuery query) {
-        final StringBuilder queryBuilder = new StringBuilder(50);
-        queryBuilder.append("SELECT membership FROM Membership membership WHERE membership.mailboxId = ").append(mailboxId);
-        final List<Criterion> criteria = query.getCriterias();
-        boolean range = false;
-        int rangeLength = -1;
-        
-        if (criteria.size() == 1) {
-            final Criterion firstCriterion = criteria.get(0);
-            if (firstCriterion instanceof SearchQuery.UidCriterion) {
-                final SearchQuery.UidCriterion uidCriterion = (SearchQuery.UidCriterion) firstCriterion;
-                final NumericRange[] ranges = uidCriterion.getOperator().getRange();
-                rangeLength = ranges.length;
-
-                for (int i = 0; i < ranges.length; i++) {
-                    final long low = ranges[i].getLowValue();
-                    final long high = ranges[i].getHighValue();
-
-                    if (low == Long.MAX_VALUE) {
-                        queryBuilder.append(" AND membership.uid<=").append(high);
-                        range = true;
-                    } else if (low == high) {
-                        queryBuilder.append(" AND membership.uid=").append(low);
-                        range = false;
-                    } else {
-                        queryBuilder.append(" AND membership.uid BETWEEN ").append(low).append(" AND ").append(high);
-                        range = true;
+            final StringBuilder queryBuilder = new StringBuilder(50);
+            queryBuilder.append("SELECT membership FROM Membership membership WHERE membership.mailboxId = ").append(mailbox.getMailboxId());
+            final List<Criterion> criteria = query.getCriterias();
+            boolean range = false;
+            int rangeLength = -1;
+            
+            if (criteria.size() == 1) {
+                final Criterion firstCriterion = criteria.get(0);
+                if (firstCriterion instanceof SearchQuery.UidCriterion) {
+                    final SearchQuery.UidCriterion uidCriterion = (SearchQuery.UidCriterion) firstCriterion;
+                    final NumericRange[] ranges = uidCriterion.getOperator().getRange();
+                    rangeLength = ranges.length;
+
+                    for (int i = 0; i < ranges.length; i++) {
+                        final long low = ranges[i].getLowValue();
+                        final long high = ranges[i].getHighValue();
+
+                        if (low == Long.MAX_VALUE) {
+                            queryBuilder.append(" AND membership.uid<=").append(high);
+                            range = true;
+                        } else if (low == high) {
+                            queryBuilder.append(" AND membership.uid=").append(low);
+                            range = false;
+                        } else {
+                            queryBuilder.append(" AND membership.uid BETWEEN ").append(low).append(" AND ").append(high);
+                            range = true;
+                        }
                     }
                 }
+            }        
+            if (rangeLength != 0 || range) {
+                queryBuilder.append(" order by membership.uid");
             }
-        }        
-        if (rangeLength != 0 || range) {
-            queryBuilder.append(" order by membership.uid");
-        }
-        
-        Query jQuery = getEntityManager().createQuery(queryBuilder.toString());
+            
+            Query jQuery = getEntityManager().createQuery(queryBuilder.toString());
 
-        // Check if we only need to fetch 1 message, if so we can set a limit to speed up things
-        if (rangeLength == 1 && range == false) {
-            jQuery.setMaxResults(1);
+            // Check if we only need to fetch 1 message, if so we can set a limit to speed up things
+            if (rangeLength == 1 && range == false) {
+                jQuery.setMaxResults(1);
+            }
+            return new SearchQueryIterator(jQuery.getResultList().iterator(), query);
+            
+        } catch (PersistenceException e) {
+            throw new StorageException(HumanReadableText.SEARCH_FAILED, e);
         }
-        return jQuery;
     }
 
     /*

Modified: james/imap/trunk/memory/src/main/java/org/apache/james/imap/inmemory/mail/InMemoryMessageMapper.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/memory/src/main/java/org/apache/james/imap/inmemory/mail/InMemoryMessageMapper.java?rev=984711&r1=984710&r2=984711&view=diff
==============================================================================
--- james/imap/trunk/memory/src/main/java/org/apache/james/imap/inmemory/mail/InMemoryMessageMapper.java (original)
+++ james/imap/trunk/memory/src/main/java/org/apache/james/imap/inmemory/mail/InMemoryMessageMapper.java Thu Aug 12 10:10:56 2010
@@ -14,6 +14,7 @@ import org.apache.james.imap.mailbox.Mes
 import org.apache.james.imap.mailbox.SearchQuery;
 import org.apache.james.imap.mailbox.StorageException;
 import org.apache.james.imap.store.MailboxMembershipComparator;
+import org.apache.james.imap.store.SearchQueryIterator;
 import org.apache.james.imap.store.mail.MessageMapper;
 import org.apache.james.imap.store.mail.model.Mailbox;
 import org.apache.james.imap.store.mail.model.MailboxMembership;
@@ -169,12 +170,13 @@ public class InMemoryMessageMapper imple
         return message.getUid();
     }
 
+
     /*
      * (non-Javadoc)
-     * @see org.apache.james.imap.store.mail.MessageMapper#searchMailbox(org.apache.james.imap.mailbox.SearchQuery)
+     * @see org.apache.james.imap.store.mail.MessageMapper#searchMailbox(org.apache.james.imap.store.mail.model.Mailbox, org.apache.james.imap.mailbox.SearchQuery)
      */
-    public List<MailboxMembership<Long>> searchMailbox(Mailbox<Long> mailbox, SearchQuery query) throws StorageException {
-        return new ArrayList<MailboxMembership<Long>>(getMembershipByUidForMailbox(mailbox).values());
+    public Iterator<Long> searchMailbox(Mailbox<Long> mailbox, SearchQuery query) throws StorageException {
+        return new SearchQueryIterator(new ArrayList<MailboxMembership<?>>(getMembershipByUidForMailbox(mailbox).values()).iterator(), query);
     }
 
     /**

Added: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/SearchQueryIterator.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/SearchQueryIterator.java?rev=984711&view=auto
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/SearchQueryIterator.java (added)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/SearchQueryIterator.java Thu Aug 12 10:10:56 2010
@@ -0,0 +1,110 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.imap.store;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.logging.Log;
+import org.apache.james.imap.mailbox.MailboxException;
+import org.apache.james.imap.mailbox.SearchQuery;
+import org.apache.james.imap.store.mail.model.MailboxMembership;
+
+/**
+ * {@link Iterator} implementation which use a {@link MessageSearches} instance to lazy 
+ * match the elements of a wrapped {@link Iterator} against a {@link SearchQuery}. This class should be used
+ * if the store implementation can not filter the {@link Iterator} fore-hand against the {@link SearchQuery}
+ * 
+ *
+ */
+public final class SearchQueryIterator implements Iterator<Long>{
+
+    private final MessageSearches searches = new MessageSearches();
+    private Iterator<MailboxMembership<?>> it;
+    private SearchQuery query;
+    private Long next;
+    
+    public SearchQueryIterator(Iterator<MailboxMembership<?>> it, SearchQuery query) {
+        this(it, query, null);
+    }
+
+    public SearchQueryIterator(Iterator<MailboxMembership<?>> it, SearchQuery query, Log log) {
+        this.it = it;
+        this.query = query;
+        if (log != null) {
+            searches.setLog(log);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.util.Iterator#hasNext()
+     */
+    public boolean hasNext() {       
+        // check if we already did the lazy loading 
+        if (next == null) {
+            while (it.hasNext()) {
+                MailboxMembership<?> nextMembership = it.next();
+                try {
+                    if (searches.isMatch(query, nextMembership)) {
+                        next = nextMembership.getUid();
+                        return true;
+                    } else {
+                        next = null;
+                    }
+                } catch (MailboxException e) {
+                    searches.getLog().info("Cannot test message against search criteria. Will continue to test other messages.", e);
+                    if (searches.getLog().isDebugEnabled())
+                        searches.getLog().debug("UID: " + nextMembership.getUid());
+                }
+            }
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+
+    /**
+     * Return the next Uid of the {@link MailboxMembership} which matched the 
+     * {@link SearchQuery}
+     * 
+     * @return uid
+     */
+    public Long next() {
+        if (hasNext()) {
+            Long copy = next;
+            next = null;
+            return copy;
+        } else {
+            throw new NoSuchElementException();
+        }
+    }
+
+    /**
+     * Not supported.
+     * 
+     * @throws UnsupportedOperationException
+     */
+    public void remove() {
+        throw new UnsupportedOperationException("Read-only Iterator");
+    }
+
+}

Modified: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/StoreMessageManager.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/StoreMessageManager.java?rev=984711&r1=984710&r2=984711&view=diff
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/StoreMessageManager.java (original)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/StoreMessageManager.java Thu Aug 12 10:10:56 2010
@@ -32,7 +32,6 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.TreeSet;
@@ -506,23 +505,7 @@ public abstract class StoreMessageManage
      * @see org.apache.james.imap.mailbox.Mailbox#search(org.apache.james.imap.mailbox.SearchQuery, org.apache.james.imap.mailbox.MailboxSession)
      */
     public Iterator<Long> search(SearchQuery query, MailboxSession mailboxSession) throws MailboxException {
-        final List<MailboxMembership<Id>> members = messageMapper.searchMailbox(mailbox, query);
-        final Set<Long> uids = new TreeSet<Long>();
-        for (MailboxMembership<Id> member:members) {
-            try {
-                final MessageSearches searches = new MessageSearches();
-                searches.setLog(mailboxSession.getLog());
-                if (searches.isMatch(query, member)) {
-                    uids.add(member.getUid());
-                }
-            } catch (MailboxException e) {
-                mailboxSession.getLog().info("Cannot test message against search criteria. Will continue to test other messages.", e);
-                if (mailboxSession.getLog().isDebugEnabled())
-                    mailboxSession.getLog().debug("UID: " + member.getUid());
-            }
-        }
-
-        return uids.iterator();
+        return messageMapper.searchMailbox(mailbox, query);    
     }
 
     /**

Modified: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/MessageMapper.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/MessageMapper.java?rev=984711&r1=984710&r2=984711&view=diff
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/MessageMapper.java (original)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/MessageMapper.java Thu Aug 12 10:10:56 2010
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.james.imap.store.mail;
 
+import java.util.Iterator;
 import java.util.List;
 
 import org.apache.james.imap.mailbox.MessageRange;
@@ -79,14 +80,14 @@ public interface MessageMapper<Id> exten
             throws StorageException;
 
     /**
-     * Return a List of {@link MailboxMembership} which matched the {@link SearchQuery}
+     * Return a List of uids which matched the {@link SearchQuery}
      * The list must be ordered by the {@link Document} uid
      * @param mailbox
      * @param query
      * @return
      * @throws StorageException
      */
-    public abstract List<MailboxMembership<Id>> searchMailbox(Mailbox<Id> mailbox, SearchQuery query) throws StorageException;
+    public abstract Iterator<Long> searchMailbox(Mailbox<Id> mailbox, SearchQuery query) throws StorageException;
 
     /**
      * Delete the given {@link MailboxMembership}



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org