/******************************************************************************* * Copyright (c) 2008, 2010 VMware Inc. * 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: * VMware Inc. - initial contribution *******************************************************************************/ package org.eclipse.virgo.kernel.agent.dm.internal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import org.osgi.framework.Bundle; import org.osgi.service.event.Event; import org.osgi.service.event.EventAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.eclipse.gemini.blueprint.context.event.OsgiBundleApplicationContextEvent; import org.eclipse.gemini.blueprint.context.event.OsgiBundleApplicationContextListener; import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextClosedEvent; import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextFailedEvent; import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextRefreshedEvent; import org.eclipse.gemini.blueprint.extender.event.BootstrappingDependencyEvent; import org.eclipse.gemini.blueprint.service.importer.OsgiServiceDependency; import org.eclipse.gemini.blueprint.service.importer.event.OsgiServiceDependencyEvent; import org.eclipse.gemini.blueprint.service.importer.event.OsgiServiceDependencyWaitEndedEvent; import org.eclipse.gemini.blueprint.service.importer.event.OsgiServiceDependencyWaitStartingEvent; import org.eclipse.gemini.blueprint.service.importer.event.OsgiServiceDependencyWaitTimedOutEvent; /** * An {@link OsgiBundleApplicationContextListener} implementation that listens to Spring DM events and sends the * equivalent Blueprint events. * * <p /> * * <strong>Concurrent Semantics</strong><br /> * * Thread-safe. * */ public final class BlueprintEventPostingOsgiBundleApplicationContextListener implements OsgiBundleApplicationContextListener<OsgiBundleApplicationContextEvent> { private static final String PROPERTY_BUNDLE_SYMBOLICNAME = "bundle.symbolicName"; private static final String PROPERTY_BUNDLE_ID = "bundle.id"; private static final String PROPERTY_BUNDLE = "bundle"; private static final String PROPERTY_BUNDLE_VERSION = "bundle.version"; private static final String PROPERTY_TIMESTAMP = "timestamp"; private static final String PROPERTY_EXCEPTION = "exception"; private static final String PROPERTY_DEPENDENCIES = "dependencies"; private static final String PROPERTY_BEAN_NAME = "bean.name"; private static final String PROPERTY_MANDATORY = "mandatory"; private static final String PROPERTY_TYPE = "type"; private static final String TOPIC_BLUEPRINT_EVENTS = "org/osgi/service/blueprint/container/"; private static final String EVENT_CREATED = TOPIC_BLUEPRINT_EVENTS + "CREATED"; private static final String EVENT_DESTROYED = TOPIC_BLUEPRINT_EVENTS + "DESTROYED"; private static final String EVENT_FAILURE = TOPIC_BLUEPRINT_EVENTS + "FAILURE"; private static final String EVENT_WAITING = TOPIC_BLUEPRINT_EVENTS + "WAITING"; private static final String EVENT_GRACE_PERIOD = TOPIC_BLUEPRINT_EVENTS + "GRACE_PERIOD"; private static final Logger logger = LoggerFactory.getLogger(BlueprintEventPostingOsgiBundleApplicationContextListener.class); private static final int TYPE_CREATED = 1; private static final int TYPE_DESTROYED = 4; private static final int TYPE_FAILURE = 5; private static final int TYPE_GRACE_PERIOD = 6; private static final int TYPE_WAITING = 7; private final EventAdmin eventAdmin; private final Map<Bundle, List<OsgiServiceDependency>> unsatisfiedDependencies = new HashMap<Bundle, List<OsgiServiceDependency>>(); private final Object monitor = new Object(); public BlueprintEventPostingOsgiBundleApplicationContextListener(EventAdmin eventAdmin) { this.eventAdmin = eventAdmin; } /** * {@inheritDoc} */ public void onOsgiApplicationEvent(OsgiBundleApplicationContextEvent event) { Bundle bundle = event.getBundle(); Dictionary<String, Object> properties = createEventProperties(event); if (event instanceof OsgiBundleContextRefreshedEvent) { clearUnsatisfiedDependencies(bundle); sendCreatedEvent(properties); } else if (event instanceof OsgiBundleContextFailedEvent) { clearUnsatisfiedDependencies(bundle); properties.put(PROPERTY_EXCEPTION, ((OsgiBundleContextFailedEvent) event).getFailureCause()); sendFailureEvent(properties); } else if (event instanceof OsgiBundleContextClosedEvent) { sendDestroyedEvent(properties); } else if (event instanceof BootstrappingDependencyEvent) { OsgiServiceDependencyEvent serviceDependencyEvent = ((BootstrappingDependencyEvent) event).getDependencyEvent(); OsgiServiceDependency dependency = serviceDependencyEvent.getServiceDependency(); if (serviceDependencyEvent instanceof OsgiServiceDependencyWaitStartingEvent) { addUnsatisfiedDependency(bundle, dependency); addDependencyProperties(dependency, properties); sendWaitingEvent(properties); } else if (serviceDependencyEvent instanceof OsgiServiceDependencyWaitTimedOutEvent) { List<OsgiServiceDependency> unsatisfiedDependencies = getUnsatisfiedDependencies(bundle); addDependenciesProperties(unsatisfiedDependencies, properties); sendFailureEvent(properties); } else if (serviceDependencyEvent instanceof OsgiServiceDependencyWaitEndedEvent) { List<OsgiServiceDependency> unsatisfiedDependencies = removeUnsatisfiedDependency(bundle, dependency); if (unsatisfiedDependencies != null) { addDependenciesProperties(unsatisfiedDependencies, properties); sendGracePeriodEvent(properties); } } } } private List<OsgiServiceDependency> getUnsatisfiedDependencies(Bundle bundle) { synchronized (this.monitor) { List<OsgiServiceDependency> dependencies = this.unsatisfiedDependencies.get(bundle); if (dependencies == null) { dependencies = Collections.<OsgiServiceDependency> emptyList(); } return dependencies; } } private List<OsgiServiceDependency> addUnsatisfiedDependency(Bundle bundle, OsgiServiceDependency dependency) { synchronized (this.monitor) { List<OsgiServiceDependency> bundlesDependencies = this.unsatisfiedDependencies.get(bundle); if (bundlesDependencies == null) { bundlesDependencies = new ArrayList<OsgiServiceDependency>(); this.unsatisfiedDependencies.put(bundle, bundlesDependencies); } bundlesDependencies.add(dependency); return bundlesDependencies; } } private List<OsgiServiceDependency> removeUnsatisfiedDependency(Bundle bundle, OsgiServiceDependency satisfiedDependency) { synchronized (this.monitor) { List<OsgiServiceDependency> bundlesDependencies = this.unsatisfiedDependencies.get(bundle); if (bundlesDependencies != null) { bundlesDependencies.remove(satisfiedDependency); } return bundlesDependencies; } } private void clearUnsatisfiedDependencies(Bundle bundle) { synchronized (this.monitor) { this.unsatisfiedDependencies.remove(bundle); } } private void addDependenciesProperties(List<OsgiServiceDependency> dependencies, Dictionary<String, Object> properties) { if (!dependencies.isEmpty()) { String[] beanNames = new String[dependencies.size()]; String[] filters = new String[dependencies.size()]; boolean[] mandatory = new boolean[dependencies.size()]; for (int i = 0; i < dependencies.size(); i++) { OsgiServiceDependency serviceDependency = dependencies.get(i); beanNames[i] = serviceDependency.getBeanName(); filters[i] = serviceDependency.getServiceFilter().toString(); mandatory[i] = serviceDependency.isMandatory(); } properties.put(PROPERTY_DEPENDENCIES, filters); properties.put(PROPERTY_BEAN_NAME, beanNames); properties.put(PROPERTY_MANDATORY, mandatory); } } private void addDependencyProperties(OsgiServiceDependency dependency, Dictionary<String, Object> properties) { addDependenciesProperties(Arrays.asList(new OsgiServiceDependency[] { dependency }), properties); } private void sendCreatedEvent(Dictionary<String, Object> properties) { sendEvent(EVENT_CREATED, properties, TYPE_CREATED); } private void sendEvent(String topic, Dictionary<String, Object> properties, int type) { properties.put(PROPERTY_TYPE, type); logger.info("Sending event to topic '{}' with properties '{}'", topic, properties); try { this.eventAdmin.sendEvent(new Event(topic, properties)); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to send event to topic '" + topic + "'. This may be expected during shutdown.", ex); } else { logger.error( "Failed to send event to topic '{}'. Exception message: '{}'. This may be expected during shutdown. Turn on debug logging for more details.", topic, ex.getMessage()); } } } private void sendFailureEvent(Dictionary<String, Object> properties) { sendEvent(EVENT_FAILURE, properties, TYPE_FAILURE); } private void sendDestroyedEvent(Dictionary<String, Object> properties) { sendEvent(EVENT_DESTROYED, properties, TYPE_DESTROYED); } private void sendWaitingEvent(Dictionary<String, Object> properties) { sendEvent(EVENT_WAITING, properties, TYPE_WAITING); } private void sendGracePeriodEvent(Dictionary<String, Object> properties) { sendEvent(EVENT_GRACE_PERIOD, properties, TYPE_GRACE_PERIOD); } private Dictionary<String, Object> createEventProperties(OsgiBundleApplicationContextEvent event) { Dictionary<String, Object> properties = new Hashtable<String, Object>(); Bundle bundle = event.getBundle(); properties.put(PROPERTY_BUNDLE, bundle); properties.put(PROPERTY_BUNDLE_ID, bundle.getBundleId()); properties.put(PROPERTY_BUNDLE_SYMBOLICNAME, bundle.getSymbolicName()); properties.put(PROPERTY_BUNDLE_VERSION, bundle.getVersion()); properties.put(PROPERTY_TIMESTAMP, event.getTimestamp()); return properties; } }