| /* |
| * 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.concurrent.RejectedExecutionException; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.apache.sling.commons.osgi.Order; |
| import org.apache.sling.commons.osgi.ServiceUtil; |
| import org.osgi.service.component.ComponentContext; |
| import org.osgi.service.component.annotations.Activate; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Deactivate; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.component.annotations.ReferenceCardinality; |
| import org.osgi.service.component.annotations.ReferencePolicy; |
| 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 = EventAdmin.class) |
| public final class MockEventAdmin implements EventAdmin { |
| |
| @Reference(name="eventHandler", service=EventHandler.class, |
| cardinality=ReferenceCardinality.MULTIPLE, policy=ReferencePolicy.DYNAMIC, |
| bind="bindEventHandler", unbind="unbindEventHandler") |
| private final Map<Object, EventHandlerItem> eventHandlers = new TreeMap<Object, EventHandlerItem>(); |
| |
| private ExecutorService asyncHandler; |
| |
| private static final Logger log = LoggerFactory.getLogger(MockEventAdmin.class); |
| |
| @Activate |
| protected void activate(ComponentContext componentContext) { |
| asyncHandler = Executors.newCachedThreadPool(); |
| } |
| |
| @Deactivate |
| protected void deactivate(ComponentContext componentContext) { |
| asyncHandler.shutdownNow(); |
| } |
| |
| @Override |
| public void postEvent(final Event event) { |
| try { |
| asyncHandler.execute(new Runnable() { |
| @Override |
| public void run() { |
| distributeEvent(event); |
| } |
| }); |
| } |
| catch (RejectedExecutionException ex) { |
| // ignore |
| log.debug("Ignore rejected execution: " + ex.getMessage(), ex);; |
| } |
| } |
| |
| @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, Order.DESCENDING), new EventHandlerItem(eventHandler, props)); |
| } |
| } |
| |
| protected void unbindEventHandler(EventHandler eventHandler, Map<String, Object> props) { |
| synchronized (eventHandlers) { |
| eventHandlers.remove(ServiceUtil.getComparableForServiceRanking(props, Order.DESCENDING)); |
| } |
| } |
| |
| 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()); |
| } |
| |
| } |
| |
| } |