This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.testing.osgi-mock-1.3.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-osgi-mock.git
commit dd6302023421029a93e78b8f125de28dc72dd916 Author: Stefan Seifert <[email protected]> AuthorDate: Sun May 17 12:33:08 2015 +0000 SLING-4719 osgi-mock: Add Mock EventAdmin implementation git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/testing/mocks/osgi-mock@1679849 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 8 + .../sling/testing/mock/osgi/MockEventAdmin.java | 169 +++++++++++++++++++++ .../testing/mock/osgi/context/OsgiContextImpl.java | 10 +- .../sling/testing/mock/osgi/package-info.java | 2 +- .../testing/mock/osgi/MockEventAdminTest.java | 133 ++++++++++++++++ 5 files changed, 320 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 564ddc1..4d2ec30 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,13 @@ </dependency> <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.osgi</artifactId> + <version>2.2.0</version> + <scope>compile</scope> + </dependency> + + <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>15.0</version> @@ -110,6 +117,7 @@ <groupId>org.apache.felix</groupId> <artifactId>maven-scr-plugin</artifactId> </plugin> + <plugin> <groupId>org.apache.rat</groupId> <artifactId>apache-rat-plugin</artifactId> diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/MockEventAdmin.java b/src/main/java/org/apache/sling/testing/mock/osgi/MockEventAdmin.java new file mode 100644 index 0000000..68d55ca --- /dev/null +++ b/src/main/java/org/apache/sling/testing/mock/osgi/MockEventAdmin.java @@ -0,0 +1,169 @@ +/* + * 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.osgi; + +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.ReferencePolicy; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.commons.osgi.ServiceUtil; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Mock implementation of {@link EventAdmin}. + * From {@link EventConstants} currently only {@link EventConstants#EVENT_TOPIC} is supported. + */ +@Component(immediate = true) +@Service(value = EventAdmin.class) +public final class MockEventAdmin implements EventAdmin { + + @Reference(name="eventHandler", referenceInterface=EventHandler.class, + cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, policy=ReferencePolicy.DYNAMIC) + private final Map<Object, EventHandlerItem> eventHandlers = new TreeMap<Object, EventHandlerItem>(); + + private ExecutorService asyncHandler = Executors.newCachedThreadPool(); + + private static final Logger log = LoggerFactory.getLogger(MockEventAdmin.class); + + @Override + public void postEvent(final Event event) { + asyncHandler.execute(new Runnable() { + @Override + public void run() { + distributeEvent(event); + } + }); + } + + @Override + public void sendEvent(final Event event) { + distributeEvent(event); + } + + private void distributeEvent(Event event) { + synchronized (eventHandlers) { + for (EventHandlerItem item : eventHandlers.values()) { + if (item.matches(event)) { + try { + item.getEventHandler().handleEvent(event); + } + catch (Throwable ex) { + log.error("Error handlihng event {} in {}", event, item.getEventHandler()); + } + } + } + } + } + + protected void bindEventHandler(EventHandler eventHandler, Map<String, Object> props) { + synchronized (eventHandlers) { + eventHandlers.put(ServiceUtil.getComparableForServiceRanking(props), new EventHandlerItem(eventHandler, props)); + } + } + + protected void unbindEventHandler(EventHandler eventHandler, Map<String, Object> props) { + synchronized (eventHandlers) { + eventHandlers.remove(ServiceUtil.getComparableForServiceRanking(props)); + } + } + + private static class EventHandlerItem { + + private final EventHandler eventHandler; + private final Pattern[] topicPatterns; + + private static final Pattern WILDCARD_PATTERN = Pattern.compile("[^*]+|(\\*)"); + + public EventHandlerItem(EventHandler eventHandler, Map<String, Object> props) { + this.eventHandler = eventHandler; + topicPatterns = generateTopicPatterns(props.get(EventConstants.EVENT_TOPIC)); + } + + public boolean matches(Event event) { + if (topicPatterns.length == 0) { + return true; + } + String topic = event.getTopic(); + if (topic != null) { + for (Pattern topicPattern : topicPatterns) { + if (topicPattern.matcher(topic).matches()) { + return true; + } + } + } + return false; + } + + public EventHandler getEventHandler() { + return eventHandler; + } + + private static Pattern[] generateTopicPatterns(Object topic) { + String[] topics; + if (topic == null) { + topics = new String[0]; + } + else if (topic instanceof String) { + topics = new String[] { (String)topic }; + } + else if (topic instanceof String[]) { + topics = (String[])topic; + } + else { + throw new IllegalArgumentException("Invalid topic: " + topic); + } + Pattern[] patterns = new Pattern[topics.length]; + for (int i=0; i<topics.length; i++) { + patterns[i] = toWildcardPattern(topics[i]); + } + return patterns; + } + + /** + * Converts a wildcard string with * to a regex pattern (from http://stackoverflow.com/questions/24337657/wildcard-matching-in-java) + * @param wildcard + * @return Regexp pattern + */ + private static Pattern toWildcardPattern(String wildcard) { + Matcher matcher = WILDCARD_PATTERN.matcher(wildcard); + StringBuffer result = new StringBuffer(); + while (matcher.find()) { + if(matcher.group(1) != null) matcher.appendReplacement(result, ".*"); + else matcher.appendReplacement(result, "\\\\Q" + matcher.group(0) + "\\\\E"); + } + matcher.appendTail(result); + return Pattern.compile(result.toString()); + } + + } + +} diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java b/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java index fb01663..27abf9f 100644 --- a/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java +++ b/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java @@ -24,6 +24,7 @@ import java.util.Hashtable; import java.util.Map; import org.apache.commons.lang3.ArrayUtils; +import org.apache.sling.testing.mock.osgi.MockEventAdmin; import org.apache.sling.testing.mock.osgi.MockOsgi; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; @@ -45,7 +46,7 @@ public class OsgiContextImpl { * Setup actions before test method execution */ protected void setUp() { - // can be overridden by subclasses + registerDefaultServices(); } /** @@ -56,6 +57,13 @@ public class OsgiContextImpl { } /** + * Default services that should be available for every unit test + */ + private void registerDefaultServices() { + registerInjectActivateService(new MockEventAdmin()); + } + + /** * @return OSGi component context */ public final ComponentContext componentContext() { diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/package-info.java b/src/main/java/org/apache/sling/testing/mock/osgi/package-info.java index 6a6de3f..76826a4 100644 --- a/src/main/java/org/apache/sling/testing/mock/osgi/package-info.java +++ b/src/main/java/org/apache/sling/testing/mock/osgi/package-info.java @@ -19,5 +19,5 @@ /** * Mock implementation of selected OSGi APIs. */ [email protected]("2.0") [email protected]("2.1") package org.apache.sling.testing.mock.osgi; diff --git a/src/test/java/org/apache/sling/testing/mock/osgi/MockEventAdminTest.java b/src/test/java/org/apache/sling/testing/mock/osgi/MockEventAdminTest.java new file mode 100644 index 0000000..fa3f675 --- /dev/null +++ b/src/test/java/org/apache/sling/testing/mock/osgi/MockEventAdminTest.java @@ -0,0 +1,133 @@ +/* + * 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.osgi; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.sling.testing.mock.osgi.junit.OsgiContext; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +public class MockEventAdminTest { + + private static final String TOPIC_SAMPLE_1 = "sample/topic1"; + private static final String TOPIC_SAMPLE_2 = "sample/topic2"; + private static final String TOPIC_SAMPLE_ALL = "sample/*"; + private static final String TOPIC_OTHER_3 = "other/topic3"; + + private static final Event EVENT_SAMPLE_1 = new Event(TOPIC_SAMPLE_1, null); + private static final Event EVENT_SAMPLE_2 = new Event(TOPIC_SAMPLE_2, null); + private static final Event EVENT_OTHER_3 = new Event(TOPIC_OTHER_3, null); + + @Rule + public OsgiContext context = new OsgiContext(); + + private DummyEventHandler eventHandler1; + private DummyEventHandler eventHandler12; + private DummyEventHandler eventHandlerSampleAll; + private DummyEventHandler eventHandlerAll; + + @Before + public void setUp() { + eventHandler1 = (DummyEventHandler)context.registerService(EventHandler.class, new DummyEventHandler(), + ImmutableMap.<String, Object>of(EventConstants.EVENT_TOPIC, TOPIC_SAMPLE_1)); + eventHandler12 = (DummyEventHandler)context.registerService(EventHandler.class, new DummyEventHandler(), + ImmutableMap.<String, Object>of(EventConstants.EVENT_TOPIC, new String[] { TOPIC_SAMPLE_1, TOPIC_SAMPLE_2 })); + eventHandlerSampleAll = (DummyEventHandler)context.registerService(EventHandler.class, new DummyEventHandler(), + ImmutableMap.<String, Object>of(EventConstants.EVENT_TOPIC, TOPIC_SAMPLE_ALL)); + eventHandlerAll = (DummyEventHandler)context.registerService(EventHandler.class, new DummyEventHandler()); + } + + @Test + public void testSendEvent_Sample1() { + EventAdmin eventAdmin = context.getService(EventAdmin.class); + eventAdmin.sendEvent(EVENT_SAMPLE_1); + + assertEquals(ImmutableList.of(EVENT_SAMPLE_1), eventHandler1.getReceivedEvents()); + assertEquals(ImmutableList.of(EVENT_SAMPLE_1), eventHandler12.getReceivedEvents()); + assertEquals(ImmutableList.of(EVENT_SAMPLE_1), eventHandlerSampleAll.getReceivedEvents()); + assertEquals(ImmutableList.of(EVENT_SAMPLE_1), eventHandlerAll.getReceivedEvents()); + } + + @Test + public void testSendEvent_Sample2() { + EventAdmin eventAdmin = context.getService(EventAdmin.class); + eventAdmin.sendEvent(EVENT_SAMPLE_2); + + assertEquals(ImmutableList.of(), eventHandler1.getReceivedEvents()); + assertEquals(ImmutableList.of(EVENT_SAMPLE_2), eventHandler12.getReceivedEvents()); + assertEquals(ImmutableList.of(EVENT_SAMPLE_2), eventHandlerSampleAll.getReceivedEvents()); + assertEquals(ImmutableList.of(EVENT_SAMPLE_2), eventHandlerAll.getReceivedEvents()); + } + + @Test + public void testSendEvent_Other3() { + EventAdmin eventAdmin = context.getService(EventAdmin.class); + eventAdmin.sendEvent(EVENT_OTHER_3); + + assertEquals(ImmutableList.of(), eventHandler1.getReceivedEvents()); + assertEquals(ImmutableList.of(), eventHandler12.getReceivedEvents()); + assertEquals(ImmutableList.of(), eventHandlerSampleAll.getReceivedEvents()); + assertEquals(ImmutableList.of(EVENT_OTHER_3), eventHandlerAll.getReceivedEvents()); + } + + @Test(timeout = 1000) + public void testPostEvents() { + EventAdmin eventAdmin = context.getService(EventAdmin.class); + eventAdmin.postEvent(EVENT_SAMPLE_2); + eventAdmin.postEvent(EVENT_OTHER_3); + + // wait until result is as expected (with timeout) + boolean expectedResult = false; + while (!expectedResult) { + expectedResult = ObjectUtils.equals(ImmutableList.of(), eventHandler1.getReceivedEvents()) + && ObjectUtils.equals(ImmutableList.of(EVENT_SAMPLE_2), eventHandler12.getReceivedEvents()) + && ObjectUtils.equals(ImmutableList.of(EVENT_SAMPLE_2), eventHandlerSampleAll.getReceivedEvents()) + && ObjectUtils.equals(ImmutableList.of(EVENT_SAMPLE_2, EVENT_OTHER_3), eventHandlerAll.getReceivedEvents()); + } + } + + private static class DummyEventHandler implements EventHandler { + + private final List<Event> receivedEvents = new ArrayList<Event>(); + + @Override + public void handleEvent(Event event) { + receivedEvents.add(event); + } + + public List<Event> getReceivedEvents() { + return ImmutableList.copyOf(receivedEvents); + } + + } + +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
