This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new e05c5a3  Slightly better initialization in RestServlet.
e05c5a3 is described below

commit e05c5a357cf9490433fcafd4df82dad02bd7755f
Author: JamesBognar <[email protected]>
AuthorDate: Wed Oct 10 12:13:08 2018 -0400

    Slightly better initialization in RestServlet.
---
 .../java/org/apache/juneau/utils/SearchArgs.java   |  6 ++
 .../apache/juneau/rest/BasicRestCallHandler.java   |  2 +
 .../java/org/apache/juneau/rest/RestContext.java   | 12 ++++
 .../java/org/apache/juneau/rest/RestServlet.java   | 50 +++++++------
 .../java/org/apache/juneau/rest/mock/MockRest.java | 13 +++-
 .../apache/juneau/rest/ThreadLocalObjectsTest.java | 81 ++++++++++++++++++++++
 6 files changed, 139 insertions(+), 25 deletions(-)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/SearchArgs.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/SearchArgs.java
index 26b2cff..fdcda4f 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/SearchArgs.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/SearchArgs.java
@@ -24,6 +24,12 @@ import org.apache.juneau.internal.*;
  * Encapsulates arguments for basic search/view/sort/position/limit 
functionality.
  */
 public class SearchArgs {
+
+       /**
+        * Default search args.
+        */
+       public static SearchArgs DEFAULT = SearchArgs.builder().build();
+
        private final Map<String,String> search;
        private final List<String> view;
        private final Map<String,Boolean> sort;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
index de95ef6..238b084 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
@@ -199,6 +199,8 @@ public class BasicRestCallHandler implements 
RestCallHandler {
                        r1.setAttribute("Exception", e);
                        r1.setAttribute("ExecTime", System.currentTimeMillis() 
- startTime);
                        handleError(r1, r2, e);
+               } finally {
+                       context.clearState();
                }
 
                context.finishCall(r1, r2);
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 5b5a505..99f6200 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -4777,6 +4777,9 @@ public final class RestContext extends BeanContext {
        }
 
        void setRequest(RestRequest req) {
+               // Must be careful not to bleed thread-locals.
+               if (this.req.get() != null)
+                       throw new RuntimeException("Thread-local request object 
was not cleaned up from previous request.  " + this + ", 
thread=["+Thread.currentThread().getId()+"]");
                this.req.set(req);
        }
 
@@ -4790,6 +4793,9 @@ public final class RestContext extends BeanContext {
        }
 
        void setResponse(RestResponse res) {
+               // Must be careful not to bleed thread-locals.
+               if (this.res.get() != null)
+                       throw new RuntimeException("Thread-local response 
object was not cleaned up from previous request.  " + this + ", 
thread=["+Thread.currentThread().getId()+"]");
                this.res.set(res);
        }
 
@@ -4801,4 +4807,10 @@ public final class RestContext extends BeanContext {
                req.remove();
                res.remove();
        }
+
+       @Override
+       public String toString() {
+               Object r = getResource();
+               return "RestContext: 
hashCode=["+System.identityHashCode(this)+"], resource=["+(r == null ? null : 
r.getClass()+","+System.identityHashCode(r))+"]";
+       }
 }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
index 05ebb3a..ee67915 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
@@ -39,20 +39,18 @@ public abstract class RestServlet extends HttpServlet {
 
        private RestContextBuilder builder;
        private volatile RestContext context;
-       private boolean isInitialized = false;
-       private Exception initException;
+       private volatile Exception initException;
+       private boolean isInitialized = false;  // Should not be volatile.
 
        @Override /* Servlet */
        public final synchronized void init(ServletConfig servletConfig) throws 
ServletException {
                try {
+                       if (context != null)
+                               return;
                        builder = RestContext.create(servletConfig, 
this.getClass(), null).init(this);
                        super.init(servletConfig);
-                       if (! isInitialized) {
-                               
builder.servletContext(this.getServletContext());
-                               context = builder.build();
-                               isInitialized = true;
-                       }
-                       context.postInit();
+                       builder.servletContext(this.getServletContext());
+                       setContext(builder.build());
                        context.postInitChildFirst();
                } catch (RestException e) {
                        // Thrown RestExceptions are simply caught and 
re-thrown on subsequent calls to service().
@@ -70,8 +68,6 @@ public abstract class RestServlet extends HttpServlet {
                        initException = new Exception(e);
                        log(SEVERE, e, "Servlet init error on class ''{0}''", 
getClass().getName());
                        throw new ServletException(e);
-               } finally {
-                       isInitialized = true;
                }
        }
 
@@ -83,13 +79,17 @@ public abstract class RestServlet extends HttpServlet {
                super.init(servletConfig);
        }
 
-       /*
+       /**
         * Sets the context object for this servlet.
-        * Used when subclasses of RestServlet are attached as child resources.
+        *
+        * @param context
+        * @throws ServletException
         */
-       synchronized void setContext(RestContext context) {
+       public synchronized void setContext(RestContext context) throws 
ServletException {
                this.builder = context.builder;
                this.context = context;
+               isInitialized = true;
+               context.postInit();
        }
 
        @Override /* GenericServlet */
@@ -131,15 +131,17 @@ public abstract class RestServlet extends HttpServlet {
        @Override /* Servlet */
        public void service(HttpServletRequest r1, HttpServletResponse r2) 
throws ServletException, InternalServerError, IOException {
                try {
-                       if (initException != null) {
-                               if (initException instanceof RestException)
-                                       throw (RestException)initException;
-                               throw new InternalServerError(initException);
+                       // To avoid checking the volatile field context on 
every call, use the non-volatile isInitialized field as a first-check check.
+                       if (! isInitialized) {
+                               if (initException != null) {
+                                       if (initException instanceof 
RestException)
+                                               throw 
(RestException)initException;
+                                       throw new 
InternalServerError(initException);
+                               }
+                               if (context == null)
+                                       throw new InternalServerError("Servlet 
{0} not initialized.  init(ServletConfig) was not called.  This can occur if 
you've overridden this method but didn't call super.init(RestConfig).", 
getClass().getName());
+                               isInitialized = true;
                        }
-                       if (context == null)
-                               throw new InternalServerError("Servlet {0} not 
initialized.  init(ServletConfig) was not called.  This can occur if you've 
overridden this method but didn't call super.init(RestConfig).", 
getClass().getName());
-                       if (! isInitialized)
-                               throw new InternalServerError("Servlet {0} has 
not been initialized", getClass().getName());
 
                        context.getCallHandler().service(r1, r2);
 
@@ -147,8 +149,6 @@ public abstract class RestServlet extends HttpServlet {
                        r2.sendError(SC_INTERNAL_SERVER_ERROR, 
e.getLocalizedMessage());
                } catch (Throwable e) {
                        r2.sendError(SC_INTERNAL_SERVER_ERROR, 
e.getLocalizedMessage());
-               } finally {
-                       context.clearState();
                }
        }
 
@@ -213,6 +213,8 @@ public abstract class RestServlet extends HttpServlet {
         * @return The current HTTP request, or <jk>null</jk> if it wasn't 
created.
         */
        public RestRequest getRequest() {
+               if (context == null) 
+                       return null;
                return context.getRequest();
        }
 
@@ -222,6 +224,8 @@ public abstract class RestServlet extends HttpServlet {
         * @return The current HTTP response, or <jk>null</jk> if it wasn't 
created.
         */
        public RestResponse getResponse() {
+               if (context == null) 
+                       return null;
                return context.getResponse();
        }
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockRest.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockRest.java
index 6742b7e..58ea9c0 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockRest.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockRest.java
@@ -61,8 +61,17 @@ public class MockRest implements MockHttpConnection {
        private final RestContext rc;
 
        private MockRest(Class<?> c, boolean debug) throws Exception {
-               if (! CONTEXTS.containsKey(c))
-                       CONTEXTS.put(c, 
RestContext.create(c.newInstance()).logger(debug ? BasicRestLogger.class : 
NoOpRestLogger.class).build().postInit().postInitChildFirst());
+               if (! CONTEXTS.containsKey(c)) {
+                       Object r = c.newInstance();
+                       RestContext rc = RestContext.create(r).logger(debug ? 
BasicRestLogger.class : NoOpRestLogger.class).build();
+                       if (r instanceof RestServlet) {
+                               ((RestServlet)r).setContext(rc);
+                       } else {
+                               rc.postInit();
+                       }
+                       rc.postInitChildFirst();
+                       CONTEXTS.put(c, rc);
+               }
                rc = CONTEXTS.get(c);
        }
 
diff --git 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/ThreadLocalObjectsTest.java
 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/ThreadLocalObjectsTest.java
new file mode 100644
index 0000000..462060b
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/ThreadLocalObjectsTest.java
@@ -0,0 +1,81 @@
+// 
***************************************************************************************************************************
+// * 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.juneau.rest;
+
+import static org.junit.Assert.*;
+
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.mock.*;
+import org.junit.*;
+import org.junit.runners.*;
+
+/**
+ * Tests various aspects of URL path parts.
+ */
+@SuppressWarnings({"javadoc"})
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ThreadLocalObjectsTest {
+
+       
//=================================================================================================================
+       // Thread-locals on top-level resource.
+       
//=================================================================================================================
+
+       @SuppressWarnings("serial")
+       @RestResource(path="/a")
+       public static class A extends BasicRestServlet {
+               @RestMethod
+               public void getA01() throws Exception {
+                       
getResponse().getWriter().append(getRequest().getQuery("foo"));
+               }
+
+               @RestHook(HookEvent.END_CALL)
+               public void assertThreadsNotSet() {
+                       assertNull(getRequest());
+                       assertNull(getResponse());
+               }
+       }
+       static MockRest a = MockRest.create(A.class);
+
+       @Test
+       public void a01() throws Exception {
+               a.get("/a01?foo=bar").execute()
+                       .assertBodyContains("bar")
+               ;
+       }
+
+       
//=================================================================================================================
+       // Thread-locals on child resource.
+       
//=================================================================================================================
+
+       @SuppressWarnings("serial")
+       @RestResource(
+               children={
+                       A.class
+               }
+       )
+       public static class B extends BasicRestServletGroup {
+               @RestHook(HookEvent.END_CALL)
+               public void assertThreadsNotSet2() {
+                       assertNull(getRequest());
+                       assertNull(getResponse());
+               }
+       }
+       static MockRest b = MockRest.create(B.class);
+
+       @Test
+       public void b01() throws Exception {
+               b.get("/a/a01?foo=bar").execute()
+                       .assertBodyContains("bar")
+               ;
+       }
+}
\ No newline at end of file

Reply via email to