This is an automated email from the ASF dual-hosted git repository. bdelacretaz pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-capabilities-jcr.git
commit 56a666e8fb580df45bdb0b82f3c8832a5bed5f08 Author: Bertrand Delacretaz <[email protected]> AuthorDate: Tue Oct 30 16:14:52 2018 +0100 Add SearchSourceTest --- pom.xml | 23 ++++ .../sling/capabilities/jcr/SearchSource.java | 12 +- .../sling/capabilities/jcr/SearchSourceTest.java | 147 +++++++++++++++++++++ 3 files changed, 179 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 69956ef..cbe73c8 100644 --- a/pom.xml +++ b/pom.xml @@ -124,6 +124,29 @@ <!-- TESTING --> <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.testing.osgi-mock.junit4</artifactId> + <version>2.4.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.testing.sling-mock.junit4</artifactId> + <version>2.3.4</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.testing.sling-mock-oak</artifactId> + <version>2.1.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-atinject_1.0_spec</artifactId> <version>1.0</version> diff --git a/src/main/java/org/apache/sling/capabilities/jcr/SearchSource.java b/src/main/java/org/apache/sling/capabilities/jcr/SearchSource.java index a4925de..74ee64b 100644 --- a/src/main/java/org/apache/sling/capabilities/jcr/SearchSource.java +++ b/src/main/java/org/apache/sling/capabilities/jcr/SearchSource.java @@ -51,11 +51,10 @@ public class SearchSource implements CapabilitiesSource { public static final String DEFAULT_QUERY = "/jcr:root/oak:index//* [@useInSimilarity = true]"; - public static final int SIMILARITY_SEARCH_CACHE_LIFETIME_SECONDS = 60; - private final Logger log = LoggerFactory.getLogger(getClass().getName()); private String similaritySearchActiveResult; private long similaritySearchCacheExpires; + private int cacheLifetimeSeconds; private String similarityIndexQuery; @Reference @@ -75,11 +74,18 @@ public class SearchSource implements CapabilitiesSource { + " The service user that this component uses must have sufficient rights to read the corresponding nodes." ) String similarityIndexQuery() default DEFAULT_QUERY; + + @AttributeDefinition( + name = "Cache time-to-live in seconds", + description = "The results of expensive operations like queries are cached for this amount of time" + ) + int cacheLifetimeSeconds() default 60; } @Activate protected void activate(Config cfg, ComponentContext ctx) { similarityIndexQuery = cfg.similarityIndexQuery(); + cacheLifetimeSeconds = cfg.cacheLifetimeSeconds(); } @Override @@ -100,7 +106,7 @@ public class SearchSource implements CapabilitiesSource { return; } - similaritySearchCacheExpires = System.currentTimeMillis() + (SIMILARITY_SEARCH_CACHE_LIFETIME_SECONDS * 1000L); + similaritySearchCacheExpires = System.currentTimeMillis() + (cacheLifetimeSeconds * 1000L); synchronized(this) { Session session = null; diff --git a/src/test/java/org/apache/sling/capabilities/jcr/SearchSourceTest.java b/src/test/java/org/apache/sling/capabilities/jcr/SearchSourceTest.java new file mode 100644 index 0000000..27e716a --- /dev/null +++ b/src/test/java/org/apache/sling/capabilities/jcr/SearchSourceTest.java @@ -0,0 +1,147 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ 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.capabilities.jcr; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.Map; +import java.util.UUID; +import javax.inject.Inject; +import javax.jcr.Node; +import javax.jcr.Session; +import org.apache.sling.capabilities.CapabilitiesSource; +import org.apache.sling.jcr.api.SlingRepository; +import org.apache.sling.serviceusermapping.ServiceUserMapped; +import org.apache.sling.testing.mock.sling.ResourceResolverType; +import org.apache.sling.testing.mock.sling.junit.SlingContext; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; + +public class SearchSourceTest { + + @Rule + public final SlingContext context = new SlingContext(ResourceResolverType.JCR_OAK); + + private static final String SIMILARITY_ACTIVE_CAP = "similarity.search.active"; + + private CapabilitiesSource searchSource; + + private Dictionary<String, Object> props(Object ... nameValuePairs) { + final Dictionary<String, Object> props = new Hashtable<>(); + for(int i=0 ; i < nameValuePairs.length; i+=2) { + props.put(nameValuePairs[i].toString(), nameValuePairs[i+1]); + } + return props; + } + + private void registerSearchSource(Object ... configNameValuePairs) throws IOException { + final ConfigurationAdmin ca = context.getService(ConfigurationAdmin.class); + assertNotNull("Expecting a ConfigurationAdmin service", ca); + final Configuration cfg = ca.getConfiguration(SearchSource.class.getName()); + cfg.update(props(configNameValuePairs)); + + final SearchSource ss = new SearchSource(); + context.registerInjectActivateService(ss); + + searchSource = context.getService(CapabilitiesSource.class); + assertNotNull("Expecting our SearchSource to be registered", searchSource); + assertEquals("Expecting the SearchSource namespace", SearchSource.NAMESPACE, searchSource.getNamespace()); + } + + private void createMockIndexNode(String parentPath, String path, String propertyName, boolean value) throws Exception { + final SlingRepository repository = context.getService(SlingRepository.class); + assertNotNull("Expecting a SlingRepository", repository); + final Session s = repository.loginAdministrative(null); + try { + final Node n = s.getNode(parentPath).addNode(path); + n.setProperty(propertyName, value); + s.save(); + } finally { + s.logout(); + } + } + + @Before + public void setup() throws IOException { + + final ServiceUserMapped sum = new ServiceUserMapped() {}; + context.registerService(ServiceUserMapped.class, sum, + props(ServiceUserMapped.SUBSERVICENAME, SearchSource.SUBSERVICE_NAME)); + } + + @Test + public void testNoSimilarity() throws Exception { + registerSearchSource(); + assertNotNull(searchSource.getCapabilities()); + assertEquals("false", searchSource.getCapabilities().get(SIMILARITY_ACTIVE_CAP)); + } + + @Test + public void testHasSimilarity() throws Exception { + registerSearchSource(); + createMockIndexNode("/oak:index", "foo", "useInSimilarity", true); + + assertNotNull(searchSource.getCapabilities()); + assertEquals("true", searchSource.getCapabilities().get(SIMILARITY_ACTIVE_CAP)); + } + + @Test + public void testCustomQueryAndCacheLifetime() throws Exception { + final int lifetimeSeconds = 2; + final String uniquePath = "testCustomQuery_" + UUID.randomUUID(); + registerSearchSource( + "similarityIndexQuery", "/jcr:root/" + uniquePath, + "cacheLifetimeSeconds", lifetimeSeconds); + + // With our custom query we get false first + assertNotNull(searchSource.getCapabilities()); + assertEquals("false", searchSource.getCapabilities().get(SIMILARITY_ACTIVE_CAP)); + + // Create a node that causes the query to return something + // The capability value will change after some time, once its cache expires + createMockIndexNode("/", uniquePath, "someProperty", true); + + // Must get a few false results and then true, once the cache expires + // (might fail if the box running this test is very very slow) + int nFalse = 0; + int nTrue = 0; + final long testEnd = System.currentTimeMillis() + lifetimeSeconds * 1000L + 1500L; + while(System.currentTimeMillis() < testEnd) { + final Object value = searchSource.getCapabilities().get(SIMILARITY_ACTIVE_CAP); + if("false".equals(value)) { + assertEquals("Expecting true value to come after all false values", 0, nTrue); + nFalse++; + } else { + nTrue++; + } + Thread.sleep(100L); + } + + assertTrue("Expecting a few true values", nTrue > 0); + assertTrue("Expecting a few false values", nTrue > 0); + } +} \ No newline at end of file
