blob: cbfe20c7a1fa1a56980e4d3231ced6336d18e098 [file] [log] [blame]
/*
* 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();
}
}