blob: 2665cae74c3f425cae743cee35d97fd7f5fb108b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2018 Cognos Incorporated, IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Cognos Incorporated - initial API and implementation
* IBM Corporation - bug fixes and enhancements
*******************************************************************************/
package org.eclipse.equinox.internal.cm;
import java.util.*;
import org.osgi.framework.*;
import org.osgi.service.cm.*;
import org.osgi.service.log.LogService;
import org.osgi.util.tracker.ServiceTracker;
/**
* ManagedServiceTracker tracks... ManagedServices and notifies them about related configuration changes
*/
class ManagedServiceTracker extends ServiceTracker<ManagedService, ManagedService> {
final ConfigurationAdminFactory configurationAdminFactory;
private final ConfigurationStore configurationStore;
/** @GuardedBy targets*/
private final TargetMap targets = new TargetMap();
private final SerializedTaskQueue queue = new SerializedTaskQueue("ManagedService Update Queue"); //$NON-NLS-1$
public ManagedServiceTracker(ConfigurationAdminFactory configurationAdminFactory, ConfigurationStore configurationStore, BundleContext context) {
super(context, ManagedService.class.getName(), null);
this.configurationAdminFactory = configurationAdminFactory;
this.configurationStore = configurationStore;
}
void notifyDeleted(ConfigurationImpl config) {
config.checkLocked();
String configLoc = config.getLocation();
if (configLoc == null) {
return;
}
boolean isMultiple = configLoc.startsWith("?"); //$NON-NLS-1$
String pid = config.getPid(false);
List<ServiceReference<ManagedService>> references = getManagedServiceReferences(pid);
for (ServiceReference<ManagedService> ref : references) {
if (!hasMoreSpecificConfigPids(ref, pid)) {
boolean hasLocPermission = configurationAdminFactory.checkTargetPermission(configLoc, ref);
ManagedService service = getService(ref);
if (hasLocPermission && service != null) {
if (isMultiple || ConfigurationAdminImpl.getLocation(ref.getBundle()).equals(configLoc)) {
// search for other matches
List<List<String>> qualifiedPidLists;
synchronized (targets) {
qualifiedPidLists = targets.getQualifiedPids(ref);
}
updateManagedService(qualifiedPidLists, ref, service);
}
}
}
}
}
void notifyUpdated(ConfigurationImpl config) {
config.checkLocked();
String configLoc = config.getLocation();
boolean isMultiple = configLoc != null && configLoc.startsWith("?"); //$NON-NLS-1$
String pid = config.getPid();
List<ServiceReference<ManagedService>> references = getManagedServiceReferences(pid);
for (ServiceReference<ManagedService> ref : references) {
if (!hasMoreSpecificConfigPids(ref, pid)) {
boolean hasLocPermission = configurationAdminFactory.checkTargetPermission(configLoc, ref);
ManagedService service = getService(ref);
if (hasLocPermission && service != null) {
if (isMultiple || config.bind(ConfigurationAdminImpl.getLocation(ref.getBundle()))) {
Dictionary<String, Object> properties = config.getProperties();
configurationAdminFactory.modifyConfiguration(ref, properties);
asynchUpdated(service, properties);
}
}
}
}
}
void notifyUpdateLocation(ConfigurationImpl config, String oldLocation) {
config.checkLocked();
String configLoc = config.getLocation();
if (configLoc == null ? oldLocation == null : configLoc.equals(oldLocation)) {
// same location do nothing
return;
}
boolean oldIsMultiple = oldLocation != null && oldLocation.startsWith("?"); //$NON-NLS-1$
boolean newIsMultiple = configLoc != null && configLoc.startsWith("?"); //$NON-NLS-1$
String pid = config.getPid();
List<ServiceReference<ManagedService>> references = getManagedServiceReferences(pid);
for (ServiceReference<ManagedService> ref : references) {
if (!hasMoreSpecificConfigPids(ref, pid)) {
boolean hasOldPermission = configurationAdminFactory.checkTargetPermission(oldLocation, ref);
boolean hasNewPermission = configurationAdminFactory.checkTargetPermission(configLoc, ref);
ManagedService service = getService(ref);
if (service != null) {
boolean delete = false;
boolean update = false;
String targetLocation = ConfigurationAdminImpl.getLocation(ref.getBundle());
if (hasOldPermission != hasNewPermission) {
if (hasOldPermission) {
delete = oldIsMultiple || targetLocation.equals(oldLocation);
} else {
update = newIsMultiple || config.bind(targetLocation);
}
} else {
// location has changed, this may be a bound configuration
if (targetLocation.equals(oldLocation)) {
delete = true;
} else {
update = newIsMultiple || config.bind(targetLocation);
}
}
if (delete) {
// search for other matches
List<List<String>> qualifiedPidLists;
synchronized (targets) {
qualifiedPidLists = targets.getQualifiedPids(ref);
}
updateManagedService(qualifiedPidLists, ref, service);
} else if (update) {
Dictionary<String, Object> properties = config.getProperties();
configurationAdminFactory.modifyConfiguration(ref, properties);
asynchUpdated(service, properties);
}
// do not break on !isMultiple since we need to check if the other refs apply no matter what
}
}
}
}
private boolean hasMoreSpecificConfigPids(ServiceReference<ManagedService> ref, String pid) {
List<List<String>> qualifiedPidsLists;
synchronized (targets) {
qualifiedPidsLists = targets.getQualifiedPids(ref);
}
for (List<String> qualifiedPids : qualifiedPidsLists) {
for (String qualifiedPid : qualifiedPids) {
if (qualifiedPid.length() <= pid.length() || !qualifiedPid.startsWith(pid)) {
break;
}
ConfigurationImpl config = configurationStore.findConfiguration(qualifiedPid);
if (config != null) {
return true;
}
}
}
return false;
}
@Override
public ManagedService addingService(ServiceReference<ManagedService> reference) {
ManagedService service = context.getService(reference);
if (service == null)
return null;
addReference(reference, service);
return service;
}
@Override
public void modifiedService(ServiceReference<ManagedService> reference, ManagedService service) {
List<String> newPids = TargetMap.getPids(reference.getProperty(Constants.SERVICE_PID));
synchronized (targets) {
List<List<String>> previousPids = targets.getQualifiedPids(reference);
if (newPids.size() == previousPids.size()) {
boolean foundAll = false;
for (String newPid : newPids) {
foundAll = false;
for (List<String> pids : previousPids) {
if (pids.contains(newPid)) {
foundAll = true;
break;
}
}
if (!foundAll) {
break;
}
}
if (foundAll) {
return;
}
}
}
untrackManagedService(reference);
addingService(reference);
}
@Override
public void removedService(ServiceReference<ManagedService> reference, ManagedService service) {
untrackManagedService(reference);
context.ungetService(reference);
}
private void addReference(ServiceReference<ManagedService> reference, ManagedService service) {
List<List<String>> qualifiedPidLists = trackManagedService(reference);
updateManagedService(qualifiedPidLists, reference, service);
}
private void updateManagedService(List<List<String>> qualifiedPidLists, ServiceReference<ManagedService> reference, ManagedService service) {
for (List<String> qualifiedPids : qualifiedPidLists) {
boolean foundConfig = false;
qualifiedPids: for (String qualifiedPid : qualifiedPids) {
ConfigurationImpl config = configurationStore.findConfiguration(qualifiedPid);
if (config != null) {
try {
config.lock();
if (!config.isDeleted()) {
if (config.getFactoryPid() != null) {
configurationAdminFactory.log(LogService.LOG_WARNING, "Configuration for " + Constants.SERVICE_PID + "=" + qualifiedPid + " should only be used by a " + ManagedServiceFactory.class.getName()); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
}
String location = config.getLocation();
boolean shouldBind = location == null || !location.startsWith("?"); //$NON-NLS-1$
boolean hasLocPermission = configurationAdminFactory.checkTargetPermission(location, reference);
if (hasLocPermission) {
if ((shouldBind && config.bind(ConfigurationAdminImpl.getLocation(reference.getBundle()))) || !shouldBind) {
Dictionary<String, Object> properties = config.getProperties();
configurationAdminFactory.modifyConfiguration(reference, properties);
asynchUpdated(service, properties);
foundConfig = true;
break qualifiedPids;
}
configurationAdminFactory.log(LogService.LOG_WARNING, "Configuration for " + Constants.SERVICE_PID + "=" + qualifiedPid + " could not be bound to " + ConfigurationAdminImpl.getLocation(reference.getBundle())); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
}
}
} finally {
config.unlock();
}
}
}
if (!foundConfig) {
// This seems questionable to me, but is required for the spec.
// if a ManagedService has multiple pids, watch out!!
asynchUpdated(service, null);
}
}
}
private List<List<String>> trackManagedService(ServiceReference<ManagedService> reference) {
synchronized (targets) {
return targets.add(reference);
}
}
private void untrackManagedService(ServiceReference<ManagedService> reference) {
synchronized (targets) {
targets.remove(reference);
}
}
private List<ServiceReference<ManagedService>> getManagedServiceReferences(String pid) {
synchronized (targets) {
@SuppressWarnings("rawtypes")
List temp = targets.getTargets(pid);
@SuppressWarnings("unchecked")
List<ServiceReference<ManagedService>> refs = temp;
Collections.sort(refs, Collections.reverseOrder());
return refs;
}
}
private void asynchUpdated(final ManagedService service, final Dictionary<String, ?> properties) {
queue.put(new Runnable() {
@Override
public void run() {
try {
service.updated(properties);
} catch (ConfigurationException e) {
// we might consider doing more for ConfigurationExceptions
Throwable cause = e.getCause();
configurationAdminFactory.log(LogService.LOG_ERROR, e.getMessage(), cause != null ? cause : e);
} catch (Throwable t) {
configurationAdminFactory.log(LogService.LOG_ERROR, t.getMessage(), t);
}
}
});
}
}