You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2011/04/16 23:22:58 UTC

svn commit: r1094055 - in /tomcat/trunk: java/org/apache/catalina/connector/CoyoteAdapter.java test/org/apache/catalina/core/TestAsyncContextImpl.java test/org/apache/catalina/valves/TesterAccessLogValve.java webapps/docs/changelog.xml

Author: markt
Date: Sat Apr 16 21:22:58 2011
New Revision: 1094055

URL: http://svn.apache.org/viewvc?rev=1094055&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=51038
Ensure async requests are included in access logs.

Added:
    tomcat/trunk/test/org/apache/catalina/valves/TesterAccessLogValve.java
Modified:
    tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java
    tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java
    tomcat/trunk/webapps/docs/changelog.xml

Modified: tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java?rev=1094055&r1=1094054&r2=1094055&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java (original)
+++ tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java Sat Apr 16 21:22:58 2011
@@ -317,6 +317,10 @@ public class CoyoteAdapter implements Ad
                 request.finishRequest();
                 response.finishResponse();
                 req.action(ActionCode.POST_REQUEST , null);
+                ((Context) request.getMappingData().context).logAccess(
+                        request, response,
+                        System.currentTimeMillis() - req.getStartTime(),
+                        false);
             }
 
         } catch (IOException e) {

Modified: tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java?rev=1094055&r1=1094054&r2=1094055&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java (original)
+++ tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java Sat Apr 16 21:22:58 2011
@@ -39,10 +39,17 @@ import org.apache.catalina.Wrapper;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.catalina.valves.TesterAccessLogValve;
+import org.apache.catalina.valves.TesterAccessLogValve.Entry;
 import org.apache.tomcat.util.buf.ByteChunk;
 
 public class TestAsyncContextImpl extends TomcatBaseTest {
 
+    // Time for a request to process (need to allow for threads to start etc.)
+    private static final long REQUEST_TIME = 500;
+    // Timeout thread (where used) checks for timeout every second
+    private static final long TIMEOUT_MARGIN = 1000;
+
     public void testBug49528() throws Exception {
         // Setup Tomcat instance
         Tomcat tomcat = getTomcatInstance();
@@ -57,6 +64,9 @@ public class TestAsyncContextImpl extend
         wrapper.setAsyncSupported(true);
         ctx.addServletMapping("/", "servlet");
         
+        TesterAccessLogValve alv = new TesterAccessLogValve();
+        ctx.getPipeline().addValve(alv);
+        
         tomcat.start();
         
         // Call the servlet once
@@ -71,6 +81,16 @@ public class TestAsyncContextImpl extend
         }
 
         assertEquals("1false2true3true4true5false", servlet.getResult());
+
+        // Check the access log
+        List<Entry> entries = alv.getEntries();
+        assertEquals(1, entries.size());
+        Entry entry = entries.get(0);
+        assertEquals(200, entry.getStatus());
+        assertTrue(entry.toString(), entry.getTime() >
+                Bug49528Servlet.THREAD_SLEEP_TIME);
+        assertTrue(entry.toString(), entry.getTime() <
+                Bug49528Servlet.THREAD_SLEEP_TIME + REQUEST_TIME);
     }
     
     public void testBug49567() throws Exception {
@@ -87,6 +107,9 @@ public class TestAsyncContextImpl extend
         wrapper.setAsyncSupported(true);
         ctx.addServletMapping("/", "servlet");
         
+        TesterAccessLogValve alv = new TesterAccessLogValve();
+        ctx.getPipeline().addValve(alv);
+        
         tomcat.start();
         
         // Call the servlet once
@@ -101,6 +124,16 @@ public class TestAsyncContextImpl extend
         }
 
         assertEquals("1false2true3true4true5false", servlet.getResult());
+
+        // Check the access log
+        List<Entry> entries = alv.getEntries();
+        assertEquals(1, entries.size());
+        Entry entry = entries.get(0);
+        assertEquals(200, entry.getStatus());
+        assertTrue(entry.toString(), entry.getTime() >
+        Bug49567Servlet.THREAD_SLEEP_TIME);
+        assertTrue(entry.toString(), entry.getTime() <
+                Bug49567Servlet.THREAD_SLEEP_TIME + REQUEST_TIME);
     }
     
     public void testAsyncStartNoComplete() throws Exception {
@@ -122,6 +155,9 @@ public class TestAsyncContextImpl extend
         wrapper.setAsyncSupported(true);
         ctx.addServletMapping("/", "servlet");
         
+        TesterAccessLogValve alv = new TesterAccessLogValve();
+        ctx.getPipeline().addValve(alv);
+        
         tomcat.start();
         
         // Call the servlet the first time
@@ -133,6 +169,18 @@ public class TestAsyncContextImpl extend
         ByteChunk bc2 = getUrl("http://localhost:" + getPort() +
                 "/?echo=run2");
         assertEquals("OK-run2", bc2.toString());
+        
+        // Check the access log
+        List<Entry> entries = alv.getEntries();
+        assertEquals(2, entries.size());
+        for (Entry entry : entries) {
+            assertEquals(200, entry.getStatus());
+            assertTrue(entry.toString(), entry.getTime() >
+                    AsyncStartNoCompleteServlet.ASYNC_TIMEOUT);
+            assertTrue(entry.toString(), entry.getTime() <
+                    AsyncStartNoCompleteServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN +
+                            REQUEST_TIME);
+        }
     }
     
     public void testAsyncStartWithComplete() throws Exception {
@@ -150,11 +198,21 @@ public class TestAsyncContextImpl extend
         wrapper.setAsyncSupported(true);
         ctx.addServletMapping("/", "servlet");
         
+        TesterAccessLogValve alv = new TesterAccessLogValve();
+        ctx.getPipeline().addValve(alv);
+        
         tomcat.start();
         
         // Call the servlet once
         ByteChunk bc = getUrl("http://localhost:" + getPort() + "/");
         assertEquals("OK", bc.toString());
+
+        // Check the access log
+        List<Entry> entries = alv.getEntries();
+        assertEquals(1, entries.size());
+        Entry entry = entries.get(0);
+        assertEquals(200, entry.getStatus());
+        assertTrue(entry.toString(), entry.getTime() < REQUEST_TIME);
     }
     
     /*
@@ -168,6 +226,8 @@ public class TestAsyncContextImpl extend
         
         private StringBuilder result;
         
+        public static final long THREAD_SLEEP_TIME = 1000;
+
         public String getResult() {
             return result.toString();
         }
@@ -194,7 +254,7 @@ public class TestAsyncContextImpl extend
                     try {
                         result.append('3');
                         result.append(req.isAsyncStarted());
-                        Thread.sleep(1000);
+                        Thread.sleep(THREAD_SLEEP_TIME);
                         result.append('4');
                         result.append(req.isAsyncStarted());
                         resp.setContentType("text/plain");
@@ -227,6 +287,8 @@ public class TestAsyncContextImpl extend
         
         private StringBuilder result;
         
+        public static final long THREAD_SLEEP_TIME = 1000;
+
         public String getResult() {
             return result.toString();
         }
@@ -256,7 +318,7 @@ public class TestAsyncContextImpl extend
                             try {
                                 result.append('3');
                                 result.append(req.isAsyncStarted());
-                                Thread.sleep(1000);
+                                Thread.sleep(THREAD_SLEEP_TIME);
                                 result.append('4');
                                 result.append(req.isAsyncStarted());
                                 resp.setContentType("text/plain");
@@ -283,6 +345,8 @@ public class TestAsyncContextImpl extend
     
     private static class AsyncStartNoCompleteServlet extends HttpServlet {
 
+        public static final long ASYNC_TIMEOUT = 1000;
+        
         private static final long serialVersionUID = 1L;
         
         @Override
@@ -298,7 +362,7 @@ public class TestAsyncContextImpl extend
                 resp.getWriter().print("-" + echo);
             }
             // Speed up the test by reducing the timeout
-            actxt.setTimeout(1000);
+            actxt.setTimeout(ASYNC_TIMEOUT);
         }
     }
 
@@ -363,6 +427,9 @@ public class TestAsyncContextImpl extend
 
         ctx.addApplicationListener(TrackingRequestListener.class.getName());
 
+        TesterAccessLogValve alv = new TesterAccessLogValve();
+        ctx.getPipeline().addValve(alv);
+        
         tomcat.start();
         ByteChunk res = getUrl("http://localhost:" + getPort() + "/async");
         StringBuilder expected = new StringBuilder("requestInitialized-");
@@ -377,6 +444,16 @@ public class TestAsyncContextImpl extend
         }
         expected.append("requestDestroyed");
         assertEquals(expected.toString(), res.toString());
+
+        // Check the access log
+        List<Entry> entries = alv.getEntries();
+        assertEquals(1, entries.size());
+        Entry entry = entries.get(0);
+        assertEquals(200, entry.getStatus());
+        assertTrue(entry.toString(), entry.getTime() >
+                TimeoutServlet.ASYNC_TIMEOUT);
+        assertTrue(entry.toString(), entry.getTime() <
+                TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + REQUEST_TIME);
     }
     
     private static class TimeoutServlet extends HttpServlet {
@@ -385,6 +462,8 @@ public class TestAsyncContextImpl extend
         private boolean completeOnTimeout;
         private String dispatchUrl;
 
+        public static final long ASYNC_TIMEOUT = 3000;
+
         public TimeoutServlet(boolean completeOnTimeout, String dispatchUrl) {
             this.completeOnTimeout = completeOnTimeout;
             this.dispatchUrl = dispatchUrl;
@@ -396,7 +475,7 @@ public class TestAsyncContextImpl extend
             if (req.isAsyncSupported()) {
                 resp.getWriter().print("TimeoutServletGet-");
                 final AsyncContext ac = req.startAsync();
-                ac.setTimeout(3000);
+                ac.setTimeout(ASYNC_TIMEOUT);
                 
                 ac.addListener(new TrackingListener(
                         false, completeOnTimeout, dispatchUrl));
@@ -450,6 +529,9 @@ public class TestAsyncContextImpl extend
 
         ctx.addApplicationListener(TrackingRequestListener.class.getName());
 
+        TesterAccessLogValve alv = new TesterAccessLogValve();
+        ctx.getPipeline().addValve(alv);
+        
         tomcat.start();
         
         StringBuilder url = new StringBuilder(48);
@@ -471,6 +553,14 @@ public class TestAsyncContextImpl extend
         expected.append("NonAsyncServletGet-");
         expected.append("requestDestroyed");
         assertEquals(expected.toString(), res.toString());
+        
+        // Check the access log
+        List<Entry> entries = alv.getEntries();
+        assertEquals(1, entries.size());
+        Entry entry = entries.get(0);
+        assertEquals(200, entry.getStatus());
+        assertTrue(entry.toString(), entry.getTime() > 0);
+        assertTrue(entry.toString(), entry.getTime() < REQUEST_TIME);
     }
     
     private static class DispatchingServlet extends HttpServlet {
@@ -547,6 +637,9 @@ public class TestAsyncContextImpl extend
         wrapper2.setAsyncSupported(true);
         ctx.addServletMapping("/stage2", "timeout");
 
+        TesterAccessLogValve alv = new TesterAccessLogValve();
+        ctx.getPipeline().addValve(alv);
+        
         tomcat.start();
         
         StringBuilder url = new StringBuilder(48);
@@ -560,6 +653,16 @@ public class TestAsyncContextImpl extend
                 "DispatchingServletGet-DispatchingServletGet-onStartAsync-" +
                 "TimeoutServletGet-onStartAsync-onTimeout-onComplete-",
                 res.toString());
+
+        // Check the access log
+        List<Entry> entries = alv.getEntries();
+        assertEquals(1, entries.size());
+        Entry entry = entries.get(0);
+        assertEquals(200, entry.getStatus());
+        assertTrue(entry.toString(), entry.getTime() >
+                TimeoutServlet.ASYNC_TIMEOUT);
+        assertTrue(entry.toString(), entry.getTime() <
+                TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + REQUEST_TIME);
     }
 
     private static class TrackingServlet extends HttpServlet {
@@ -754,6 +857,9 @@ public class TestAsyncContextImpl extend
 
         ctx.addApplicationListener(TrackingRequestListener.class.getName());
 
+        TesterAccessLogValve alv = new TesterAccessLogValve();
+        ctx.getPipeline().addValve(alv);
+        
         tomcat.start();
         
         StringBuilder url = new StringBuilder(48);
@@ -777,6 +883,14 @@ public class TestAsyncContextImpl extend
         }
         expected.append("ErrorServletGet-onError-onComplete-requestDestroyed");
         assertEquals(expected.toString(), res.toString());
+        
+        // Check the access log
+        List<Entry> entries = alv.getEntries();
+        assertEquals(1, entries.size());
+        Entry entry = entries.get(0);
+        assertEquals(200, entry.getStatus());
+        assertTrue(entry.toString(), entry.getTime() > 0);
+        assertTrue(entry.toString(), entry.getTime() < REQUEST_TIME);
     }
     
     private static class ErrorServlet extends HttpServlet {
@@ -806,21 +920,32 @@ public class TestAsyncContextImpl extend
         wrapper.setAsyncSupported(true);
         ctx.addServletMapping("/", "servlet");
 
-        ErrorServlet error = new ErrorServlet();
-        Tomcat.addServlet(ctx, "error", error);
-        ctx.addServletMapping("/stage2", "error");
-
+        TesterAccessLogValve alv = new TesterAccessLogValve();
+        ctx.getPipeline().addValve(alv);
+        
         tomcat.start();
         
         ByteChunk res = getUrl("http://localhost:" + getPort() + "/");
         
         assertEquals("Runnable-onComplete-", res.toString());
+
+        // Check the access log
+        List<Entry> entries = alv.getEntries();
+        assertEquals(1, entries.size());
+        Entry entry = entries.get(0);
+        assertEquals(200, entry.getStatus());
+        assertTrue(entry.toString(), entry.getTime() >
+        AsyncStartRunnable.THREAD_SLEEP_TIME);
+        assertTrue(entry.toString(), entry.getTime() <
+                AsyncStartRunnable.THREAD_SLEEP_TIME + REQUEST_TIME);
     }
     
     private static final class AsyncStartRunnable extends HttpServlet {
         
         private static final long serialVersionUID = 1L;
 
+        public static final long THREAD_SLEEP_TIME = 3000;
+
         @Override
         protected void doGet(HttpServletRequest request,
                 HttpServletResponse response)
@@ -836,7 +961,7 @@ public class TestAsyncContextImpl extend
                 @Override
                 public void run() {
                     try {
-                        Thread.sleep(3 * 1000);
+                        Thread.sleep(THREAD_SLEEP_TIME);
                         asyncContext.getResponse().getWriter().write(
                                 "Runnable-");
                         asyncContext.complete();
@@ -862,6 +987,9 @@ public class TestAsyncContextImpl extend
         wrapper.setAsyncSupported(true);
         ctx.addServletMapping("/", "servlet");
         
+        TesterAccessLogValve alv = new TesterAccessLogValve();
+        ctx.getPipeline().addValve(alv);
+        
         tomcat.start();
         
         // Call the servlet once
@@ -875,12 +1003,25 @@ public class TestAsyncContextImpl extend
         assertNotNull(testHeader);
         assertEquals(1, testHeader.size());
         assertEquals("xyz",testHeader.get(0));
+        
+        // Check the access log
+        List<Entry> entries = alv.getEntries();
+        assertEquals(1, entries.size());
+        Entry entry = entries.get(0);
+        assertEquals(200, entry.getStatus());
+        assertTrue(entry.toString(), entry.getTime() >
+        Bug50753Servlet.THREAD_SLEEP_TIME);
+        assertTrue(entry.toString(), entry.getTime() <
+                Bug50753Servlet.THREAD_SLEEP_TIME + REQUEST_TIME);
+
     }
 
     private static class Bug50753Servlet extends HttpServlet {
 
         private static final long serialVersionUID = 1L;
 
+        public static final long THREAD_SLEEP_TIME = 5000;
+
         @Override
         protected void doGet(HttpServletRequest req,
                 final HttpServletResponse resp)
@@ -890,7 +1031,7 @@ public class TestAsyncContextImpl extend
                 @Override
                 public void run() { 
                     try { 
-                        Thread.sleep(5000); 
+                        Thread.sleep(THREAD_SLEEP_TIME); 
                         resp.setHeader("A", "xyz"); 
                         resp.setContentType("text/plain"); 
                         resp.setContentLength("OK".getBytes().length); 

Added: tomcat/trunk/test/org/apache/catalina/valves/TesterAccessLogValve.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/valves/TesterAccessLogValve.java?rev=1094055&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/valves/TesterAccessLogValve.java (added)
+++ tomcat/trunk/test/org/apache/catalina/valves/TesterAccessLogValve.java Sat Apr 16 21:22:58 2011
@@ -0,0 +1,94 @@
+/*
+ * 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.catalina.valves;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.AccessLog;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+
+public class TesterAccessLogValve extends ValveBase implements AccessLog {
+
+    private List<Entry> entries = new ArrayList<Entry>();
+
+    public TesterAccessLogValve() {
+        // Async requests are supported
+        super(true);
+    }
+
+    @Override
+    public void log(Request request, Response response, long time) {
+        entries.add(new Entry(request.getRequestURI(), response.getStatus(),
+                time));
+    }
+
+    @Override
+    public void setRequestAttributesEnabled(boolean requestAttributesEnabled) {
+        // NOOP - test code
+    }
+
+    @Override
+    public boolean getRequestAttributesEnabled() {
+        // Always false - test code
+        return false;
+    }
+
+    @Override
+    public void invoke(Request request, Response response) throws IOException,
+            ServletException {
+        // Just invoke next - access logging happens via log() method
+        getNext().invoke(request, response);
+    }
+
+    public List<Entry> getEntries() {
+        return entries;
+    }
+
+    public static class Entry {
+        private String uri;
+        private int status;
+        private long time;
+
+        public Entry(String uri, int status, long time) {
+            this.uri = uri;
+            this.status = status;
+            this.time = time;
+        }
+
+        public String getUri() {
+            return uri;
+        }
+
+        public int getStatus() {
+            return status;
+        }
+
+        public long getTime() {
+            return time;
+        }
+
+        @Override
+        public String toString() {
+            return "Uri: " + uri + ", Status: " + status + ", Time: " + time;
+        }
+    }
+}

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1094055&r1=1094054&r2=1094055&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Sat Apr 16 21:22:58 2011
@@ -61,6 +61,10 @@
         take a long time to process, which might indicate that their processing
         threads are stuck. Based on a patch provided by TomLu. (slaurent)
       </add>
+      <fix>
+        <bug>51038</bug>: Ensure that asynchronous requests are included in
+        access logs. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">



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