/* * 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.felix.dm.impl.index; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.felix.dm.DependencyManager; import org.apache.felix.dm.FilterIndex; import org.apache.felix.dm.impl.ServiceUtil; import org.apache.felix.dm.tracker.ServiceTracker; import org.apache.felix.dm.tracker.ServiceTrackerCustomizer; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; /** * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ @SuppressWarnings("rawtypes") public class AdapterFilterIndex extends AbstractFactoryFilterIndex implements FilterIndex, ServiceTrackerCustomizer { // (&(objectClass=foo.Bar)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233))) private static final String FILTER_REGEXP = "\\(&\\(" + Constants.OBJECTCLASS + "=([a-zA-Z\\.\\$0-9]*)\\)\\(\\|\\(" + Constants.SERVICE_ID + "=([0-9]*)\\)\\(" + DependencyManager.ASPECT + "=([0-9]*)\\)\\)\\)"; private static final Pattern PATTERN = Pattern.compile(FILTER_REGEXP); private final Object m_lock = new Object(); private ServiceTracker m_tracker; private BundleContext m_context; private final Map<Object, List<ServiceListener>> m_sidToListenersMap = new HashMap<>(); protected final Map<ServiceListener, String> m_listenerToObjectClassMap = new HashMap<>(); public void open(BundleContext context) { synchronized (m_lock) { if (m_context != null) { throw new IllegalStateException("Filter already open."); } try { m_tracker = new ServiceTracker(context, context.createFilter("(" + Constants.OBJECTCLASS + "=*)"), this); } catch (InvalidSyntaxException e) { throw new Error(); } m_context = context; } m_tracker.open(true, true); } public void close() { ServiceTracker tracker; synchronized (m_lock) { if (m_context == null) { throw new IllegalStateException("Filter already closed."); } tracker = m_tracker; m_tracker = null; m_context = null; } tracker.close(); } public boolean isApplicable(String clazz, String filter) { return getFilterData(clazz, filter) != null; } /** Returns a value object with the relevant filter data, or <code>null</code> if this filter was not valid. */ private FilterData getFilterData(String clazz, String filter) { // something like: // (&(objectClass=foo.Bar)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233))) FilterData resultData = null; if (filter != null) { Matcher matcher = PATTERN.matcher(filter); if (matcher.matches()) { String sid = matcher.group(2); String sid2 = matcher.group(3); if (sid.equals(sid2)) { resultData = new FilterData(); resultData.m_serviceId = Long.parseLong(sid); } } } return resultData; } public List<ServiceReference> getAllServiceReferences(String clazz, String filter) { List<ServiceReference> result = new ArrayList<>(); Matcher matcher = PATTERN.matcher(filter); if (matcher.matches()) { FilterData data = getFilterData(clazz, filter); if (data != null) { SortedSet<ServiceReference> list = null; synchronized (m_sidToServiceReferencesMap) { list = m_sidToServiceReferencesMap.get(Long.valueOf(data.m_serviceId)); if (list != null) { Iterator<ServiceReference> iterator = list.iterator(); while (iterator.hasNext()) { ServiceReference ref = (ServiceReference) iterator.next(); String objectClass = matcher.group(1); if (referenceMatchesObjectClass(ref, objectClass)) { result.add(ref); } } } } } } return result; } public void serviceChanged(ServiceEvent event) { ServiceReference reference = event.getServiceReference(); Long sid = ServiceUtil.getServiceIdObject(reference); List<ServiceListener> notificationList = new ArrayList<>(); synchronized (m_sidToListenersMap) { List<ServiceListener> list = m_sidToListenersMap.get(sid); if (list != null) { Iterator<ServiceListener> iterator = list.iterator(); while (iterator.hasNext()) { ServiceListener listener = (ServiceListener) iterator.next(); String objectClass = m_listenerToObjectClassMap.get(listener); if (referenceMatchesObjectClass(reference, objectClass)) { notificationList.add(listener); } } } } // notify Iterator<ServiceListener> iterator = notificationList.iterator(); while (iterator.hasNext()) { ServiceListener listener = (ServiceListener) iterator.next(); listener.serviceChanged(event); } } public void addServiceListener(ServiceListener listener, String filter) { FilterData data = getFilterData(null, filter); if (data != null) { Long sidObject = Long.valueOf(data.m_serviceId); synchronized (m_sidToListenersMap) { List<ServiceListener> listeners = m_sidToListenersMap.get(sidObject); if (listeners == null) { listeners = new ArrayList<>(); m_sidToListenersMap.put(sidObject, listeners); } listeners.add(listener); m_listenerToFilterMap.put(listener, filter); Matcher matcher = PATTERN.matcher(filter); if (matcher.matches()) { String objectClass = matcher.group(1); m_listenerToObjectClassMap.put(listener, objectClass); } else { throw new IllegalArgumentException("Filter string does not match index pattern"); } } } } public void removeServiceListener(ServiceListener listener) { synchronized (m_sidToListenersMap) { m_listenerToObjectClassMap.remove(listener); String filter = (String) m_listenerToFilterMap.remove(listener); if (filter != null) { // the listener does exist FilterData data = getFilterData(null, filter); if (data != null) { Long sidObject = Long.valueOf(data.m_serviceId); List<ServiceListener> listeners = m_sidToListenersMap.get(sidObject); if (listeners != null) { listeners.remove(listener); } } } } } @SuppressWarnings("unchecked") public Object addingService(ServiceReference reference) { BundleContext context; synchronized (m_lock) { context = m_context; } if (context != null) { return context.getService(reference); } else { throw new IllegalStateException("No valid bundle context."); } } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("AdapterFilterIndex["); sb.append("S2L: " + m_sidToListenersMap.size()); sb.append(", S2SR: " + m_sidToServiceReferencesMap.size()); sb.append(", L2F: " + m_listenerToFilterMap.size()); sb.append("]"); return sb.toString(); } }