blob: 206f0fa3cb52bc1f5f2cc4651a4564bd3b65e6cb [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.cm.impl;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationPlugin;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* This tracker tracks existence of configuration plugins which have a
* {@link #PROPERTY_NAME} property. The tracker checks just for existence of a
* plugin and tries to get it. However, this is not a guarantee that the plugin
* can and actually is used by the configuration admin as the admin has a
* separate tracker.
*/
public class RequiredConfigurationPluginTracker
implements ServiceTrackerCustomizer<ConfigurationPlugin, ConfigurationPlugin> {
public static final String PROPERTY_NAME = "config.plugin.id";
/** Tracker for the configuration plugins. */
private final ServiceTracker<ConfigurationPlugin, ConfigurationPlugin> pluginTracker;
private final BundleContext bundleContext;
private final ConcurrentHashMap<String, AtomicInteger> serviceMap = new ConcurrentHashMap<>();
private final Map<Long, String> idToNameMap = new ConcurrentHashMap<>();
private final ConfigurationAdminStarter starter;
private final Set<String> requiredNames = new HashSet<>();
private final Set<String> registeredPluginNames = new TreeSet<>();
private final ActivatorWorkerQueue workerQueue;
public RequiredConfigurationPluginTracker(final BundleContext bundleContext,
final ActivatorWorkerQueue workerQueue,
final ConfigurationAdminStarter starter,
final String[] pluginNames) throws BundleException, InvalidSyntaxException {
this.workerQueue = workerQueue;
this.starter = starter;
for (final String name : pluginNames) {
requiredNames.add(name);
}
this.bundleContext = bundleContext;
pluginTracker = new ServiceTracker<>(bundleContext, ConfigurationPlugin.class, this);
pluginTracker.open();
}
/**
* Stop the tracker
*/
public void stop() {
if (this.pluginTracker != null) {
this.pluginTracker.close();
}
}
private boolean hasRequiredPlugins() {
for (final String name : this.requiredNames) {
final AtomicInteger v = this.serviceMap.get(name);
if (v == null || v.get() == 0) {
return false;
}
}
return true;
}
@Override
public ConfigurationPlugin addingService(final ServiceReference<ConfigurationPlugin> reference) {
ConfigurationPlugin plugin = null;
final Object nameObj = reference.getProperty(PROPERTY_NAME);
if (nameObj != null) {
final String name = nameObj.toString();
idToNameMap.put((Long) reference.getProperty(Constants.SERVICE_ID), name);
this.serviceMap.putIfAbsent(name, new AtomicInteger());
final AtomicInteger counter = this.serviceMap.get(name);
final boolean checkActivate = counter.getAndIncrement() == 0;
boolean activate = false;
if (this.requiredNames.contains(name)) {
plugin = bundleContext.getService(reference);
if (plugin != null) {
activate = checkActivate && hasRequiredPlugins();
}
}
final boolean activateCA = activate;
this.workerQueue.enqueue(new Runnable() {
@Override
public void run() {
if (activateCA) {
starter.updatePluginsSet(true);
}
registeredPluginNames.add(name);
updateRegisteredConfigurationPlugins();
}
});
}
return plugin;
}
@Override
public void modifiedService(final ServiceReference<ConfigurationPlugin> reference,
ConfigurationPlugin service) {
removedService(reference, service);
addingService(reference);
}
@Override
public void removedService(final ServiceReference<ConfigurationPlugin> reference,
ConfigurationPlugin service) {
final String name = idToNameMap.remove(reference.getProperty(Constants.SERVICE_ID));
if (name != null) {
final AtomicInteger counter = this.serviceMap.get(name);
final boolean deactivate = counter.decrementAndGet() == 0;
if (this.requiredNames.contains(name)) {
bundleContext.ungetService(reference);
}
if (deactivate) {
this.workerQueue.enqueue(new Runnable() {
@Override
public void run() {
if (!hasRequiredPlugins()) {
starter.updatePluginsSet(false);
}
registeredPluginNames.remove(name);
updateRegisteredConfigurationPlugins();
}
});
}
}
}
private void updateRegisteredConfigurationPlugins() {
final String propValue;
if (this.registeredPluginNames.isEmpty()) {
propValue = "";
} else {
final StringBuilder sb = new StringBuilder();
boolean first = true;
for (final String name : this.registeredPluginNames) {
if (first) {
first = false;
} else {
sb.append(",");
}
sb.append(name);
}
propValue = sb.toString();
}
starter.updateRegisteredConfigurationPlugins(propValue);
}
}