SLING-10373 - fixes search service resource resolver cleanup
diff --git a/reference/src/main/java/org/apache/sling/cms/reference/SearchService.java b/reference/src/main/java/org/apache/sling/cms/reference/SearchService.java
index 271f514..3a3ebaf 100644
--- a/reference/src/main/java/org/apache/sling/cms/reference/SearchService.java
+++ b/reference/src/main/java/org/apache/sling/cms/reference/SearchService.java
@@ -23,14 +23,7 @@
* Service for managing the search user.
*/
public interface SearchService {
-
- /**
- * Closes the resource resolver if appropriate
- *
- * @param resolver the resource resolver used in search
- */
- void closeResolver(ResourceResolver resolver);
-
+
/**
* Gets either the service user resource resolver of the request resource
* resolver depending if the service user is configured.
diff --git a/reference/src/main/java/org/apache/sling/cms/reference/impl/SearchServiceImpl.java b/reference/src/main/java/org/apache/sling/cms/reference/impl/SearchServiceImpl.java
index 805a87f..c9fa7c2 100644
--- a/reference/src/main/java/org/apache/sling/cms/reference/impl/SearchServiceImpl.java
+++ b/reference/src/main/java/org/apache/sling/cms/reference/impl/SearchServiceImpl.java
@@ -20,6 +20,8 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.request.SlingRequestEvent;
+import org.apache.sling.api.request.SlingRequestListener;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
@@ -37,16 +39,17 @@
/**
* Implementation of the SearchService
*/
-@Component(service = { SearchService.class })
+@Component(service = { SearchService.class, SlingRequestListener.class })
@Designate(ocd = Config.class)
-public class SearchServiceImpl implements SearchService {
+public class SearchServiceImpl implements SearchService, SlingRequestListener {
private static final Logger log = LoggerFactory.getLogger(SearchServiceImpl.class);
- @Reference
- private ResourceResolverFactory factory;
+ private final ResourceResolverFactory factory;
- private Config config;
+ private final Config config;
+
+ private static final String RESOURCE_RESOLVER_ATTR = SearchServiceImpl.class.getName() + ":ResourceResolver";
@ObjectClassDefinition(name = "%cms.reference.search.name", description = "%cms.reference.search.description", localization = "OSGI-INF/l10n/bundle")
public @interface Config {
@@ -56,17 +59,21 @@
}
@Activate
- public void init(Config config) {
+ public SearchServiceImpl(@Reference ResourceResolverFactory factory, Config config) {
+ this.factory = factory;
this.config = config;
}
@Override
public ResourceResolver getResourceResolver(SlingHttpServletRequest request) {
+
if (config != null && StringUtils.isNotBlank(config.searchServiceUsername())) {
try {
log.debug("Retrieving Service User {}", config.searchServiceUsername());
- return factory.getServiceResourceResolver(Collections.singletonMap(ResourceResolverFactory.SUBSERVICE,
- (Object) config.searchServiceUsername()));
+ ResourceResolver resolver = factory.getServiceResourceResolver(
+ Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, config.searchServiceUsername()));
+ request.setAttribute(RESOURCE_RESOLVER_ATTR, resolver);
+ return resolver;
} catch (LoginException e) {
log.warn("Failed to retrieve Service User {}, falling back to request user",
config.searchServiceUsername(), e);
@@ -79,11 +86,14 @@
}
@Override
- public void closeResolver(ResourceResolver resolver) {
- if (resolver != null && resolver.isLive() && StringUtils.isNotBlank(config.searchServiceUsername())
- && config.searchServiceUsername().equals(resolver.getUserID())) {
+ public void onEvent(SlingRequestEvent sre) {
+ if (sre.getType() == SlingRequestEvent.EventType.EVENT_DESTROY
+ && sre.getServletRequest().getAttribute(RESOURCE_RESOLVER_ATTR) != null
+ && sre.getServletRequest().getAttribute(RESOURCE_RESOLVER_ATTR) instanceof ResourceResolver) {
+ ResourceResolver resolver = (ResourceResolver) sre.getServletRequest().getAttribute(RESOURCE_RESOLVER_ATTR);
resolver.close();
}
+
}
}
diff --git a/reference/src/main/java/org/apache/sling/cms/reference/models/Search.java b/reference/src/main/java/org/apache/sling/cms/reference/models/Search.java
index 08fa94a..6f053bb 100644
--- a/reference/src/main/java/org/apache/sling/cms/reference/models/Search.java
+++ b/reference/src/main/java/org/apache/sling/cms/reference/models/Search.java
@@ -62,8 +62,6 @@
private final List<Resource> results;
- private final SearchService searchService;
-
private final int start;
private final ResourceResolver resolver;
@@ -72,7 +70,6 @@
public Search(@Self SlingHttpServletRequest request, @ValueMapValue @Named("limit") int limit,
@OSGiService SearchService searchService, @ValueMapValue @Named("basePath") String basePath) {
this.request = request;
- this.searchService = searchService;
Set<String> distinct = new HashSet<>();
String term = Text.escapeIllegalXpathSearchChars(request.getParameter(TERM_PARAMETER)).replace("'", "''");
@@ -156,17 +153,6 @@
return request.getParameter(TERM_PARAMETER);
}
- /**
- * This is a horrible hack to close the resource resolver used for retrieving
- * the search results
- *
- * @return true, always
- */
- public String getFinalize() {
- searchService.closeResolver(resolver);
- return "";
- }
-
public boolean isFirst() {
return page == 0;
}
diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/general/search/search.jsp b/reference/src/main/resources/jcr_root/apps/reference/components/general/search/search.jsp
index f9f42d1..f160511 100644
--- a/reference/src/main/resources/jcr_root/apps/reference/components/general/search/search.jsp
+++ b/reference/src/main/resources/jcr_root/apps/reference/components/general/search/search.jsp
@@ -38,4 +38,4 @@
</div>
<sling:call script="pagination.jsp" />
</div>
-</c:if>${search.finalize}
\ No newline at end of file
+</c:if>
\ No newline at end of file
diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/SearchServiceTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/SearchServiceTest.java
new file mode 100644
index 0000000..db98963
--- /dev/null
+++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/SearchServiceTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.sling.cms.reference.forms.impl;
+
+import java.lang.annotation.Annotation;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.request.SlingRequestEvent;
+import org.apache.sling.api.request.SlingRequestListener;
+import org.apache.sling.api.request.SlingRequestEvent.EventType;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.cms.reference.SearchService;
+import org.apache.sling.cms.reference.impl.SearchServiceImpl;
+import org.apache.sling.cms.reference.impl.SearchServiceImpl.Config;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class SearchServiceTest {
+
+ private ResourceResolverFactory factory;
+ private SlingHttpServletRequest request;
+ private ResourceResolver resolver;
+
+ @Before
+ public void init() {
+ factory = Mockito.mock(ResourceResolverFactory.class);
+ request = Mockito.mock(SlingHttpServletRequest.class);
+ resolver = Mockito.mock(ResourceResolver.class);
+ }
+
+ @Test
+ public void testNoConfig() {
+ SearchService search = new SearchServiceImpl(factory, new ConfigImpl(null));
+ search.getResourceResolver(request);
+ Mockito.verify(request).getResourceResolver();
+ }
+
+ @Test
+ public void testWithConfig() throws LoginException {
+ SearchService search = new SearchServiceImpl(factory, new ConfigImpl("hello-world"));
+ search.getResourceResolver(request);
+ Mockito.verify(factory).getServiceResourceResolver(Mockito.anyMap());
+ }
+
+ @Test
+ public void testInvalidUser() throws LoginException {
+ SearchService search = new SearchServiceImpl(factory, new ConfigImpl("invalid"));
+ Mockito.when(factory.getServiceResourceResolver(Mockito.anyMap())).thenThrow(new LoginException("Bad user"));
+ search.getResourceResolver(request);
+ Mockito.verify(request).getResourceResolver();
+ }
+
+ @Test
+ public void testRequestListener() throws LoginException {
+ String name = SearchServiceImpl.class.getName() + ":ResourceResolver";
+ SlingRequestListener listener = new SearchServiceImpl(factory, new ConfigImpl("invalid"));
+
+ listener.onEvent(new SlingRequestEvent(null, request, EventType.EVENT_DESTROY));
+ Mockito.verify(resolver, Mockito.never()).close();
+
+ Mockito.when(request.getAttribute(name)).thenReturn(null);
+ listener.onEvent(new SlingRequestEvent(null, request, EventType.EVENT_DESTROY));
+ Mockito.verify(resolver, Mockito.never()).close();
+
+ listener.onEvent(new SlingRequestEvent(null, request, EventType.EVENT_INIT));
+ Mockito.verify(resolver, Mockito.never()).close();
+
+ Mockito.when(request.getAttribute(name)).thenReturn(resolver);
+ listener.onEvent(new SlingRequestEvent(null, request, EventType.EVENT_DESTROY));
+ Mockito.verify(resolver).close();
+ }
+
+}
+
+class ConfigImpl implements Config {
+ private final String name;
+
+ ConfigImpl(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return null;
+ }
+
+ @Override
+ public String searchServiceUsername() {
+ return name;
+ }
+
+}
\ No newline at end of file