You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2011/03/01 18:52:26 UTC

svn commit: r1075945 - in /cxf/trunk: rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/tl/ rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/ rt/management-web/src/...

Author: sergeyb
Date: Tue Mar  1 17:52:26 2011
New Revision: 1075945

URL: http://svn.apache.org/viewvc?rev=1075945&view=rev
Log:
[CXF-3370] Initial support for searching for individual log entries with related fixes to search extensions

Modified:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/Beanspector.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParser.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContext.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContextImpl.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchCondition.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/tl/ThreadLocalSearchContext.java
    cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/FiqlParserTest.java
    cxf/trunk/rt/management-web/src/main/java/org/apache/cxf/management/web/logging/LogRecord.java
    cxf/trunk/rt/management-web/src/main/java/org/apache/cxf/management/web/logging/atom/AtomPullServer.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPullSpringTest.java
    cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_logging_atompull/WEB-INF/beans.xml

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/Beanspector.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/Beanspector.java?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/Beanspector.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/Beanspector.java Tue Mar  1 17:52:26 2011
@@ -22,8 +22,8 @@ import java.beans.IntrospectionException
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
 
@@ -34,8 +34,8 @@ class Beanspector<T> {
 
     private Class<T> tclass;
     private T tobj;
-    private Map<String, Method> getters = new HashMap<String, Method>();
-    private Map<String, Method> setters = new HashMap<String, Method>();
+    private Map<String, Method> getters = new LinkedHashMap<String, Method>();
+    private Map<String, Method> setters = new LinkedHashMap<String, Method>();
 
     public Beanspector(Class<T> tclass) {
         if (tclass == null) {

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParser.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParser.java?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParser.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/FiqlParser.java Tue Mar  1 17:52:26 2011
@@ -25,6 +25,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
@@ -292,7 +293,7 @@ public class FiqlParser<T> {
             if (!hasSubtree && AND.equals(operator)) {
                 try {
                     // Optimization: single SimpleSearchCondition for 'AND' conditions
-                    Map<String, ConditionType> map = new HashMap<String, ConditionType>();
+                    Map<String, ConditionType> map = new LinkedHashMap<String, ConditionType>();
                     beanspector.instantiate();
                     for (ASTNode<T> node : subnodes) {
                         FiqlParser<T>.Comparison comp = (Comparison)node;
@@ -348,9 +349,19 @@ public class FiqlParser<T> {
         public SearchCondition<T> build() throws FiqlParseException {
             T cond = createTemplate(name, value);
             ConditionType ct = operatorsMap.get(operator);
-            return new SimpleSearchCondition<T>(ct, cond);
+            
+            if (isPrimitive(cond)) {
+                return new SimpleSearchCondition<T>(ct, cond); 
+            } else {
+                return new SimpleSearchCondition<T>(Collections.singletonMap(name, ct), 
+                                                   cond);
+            }
         }
 
+        private boolean isPrimitive(T pojo) {
+            return pojo.getClass().getName().startsWith("java.lang");
+        }
+        
         private T createTemplate(String setter, Object val) throws FiqlParseException {
             try {
                 beanspector.instantiate().setValue(setter, val);

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContext.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContext.java?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContext.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContext.java Tue Mar  1 17:52:26 2011
@@ -18,6 +18,24 @@
  */
 package org.apache.cxf.jaxrs.ext.search;
 
+/**
+ * Represents the current search expression
+ */
 public interface SearchContext {
+    
+    /**
+     * Returns the typed search condition representing the search expression
+     * 
+     * @param cls the type of the bean(s) the new search condition will 
+     *        attempt to match
+     * @return the search condition
+     */
     <T> SearchCondition<T> getCondition(Class<T> cls);
+    
+    
+    /**
+     * Returns the actual query expression
+     * @return the expression
+     */
+    String getSearchExpression();
 }

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContextImpl.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContextImpl.java?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContextImpl.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContextImpl.java Tue Mar  1 17:52:26 2011
@@ -37,7 +37,7 @@ public class SearchContextImpl implement
     public <T> SearchCondition<T> getCondition(Class<T> cls) {
         FiqlParser<T> parser = getParser(cls);
         
-        String expression = getExpression();
+        String expression = getSearchExpression();
         if (expression != null) {
             try {
                 return parser.parse(expression);
@@ -50,7 +50,7 @@ public class SearchContextImpl implement
         
     }
 
-    private String getExpression() {
+    public String getSearchExpression() {
         String queryStr = (String)message.get(Message.QUERY_STRING);
         if (queryStr != null 
             && (queryStr.contains(SHORT_SEARCH_QUERY) || queryStr.contains(SEARCH_QUERY))) {

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchCondition.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchCondition.java?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchCondition.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SimpleSearchCondition.java Tue Mar  1 17:52:26 2011
@@ -21,8 +21,8 @@ package org.apache.cxf.jaxrs.ext.search;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -235,7 +235,7 @@ public class SimpleSearchCondition<T> im
      */
     private Map<String, Object> getGettersAndValues() {
         
-        Map<String, Object> getters2values = new HashMap<String, Object>();
+        Map<String, Object> getters2values = new LinkedHashMap<String, Object>();
         Beanspector<T> beanspector = new Beanspector<T>(condition);
         for (String getter : beanspector.getGettersNames()) {
             Object value = getValue(beanspector, getter, condition);

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/tl/ThreadLocalSearchContext.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/tl/ThreadLocalSearchContext.java?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/tl/ThreadLocalSearchContext.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/tl/ThreadLocalSearchContext.java Tue Mar  1 17:52:26 2011
@@ -32,4 +32,7 @@ public class ThreadLocalSearchContext ex
     }
 
 
+    public String getSearchExpression() {
+        return get().getSearchExpression();
+    }
 }

Modified: cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/FiqlParserTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/FiqlParserTest.java?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/FiqlParserTest.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/ext/search/FiqlParserTest.java Tue Mar  1 17:52:26 2011
@@ -22,6 +22,7 @@ import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.List;
 
 import javax.xml.datatype.DatatypeFactory;
 
@@ -164,6 +165,16 @@ public class FiqlParserTest extends Asse
     @Test
     public void testParseComplex1() throws FiqlParseException {
         SearchCondition<Condition> filter = parser.parse("name==ami*;level=gt=10");
+        assertEquals(ConditionType.AND, filter.getConditionType());
+        
+        List<SearchCondition<Condition>> conditions = filter.getSearchConditions();
+        assertEquals(2, conditions.size());
+        PrimitiveStatement st1 = conditions.get(0).getStatement();
+        assertEquals(ConditionType.EQUALS, st1.getCondition());
+        
+        PrimitiveStatement st2 = conditions.get(1).getStatement();
+        assertEquals(ConditionType.GREATER_THAN, st2.getCondition());
+        
         assertTrue(filter.isMet(new Condition("amichalec", 12, new Date())));
         assertTrue(filter.isMet(new Condition("ami", 12, new Date())));
         assertFalse(filter.isMet(new Condition("ami", 8, null)));
@@ -182,6 +193,17 @@ public class FiqlParserTest extends Asse
     @Test
     public void testParseComplex2() throws FiqlParseException {
         SearchCondition<Condition> filter = parser.parse("name==ami*,level=gt=10");
+        assertEquals(ConditionType.OR, filter.getConditionType());
+        
+        List<SearchCondition<Condition>> conditions = filter.getSearchConditions();
+        assertEquals(2, conditions.size());
+        
+        PrimitiveStatement st1 = conditions.get(0).getStatement();
+        assertEquals(ConditionType.EQUALS, st1.getCondition());
+        
+        PrimitiveStatement st2 = conditions.get(1).getStatement();
+        assertEquals(ConditionType.GREATER_THAN, st2.getCondition());
+        
         assertTrue(filter.isMet(new Condition("ami", 0, new Date())));
         assertTrue(filter.isMet(new Condition("foo", 20, null)));
         assertFalse(filter.isMet(new Condition("foo", 0, null)));

Modified: cxf/trunk/rt/management-web/src/main/java/org/apache/cxf/management/web/logging/LogRecord.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/management-web/src/main/java/org/apache/cxf/management/web/logging/LogRecord.java?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/rt/management-web/src/main/java/org/apache/cxf/management/web/logging/LogRecord.java (original)
+++ cxf/trunk/rt/management-web/src/main/java/org/apache/cxf/management/web/logging/LogRecord.java Tue Mar  1 17:52:26 2011
@@ -88,7 +88,9 @@ public class LogRecord {
         record.setLevel(LogLevel.fromJUL(julRecord.getLevel()));
         record.setLoggerName(julRecord.getLoggerName());
         if (julRecord.getThrown() != null) {
-            record.setThrowable(julRecord.getThrown());
+            StringWriter sw = new StringWriter();
+            julRecord.getThrown().printStackTrace(new PrintWriter(sw));
+            record.setThrowable(sw.getBuffer().toString());
         }
         if (julRecord.getParameters() != null) {
             record.setMessage(MessageFormat.format(julRecord.getMessage(), julRecord.getParameters()));
@@ -169,13 +171,6 @@ public class LogRecord {
         this.throwable = throwable;
     }
 
-    public void setThrowable(Throwable thr) {
-        Validate.notNull(thr, "throwable is null");
-        StringWriter sw = new StringWriter();
-        thr.printStackTrace(new PrintWriter(sw));
-        this.throwable = sw.getBuffer().toString();
-    }
-
     @Override
     public boolean equals(Object obj) {
         return EqualsBuilder.reflectionEquals(obj, this);

Modified: cxf/trunk/rt/management-web/src/main/java/org/apache/cxf/management/web/logging/atom/AtomPullServer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/management-web/src/main/java/org/apache/cxf/management/web/logging/atom/AtomPullServer.java?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/rt/management-web/src/main/java/org/apache/cxf/management/web/logging/atom/AtomPullServer.java (original)
+++ cxf/trunk/rt/management-web/src/main/java/org/apache/cxf/management/web/logging/atom/AtomPullServer.java Tue Mar  1 17:52:26 2011
@@ -36,6 +36,7 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 
 import org.apache.abdera.model.Entry;
@@ -47,6 +48,7 @@ import org.apache.cxf.jaxrs.ext.search.O
 import org.apache.cxf.jaxrs.ext.search.PrimitiveStatement;
 import org.apache.cxf.jaxrs.ext.search.SearchCondition;
 import org.apache.cxf.jaxrs.ext.search.SearchConditionVisitor;
+import org.apache.cxf.jaxrs.ext.search.SearchContext;
 import org.apache.cxf.management.web.logging.LogLevel;
 import org.apache.cxf.management.web.logging.LogRecord;
 import org.apache.cxf.management.web.logging.ReadWriteLogStorage;
@@ -60,12 +62,12 @@ public class AtomPullServer extends Abst
     private List<LogRecord> records = new LinkedList<LogRecord>();
     private WeakHashMap<Integer, Feed> feeds = new WeakHashMap<Integer, Feed>();
     private ReadableLogStorage storage;
-    private int pageSize = 40;
-    private int maxInMemorySize = 500;
+    private int pageSize = 20;
+    private int maxInMemorySize = 1000;
     private boolean useArchivedFeeds;
-    private int recordsSize;
+    private volatile int recordsSize;
     private volatile boolean alreadyClosed;
-    private SearchCondition<LogRecord> condition;
+    private SearchCondition<LogRecord> readableStorageCondition;
         
     @Context
     private MessageContext context;
@@ -112,7 +114,7 @@ public class AtomPullServer extends Abst
                 r.setLevel(LogLevel.valueOf(l.getLevel()));
                 list.add(new SearchConditionImpl(r));
             }
-            condition = new OrSearchCondition<LogRecord>(list);
+            readableStorageCondition = new OrSearchCondition<LogRecord>(list);
         }
         initBusProperty();
     }
@@ -142,7 +144,7 @@ public class AtomPullServer extends Abst
     
     @GET
     @Produces("application/atom+xml")
-    public Feed getXmlFeed(@PathParam("id") int page) {
+    public Feed getXmlFeed() {
         return getXmlFeedWithPage(1);
     }
     
@@ -162,16 +164,18 @@ public class AtomPullServer extends Abst
         }
         
         Feed feed = null;
+        SearchCondition<LogRecord> condition = getCurrentCondition();
         synchronized (records) {
-            List<LogRecord> list = getSubList(page);
+            List<LogRecord> list = new LinkedList<LogRecord>();
+            int lastPage = fillSubList(list, page, condition);
             Collections.sort(list, new LogRecordComparator());
             feed = (Feed)new CustomFeedConverter(page).convert(list).get(0);
-            setFeedPageProperties(feed, page);
+            setFeedPageProperties(feed, page, lastPage);
         }
         // if at the moment we've converted n < pageSize number of records only and
         // persist a Feed keyed by a page then another reader requesting the same page 
         // may miss latest records which might've been added since the original request
-        if (feed.getEntries().size() == pageSize) {
+        if (condition == null && feed.getEntries().size() == pageSize) {
             synchronized (feeds) {
                 feeds.put(page, feed);
             }
@@ -183,7 +187,8 @@ public class AtomPullServer extends Abst
     @Produces({"text/html", "application/xhtml+xml" })
     @Path("alternate/{id}")
     public String getAlternateFeed(@PathParam("id") int page) {
-        List<LogRecord> list = getSubList(page);
+        List<LogRecord> list = new LinkedList<LogRecord>();
+        fillSubList(list, page, getCurrentCondition());
         Collections.sort(list, new LogRecordComparator());
         return convertEntriesToHtml(list);
         
@@ -193,7 +198,7 @@ public class AtomPullServer extends Abst
     @Path("entry/{id}")
     @Produces("application/atom+xml;type=entry")
     public Entry getEntry(@PathParam("id") int index) {
-        List<LogRecord> list = getLogRecords(index);
+        List<LogRecord> list = getLogRecords(index, getCurrentCondition());
         return (Entry)new CustomEntryConverter(index).convert(list).get(0);
     }
     
@@ -201,7 +206,7 @@ public class AtomPullServer extends Abst
     @Path("entry/alternate/{id}")
     @Produces({"text/html", "application/xhtml+xml" })
     public String getAlternateEntry(@PathParam("id") int index) {
-        List<LogRecord> logRecords = getLogRecords(index);
+        List<LogRecord> logRecords = getLogRecords(index, getCurrentCondition());
         return convertEntryToHtml(logRecords.get(0));
     }
     
@@ -212,12 +217,12 @@ public class AtomPullServer extends Abst
         return recordsSize;
     }
     
-    private List<LogRecord> getLogRecords(int index) {
+    private List<LogRecord> getLogRecords(int index, SearchCondition<LogRecord> theSearch) {
         List<LogRecord> list = new LinkedList<LogRecord>();
         if (storage != null) {
             int storageSize = storage.getSize();
             if (recordsSize == -1 || index < storageSize) {
-                storage.load(list, condition, index, 1);
+                storage.load(list, theSearch, index, 1);
             } else if (index < recordsSize) {
                 list.add(records.get(index - storageSize));   
             }
@@ -231,17 +236,16 @@ public class AtomPullServer extends Abst
     }
     
     
-    protected List<LogRecord> getSubList(int page) {
+    protected int fillSubList(List<LogRecord> list, int page, SearchCondition<LogRecord> theSearch) {
         
         if (recordsSize == -1) {
             // let the external storage load the records it knows about
-            List<LogRecord> list = new LinkedList<LogRecord>();
-            storage.load(list, condition, page == 1 ? 0 : (page - 1) * pageSize, pageSize);
-            return list;
+            storage.load(list, theSearch, page == 1 ? 0 : (page - 1) * pageSize, pageSize);
+            return page;
         }
         
         if (recordsSize == 0) {
-            return records;
+            return 1;
         }
         
         int fromIndex = 0;
@@ -267,58 +271,93 @@ public class AtomPullServer extends Abst
         }
 
         // if we have the storage then try to load from it
+        boolean loaded = false;
         if (storage != null) {
             if (fromIndex < storage.getSize()) {
                 int storageSize = storage.getSize();
                 int maxQuantityToLoad = toIndex > storageSize ? toIndex - storageSize : toIndex - fromIndex;
-                List<LogRecord> list = new LinkedList<LogRecord>();
-                storage.load(list, condition, fromIndex, maxQuantityToLoad);
+                storage.load(list, theSearch, fromIndex, maxQuantityToLoad);
+                loaded = true;
+                
                 int totalQuantity = toIndex - fromIndex;
                 if (list.size() < totalQuantity) {
                     int remaining = totalQuantity - list.size();
                     if (remaining > records.size()) {
                         remaining = records.size();
                     }
-                    list.addAll(records.subList(0, remaining));
+                    fromIndex = 0;
+                    toIndex = remaining;
+                    loaded = false;
                 }
-                return list;
             } else {
                 fromIndex -= storage.getSize();
                 toIndex -= storage.getSize();
             }
         } 
-        return records.subList(fromIndex, toIndex);
+        if (!loaded) {
+            list.addAll(filterRecords(records.subList(fromIndex, toIndex), theSearch));
+        }
         
+        if (theSearch != null && list.size() < pageSize && page * pageSize < recordsSize) {
+            return fillSubList(list, page + 1, theSearch);    
+        } else {
+            return page;
+        }
+    }
+    
+    private List<LogRecord> filterRecords(List<LogRecord> list, SearchCondition<LogRecord> theSearch) {
+        return theSearch == null ? list : theSearch.findAll(list);
     }
     
-    protected void setFeedPageProperties(Feed feed, int page) {
+    private SearchCondition<LogRecord> getCurrentCondition() {
+        SearchCondition<LogRecord> current = context.getContext(SearchContext.class)
+            .getCondition(LogRecord.class);
+        if (current == null) {
+            return readableStorageCondition;
+        } else {
+            return current;
+        }
+    }
+    
+    private String getSearchExpression() {
+        return context.getContext(SearchContext.class).getSearchExpression();
+    }
+    
+    protected void setFeedPageProperties(Feed feed, int page, int lastPage) {
         String self = context.getUriInfo().getAbsolutePath().toString();
         feed.addLink(self, "self");
         
+        int feedSize = feed.getEntries().size();
+        String searchExpression = getSearchExpression();
+        
         String uri = context.getUriInfo().getBaseUriBuilder().path("logs").build().toString();
         feed.addLink(uri + "/alternate/" + page, "alternate");
         if (!useArchivedFeeds) {
             if (recordsSize != -1) {
                 if (page > 2) {
-                    feed.addLink(uri, "first");
+                    feed.addLink(createLinkUri(uri, searchExpression), "first");
                 }
                 
-                if (page * pageSize < recordsSize) {
-                    feed.addLink(uri + "/" + (page + 1), "next");
+                if (searchExpression == null && lastPage * pageSize < recordsSize
+                    || searchExpression != null && feedSize == pageSize) {
+                    feed.addLink(createLinkUri(uri + "/" + (lastPage + 1), searchExpression), "next");
                 }
                 
-                if (page * (pageSize + 1) < recordsSize) {
+                if (searchExpression == null && page * (pageSize + 1) < recordsSize) {
                     feed.addLink(uri + "/" + (recordsSize / pageSize + 1), "last");
                 }
-            } else if (feed.getEntries().size() == pageSize) {
-                feed.addLink(uri + "/" + (page + 1), "next");
+            } else if (feedSize == pageSize) {
+                feed.addLink(createLinkUri(uri + "/" + (page + 1), searchExpression), "next");
             }
-            if (page > 1) {
+            if (searchExpression == null && page > 1) {
                 uri = page > 2 ? uri + "/" + (page - 1) : uri;
-                feed.addLink(uri, "previous");
+                feed.addLink(createLinkUri(uri, searchExpression), "previous");
             }
         } else {
-            feed.addLink(self, "current");
+            throw new WebApplicationException(Response.serverError()
+                                              .entity("Archived feeds are not supported yet")
+                                              .build());
+            // feed.addLink(self, "current");
             // TODO : add prev-archive and next-archive; next-archive should not be set if it will point to
             // current
             // and xmlns:fh="http://purl.org/syndication/history/1.0":archive extension but only if
@@ -327,6 +366,9 @@ public class AtomPullServer extends Abst
         
     }
     
+    private String createLinkUri(String uri, String search) {
+        return search == null ? uri : uri + "?_s=" + search; 
+    }
     
     public void publish(LogRecord record) {
         if (alreadyClosed) {
@@ -371,6 +413,12 @@ public class AtomPullServer extends Abst
         }
     }
     
+    public synchronized void reset() {
+        records.clear();
+        recordsSize = 0;
+        feeds.clear();
+    }
+    
     // TODO : this all can be done later on in a simple xslt template
     private String convertEntriesToHtml(List<LogRecord> rs) {
         StringBuilder sb = new StringBuilder();

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPullSpringTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPullSpringTest.java?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPullSpringTest.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPullSpringTest.java Tue Mar  1 17:52:26 2011
@@ -18,7 +18,9 @@
  */
 package org.apache.cxf.systest.jaxrs;
 
+import java.io.PrintWriter;
 import java.io.StringReader;
+import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedList;
@@ -28,6 +30,7 @@ import java.util.logging.LogRecord;
 import java.util.logging.Logger;
 
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.xml.bind.JAXBContext;
 
@@ -42,8 +45,8 @@ import org.apache.cxf.jaxrs.provider.Ato
 import org.apache.cxf.management.web.logging.LogLevel;
 import org.apache.cxf.management.web.logging.ReadWriteLogStorage;
 import org.apache.cxf.management.web.logging.ReadableLogStorage;
+import org.apache.cxf.management.web.logging.atom.AtomPullServer;
 import org.apache.cxf.testutil.common.AbstractClientServerTestBase;
-import org.apache.cxf.transport.http.HTTPConduit;
 
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -58,6 +61,11 @@ public class JAXRSLoggingAtomPullSpringT
     private int namedLogger;
     private int resourceLogger;
     private int throwables;
+    private int errorLevels;
+    private int traceLevels;
+    private int infoLevels;
+    private int debugLevels;
+    private int warningLevels;
     
     @BeforeClass
     public static void beforeClass() throws Exception {
@@ -101,9 +109,6 @@ public class JAXRSLoggingAtomPullSpringT
         WebClient wcEntry = WebClient.create("http://localhost:" + PORT + "/atom/logs",
             Collections.singletonList(new AtomEntryProvider()))
             .accept("application/atom+xml;type=entry");
-        HTTPConduit conduit = WebClient.getConfig(wcEntry).getHttpConduit();
-        conduit.getClient().setReceiveTimeout(1000000);
-        conduit.getClient().setConnectionTimeout(1000000);
         for (int i = 0; i < 8; i++) {
             Entry entry = wcEntry.path("entry/" + i).get(Entry.class);
             entry.toString();
@@ -135,38 +140,51 @@ public class JAXRSLoggingAtomPullSpringT
     }
     
     @Test
+    public void testPagedFeedWithQuery() throws Exception {
+        WebClient wcReset = WebClient.create("http://localhost:" + PORT + "/reset");
+        wcReset.post(null);
+        
+        WebClient wc = WebClient.create("http://localhost:" + PORT + "/resource2/paged/log");
+        wc.get();
+        Thread.sleep(3000);
+        
+        String address = "http://localhost:" + PORT + "/atom2/logs"
+                         + "?_s=level==INFO,level==ERROR,level==WARN";
+        verifyPagesWithQuery(address, "next", 3, 2, "theNamedLogger");
+        verifyPagesWithQuery(address, "next", 3, 2, "theNamedLogger");    
+    }
+    
+    @Test
     public void testPagedFeedWithReadWriteStorage() throws Exception {
         WebClient wc = WebClient.create("http://localhost:" + PORT + "/resource3/storage");
-        HTTPConduit conduit = WebClient.getConfig(wc).getHttpConduit();
-        conduit.getClient().setReceiveTimeout(1000000);
-        conduit.getClient().setConnectionTimeout(1000000);
         wc.path("/log").get();
         Thread.sleep(3000);
         
         verifyStoragePages("http://localhost:" 
-                           + PORT + "/atom3/logs", "next", "Resource3", "theStorageLogger");
+                           + PORT + "/atom3/logs", "next", "Resource3", "theStorageLogger", false);
         List<org.apache.cxf.management.web.logging.LogRecord> list = Storage.getRecords();
         assertEquals(4, list.size());
         verifyStoragePages("http://localhost:" 
-                           + PORT + "/atom3/logs", "next", "Resource3", "theStorageLogger");
+                           + PORT + "/atom3/logs", "next", "Resource3", "theStorageLogger", false);
         verifyStoragePages("http://localhost:" + PORT + "/atom3/logs/2", "previous", "Resource3", 
-                           "theStorageLogger");
+                           "theStorageLogger", false);
     }
     
     @Test
     public void testPagedFeedWithReadOnlyStorage() throws Exception {
         verifyStoragePages("http://localhost:" 
-                           + PORT + "/atom4/logs", "next", "Resource4", "readOnlyStorageLogger");
+                           + PORT + "/atom4/logs", "next", "Resource4", "readOnlyStorageLogger", true);
         verifyStoragePages("http://localhost:" + PORT + "/atom4/logs/2", "previous", "Resource4", 
-                           "readOnlyStorageLogger");
+                           "readOnlyStorageLogger", true);
     }
     
     private void verifyStoragePages(String startAddress, String rel, 
-                                    String resourceName, String nLogger) 
+                                    String resourceName, String nLogger,
+                                    boolean readOnly) 
         throws Exception {
         List<Entry> entries = new ArrayList<Entry>();
         String href1 = fillPagedEntries(entries, startAddress, 4, rel, true);
-        assertNull(fillPagedEntries(entries, href1, 4, rel, false));
+        fillPagedEntries(entries, href1, 4, rel, "next".equals(rel) && readOnly ? true : false);
         assertEquals(8, entries.size());
         
         resetCounters();
@@ -190,7 +208,7 @@ public class JAXRSLoggingAtomPullSpringT
         String href1 = fillPagedEntries(entries, startAddress, 
                                         firstValue, rel, true);
         String href2 = fillPagedEntries(entries, href1, 3, rel, true);
-        assertNull(fillPagedEntries(entries, href2, lastValue, rel, false));
+        fillPagedEntries(entries, href2, lastValue, rel, false);
         assertEquals(8, entries.size());
         
         resetCounters();
@@ -200,6 +218,26 @@ public class JAXRSLoggingAtomPullSpringT
         verifyCounters();
     }
     
+    private void verifyPagesWithQuery(String startAddress, String rel, 
+                             int firstValue, int lastValue, String nLogger) 
+        throws Exception {
+        List<Entry> entries = new ArrayList<Entry>();
+        String hrefRel = fillPagedEntries(entries, startAddress, 
+                                        firstValue, rel, true);
+        hrefRel = fillPagedEntries(entries, hrefRel, lastValue, rel, false);
+        assertEquals(firstValue + lastValue, entries.size());
+        
+        resetCounters();
+        for (Entry e : entries) {
+            updateCounters(readLogRecord(e.getContent()), "Resource2", nLogger);
+        }
+        assertEquals(3, errorLevels);
+        assertEquals(1, infoLevels);
+        assertEquals(0, debugLevels);
+        assertEquals(0, traceLevels);
+        assertEquals(1, warningLevels);
+    }
+    
     private String fillPagedEntries(List<Entry> entries, String href, int expected, 
                                     String rel, boolean relExpected) {
         Feed feed = getFeed(href);
@@ -211,6 +249,7 @@ public class JAXRSLoggingAtomPullSpringT
             assertNotNull(link);
             return link.getHref().toString();
         } else {
+            assertNull(link);
             return null;
         }
     }
@@ -218,6 +257,8 @@ public class JAXRSLoggingAtomPullSpringT
     private Feed getFeed(String address) {
         WebClient wc = WebClient.create(address,
                                          Collections.singletonList(new AtomFeedProvider()));
+        WebClient.getConfig(wc).getHttpConduit().getClient().setReceiveTimeout(10000000);
+        
         Feed feed = wc.accept("application/atom+xml").get(Feed.class);
         feed.toString();
         return feed;
@@ -265,6 +306,23 @@ public class JAXRSLoggingAtomPullSpringT
 
     }
     
+    @Path("/")
+    public static class AtomServerReset {
+        
+        private AtomPullServer server;
+        
+        public void setServer(AtomPullServer s) {
+            this.server = s;
+        }
+        
+        
+        @POST
+        public void reset() {
+            server.reset();
+        }
+
+    }
+    
     @Ignore
     public static class ExternalStorage implements ReadableLogStorage {
 
@@ -294,7 +352,9 @@ public class JAXRSLoggingAtomPullSpringT
             lr.setLoggerName(loggerName);
             lr.setLevel(LogLevel.fromJUL(level));
             if (t != null) {
-                lr.setThrowable(t);
+                StringWriter sw = new StringWriter();
+                t.printStackTrace(new PrintWriter(sw));
+                lr.setThrowable(sw.getBuffer().toString());
             }
             records.add(lr);
         }
@@ -359,25 +419,36 @@ public class JAXRSLoggingAtomPullSpringT
     
     
     private static void doLog(Logger l1, Logger l2) {
+        
+        // 3 severe, 1 warning, 1 info and 3 debug messages 
+        
+        // 1
         l1.severe("severe message");
+        // 2
         l1.warning("warning message");
+        // 3
         l1.info("info message");
         LogRecord r = new LogRecord(Level.FINE, "fine message");
         if ("Resource4".equals(l1.getName())) {
             r.setLoggerName(l1.getName());
         }
         r.setThrown(new IllegalArgumentException("tadaam"));
+        // 4
         l1.log(r);
         r = new LogRecord(Level.FINER, "finer message with {0} and {1}");
         r.setParameters(new Object[] {
             "param1", "param2"
         });
         r.setLoggerName("faky-logger");
+        // 5
         l1.log(r);
+        // 6
         l1.finest("finest message");
 
         // for LOG2 only 'warning' and above messages should be logged
+        // 7
         l2.severe("severe message");
+        // 8
         l2.severe("severe message2");
         l2.info("info message - should not pass!");
         l2.finer("finer message - should not pass!");
@@ -407,6 +478,19 @@ public class JAXRSLoggingAtomPullSpringT
         if (record.getThrowable().length() > 0) {
             throwables++;
         }
+        
+        LogLevel level = record.getLevel();
+        if (level == LogLevel.ERROR) {
+            errorLevels++;
+        } else if (level == LogLevel.DEBUG) {
+            debugLevels++;
+        } else if (level == LogLevel.TRACE) {
+            traceLevels++;    
+        } else if (level == LogLevel.INFO) {
+            infoLevels++;
+        } else {
+            warningLevels++;    
+        }
     }
     
     private void resetCounters() {
@@ -414,6 +498,12 @@ public class JAXRSLoggingAtomPullSpringT
         namedLogger = 0;
         resourceLogger = 0;
         throwables = 0;
+        
+        errorLevels = 0;
+        infoLevels = 0;
+        debugLevels = 0;
+        traceLevels = 0;
+        warningLevels = 0;
     }
     
     private void verifyCounters() {
@@ -421,5 +511,11 @@ public class JAXRSLoggingAtomPullSpringT
         assertEquals(4, resourceLogger);
         assertEquals(2, namedLogger);
         assertEquals(1, fakyLogger);
+        
+        assertEquals(3, errorLevels);
+        assertEquals(1, infoLevels);
+        assertEquals(2, debugLevels);
+        assertEquals(1, traceLevels);
+        assertEquals(1, warningLevels);
     }
 }

Modified: cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_logging_atompull/WEB-INF/beans.xml
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_logging_atompull/WEB-INF/beans.xml?rev=1075945&r1=1075944&r2=1075945&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_logging_atompull/WEB-INF/beans.xml (original)
+++ cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_logging_atompull/WEB-INF/beans.xml Tue Mar  1 17:52:26 2011
@@ -150,7 +150,7 @@ http://www.springframework.org/schema/ut
 			<ref bean="feed" />
 		</jaxrs:providers>
 	</jaxrs:server>
-
+	
 	<jaxrs:server id="atomServer4" address="/atom4">
 		<jaxrs:serviceBeans>
 			<ref bean="atomPullServer4"/>
@@ -160,6 +160,17 @@ http://www.springframework.org/schema/ut
 		</jaxrs:providers>
 	</jaxrs:server>
     
+    
+    <jaxrs:server id="atomReset" address="/reset">
+		<jaxrs:serviceBeans>
+			<ref bean="atomServerReset"/>
+		</jaxrs:serviceBeans>
+	</jaxrs:server>
+
+    <bean id="atomServerReset" class="org.apache.cxf.systest.jaxrs.JAXRSLoggingAtomPullSpringTest$AtomServerReset">
+        <property name="server" ref="atomPullServer2"/>
+    </bean>
+    
 </beans>
 <!-- END SNIPPET: beans -->