blob: 5d710393a9cf2fba1a71b3d11b752062f7b8c6b6 [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.ConfigurationException;
import org.osgi.service.cm.ManagedServiceFactory;
import org.osgi.service.log.LogService;
import org.osgi.util.tracker.ServiceTracker;
/**
* ManagedServiceFactoryTracker tracks... ManagedServiceFactory(s) and notifies them about related configuration changes
*/
class ManagedServiceFactoryTracker extends ServiceTracker<ManagedServiceFactory, ManagedServiceFactory> {
final ConfigurationAdminFactory configurationAdminFactory;
private final ConfigurationStore configurationStore;
/** @GuardedBy targets*/
private final TargetMap targets = new TargetMap();
private final SerializedTaskQueue queue = new SerializedTaskQueue("ManagedServiceFactory Update Queue"); //$NON-NLS-1$
public ManagedServiceFactoryTracker(ConfigurationAdminFactory configurationAdminFactory, ConfigurationStore configurationStore, BundleContext context) {
super(context, ManagedServiceFactory.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 factoryPid = config.getFactoryPid(false);
List<ServiceReference<ManagedServiceFactory>> references = getManagedServiceFactoryReferences(factoryPid);
for (ServiceReference<ManagedServiceFactory> ref : references) {
if (!hasMoreSpecificConfigPids(ref, factoryPid)) {
boolean hasLocPermission = configurationAdminFactory.checkTargetPermission(configLoc, ref);
ManagedServiceFactory serviceFactory = getService(ref);
if (hasLocPermission && serviceFactory != null) {
if (isMultiple || ConfigurationAdminImpl.getLocation(ref.getBundle()).equals(configLoc)) {
asynchDeleted(serviceFactory, config.getPid(false));
}
}
}
}
}
void notifyUpdated(ConfigurationImpl config) {
config.checkLocked();
String configLoc = config.getLocation();
boolean isMultiple = configLoc != null && configLoc.startsWith("?"); //$NON-NLS-1$
String factoryPid = config.getFactoryPid();
List<ServiceReference<ManagedServiceFactory>> references = getManagedServiceFactoryReferences(factoryPid);
for (ServiceReference<ManagedServiceFactory> ref : references) {
if (!hasMoreSpecificConfigPids(ref, factoryPid)) {
boolean hasLocPermission = configurationAdminFactory.checkTargetPermission(configLoc, ref);
ManagedServiceFactory serviceFactory = getService(ref);
if (hasLocPermission && serviceFactory != null) {
if (isMultiple || config.bind(ConfigurationAdminImpl.getLocation(ref.getBundle()))) {
Dictionary<String, Object> properties = config.getProperties();
configurationAdminFactory.modifyConfiguration(ref, properties);
asynchUpdated(serviceFactory, config.getPid(), 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 factoryPid = config.getFactoryPid();
List<ServiceReference<ManagedServiceFactory>> references = getManagedServiceFactoryReferences(factoryPid);
for (ServiceReference<ManagedServiceFactory> ref : references) {
if (!hasMoreSpecificConfigPids(ref, factoryPid)) {
boolean hasOldPermission = configurationAdminFactory.checkTargetPermission(oldLocation, ref);
boolean hasNewPermission = configurationAdminFactory.checkTargetPermission(configLoc, ref);
ManagedServiceFactory serviceFactory = getService(ref);
if (serviceFactory != 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) {
asynchDeleted(serviceFactory, config.getPid());
} else if (update) {
Dictionary<String, Object> properties = config.getProperties();
configurationAdminFactory.modifyConfiguration(ref, properties);
asynchUpdated(serviceFactory, config.getPid(), properties);
}
// do not break on !isMultiple since we need to check if the other refs apply no matter what
}
}
}
}
private boolean hasMoreSpecificConfigPids(ServiceReference<ManagedServiceFactory> 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 ManagedServiceFactory addingService(ServiceReference<ManagedServiceFactory> reference) {
ManagedServiceFactory service = context.getService(reference);
if (service == null)
return null;
addReference(reference, service);
return service;
}
@Override
public void modifiedService(ServiceReference<ManagedServiceFactory> reference, ManagedServiceFactory 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;
}
}
}
untrackManagedServiceFactory(reference);
addingService(reference);
}
@Override
public void removedService(ServiceReference<ManagedServiceFactory> reference, ManagedServiceFactory service) {
untrackManagedServiceFactory(reference);
context.ungetService(reference);
}
private void addReference(ServiceReference<ManagedServiceFactory> reference, ManagedServiceFactory service) {
List<List<String>> qualifiedPidLists = trackManagedServiceFactory(reference);
updateManagedServiceFactory(qualifiedPidLists, reference, service);
}
private void updateManagedServiceFactory(List<List<String>> qualifiedPidLists, ServiceReference<ManagedServiceFactory> reference, ManagedServiceFactory serviceFactory) {
for (List<String> qualifiedPids : qualifiedPidLists) {
qualifiedPids: for (String qualifiedPid : qualifiedPids) {
ConfigurationImpl[] configs = configurationStore.getFactoryConfigurations(qualifiedPid);
try {
for (int i = 0; i < configs.length; ++i) {
configs[i].lock();
}
boolean foundConfig = false;
for (int i = 0; i < configs.length; ++i) {
if (configs[i].isDeleted()) {
// ignore this config
} else {
String location = configs[i].getLocation();
boolean shouldBind = location == null || !location.startsWith("?"); //$NON-NLS-1$
boolean hasLocPermission = configurationAdminFactory.checkTargetPermission(location, reference);
if (hasLocPermission) {
if (shouldBind && configs[i].bind(ConfigurationAdminImpl.getLocation(reference.getBundle())) || !shouldBind) {
Dictionary<String, Object> properties = configs[i].getProperties();
configurationAdminFactory.modifyConfiguration(reference, properties);
asynchUpdated(serviceFactory, configs[i].getPid(), properties);
foundConfig = true;
} else {
configurationAdminFactory.log(LogService.LOG_WARNING, "Configuration for " + Constants.SERVICE_PID + "=" + configs[i].getPid() + " could not be bound to " + ConfigurationAdminImpl.getLocation(reference.getBundle())); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
}
}
}
}
if (foundConfig) {
break qualifiedPids;
}
} finally {
for (int i = 0; i < configs.length; ++i)
configs[i].unlock();
}
}
}
}
private List<List<String>> trackManagedServiceFactory(ServiceReference<ManagedServiceFactory> reference) {
synchronized (targets) {
return targets.add(reference);
}
}
private void untrackManagedServiceFactory(ServiceReference<ManagedServiceFactory> reference) {
synchronized (targets) {
targets.remove(reference);
}
}
private List<ServiceReference<ManagedServiceFactory>> getManagedServiceFactoryReferences(String pid) {
synchronized (targets) {
@SuppressWarnings("rawtypes")
List temp = targets.getTargets(pid);
@SuppressWarnings("unchecked")
List<ServiceReference<ManagedServiceFactory>> refs = temp;
Collections.sort(refs, Collections.reverseOrder());
return refs;
}
}
private void asynchDeleted(final ManagedServiceFactory service, final String pid) {
queue.put(new Runnable() {
@Override
public void run() {
try {
service.deleted(pid);
} catch (Throwable t) {
configurationAdminFactory.log(LogService.LOG_ERROR, t.getMessage(), t);
}
}
});
}
private void asynchUpdated(final ManagedServiceFactory service, final String pid, final Dictionary<String, Object> properties) {
if (properties == null) {
return;
}
queue.put(new Runnable() {
@Override
public void run() {
try {
service.updated(pid, 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);
}
}
});
}
}