/******************************************************************************* * 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.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; } public ManagedServiceFactory addingService(ServiceReference<ManagedServiceFactory> reference) { ManagedServiceFactory service = context.getService(reference); if (service == null) return null; addReference(reference, service); return service; } 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); } 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() { 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() { 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); } } }); } }