Author: radu
Date: Wed Oct 26 12:38:50 2016
New Revision: 1766676

URL: http://svn.apache.org/viewvc?rev=1766676&view=rev
Log:
SLING-6165 - Expose a service for Sling Scripting that provides request-scoped 
Resource Resolvers for scripting dependencies

* made resolver initialisation lazy
* extended tests for concurrent access scenarios

Modified:
    
sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingResourceResolverFactoryImpl.java
    
sling/trunk/bundles/scripting/core/src/test/java/org/apache/sling/scripting/core/impl/ScriptingResourceResolverFactoryImplTest.java

Modified: 
sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingResourceResolverFactoryImpl.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingResourceResolverFactoryImpl.java?rev=1766676&r1=1766675&r2=1766676&view=diff
==============================================================================
--- 
sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingResourceResolverFactoryImpl.java
 (original)
+++ 
sling/trunk/bundles/scripting/core/src/main/java/org/apache/sling/scripting/core/impl/ScriptingResourceResolverFactoryImpl.java
 Wed Oct 26 12:38:50 2016
@@ -43,7 +43,7 @@ public class ScriptingResourceResolverFa
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(ScriptingResourceResolverFactoryImpl.class);
 
-    private ThreadLocal<ScriptingResourceResolver> perThreadResourceResolver = 
new ThreadLocal<>();
+    private static final ThreadLocal<ScriptingResourceResolver> 
perThreadResourceResolver = new ThreadLocal<>();
     private boolean logStackTraceOnResolverClose;
 
     @Reference
@@ -68,7 +68,23 @@ public class ScriptingResourceResolverFa
 
     @Override
     public ResourceResolver getRequestScopedResourceResolver() {
-        return perThreadResourceResolver.get();
+        ScriptingResourceResolver threadResolver = 
perThreadResourceResolver.get();
+        if (threadResolver == null) {
+            // no per thread; need to synchronize access
+            synchronized (perThreadResourceResolver) {
+                try {
+                    ResourceResolver delegate = 
rrf.getServiceResourceResolver(null);
+                    threadResolver = new 
ScriptingResourceResolver(logStackTraceOnResolverClose, delegate);
+                    perThreadResourceResolver.set(threadResolver);
+                    if (LOGGER.isDebugEnabled()) {
+                        LOGGER.debug("Setting per thread resource resolver for 
thread {}.", Thread.currentThread().getName());
+                    }
+                } catch (LoginException e) {
+                    throw new IllegalStateException("Cannot create per thread 
resource resolver.", e);
+                }
+            }
+        }
+        return threadResolver;
     }
 
     @Override
@@ -76,24 +92,13 @@ public class ScriptingResourceResolverFa
         try {
             return rrf.getServiceResourceResolver(null);
         } catch (LoginException e) {
-            LOGGER.error("Unable to retrieve a scripting resource resolver.");
-            throw new IllegalStateException(e);
+            throw new IllegalStateException("Unable to retrieve a scripting 
resource resolver.", e);
         }
     }
 
     @Override
     public void onEvent(SlingRequestEvent sre) {
-        if (sre.getType().equals(SlingRequestEvent.EventType.EVENT_INIT)) {
-            try {
-                ResourceResolver delegate = 
rrf.getServiceResourceResolver(null);
-                this.perThreadResourceResolver.set(new 
ScriptingResourceResolver(logStackTraceOnResolverClose, delegate));
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("Setting per thread resource resolver for 
thread {}.", Thread.currentThread().getName());
-                }
-            } catch (LoginException e) {
-                LOGGER.error("Cannot create per thread resource resolver.", e);
-            }
-        } else if 
(sre.getType().equals(SlingRequestEvent.EventType.EVENT_DESTROY)) {
+        if (sre.getType().equals(SlingRequestEvent.EventType.EVENT_DESTROY)) {
             ScriptingResourceResolver scriptingResourceResolver = 
perThreadResourceResolver.get();
             if (scriptingResourceResolver != null) {
                 scriptingResourceResolver._close();

Modified: 
sling/trunk/bundles/scripting/core/src/test/java/org/apache/sling/scripting/core/impl/ScriptingResourceResolverFactoryImplTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/core/src/test/java/org/apache/sling/scripting/core/impl/ScriptingResourceResolverFactoryImplTest.java?rev=1766676&r1=1766675&r2=1766676&view=diff
==============================================================================
--- 
sling/trunk/bundles/scripting/core/src/test/java/org/apache/sling/scripting/core/impl/ScriptingResourceResolverFactoryImplTest.java
 (original)
+++ 
sling/trunk/bundles/scripting/core/src/test/java/org/apache/sling/scripting/core/impl/ScriptingResourceResolverFactoryImplTest.java
 Wed Oct 26 12:38:50 2016
@@ -29,56 +29,63 @@ import javax.servlet.ServletContext;
 import javax.servlet.ServletRequest;
 
 import org.apache.sling.api.request.SlingRequestEvent;
+import org.apache.sling.api.resource.LoginException;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.internal.util.reflection.Whitebox;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.*;
 
 public class ScriptingResourceResolverFactoryImplTest {
 
-    private static final int MAX_CONCURRENT_RESOLVERS = 100;
+    private static final int MAX_CONCURRENT_RESOLVERS = 200;
+    private static final int RESOLVER_REUSE_FOR_SAME_THREAD = 100;
+    private ScriptingResourceResolverFactoryImpl 
scriptingResourceResolverFactory;
+    private Set<ResourceResolver> delegates;
+
+    @Before
+    public void setUp() throws LoginException {
+        delegates = new HashSet<>();
+        ResourceResolverFactory rrf = mock(ResourceResolverFactory.class);
+        when(rrf.getServiceResourceResolver(null)).thenAnswer(new 
Answer<ResourceResolver>() {
+            @Override
+            public ResourceResolver answer(InvocationOnMock invocation) throws 
Throwable {
+                ResourceResolver delegate = getMockedRR();
+                delegates.add(delegate);
+                return delegate;
+            }
+        });
+        scriptingResourceResolverFactory = new 
ScriptingResourceResolverFactoryImpl();
+        Whitebox.setInternalState(scriptingResourceResolverFactory, "rrf", 
rrf);
+    }
+
+    @After
+    public void tearDown() {
+        scriptingResourceResolverFactory = null;
+        delegates = null;
+    }
 
     @Test
     public void testGetRequestScopedResourceResolver() throws Exception {
-        ResourceResolverFactory rrf = mock(ResourceResolverFactory.class);
-        ResourceResolver delegate = mock(ResourceResolver.class);
-        when(delegate.getUserID()).thenReturn("sling-scripting");
-        when(rrf.getServiceResourceResolver(null)).thenReturn(delegate);
-        ScriptingResourceResolverFactoryImpl scriptingResourceResolverFactory 
= new ScriptingResourceResolverFactoryImpl();
-        Whitebox.setInternalState(scriptingResourceResolverFactory, "rrf", 
rrf);
-        
assertNull(scriptingResourceResolverFactory.getRequestScopedResourceResolver());
-        SlingRequestEvent sre1 = new 
SlingRequestEvent(mock(ServletContext.class), mock(ServletRequest.class), 
SlingRequestEvent.EventType
-                .EVENT_INIT);
-        scriptingResourceResolverFactory.onEvent(sre1);
         ResourceResolver resourceResolver = 
scriptingResourceResolverFactory.getRequestScopedResourceResolver();
         assertEquals("sling-scripting", resourceResolver.getUserID());
         SlingRequestEvent sre2 = new 
SlingRequestEvent(mock(ServletContext.class), mock(ServletRequest.class), 
SlingRequestEvent.EventType
                 .EVENT_DESTROY);
         scriptingResourceResolverFactory.onEvent(sre2);
-        
assertNull(scriptingResourceResolverFactory.getRequestScopedResourceResolver());
-        verify(delegate).close();
+        assertEquals(1, delegates.size());
+        for (ResourceResolver delegate : delegates) {
+            verify(delegate).close();
+        }
     }
 
     @Test
     public void testGetRequestScopedResourceResolverWithThreads() throws 
Exception {
-        ResourceResolverFactory rrf = mock(ResourceResolverFactory.class);
-        final Set<ResourceResolver> delegates = new HashSet<>();
-        when(rrf.getServiceResourceResolver(null)).thenAnswer(new 
Answer<ResourceResolver>() {
-            @Override
-            public ResourceResolver answer(InvocationOnMock invocation) throws 
Throwable {
-                ResourceResolver delegate = getMockedRR();
-                delegates.add(delegate);
-                return delegate;
-            }
-        });
-        final ScriptingResourceResolverFactoryImpl 
scriptingResourceResolverFactory = new ScriptingResourceResolverFactoryImpl();
-        Whitebox.setInternalState(scriptingResourceResolverFactory, "rrf", 
rrf);
         Collection<Callable<ResourceResolver>> callables = new 
ArrayList<>(MAX_CONCURRENT_RESOLVERS);
         for (int i = 0; i < MAX_CONCURRENT_RESOLVERS; i++) {
             callables.add(createCallable(scriptingResourceResolverFactory));
@@ -89,8 +96,8 @@ public class ScriptingResourceResolverFa
         for (Future<ResourceResolver> future : futures) {
             resolvers.add(future.get());
         }
-        assertEquals(MAX_CONCURRENT_RESOLVERS, resolvers.size());
-        assertEquals(MAX_CONCURRENT_RESOLVERS, delegates.size());
+        assertEquals("The number of ScriptingResourceResolvers is not what we 
expected.", MAX_CONCURRENT_RESOLVERS, resolvers.size());
+        assertEquals("The number of delegate resource resolvers is not what we 
expected.", MAX_CONCURRENT_RESOLVERS, delegates.size());
         for (ResourceResolver delegate : delegates) {
             verify(delegate).close();
         }
@@ -100,16 +107,16 @@ public class ScriptingResourceResolverFa
     private Callable<ResourceResolver> createCallable(final 
ScriptingResourceResolverFactoryImpl scriptingResourceResolverFactory) {
         return new Callable<ResourceResolver>() {
             @Override
-            public ResourceResolver call() throws Exception {
-                try {
-                    scriptingResourceResolverFactory.onEvent(new 
SlingRequestEvent(mock(ServletContext.class), mock(ServletRequest.class),
-                            SlingRequestEvent.EventType.EVENT_INIT));
-                    return 
scriptingResourceResolverFactory.getRequestScopedResourceResolver();
-                } finally {
-                    scriptingResourceResolverFactory.onEvent(new 
SlingRequestEvent(mock(ServletContext.class), mock(ServletRequest.class),
-                            SlingRequestEvent.EventType.EVENT_DESTROY));
-                    
assertNull(scriptingResourceResolverFactory.getRequestScopedResourceResolver());
+            public ResourceResolver call() {
+                ResourceResolver resourceResolver = 
scriptingResourceResolverFactory.getRequestScopedResourceResolver();
+                for (int i = 0; i < RESOLVER_REUSE_FOR_SAME_THREAD; i++) {
+                    ResourceResolver subsequentResolver = 
scriptingResourceResolverFactory.getRequestScopedResourceResolver();
+                    assertEquals("Expected that subsequent calls to 
ScriptingResourceResolverFactory#getRequestScopedResourceResolver() " +
+                            "from the same thread will not create additional 
resolvers.", resourceResolver, subsequentResolver);
                 }
+                scriptingResourceResolverFactory.onEvent(new 
SlingRequestEvent(mock(ServletContext.class), mock(ServletRequest.class),
+                            SlingRequestEvent.EventType.EVENT_DESTROY));
+                return resourceResolver;
             }
         };
     }


Reply via email to