/******************************************************************************* * Copyright (c) 2005, 2015 Cognos Incorporated, IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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; } public ManagedService addingService(ServiceReference<ManagedService> reference) { ManagedService service = context.getService(reference); if (service == null) return null; addReference(reference, service); return service; } 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); } 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() { 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); } } }); } }