X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fblueprint%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fblueprint%2FBlueprintBundleTracker.java;h=b79d3662f966b36aca51fd6fee3ba106cb951bd4;hp=ed416c5fb4481cc75d9f9a062262b237823e1083;hb=4e5f900eed31c3de0a9c599a206b6b3909697cc7;hpb=f622d1ef05e04d9bbebed6ee9afa4d825ab23cab diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintBundleTracker.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintBundleTracker.java index ed416c5fb4..b79d3662f9 100644 --- a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintBundleTracker.java +++ b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintBundleTracker.java @@ -11,17 +11,18 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.List; +import javax.annotation.Nullable; import org.apache.aries.blueprint.NamespaceHandler; import org.apache.aries.blueprint.services.BlueprintExtenderService; +import org.apache.aries.quiesce.participant.QuiesceParticipant; import org.apache.aries.util.AriesFrameworkUtil; import org.opendaylight.controller.blueprint.ext.OpendaylightNamespaceHandler; -import org.opendaylight.controller.config.api.ConfigSystemService; +import org.opendaylight.yangtools.util.xml.UntrustedXML; import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; @@ -30,9 +31,8 @@ import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.framework.SynchronousBundleListener; import org.osgi.service.blueprint.container.BlueprintContainer; -import org.osgi.service.blueprint.container.EventConstants; -import org.osgi.service.event.Event; -import org.osgi.service.event.EventHandler; +import org.osgi.service.blueprint.container.BlueprintEvent; +import org.osgi.service.blueprint.container.BlueprintListener; import org.osgi.util.tracker.BundleTracker; import org.osgi.util.tracker.BundleTrackerCustomizer; import org.osgi.util.tracker.ServiceTracker; @@ -48,17 +48,20 @@ import org.slf4j.LoggerFactory; * * @author Thomas Pantelis */ -public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCustomizer, EventHandler, +public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCustomizer, BlueprintListener, SynchronousBundleListener { private static final Logger LOG = LoggerFactory.getLogger(BlueprintBundleTracker.class); - private static final String BLUEPRINT_FILE_PATH = "org/opendaylight/blueprint/"; + private static final String ODL_CUSTOM_BLUEPRINT_FILE_PATH = "org/opendaylight/blueprint/"; + private static final String STANDARD_BLUEPRINT_FILE_PATH = "OSGI-INF/blueprint/"; private static final String BLUEPRINT_FLE_PATTERN = "*.xml"; private static final long SYSTEM_BUNDLE_ID = 0; - private ServiceTracker serviceTracker; + private ServiceTracker blueprintExtenderServiceTracker; + private ServiceTracker quiesceParticipantTracker; private BundleTracker bundleTracker; private BundleContext bundleContext; private volatile BlueprintExtenderService blueprintExtenderService; + private volatile QuiesceParticipant quiesceParticipant; private volatile ServiceRegistration blueprintContainerRestartReg; private volatile BlueprintContainerRestartServiceImpl restartService; private volatile boolean shuttingDown; @@ -69,9 +72,14 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus * Implemented from BundleActivator. */ @Override - public void start(BundleContext context) { + public void start(final BundleContext context) { LOG.info("Starting {}", getClass().getSimpleName()); + // CONTROLLER-1867: force UntrustedXML initialization, so that it uses our TCCL to initialize + UntrustedXML.newDocumentBuilder(); + + restartService = new BlueprintContainerRestartServiceImpl(); + bundleContext = context; registerBlueprintEventHandler(context); @@ -80,59 +88,93 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus bundleTracker = new BundleTracker<>(context, Bundle.ACTIVE, this); - serviceTracker = new ServiceTracker<>(context, BlueprintExtenderService.class.getName(), + blueprintExtenderServiceTracker = new ServiceTracker<>(context, BlueprintExtenderService.class.getName(), new ServiceTrackerCustomizer() { @Override public BlueprintExtenderService addingService( - ServiceReference reference) { - blueprintExtenderService = reference.getBundle().getBundleContext().getService(reference); - bundleTracker.open(); - - context.addBundleListener(BlueprintBundleTracker.this); + final ServiceReference reference) { + return onBlueprintExtenderServiceAdded(reference); + } - LOG.debug("Got BlueprintExtenderService"); + @Override + public void modifiedService(final ServiceReference reference, + final BlueprintExtenderService service) { + } - restartService = new BlueprintContainerRestartServiceImpl(blueprintExtenderService); - blueprintContainerRestartReg = context.registerService( - BlueprintContainerRestartService.class.getName(), restartService, new Hashtable<>()); + @Override + public void removedService(final ServiceReference reference, + final BlueprintExtenderService service) { + } + }); + blueprintExtenderServiceTracker.open(); - return blueprintExtenderService; + quiesceParticipantTracker = new ServiceTracker<>(context, QuiesceParticipant.class.getName(), + new ServiceTrackerCustomizer() { + @Override + public QuiesceParticipant addingService( + final ServiceReference reference) { + return onQuiesceParticipantAdded(reference); } @Override - public void modifiedService(ServiceReference reference, - BlueprintExtenderService service) { + public void modifiedService(final ServiceReference reference, + final QuiesceParticipant service) { } @Override - public void removedService(ServiceReference reference, - BlueprintExtenderService service) { + public void removedService(final ServiceReference reference, + final QuiesceParticipant service) { } }); - serviceTracker.open(); + quiesceParticipantTracker.open(); + } + + private QuiesceParticipant onQuiesceParticipantAdded(final ServiceReference reference) { + quiesceParticipant = reference.getBundle().getBundleContext().getService(reference); + + LOG.debug("Got QuiesceParticipant"); + + restartService.setQuiesceParticipant(quiesceParticipant); + + return quiesceParticipant; } - private void registerNamespaceHandler(BundleContext context) { + private BlueprintExtenderService onBlueprintExtenderServiceAdded( + final ServiceReference reference) { + blueprintExtenderService = reference.getBundle().getBundleContext().getService(reference); + bundleTracker.open(); + + bundleContext.addBundleListener(BlueprintBundleTracker.this); + + LOG.debug("Got BlueprintExtenderService"); + + restartService.setBlueprintExtenderService(blueprintExtenderService); + + blueprintContainerRestartReg = bundleContext.registerService( + BlueprintContainerRestartService.class.getName(), restartService, new Hashtable<>()); + + return blueprintExtenderService; + } + + private void registerNamespaceHandler(final BundleContext context) { Dictionary props = new Hashtable<>(); props.put("osgi.service.blueprint.namespace", OpendaylightNamespaceHandler.NAMESPACE_1_0_0); namespaceReg = context.registerService(NamespaceHandler.class.getName(), new OpendaylightNamespaceHandler(), props); } - private void registerBlueprintEventHandler(BundleContext context) { - Dictionary props = new Hashtable<>(); - props.put(org.osgi.service.event.EventConstants.EVENT_TOPIC, - new String[]{EventConstants.TOPIC_CREATED, EventConstants.TOPIC_FAILURE}); - eventHandlerReg = context.registerService(EventHandler.class.getName(), this, props); + private void registerBlueprintEventHandler(final BundleContext context) { + eventHandlerReg = context.registerService(BlueprintListener.class.getName(), this, new Hashtable<>()); } /** * Implemented from BundleActivator. */ @Override - public void stop(BundleContext context) { + public void stop(final BundleContext context) { bundleTracker.close(); - serviceTracker.close(); + blueprintExtenderServiceTracker.close(); + quiesceParticipantTracker.close(); AriesFrameworkUtil.safeUnregisterService(eventHandlerReg); AriesFrameworkUtil.safeUnregisterService(namespaceReg); @@ -143,10 +185,10 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus * Implemented from SynchronousBundleListener. */ @Override - public void bundleChanged(BundleEvent event) { + public void bundleChanged(final BundleEvent event) { // If the system bundle (id 0) is stopping, do an orderly shutdown of all blueprint containers. On // shutdown the system bundle is stopped first. - if(event.getBundle().getBundleId() == SYSTEM_BUNDLE_ID && event.getType() == BundleEvent.STOPPING) { + if (event.getBundle().getBundleId() == SYSTEM_BUNDLE_ID && event.getType() == BundleEvent.STOPPING) { shutdownAllContainers(); } } @@ -155,24 +197,24 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus * Implemented from BundleActivator. */ @Override - public Bundle addingBundle(Bundle bundle, BundleEvent event) { + public Bundle addingBundle(final Bundle bundle, final BundleEvent event) { modifiedBundle(bundle, event, bundle); return bundle; } /** - * Implemented from BundleActivator. + * Implemented from BundleTrackerCustomizer. */ @Override - public void modifiedBundle(Bundle bundle, BundleEvent event, Bundle object) { - if(shuttingDown) { + public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Bundle object) { + if (shuttingDown) { return; } - if(bundle.getState() == Bundle.ACTIVE) { - List paths = findBlueprintPaths(bundle); + if (bundle.getState() == Bundle.ACTIVE) { + List paths = findBlueprintPaths(bundle, ODL_CUSTOM_BLUEPRINT_FILE_PATH); - if(!paths.isEmpty()) { + if (!paths.isEmpty()) { LOG.info("Creating blueprint container for bundle {} with paths {}", bundle, paths); blueprintExtenderService.createContainer(bundle, paths); @@ -181,49 +223,54 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus } /** - * Implemented from BundleActivator. + * Implemented from BundleTrackerCustomizer. */ @Override - public void removedBundle(Bundle bundle, BundleEvent event, Bundle object) { + public void removedBundle(final Bundle bundle, final BundleEvent event, final Bundle object) { // BlueprintExtenderService will handle this. } /** - * Implemented from EventHandler to listen for blueprint events. + * Implemented from BlueprintListener to listen for blueprint events. * - * @param event + * @param event the event to handle */ @Override - public void handleEvent(Event event) { - if(EventConstants.TOPIC_CREATED.equals(event.getTopic())) { - LOG.info("Blueprint container for bundle {} was successfully created", - event.getProperty(EventConstants.BUNDLE)); - } else if(EventConstants.TOPIC_FAILURE.equals(event.getTopic())) { - // If the container timed out waiting for dependencies, we'll destroy it and start it again. This - // is indicated via a non-null DEPENDENCIES property containing the missing dependencies. The - // default timeout is 5 min and ideally we would set this to infinite but the timeout can only - // be set at the bundle level in the manifest - there's no way to set it globally. - if(event.getProperty(EventConstants.DEPENDENCIES) != null) { - Bundle bundle = (Bundle) event.getProperty(EventConstants.BUNDLE); - - List paths = findBlueprintPaths(bundle); - if(!paths.isEmpty()) { - LOG.warn("Blueprint container for bundle {} timed out waiting for dependencies - restarting it", - event.getProperty(EventConstants.BUNDLE)); - - restartService.restartContainer(bundle, paths); - } + public void blueprintEvent(final BlueprintEvent event) { + if (event.getType() == BlueprintEvent.CREATED) { + LOG.info("Blueprint container for bundle {} was successfully created", event.getBundle()); + return; + } + + // If the container timed out waiting for dependencies, we'll destroy it and start it again. This + // is indicated via a non-null DEPENDENCIES property containing the missing dependencies. The + // default timeout is 5 min and ideally we would set this to infinite but the timeout can only + // be set at the bundle level in the manifest - there's no way to set it globally. + if (event.getType() == BlueprintEvent.FAILURE && event.getDependencies() != null) { + Bundle bundle = event.getBundle(); + + List paths = findBlueprintPaths(bundle); + if (!paths.isEmpty()) { + LOG.warn("Blueprint container for bundle {} timed out waiting for dependencies - restarting it", + bundle); + + restartService.restartContainer(bundle, paths); } } } + static List findBlueprintPaths(final Bundle bundle) { + List paths = findBlueprintPaths(bundle, STANDARD_BLUEPRINT_FILE_PATH); + return !paths.isEmpty() ? paths : findBlueprintPaths(bundle, ODL_CUSTOM_BLUEPRINT_FILE_PATH); + } + @SuppressWarnings({ "rawtypes", "unchecked" }) - static List findBlueprintPaths(Bundle bundle) { - Enumeration e = bundle.findEntries(BLUEPRINT_FILE_PATH, BLUEPRINT_FLE_PATTERN, false); - if(e == null) { + private static List findBlueprintPaths(final Bundle bundle, final String path) { + Enumeration rntries = bundle.findEntries(path, BLUEPRINT_FLE_PATTERN, false); + if (rntries == null) { return Collections.emptyList(); } else { - return Collections.list((Enumeration)e); + return Collections.list((Enumeration)rntries); } } @@ -232,22 +279,16 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus restartService.close(); - // Close all CSS modules first. - ConfigSystemService configSystem = getOSGiService(ConfigSystemService.class); - if(configSystem != null) { - configSystem.closeAllConfigModules(); - } - LOG.info("Shutting down all blueprint containers..."); Collection containerBundles = new HashSet<>(Arrays.asList(bundleContext.getBundles())); - while(!containerBundles.isEmpty()) { + while (!containerBundles.isEmpty()) { // For each iteration of getBundlesToDestroy, as containers are destroyed, other containers become // eligible to be destroyed. We loop until we've destroyed them all. - for(Bundle bundle : getBundlesToDestroy(containerBundles)) { + for (Bundle bundle : getBundlesToDestroy(containerBundles)) { containerBundles.remove(bundle); BlueprintContainer container = blueprintExtenderService.getContainer(bundle); - if(container != null) { + if (container != null) { blueprintExtenderService.destroyContainer(bundle, container); } } @@ -256,33 +297,28 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus LOG.info("Shutdown of blueprint containers complete"); } - private List getBundlesToDestroy(Collection containerBundles) { + private List getBundlesToDestroy(final Collection containerBundles) { List bundlesToDestroy = new ArrayList<>(); // Find all container bundles that either have no registered services or whose services are no // longer in use. - for(Bundle bundle : containerBundles) { + for (Bundle bundle : containerBundles) { ServiceReference[] references = bundle.getRegisteredServices(); int usage = 0; - if(references != null) { - for(ServiceReference reference : references) { + if (references != null) { + for (ServiceReference reference : references) { usage += getServiceUsage(reference); } } LOG.debug("Usage for bundle {} is {}", bundle, usage); - if(usage == 0) { + if (usage == 0) { bundlesToDestroy.add(bundle); } } - if(!bundlesToDestroy.isEmpty()) { - Collections.sort(bundlesToDestroy, new Comparator() { - @Override - public int compare(Bundle b1, Bundle b2) { - return (int) (b2.getLastModified() - b1.getLastModified()); - } - }); + if (!bundlesToDestroy.isEmpty()) { + bundlesToDestroy.sort((b1, b2) -> (int) (b2.getLastModified() - b1.getLastModified())); LOG.debug("Selected bundles {} for destroy (no services in use)", bundlesToDestroy); } else { @@ -294,32 +330,9 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus // ID, we're picking the bundle that was (likely) started after all the others and thus // is likely the safest to destroy at this point. - ServiceReference ref = null; - for(Bundle bundle : containerBundles) { - ServiceReference[] references = bundle.getRegisteredServices(); - if(references == null) { - continue; - } - - for(ServiceReference reference : references) { - // We did check the service usage above but it's possible the usage has changed since - // then, - if(getServiceUsage(reference) == 0) { - continue; - } - - // Choose 'reference' if it has a lower service ranking or, if the rankings are equal - // which is usually the case, if it has a higher service ID. For the latter the < 0 - // check looks backwards but that's how ServiceReference#compareTo is documented to work. - if(ref == null || reference.compareTo(ref) < 0) { - LOG.debug("Currently selecting bundle {} for destroy (with reference {})", bundle, reference); - ref = reference; - } - } - } - - if(ref != null) { - bundlesToDestroy.add(ref.getBundle()); + Bundle bundle = findBundleWithHighestUsedServiceId(containerBundles); + if (bundle != null) { + bundlesToDestroy.add(bundle); } LOG.debug("Selected bundle {} for destroy (lowest ranking service or highest service ID)", @@ -329,32 +342,36 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus return bundlesToDestroy; } - private static int getServiceUsage(ServiceReference ref) { - Bundle[] usingBundles = ref.getUsingBundles(); - return usingBundles != null ? usingBundles.length : 0; - } - - private T getOSGiService(Class serviceInterface) { - try { - ServiceReference serviceReference = bundleContext.getServiceReference(serviceInterface); - if(serviceReference == null) { - LOG.warn("{} service reference not found", serviceInterface.getSimpleName()); - return null; + @Nullable + private Bundle findBundleWithHighestUsedServiceId(final Collection containerBundles) { + ServiceReference highestServiceRef = null; + for (Bundle bundle : containerBundles) { + ServiceReference[] references = bundle.getRegisteredServices(); + if (references == null) { + continue; } - T service = bundleContext.getService(serviceReference); - if(service == null) { - // This could happen on shutdown if the service was already unregistered so we log as debug. - LOG.debug("{} service instance was not found", serviceInterface.getSimpleName()); - } + for (ServiceReference reference : references) { + // We did check the service usage previously but it's possible the usage has changed since then. + if (getServiceUsage(reference) == 0) { + continue; + } - return service; - } catch(IllegalStateException e) { - // This is thrown if the BundleContext is no longer valid which is possible on shutdown so we - // log as debug. - LOG.debug("Error obtaining OSGi service {}", serviceInterface.getSimpleName(), e); + // Choose 'reference' if it has a lower service ranking or, if the rankings are equal + // which is usually the case, if it has a higher service ID. For the latter the < 0 + // check looks backwards but that's how ServiceReference#compareTo is documented to work. + if (highestServiceRef == null || reference.compareTo(highestServiceRef) < 0) { + LOG.debug("Currently selecting bundle {} for destroy (with reference {})", bundle, reference); + highestServiceRef = reference; + } + } } - return null; + return highestServiceRef == null ? null : highestServiceRef.getBundle(); + } + + private static int getServiceUsage(final ServiceReference ref) { + Bundle[] usingBundles = ref.getUsingBundles(); + return usingBundles != null ? usingBundles.length : 0; } }