blob: 1b82f4fa920987e59e151791a1a872a84835deb2 [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.configurator.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.felix.configurator.impl.logger.SystemLogger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* The {@code ServicesListener} listens for the required services
* and starts the configurator when all services are available.
* It also handles optional services
*/
public class ServicesListener {
/** The bundle context. */
private final BundleContext bundleContext;
/** The service tracker for configuration admin */
private final ServiceTracker<ConfigurationAdmin, ServiceReference<ConfigurationAdmin>> caTracker;
/** The listener for the coordinator. */
private final Listener coordinatorListener;
/** The current configurator. */
private volatile Configurator configurator;
private final List<ServiceReference<ConfigurationAdmin>> configAdminReferences;
/**
* Start listeners
*/
public ServicesListener(final BundleContext bundleContext) {
this.bundleContext = bundleContext;
this.configAdminReferences = new ArrayList<>();
this.caTracker = new ServiceTracker<>(bundleContext, ConfigurationAdmin.class,
new ServiceTrackerCustomizer<ConfigurationAdmin, ServiceReference<ConfigurationAdmin>>() {
@Override
public ServiceReference<ConfigurationAdmin> addingService(final ServiceReference<ConfigurationAdmin> reference) {
synchronized ( configAdminReferences ) {
configAdminReferences.add(reference);
Collections.sort(configAdminReferences);
}
notifyChange();
return reference;
}
@Override
public void modifiedService(final ServiceReference<ConfigurationAdmin> reference,
final ServiceReference<ConfigurationAdmin> service) {
// nothing to do
}
@Override
public void removedService(final ServiceReference<ConfigurationAdmin> reference,
final ServiceReference<ConfigurationAdmin> service) {
synchronized ( configAdminReferences ) {
configAdminReferences.remove(reference);
}
notifyChange();
}
});
this.coordinatorListener = new Listener("org.osgi.service.coordinator.Coordinator");
this.caTracker.open();
this.coordinatorListener.start();
SystemLogger.debug("Started services listener for configurator.");
}
/**
* Notify of service changes from the listeners.
* If all services are available, start
*/
public void notifyChange() {
synchronized ( configAdminReferences ) {
// check if there is at least a single configuration admin
final boolean hasConfigAdmin = !this.configAdminReferences.isEmpty();
final Object coordinator = this.coordinatorListener.getService();
SystemLogger.debug("Services updated for configurator: " + configAdminReferences + " - " + coordinator);
if ( hasConfigAdmin ) {
boolean isNew = configurator == null;
if ( isNew ) {
SystemLogger.debug("Starting new configurator");
configurator = new Configurator(this.bundleContext, this.configAdminReferences);
}
configurator.setCoordinator(coordinator);
if ( isNew ) {
configurator.start();
} else {
configurator.configAdminAdded();
}
} else {
if ( configurator != null ) {
SystemLogger.debug("Stopping configurator");
configurator.shutdown();
configurator = null;
}
}
}
}
/**
* Deactivate this listener.
*/
public void deactivate() {
this.caTracker.close();
this.coordinatorListener.deactivate();
if ( configurator != null ) {
configurator.shutdown();
configurator = null;
}
}
/**
* Helper class listening for service events for a defined service.
*/
protected final class Listener implements ServiceListener {
/** The name of the service. */
private final String serviceName;
/** The service reference. */
private volatile ServiceReference<?> reference;
/** The service. */
private volatile Object service;
/**
* Constructor
*/
public Listener(final String serviceName) {
this.serviceName = serviceName;
}
/**
* Start the listener.
* First register a service listener and then check for the service.
*/
public void start() {
try {
bundleContext.addServiceListener(this, "("
+ Constants.OBJECTCLASS + "=" + serviceName + ")");
} catch (final InvalidSyntaxException ise) {
// this should really never happen
throw new RuntimeException("Unexpected exception occured.", ise);
}
this.retainService();
}
/**
* Unregister the listener.
*/
public void deactivate() {
bundleContext.removeServiceListener(this);
}
/**
* Return the service (if available)
*/
public synchronized Object getService() {
return this.service;
}
/**
* Try to get the service and notify the change.
*/
private synchronized void retainService() {
if ( this.reference == null ) {
this.reference = bundleContext.getServiceReference(this.serviceName);
if ( this.reference != null ) {
this.service = bundleContext.getService(this.reference);
if ( this.service == null ) {
this.reference = null;
} else {
notifyChange();
}
}
}
}
/**
* Try to release the service and notify the change.
*/
private synchronized void releaseService(final ServiceReference<?> ref) {
if ( this.reference != null && this.reference.compareTo(ref) == 0 ) {
this.service = null;
bundleContext.ungetService(this.reference);
this.reference = null;
notifyChange();
}
}
/**
* @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
*/
@Override
public void serviceChanged(ServiceEvent event) {
if (event.getType() == ServiceEvent.REGISTERED) {
this.retainService();
} else if ( event.getType() == ServiceEvent.UNREGISTERING ) {
this.releaseService(event.getServiceReference());
this.retainService();
}
}
}
}