/* * 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.tuscany.sca.osgi.remoteserviceadmin.impl; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.tuscany.sca.common.java.collection.CollectionMap; import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.hooks.service.ListenerHook.ListenerInfo; import org.osgi.service.remoteserviceadmin.EndpointDescription; /** * Matching endpoint descriptions against the sevice listeners using OSGi filiters */ public class EndpointMatcher { private static final Logger logger = Logger.getLogger(EndpointMatcher.class.getName()); private final EndpointMap endpointDescriptions = new EndpointMap(); private final ListenerMap listeners = new ListenerMap(); private final BundleContext context; private final BlockingQueue<ImportAction> importQueue = new ArrayBlockingQueue<ImportAction>(256, true); public EndpointMatcher(BundleContext context) { super(); this.context = context; } public static boolean matches(String filter, EndpointDescription endpointDescription) { Filter f = null; try { f = FrameworkUtil.createFilter(filter); } catch (InvalidSyntaxException e) { throw new IllegalArgumentException(e); } Hashtable<String, Object> props = new Hashtable<String, Object>(endpointDescription.getProperties()); return f.match(props); } private void importEndpoint(ListenerInfo listener, EndpointDescription ep) { ImportAction request = new ImportAction(ImportAction.Type.Add, listener, ep); try { importQueue.put(request); } catch (InterruptedException e) { throw new IllegalArgumentException(e); } } private void unimportEndpoint(ListenerInfo listener, EndpointDescription ep) { ImportAction request = new ImportAction(ImportAction.Type.Remove, listener, ep); try { importQueue.put(request); } catch (InterruptedException e) { throw new IllegalArgumentException(e); } } public synchronized void added(ListenerInfo listener) { String filter = listener.getFilter(); listeners.putValue(filter, listener); for (EndpointDescription ep : getEndpoints(filter)) { importEndpoint(listener, ep); } } public synchronized Collection<String> added(Collection<ListenerInfo> listeners) { for (ListenerInfo listener : listeners) { if (accepts(listener)) { if (!listener.isRemoved() && listener.getBundleContext().getBundle().getBundleId() != 0L) { added(listener); } } } return getFilters(); } private boolean accepts(ListenerInfo listener) { BundleContext context = listener.getBundleContext(); return context != null && listener.getFilter() != null && context != this.context; } public synchronized void removed(ListenerInfo listener) { String filter = listener.getFilter(); if (accepts(listener)) if (listeners.removeValue(filter, listener, true)) { // Find the corresponding ImportRegistration with the listener for (EndpointDescription ep : getEndpoints(filter)) { unimportEndpoint(listener, ep); } if (getListeners(filter).isEmpty()) { // No more listeners on the this filter, clean up the endpoint descriptionss endpointDescriptions.remove(filter); } } } public synchronized Collection<String> removed(Collection<ListenerInfo> listeners) { for (ListenerInfo listener : listeners) { removed(listener); } return getFilters(); } public synchronized void added(EndpointDescription endpointDescription) { for (Map.Entry<String, Collection<ListenerInfo>> entry : listeners.entrySet()) { if (matches(entry.getKey(), endpointDescription)) { endpointDescriptions.putValue(entry.getKey(), endpointDescription); for (ListenerInfo listener : entry.getValue()) { importEndpoint(listener, endpointDescription); } } } } public synchronized void added(EndpointDescription endpointDescription, String matchedFilter) { if (endpointDescriptions.putValue(matchedFilter, endpointDescription)) { Collection<ListenerInfo> listenerInfos = listeners.get(matchedFilter); if (listenerInfos != null) { for (ListenerInfo listener : listenerInfos) { importEndpoint(listener, endpointDescription); } } } } public synchronized void removed(EndpointDescription endpointDescription, String matchedFilter) { if (endpointDescriptions.removeValue(matchedFilter, endpointDescription, true)) { for (ListenerInfo listener : getListeners(matchedFilter)) { unimportEndpoint(listener, endpointDescription); } } } public synchronized Set<String> getFilters() { return new HashSet<String>(listeners.keySet()); } public synchronized void clear() { endpointDescriptions.clear(); listeners.clear(); importQueue.clear(); } public synchronized Collection<ListenerInfo> getListeners(String filter) { Collection<ListenerInfo> collection = listeners.get(filter); if (collection == null) { return Collections.emptySet(); } else { return collection; } } public synchronized Collection<EndpointDescription> getEndpoints(String filter) { Collection<EndpointDescription> collection = endpointDescriptions.get(filter); if (collection == null) { return Collections.emptySet(); } else { return collection; } } public CollectionMap<Class<?>, ListenerInfo> groupListeners(EndpointDescription endpointDescription, String matchedFilter) { Collection<ListenerInfo> snapshot = new HashSet<ListenerInfo>(getListeners(matchedFilter)); // Try to partition the listeners by the interface classes List<String> interfaceNames = endpointDescription.getInterfaces(); CollectionMap<Class<?>, ListenerInfo> interfaceToListeners = new CollectionMap<Class<?>, ListenerInfo>(); for (String i : interfaceNames) { for (Iterator<ListenerInfo> it = snapshot.iterator(); it.hasNext();) { try { ListenerInfo listener = it.next(); if (listener.isRemoved()) { it.remove(); continue; } if (!matchedFilter.equals(listener.getFilter())) { continue; } try { // The classloading can be synchronzed against the serviceListeners Class<?> interfaceClass = listener.getBundleContext().getBundle().loadClass(i); interfaceToListeners.putValue(interfaceClass, listener); } catch (IllegalStateException e) { logger.log(Level.WARNING, e.getMessage(), e); // Ignore the exception } } catch (ClassNotFoundException e) { // Ignore the listener as it cannot load the interface class } } } return interfaceToListeners; } public BlockingQueue<ImportAction> getImportQueue() { return importQueue; } private static class ListenerMap extends CollectionMap<String, ListenerInfo> { private static final long serialVersionUID = -8612202123531331219L; @Override protected Collection<ListenerInfo> createCollection() { return new HashSet<ListenerInfo>(); } } private static class EndpointMap extends CollectionMap<String, EndpointDescription> { private static final long serialVersionUID = -6261405398109798549L; @Override protected Collection<EndpointDescription> createCollection() { return new HashSet<EndpointDescription>(); } } /** * Representation of an import/unimport request */ public static class ImportAction { enum Type { Add, Remove }; public final Type type; public final ListenerInfo listenerInfo; public final EndpointDescription endpointDescription; /** * @param type * @param listenerInfo * @param endpointDescription */ public ImportAction(Type type, ListenerInfo listenerInfo, EndpointDescription endpointDescription) { super(); this.type = type; this.listenerInfo = listenerInfo; this.endpointDescription = endpointDescription; } } }