This is an automated email from the ASF dual-hosted git repository. sseifert pushed a commit to branch feature/SLING-9680-custom-bindingsvaluesprovider in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-sling-mock.git
commit a5de1c21362966799ef5bf69d35632542297d23d Author: sseifert <[email protected]> AuthorDate: Thu Oct 8 17:17:16 2020 +0200 SLING-9680 support custom BindingsValuesProvider in a way that keeps the dynamic nature of the "OOTB" bindings depending on current resource --- .../mock/sling/context/MockSlingBindings.java | 39 +++++++- .../mock/sling/context/SlingContextImpl.java | 14 ++- .../testing/mock/sling/SlingBindingsTest.java | 58 ----------- .../mock/sling/context/SlingBindingsTest.java | 106 +++++++++++++++++++++ .../sling/context/models/SlingBindingsModel.java | 12 ++- 5 files changed, 164 insertions(+), 65 deletions(-) diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/context/MockSlingBindings.java b/core/src/main/java/org/apache/sling/testing/mock/sling/context/MockSlingBindings.java index 0f6f464..8bcb9d8 100644 --- a/core/src/main/java/org/apache/sling/testing/mock/sling/context/MockSlingBindings.java +++ b/core/src/main/java/org/apache/sling/testing/mock/sling/context/MockSlingBindings.java @@ -20,28 +20,39 @@ package org.apache.sling.testing.mock.sling.context; import javax.jcr.Node; import javax.jcr.Session; +import javax.script.SimpleBindings; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.scripting.api.BindingsValuesProvider; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventHandler; /** * Mock extension of {@link SlingBindings} that dynamically evaluates properties read from SlingBindings from the current mock context. * Normally the SlingBingings are set statically for each script execution, but in mocks where no script is really executed * it's easier to evaluate them from current context. */ -class MockSlingBindings extends SlingBindings { +class MockSlingBindings extends SlingBindings implements EventHandler { private static final long serialVersionUID = 1L; private static final String PROP_CURRENT_NODE = "currentNode"; private static final String PROP_CURRENT_SESSION = "currentSession"; + /** + * OSGi service property to set to "true" on BindingsValuesProvider implementations that should be ignored + * when populating the "non-dynamic" bindings properties. + */ + static final String SERVICE_PROPERTY_MOCK_SLING_BINDINGS_IGNORE = "MockSlingBindings-ignore"; + private final SlingContextImpl context; public MockSlingBindings(SlingContextImpl context) { this.context = context; + populateFromBindingsValuesProvider(); } @Override @@ -54,7 +65,27 @@ class MockSlingBindings extends SlingBindings { } return super.get(key); } - + + /** + * Removes all (non-dynamic) properties from bindings and populates them from all registered {@link BindingsValuesProvider} implementations. + */ + private void populateFromBindingsValuesProvider() { + SimpleBindings bindings = new SimpleBindings(); + for (BindingsValuesProvider provider : context.getServices(BindingsValuesProvider.class, + "(!(" + SERVICE_PROPERTY_MOCK_SLING_BINDINGS_IGNORE + "=true))")) { + provider.addBindings(bindings); + } + this.clear(); + this.putAll(bindings); + } + + @Override + public void handleEvent(Event event) { + // is triggered by OSGi events fired by {@link org.apache.sling.scripting.core.impl.BindingsValuesProvidersByContextImpl} + // whenever a new bindings value provider is added or removed + populateFromBindingsValuesProvider(); + } + static @Nullable Object resolveSlingBindingProperty(@NotNull SlingContextImpl context, @NotNull String property) { // -- Sling -- @@ -79,7 +110,7 @@ class MockSlingBindings extends SlingBindings { if (StringUtils.equals(property, OUT)) { return context.response().getWriter(); } - + // -- JCR -- // this emulates behavior of org.apache.sling.jcr.resource.internal.scripting.JcrObjectsBindingsValuesProvider if (StringUtils.equals(property, PROP_CURRENT_NODE)) { @@ -91,7 +122,7 @@ class MockSlingBindings extends SlingBindings { if (StringUtils.equals(property, PROP_CURRENT_SESSION)) { return context.resourceResolver().adaptTo(Session.class); } - + return null; } diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java b/core/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java index 7389b97..8211bda 100644 --- a/core/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java +++ b/core/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java @@ -18,6 +18,9 @@ */ package org.apache.sling.testing.mock.sling.context; +import static org.apache.sling.testing.mock.sling.context.MockSlingBindings.SERVICE_PROPERTY_MOCK_SLING_BINDINGS_IGNORE; +import static org.osgi.service.event.EventConstants.EVENT_TOPIC; + import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -63,6 +66,7 @@ import org.jetbrains.annotations.Nullable; import org.osgi.annotation.versioning.ConsumerType; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; +import org.osgi.service.event.EventHandler; /** * Defines Sling context objects with lazy initialization. @@ -180,7 +184,8 @@ public class SlingContextImpl extends OsgiContextImpl { registerService(SlingSettingsService.class, new MockSlingSettingService(DEFAULT_RUN_MODES)); registerService(MimeTypeService.class, new MockMimeTypeService()); registerInjectActivateService(new ResourceBuilderFactoryService()); - registerInjectActivateService(new JcrObjectsBindingsValuesProvider()); + registerInjectActivateService(new JcrObjectsBindingsValuesProvider(), + SERVICE_PROPERTY_MOCK_SLING_BINDINGS_IGNORE, true); registerInjectActivateService(new MockResourceBundleProvider()); registerInjectActivateService(new MockXSSAPIImpl()); @@ -283,7 +288,12 @@ public class SlingContextImpl extends OsgiContextImpl { this.request = new MockSlingHttpServletRequest(this.resourceResolver(), this.bundleContext()); // initialize sling bindings - SlingBindings bindings = new MockSlingBindings(this); + MockSlingBindings bindings = new MockSlingBindings(this); + + // register as OSGi event handler to get notified on events fired by BindingsValuesProvidersByContextImpl + this.registerService(EventHandler.class, bindings, + EVENT_TOPIC, "org/apache/sling/scripting/core/BindingsValuesProvider/*"); + this.request.setAttribute(SlingBindings.class.getName(), bindings); } return this.request; diff --git a/core/src/test/java/org/apache/sling/testing/mock/sling/SlingBindingsTest.java b/core/src/test/java/org/apache/sling/testing/mock/sling/SlingBindingsTest.java deleted file mode 100644 index 9f1f823..0000000 --- a/core/src/test/java/org/apache/sling/testing/mock/sling/SlingBindingsTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.testing.mock.sling; - -import static org.junit.Assert.assertNotNull; - -import org.apache.sling.api.resource.Resource; -import org.apache.sling.testing.mock.sling.context.models.SlingBindingsModel; -import org.apache.sling.testing.mock.sling.junit.SlingContext; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -@SuppressWarnings("null") -public class SlingBindingsTest { - - @Rule - public SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK); - - private Resource currentResource; - - @Before - public void setUp() throws Exception { - context.addModelsForClasses(SlingBindingsModel.class); - currentResource = context.create().resource("/content/testPage/testResource"); - context.currentResource(currentResource); - } - - @Test - public void testBindings() { - SlingBindingsModel model = context.request().adaptTo(SlingBindingsModel.class); - - assertNotNull(model); - assertNotNull(model.getResolver()); - assertNotNull(model.getResource()); - assertNotNull(model.getRequest()); - assertNotNull(model.getResponse()); - assertNotNull(model.getCurrentNode()); - assertNotNull(model.getcurrentSession()); - } - -} diff --git a/core/src/test/java/org/apache/sling/testing/mock/sling/context/SlingBindingsTest.java b/core/src/test/java/org/apache/sling/testing/mock/sling/context/SlingBindingsTest.java new file mode 100644 index 0000000..7a9495d --- /dev/null +++ b/core/src/test/java/org/apache/sling/testing/mock/sling/context/SlingBindingsTest.java @@ -0,0 +1,106 @@ +/* + * 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.testing.mock.sling.context; + +import static org.apache.sling.testing.mock.sling.context.MockSlingBindings.SERVICE_PROPERTY_MOCK_SLING_BINDINGS_IGNORE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import javax.script.Bindings; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.scripting.api.BindingsValuesProvider; +import org.apache.sling.testing.mock.sling.ResourceResolverType; +import org.apache.sling.testing.mock.sling.context.models.SlingBindingsModel; +import org.apache.sling.testing.mock.sling.junit.SlingContext; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +@SuppressWarnings("null") +public class SlingBindingsTest { + + @Rule + public SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK); + + private Resource currentResource; + + @Before + public void setUp() throws Exception { + // setup a custom BindingsValuesProvider + context.registerService(BindingsValuesProvider.class, new BindingsValuesProvider() { + @Override + public void addBindings(Bindings bindings) { + bindings.put("custom-param-1", "value-1"); + } + }); + + // setup another custom BindingsValuesProvider which should be ignored + context.registerService(BindingsValuesProvider.class, new BindingsValuesProvider() { + @Override + public void addBindings(Bindings bindings) { + bindings.put("custom-param-2", "value-2"); + } + }, SERVICE_PROPERTY_MOCK_SLING_BINDINGS_IGNORE, true); + + context.addModelsForClasses(SlingBindingsModel.class); + currentResource = context.create().resource("/content/testPage/testResource"); + context.currentResource(currentResource); + + // setup a custom BindingsValuesProvider after touching request first time/setting current resource + context.registerService(BindingsValuesProvider.class, new BindingsValuesProvider() { + @Override + public void addBindings(Bindings bindings) { + bindings.put("custom-param-3", "value-3"); + } + }); + // wait a short time to get sure OSGi events get distributed announcing the new BindingsValueProvider + Thread.sleep(25); + } + + @Test + public void testModelBindings() { + SlingBindingsModel model = context.request().adaptTo(SlingBindingsModel.class); + + assertNotNull(model); + assertNotNull(model.getResolver()); + assertNotNull(model.getResource()); + assertEquals(currentResource.getPath(), model.getResource().getPath()); + assertNotNull(model.getRequest()); + assertNotNull(model.getResponse()); + assertNotNull(model.getCurrentNode()); + assertNotNull(model.getCurrentSession()); + assertEquals("value-1", model.getCustomParam1()); + assertNull(model.getCustomParam2()); + assertEquals("value-3", model.getCustomParam3()); + } + + @Test + public void testCustomBindingsValuesProvider() { + SlingBindings bindings = (SlingBindings)context.request().getAttribute(SlingBindings.class.getName()); + assertNotNull(bindings); + assertEquals(currentResource.getPath(), bindings.getResource().getPath()); + assertEquals("value-1", bindings.get("custom-param-1")); + assertNull(bindings.get("custom-param-2")); + assertEquals("value-3", bindings.get("custom-param-3")); + } + +} diff --git a/core/src/test/java/org/apache/sling/testing/mock/sling/context/models/SlingBindingsModel.java b/core/src/test/java/org/apache/sling/testing/mock/sling/context/models/SlingBindingsModel.java index 0d26bea..6a15464 100644 --- a/core/src/test/java/org/apache/sling/testing/mock/sling/context/models/SlingBindingsModel.java +++ b/core/src/test/java/org/apache/sling/testing/mock/sling/context/models/SlingBindingsModel.java @@ -50,6 +50,16 @@ public interface SlingBindingsModel { Node getCurrentNode(); @ScriptVariable(injectionStrategy = InjectionStrategy.OPTIONAL) - Session getcurrentSession(); + Session getCurrentSession(); + + // -- Custom -- + @ScriptVariable(name = "custom-param-1", injectionStrategy = InjectionStrategy.OPTIONAL) + String getCustomParam1(); + + @ScriptVariable(name = "custom-param-2", injectionStrategy = InjectionStrategy.OPTIONAL) + String getCustomParam2(); + + @ScriptVariable(name = "custom-param-3", injectionStrategy = InjectionStrategy.OPTIONAL) + String getCustomParam3(); }
