From cc42ab4ee40d6010bc7d85d26a4206d9d755f077 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 18 Nov 2009 23:32:14 +0000 Subject: [PATCH] INT-881, Finished up with initial version of SI Service Extender. Added some code improvements. Finished with lifecycle binding listeners --- .../osgi/config/xml/BusConfigParser.java | 4 +- .../osgi/config/xml/ConfigParser.java | 58 +++++----- ...il.java => ControlBusOSGiConfigUtils.java} | 104 +++++++++++++++--- .../osgi/config/xml/ControlBusOSGiUtils.java | 58 ---------- ...BusBindingMessageDistributionListener.java | 3 +- ...gistrationMessageDistributionListener.java | 3 +- .../IntegrationServiceBindingListener.java | 67 ----------- .../LifecycleServiceManagingListener.java | 86 +++++++++++++++ .../osgi/config/xml/BusConfigParserTests.java | 4 +- .../xml/ConfigParserImporterTests-default.xml | 7 ++ .../config/xml/ConfigParserImporterTests.java | 42 ++++++- .../osgi/demo/SampleActivator.java | 10 ++ .../osgi/demo/SpringDMnoOSGiDemo.java | 57 ++++++++++ .../osgi/demo/ac-service-exporter.xml | 16 +++ .../osgi/demo/ac-service-importer.xml | 18 +++ 15 files changed, 364 insertions(+), 173 deletions(-) rename org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/{AbstractOSGiServiceManagingParserUtil.java => ControlBusOSGiConfigUtils.java} (55%) delete mode 100644 org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ControlBusOSGiUtils.java delete mode 100644 org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/IntegrationServiceBindingListener.java create mode 100644 org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/LifecycleServiceManagingListener.java create mode 100644 org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/SampleActivator.java create mode 100644 org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/SpringDMnoOSGiDemo.java create mode 100644 org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/ac-service-exporter.xml create mode 100644 org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/ac-service-importer.xml diff --git a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/BusConfigParser.java b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/BusConfigParser.java index 37540e1a89..b7354af466 100644 --- a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/BusConfigParser.java +++ b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/BusConfigParser.java @@ -59,11 +59,11 @@ public class BusConfigParser extends AbstractBeanDefinitionParser { } BeanDefinitionBuilder rootBuilder = BeanDefinitionBuilder.rootBeanDefinition(OSGiIntegrationControlBus.class); - rootBuilder.addConstructorArgReference(ControlBusOSGiUtils.DEFAULT_CONTROL_DIST_CHANNEL); + rootBuilder.addConstructorArgReference(ControlBusOSGiConfigUtils.DEFAULT_CONTROL_DIST_CHANNEL); rootBuilder.addConstructorArgValue(beanName); BeanDefinitionBuilder osgiServiceDefinition = - AbstractOSGiServiceManagingParserUtil.defineServiceExporterFor(beanName, parserContext.getRegistry(), ControlBus.class); + ControlBusOSGiConfigUtils.defineServiceExporterFor(beanName, parserContext.getRegistry(), ControlBus.class); BeanDefinitionReaderUtils.registerWithGeneratedName(osgiServiceDefinition.getBeanDefinition(), parserContext.getRegistry()); // NOTE add listeners diff --git a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ConfigParser.java b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ConfigParser.java index 92e972575e..45c0399fa1 100644 --- a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ConfigParser.java +++ b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ConfigParser.java @@ -16,15 +16,10 @@ package org.springframework.integration.osgi.config.xml; import java.util.List; -import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanReference; -import org.springframework.beans.factory.config.RuntimeBeanNameReference; -import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; @@ -32,14 +27,11 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.integration.osgi.extender.ControlBusBindingMessageDistributionListener; import org.springframework.integration.osgi.extender.ControlBusRegistrationMessageDistributionListener; -import org.springframework.osgi.service.exporter.OsgiServiceRegistrationListener; -import org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean; +import org.springframework.integration.osgi.extender.LifecycleServiceManagingListener; import org.springframework.util.Assert; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; -import org.w3c.dom.NodeList; /** * Parser to process 'config' element of SI Service Extender @@ -91,16 +83,30 @@ public class ConfigParser implements BeanDefinitionParser { Assert.hasText(serviceName, "you must enter a valid value in the " + NAME + " attribute"); Class[] interfaces = this.discoverInterfaces(serviceName, element); BeanDefinitionBuilder importerBuilder = - AbstractOSGiServiceManagingParserUtil.defineServiceImporterFor(serviceName, null, registry, interfaces); - // + ControlBusOSGiConfigUtils.defineServiceImporterFor(serviceName, null, registry, interfaces); + // define binding listeners + ManagedList listenerDefinitions = new ManagedList(); if (element.hasAttribute(CONTROL_BUS)){ String busBeanName = element.getAttribute(CONTROL_BUS); - BeanDefinition listenerDefinition = - AbstractOSGiServiceManagingParserUtil.defineBindingListenerForBus(registry, importerBuilder, busBeanName); - importerBuilder.addPropertyValue("listeners", listenerDefinition); + BeanDefinition busBindingListener = + ControlBusOSGiConfigUtils.defineBindingMessageDistributor(registry, importerBuilder, busBeanName); + listenerDefinitions.add(busBindingListener); } + List dependentSources = ControlBusOSGiConfigUtils.discoverDependentElements(element, serviceName); + // add binding listener that will execute start/stop on dependent lifecycle beans + if (dependentSources.size() > 0){ + log.debug("Candidate beans dependent on '" + serviceName + + "' and to be managed by IntegrationServiceBindingListener: " + dependentSources); + BeanDefinitionBuilder lifecycleBindingManagerBuilder = + BeanDefinitionBuilder.genericBeanDefinition(LifecycleServiceManagingListener.class); + lifecycleBindingManagerBuilder.addPropertyValue("dependentSources", dependentSources); + listenerDefinitions.add(lifecycleBindingManagerBuilder.getBeanDefinition()); + } + + if (listenerDefinitions.size() > 0){ + importerBuilder.addPropertyValue("listeners", listenerDefinitions); + } - // registry.registerBeanDefinition(serviceName, importerBuilder.getBeanDefinition()); } /** @@ -131,24 +137,28 @@ public class ConfigParser implements BeanDefinitionParser { */ private void generateServiceExorterDefinition(String beanName, Element element, BeanDefinitionRegistry registry){ BeanDefinitionBuilder exportedElementBuilder = - AbstractOSGiServiceManagingParserUtil.defineServiceExporterFor(beanName, registry); - BeanDefinition controlBusMessageDistributorDefinition = - this.defineControlBusMessageDistributor(element, exportedElementBuilder, registry, beanName); + ControlBusOSGiConfigUtils.defineServiceExporterFor(beanName, registry); ManagedList listenerDefinitions = new ManagedList(); - listenerDefinitions.add(controlBusMessageDistributorDefinition); + if (element.hasAttribute(CONTROL_BUS)){ + BeanDefinition controlBusMessageDistributorDefinition = + this.defineControlBusMessageDistributor(element, exportedElementBuilder, registry, beanName); + listenerDefinitions.add(controlBusMessageDistributorDefinition); + } // NOTE: add more listeners here if needed - exportedElementBuilder.addPropertyValue("listeners", listenerDefinitions); + if (listenerDefinitions.size() > 0){ + exportedElementBuilder.addPropertyValue("listeners", listenerDefinitions); + } registry.registerBeanDefinition(beanName+EXPORTER_SUFFIX, exportedElementBuilder.getBeanDefinition()); } /** - * If element specifies 'control-bus' attribute, this method will register {@link ControlBusRegistrationMessageDistributionListener} + * If element specifies 'control-bus' attribute, this method will register + * {@link ControlBusRegistrationMessageDistributionListener} * which will send registration messages to the ControlBus */ private BeanDefinition defineControlBusMessageDistributor(Element originalElement, BeanDefinitionBuilder exportedElementBuilder, BeanDefinitionRegistry registry, String componentName) { - //String beanName = null; AbstractBeanDefinition listenerDefinition = null; if (originalElement.hasAttribute(CONTROL_BUS)){ String controlBusAttributeValue = originalElement.getAttribute(CONTROL_BUS); @@ -156,7 +166,7 @@ public class ConfigParser implements BeanDefinitionParser { log.trace("Adding registration listener for exported OSGi service for:" + componentName); listenerDefinition = - AbstractOSGiServiceManagingParserUtil.defineRegistrationListenerForBus(registry, exportedElementBuilder, controlBusAttributeValue); + ControlBusOSGiConfigUtils.defineRegistrationMessageDistributor(registry, exportedElementBuilder, controlBusAttributeValue); BeanDefinitionReaderUtils.registerWithGeneratedName(listenerDefinition, registry); } return listenerDefinition; @@ -177,8 +187,6 @@ public class ConfigParser implements BeanDefinitionParser { if (element.hasAttribute(TYPE)){ return SiTypeToJavaTypeMaper.mapSiType(element.getAttribute(TYPE)); } -// Element documentElement = element.getOwnerDocument().getDocumentElement(); -// NodeList children = documentElement.getChildNodes(); return null; } } diff --git a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/AbstractOSGiServiceManagingParserUtil.java b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ControlBusOSGiConfigUtils.java similarity index 55% rename from org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/AbstractOSGiServiceManagingParserUtil.java rename to org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ControlBusOSGiConfigUtils.java index a9e880d406..dbd8f305e0 100644 --- a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/AbstractOSGiServiceManagingParserUtil.java +++ b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ControlBusOSGiConfigUtils.java @@ -15,10 +15,18 @@ */ package org.springframework.integration.osgi.config.xml; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.integration.controlbus.ControlBus; +import org.springframework.integration.osgi.IntegrationOSGiConstants; import org.springframework.integration.osgi.extender.ControlBusBindingMessageDistributionListener; +import org.springframework.integration.osgi.extender.ControlBusListeningDecorator; import org.springframework.integration.osgi.extender.ControlBusRegistrationMessageDistributionListener; import org.springframework.osgi.service.exporter.support.AutoExport; import org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean; @@ -26,17 +34,42 @@ import org.springframework.osgi.service.importer.support.Cardinality; import org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean; import org.springframework.util.Assert; import org.springframework.util.StringUtils; - +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; /** - * Utility class which wraps Spring-DM factory bean creation for exporting and importing SI components as - * OSGi services. + * Helper class used by varilus bean parsers to create OSGi Factory bean definitions * * @author Oleg Zhurakousky * @since 2.0 */ -public class AbstractOSGiServiceManagingParserUtil { - +class ControlBusOSGiConfigUtils { + public final static String DEFAULT_BUS_GROUP_NAME = "DEFAULT_CONTROL_GROUP"; + public final static String DEFAULT_CONTROL_DIST_CHANNEL = "DEFAULT_CONTROL_DIST_CHANNEL"; + /** + * Will define a service importer (equivalent to ) for the ControlBus service. + * It will also register binding listener for this 'controlbus' reference which could be used + * to react to the life-cycle events of this 'controlbus' reference. + * For example: Individual service reference listeners coiuld use it to determine when the bus went away and + * no control events need to be dispatched to the bus. + */ + public static BeanDefinition registerImporterForControlBus(BeanDefinitionRegistry registry, String busGroupName){ + String filter = "(&(" + IntegrationOSGiConstants.OSGI_BEAN_NAME + "=" + busGroupName + "))"; + BeanDefinitionBuilder controlBusImporterBuilder = + defineServiceImporterFor(busGroupName, filter, registry, ControlBus.class); + + BeanDefinitionBuilder busListenerBuilder = + BeanDefinitionBuilder.genericBeanDefinition(ControlBusListeningDecorator.class); + AbstractBeanDefinition busListenerDefinition = busListenerBuilder.getBeanDefinition(); + //String listenerName = IntegrationOSGiConstants.BUS_LISTENER_PREFIX + busGroupName; + String listenerName = busGroupName; + registry.registerBeanDefinition(listenerName, busListenerDefinition); + controlBusImporterBuilder.addPropertyReference("listeners", listenerName); + BeanDefinition controlBusImporterDefinition = controlBusImporterBuilder.getBeanDefinition(); + BeanDefinitionReaderUtils.registerWithGeneratedName(controlBusImporterBuilder.getBeanDefinition(), registry); + return controlBusImporterDefinition; + } /** * Will define OSGi Service exporter BeanDefinitionBuilder for a bean identified by the 'beanName' * parameter as an OSGi Service. If 'publishInterfaces' parameter is not provided this exporter will @@ -86,10 +119,7 @@ public class AbstractOSGiServiceManagingParserUtil { serviceBuilder.addPropertyValue("serviceBeanName", beanName); //TODO: pf.setTimeout(timeoutInMillis) - -// OsgiServiceProxyFactoryBean b = null; -// b.setListeners(listeners) - + return serviceBuilder; } /** @@ -101,7 +131,7 @@ public class AbstractOSGiServiceManagingParserUtil { * @param busBeanName * @return */ - public static AbstractBeanDefinition defineRegistrationListenerForBus(BeanDefinitionRegistry registry, + public static AbstractBeanDefinition defineRegistrationMessageDistributor(BeanDefinitionRegistry registry, BeanDefinitionBuilder exporterBuilder, String busBeanName){ // create listener builder @@ -111,15 +141,21 @@ public class AbstractOSGiServiceManagingParserUtil { // if reference to the bus doesn't exist yet, create one // corresponding bean is not the actual bus but a ControlBusListentingDecorator if (!registry.containsBeanDefinition(busGroupName)){ - ControlBusOSGiUtils.registerImporterForControlBus(registry, busGroupName); + ControlBusOSGiConfigUtils.registerImporterForControlBus(registry, busGroupName); } listenerBuilder.addConstructorArgReference(busGroupName); AbstractBeanDefinition listenerDefinition = listenerBuilder.getBeanDefinition(); //exporterBuilder.addPropertyValue("listeners", listenerDefinition); return listenerDefinition; } - - public static AbstractBeanDefinition defineBindingListenerForBus(BeanDefinitionRegistry registry, + /** + * + * @param registry + * @param exporterBuilder + * @param busBeanName + * @return + */ + public static AbstractBeanDefinition defineBindingMessageDistributor(BeanDefinitionRegistry registry, BeanDefinitionBuilder exporterBuilder, String busBeanName){ // create listener builder @@ -129,12 +165,50 @@ public class AbstractOSGiServiceManagingParserUtil { // if reference to the bus doesn't exist yet, create one // corresponding bean is not the actual bus but a ControlBusListentingDecorator if (!registry.containsBeanDefinition(busGroupName)){ - ControlBusOSGiUtils.registerImporterForControlBus(registry, busGroupName); + ControlBusOSGiConfigUtils.registerImporterForControlBus(registry, busGroupName); } listenerBuilder.addConstructorArgReference(busGroupName); AbstractBeanDefinition listenerDefinition = listenerBuilder.getBeanDefinition(); - //exporterBuilder.addPropertyValue("listeners", listenerDefinition); return listenerDefinition; } + public static List discoverDependentElements(Element element, String attributeValue){ + List dependentSources = new ArrayList(); + Element documentElement = element.getOwnerDocument().getDocumentElement(); + NodeList nodes = documentElement.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE){ + Element elementNode = (Element) node; + String elementName = isDependent(elementNode, attributeValue); + if (StringUtils.hasText(elementName)){ + dependentSources.add(elementName); + } + } + } + return dependentSources; + } + // + private static String isDependent(Element element, String attributeValue){ + String[] monitoredAttributes = new String[]{"input-channel", + "output-channel", + "default-request-channel", + "default-reply-channel", + "channel"}; + for (String monitoredAttribute : monitoredAttributes) { + if (element.hasAttribute(monitoredAttribute) && + element.getAttribute(monitoredAttribute).equals(attributeValue) ){ + if (!element.hasAttribute("name") && !element.hasAttribute("id")){ + // need to generate id for elements that do not have one + // so they can be included in the listener dependentSource list + element.setAttribute("id", element.getTagName() + "-" + attributeValue + "-" + element.hashCode()); + element.setIdAttribute("id", true); + } + String elementName = StringUtils.hasText(element.getAttribute("id")) ? + element.getAttribute("id") : element.getAttribute("name"); + return elementName; + } + } + return null; + } } diff --git a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ControlBusOSGiUtils.java b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ControlBusOSGiUtils.java deleted file mode 100644 index c7beec3655..0000000000 --- a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/config/xml/ControlBusOSGiUtils.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2008 the original author or authors. - * - * Licensed 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.springframework.integration.osgi.config.xml; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.integration.controlbus.ControlBus; -import org.springframework.integration.osgi.IntegrationOSGiConstants; -import org.springframework.integration.osgi.extender.ControlBusListeningDecorator; - -/** - * TODO - insert COMMENT - * @author Oleg Zhurakousky - * @since 2.0 - */ -public class ControlBusOSGiUtils { - public final static String DEFAULT_BUS_GROUP_NAME = "DEFAULT_CONTROL_GROUP"; - public final static String DEFAULT_CONTROL_DIST_CHANNEL = "DEFAULT_CONTROL_DIST_CHANNEL"; - /** - * Will define a service importer (equivalent to ) for the ControlBus service. - * It will also register binding listener for this 'controlbus' reference which could be used - * to react to the life-cycle events of this 'controlbus' reference. - * For example: Individual service reference listeners coiuld use it to determine when the bus went away and - * no control events need to be dispatched to the bus. - */ - public static BeanDefinition registerImporterForControlBus(BeanDefinitionRegistry registry, String busGroupName){ - String filter = "(&(" + IntegrationOSGiConstants.OSGI_BEAN_NAME + "=" + busGroupName + "))"; - BeanDefinitionBuilder controlBusImporterBuilder = - AbstractOSGiServiceManagingParserUtil.defineServiceImporterFor(busGroupName, filter, registry, ControlBus.class); - - BeanDefinitionBuilder busListenerBuilder = - BeanDefinitionBuilder.genericBeanDefinition(ControlBusListeningDecorator.class); - AbstractBeanDefinition busListenerDefinition = busListenerBuilder.getBeanDefinition(); - //String listenerName = IntegrationOSGiConstants.BUS_LISTENER_PREFIX + busGroupName; - String listenerName = busGroupName; - registry.registerBeanDefinition(listenerName, busListenerDefinition); - controlBusImporterBuilder.addPropertyReference("listeners", listenerName); - BeanDefinition controlBusImporterDefinition = controlBusImporterBuilder.getBeanDefinition(); - BeanDefinitionReaderUtils.registerWithGeneratedName(controlBusImporterBuilder.getBeanDefinition(), registry); - return controlBusImporterDefinition; - } -} diff --git a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/ControlBusBindingMessageDistributionListener.java b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/ControlBusBindingMessageDistributionListener.java index 498ffc48a6..5840965e3a 100644 --- a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/ControlBusBindingMessageDistributionListener.java +++ b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/ControlBusBindingMessageDistributionListener.java @@ -25,7 +25,8 @@ import org.springframework.integration.osgi.IntegrationOSGiConstants; import org.springframework.osgi.service.importer.OsgiServiceLifecycleListener; /** - * TODO - insert COMMENT + * Will send control messages to the ControlBus when OSGi references are bound to the backing services. + * * @author Oleg Zhurakousky * @since 2.0 */ diff --git a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/ControlBusRegistrationMessageDistributionListener.java b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/ControlBusRegistrationMessageDistributionListener.java index 1023cea96a..1bfd42810d 100644 --- a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/ControlBusRegistrationMessageDistributionListener.java +++ b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/ControlBusRegistrationMessageDistributionListener.java @@ -25,8 +25,7 @@ import org.springframework.integration.osgi.IntegrationOSGiConstants; import org.springframework.osgi.service.exporter.OsgiServiceRegistrationListener; /** - * Service Registration listener which publishes registration life-cycle Messages - * to the {@link ControlBus} + * Will send control messages to the ControlBus when OSGi services are registered. * * @author Oleg Zhurakousky * @since 2.0 diff --git a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/IntegrationServiceBindingListener.java b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/IntegrationServiceBindingListener.java deleted file mode 100644 index a886c52599..0000000000 --- a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/IntegrationServiceBindingListener.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2002-2008 the original author or authors. - * - * Licensed 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.springframework.integration.osgi.extender; - -import java.util.List; -import java.util.Map; - -import org.springframework.context.Lifecycle; -import org.springframework.osgi.service.importer.OsgiServiceLifecycleListener; - -/** - * TODO - insert COMMENT - * @author Oleg Zhurakousky - * @since 2.0 - */ -public class IntegrationServiceBindingListener implements - OsgiServiceLifecycleListener { - private List dependentSources; - - /* (non-Javadoc) - * @see org.springframework.osgi.service.importer.OsgiServiceLifecycleListener#bind(java.lang.Object, java.util.Map) - */ - public void bind(Object service, Map properties) throws Exception { - for (Object dependentSource : dependentSources) { - if (dependentSource instanceof Lifecycle){ - Lifecycle lifecycle = (Lifecycle) dependentSource; - if (lifecycle.isRunning()){ - lifecycle.stop(); - } - } - } - } - - /* (non-Javadoc) - * @see org.springframework.osgi.service.importer.OsgiServiceLifecycleListener#unbind(java.lang.Object, java.util.Map) - */ - public void unbind(Object service, Map properties) throws Exception { - for (Object dependentSource : dependentSources) { - if (dependentSource instanceof Lifecycle){ - Lifecycle lifecycle = (Lifecycle) dependentSource; - if (!lifecycle.isRunning()){ - lifecycle.start(); - } - } - } - } - public List getDependentSources() { - return dependentSources; - } - - public void setDependentSources(List dependentSources) { - this.dependentSources = dependentSources; - } -} diff --git a/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/LifecycleServiceManagingListener.java b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/LifecycleServiceManagingListener.java new file mode 100644 index 0000000000..107c1f8171 --- /dev/null +++ b/org.springframework.integration.osgi/src/main/java/org/springframework/integration/osgi/extender/LifecycleServiceManagingListener.java @@ -0,0 +1,86 @@ +/* + * Copyright 2002-2008 the original author or authors. + * + * Licensed 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.springframework.integration.osgi.extender; + +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.context.Lifecycle; +import org.springframework.osgi.service.importer.OsgiServiceLifecycleListener; + +/** + * Will start/stop {@link Lifecycle} beans that are dependent on OSGi Service references that are being bound/unbound + * + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class LifecycleServiceManagingListener implements OsgiServiceLifecycleListener, BeanFactoryAware { + private static final Log log = LogFactory.getLog(LifecycleServiceManagingListener.class); + private List dependentSources; + private BeanFactory beanFactory; + + /* (non-Javadoc) + * @see org.springframework.osgi.service.importer.OsgiServiceLifecycleListener#bind(java.lang.Object, java.util.Map) + */ + public void bind(Object service, Map properties) throws Exception { + for (String dependentSource : dependentSources) { + Object bean = beanFactory.getBean(dependentSource); + if (bean instanceof Lifecycle){ + Lifecycle lifecycle = (Lifecycle) bean; + if (!lifecycle.isRunning()){ + log.debug("Staring: " + lifecycle); + lifecycle.start(); + } + } + } + } + + /* (non-Javadoc) + * @see org.springframework.osgi.service.importer.OsgiServiceLifecycleListener#unbind(java.lang.Object, java.util.Map) + */ + public void unbind(Object service, Map properties) throws Exception { + for (String dependentSource : dependentSources) { + Object bean = beanFactory.getBean(dependentSource); + if (bean instanceof Lifecycle){ + Lifecycle lifecycle = (Lifecycle) bean; + if (lifecycle.isRunning()){ + log.debug("Stopping: " + lifecycle); + //lifecycle.stop(); + DirectFieldAccessor lifecycleAccessor = new DirectFieldAccessor(lifecycle); + lifecycleAccessor.setPropertyValue("running", false); + } + } + } + } + public List getDependentSources() { + return dependentSources; + } + + public void setDependentSources(List dependentSources) { + this.dependentSources = dependentSources; + } + + + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } +} diff --git a/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/BusConfigParserTests.java b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/BusConfigParserTests.java index 7f6c0ec650..1b94c0113a 100644 --- a/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/BusConfigParserTests.java +++ b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/BusConfigParserTests.java @@ -40,11 +40,11 @@ public class BusConfigParserTests extends AbstractSIConfigBundleTestDeployer { ApplicationContext ac = this.deploySIConfig(bundleContext, "org/springframework/integration/osgi/config/xml/", "BusConfigParserTests-default.xml"); - ControlBus controlBus = (ControlBus) ac.getBean(ControlBusOSGiUtils.DEFAULT_BUS_GROUP_NAME); + ControlBus controlBus = (ControlBus) ac.getBean(ControlBusOSGiConfigUtils.DEFAULT_BUS_GROUP_NAME); assertNotNull(controlBus); ServiceReference[] sr = bundleContext.getServiceReferences(ControlBus.class.getName(), "(&(" + IntegrationOSGiConstants.OSGI_BEAN_NAME + "=" + - ControlBusOSGiUtils.DEFAULT_BUS_GROUP_NAME + "))"); + ControlBusOSGiConfigUtils.DEFAULT_BUS_GROUP_NAME + "))"); assertNotNull(sr); assertTrue(sr.length == 1); controlBus = (ControlBus) bundleContext.getService(sr[0]); diff --git a/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/ConfigParserImporterTests-default.xml b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/ConfigParserImporterTests-default.xml index 7a75815ab7..0cf2195d81 100644 --- a/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/ConfigParserImporterTests-default.xml +++ b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/ConfigParserImporterTests-default.xml @@ -12,5 +12,12 @@ + + + + + + + diff --git a/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/ConfigParserImporterTests.java b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/ConfigParserImporterTests.java index 879fff6ab5..567691ae64 100644 --- a/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/ConfigParserImporterTests.java +++ b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/config/xml/ConfigParserImporterTests.java @@ -16,12 +16,14 @@ package org.springframework.integration.osgi.config.xml; import static org.junit.Assert.assertNotNull; +import junit.framework.Assert; import org.junit.Test; import org.mockito.Mockito; import org.osgi.framework.BundleContext; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.Lifecycle; import org.springframework.integration.channel.SubscribableChannel; import org.springframework.integration.controlbus.ControlBus; import org.springframework.integration.core.Message; @@ -48,6 +50,7 @@ public class ConfigParserImporterTests extends AbstractSIConfigBundleTestDeploye SubscribableChannel channel = ac.getBean("channelA", SubscribableChannel.class); assertNotNull(channel); } + @Test(expected=ServiceUnavailableException.class) public void testBasicSIServiceConfigNoBackingServiceError() throws Exception { BundleContext bundleContext = SIBundleContextStub.getNewInstance(); @@ -81,6 +84,39 @@ public class ConfigParserImporterTests extends AbstractSIConfigBundleTestDeploye channel.send(message); Mockito.verify(handler, Mockito.times(1)).handleMessage(message); } + @Test + public void testBasicSIServiceConfigWithBackingServiceLate() throws Exception { + BundleContext bundleContext = SIBundleContextStub.getNewInstance(); + ApplicationContext ac = this.deploySIConfig(bundleContext, + "org/springframework/integration/osgi/config/xml/", + "ConfigParserImporterTests-default.xml"); + SubscribableChannel channel = ac.getBean("channelA", SubscribableChannel.class); + assertNotNull(channel); + Lifecycle sampleActivator = (Lifecycle) ac.getBean("sampleActivator"); + Assert.assertTrue(!sampleActivator.isRunning()); + try { + channel.send(new StringMessage("hello")); + Assert.fail("Should have failed with ServiceUnavailableException"); + } catch (ServiceUnavailableException e) {} + + + ConfigurableApplicationContext exporterAC = this.deploySIConfig(bundleContext, + "org/springframework/integration/osgi/config/xml/", + "ConfigParserImporterTests-exporter.xml"); + Assert.assertTrue(sampleActivator.isRunning()); + try { + channel.send(new StringMessage("hello")); + } catch (Exception e) { + Assert.fail("Should not have failed"); + } + exporterAC.close(); + Assert.assertTrue(!sampleActivator.isRunning()); + exporterAC = this.deploySIConfig(bundleContext, + "org/springframework/integration/osgi/config/xml/", + "ConfigParserImporterTests-exporter.xml"); + Assert.assertTrue(sampleActivator.isRunning()); + } + @Test public void testControlBusAttributeWithBusPresent() throws Exception { BundleContext bundleContext = SIBundleContextStub.getNewInstance(); @@ -100,7 +136,11 @@ public class ConfigParserImporterTests extends AbstractSIConfigBundleTestDeploye MessageHandler handler = Mockito.mock(MessageHandler.class); bus.subscribe(handler); exporterAC.close(); - // should be 3 notification messages sent to the bus + // should be 1 notification message sent to the bus Mockito.verify(handler, Mockito.times(1)).handleMessage((Message) Mockito.any()); } + + public static class TestBean{ + public void foo(Message message){} + } } diff --git a/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/SampleActivator.java b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/SampleActivator.java new file mode 100644 index 0000000000..d743e52332 --- /dev/null +++ b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/SampleActivator.java @@ -0,0 +1,10 @@ +package org.springframework.integration.osgi.demo; + +import org.springframework.integration.core.Message; + +public class SampleActivator { + + public void handle(Message message){ + System.out.println("Message: " + message); + } +} diff --git a/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/SpringDMnoOSGiDemo.java b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/SpringDMnoOSGiDemo.java new file mode 100644 index 0000000000..3c819e0a6e --- /dev/null +++ b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/SpringDMnoOSGiDemo.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2008 the original author or authors. + * + * Licensed 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.springframework.integration.osgi.demo; + +import org.junit.Test; +import org.osgi.framework.BundleContext; +import org.springframework.context.ApplicationContext; +import org.springframework.integration.channel.SubscribableChannel; +import org.springframework.integration.message.StringMessage; +import org.springframework.integration.osgi.AbstractSIConfigBundleTestDeployer; +import org.springframework.integration.osgi.stubs.SIBundleContextStub; +import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext; + +/** + * Simple demo shows how Spring-DM namespace support could be applied without using + * real OSGi runtime, by simply stubbing out OSGi BundleContext and passing it to + * OsgiBundleXmlApplicationContext + * + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class SpringDMnoOSGiDemo extends AbstractSIConfigBundleTestDeployer { + + public static void main(String[] args) throws Exception { + new SpringDMnoOSGiDemo().runDemo(); + } + /** + * + */ + public void runDemo() throws Exception { + BundleContext bundleContext = SIBundleContextStub.getNewInstance(); + ApplicationContext serviceExporterAC = this.deploySIConfig(bundleContext, + "org/springframework/integration/osgi/extender/", + "ac-service-exporter.xml"); + ApplicationContext serviceImporterAC = this.deploySIConfig(bundleContext, + "org/springframework/integration/osgi/extender/", + "ac-service-importer.xml"); + // get channel from the exporter AC and send message + SubscribableChannel channel = serviceExporterAC.getBean("input", SubscribableChannel.class); + channel.send(new StringMessage("Hello String-DM without OSGi")); + // the output coming out of Service Activator defined in importer AC should display the message + } + +} diff --git a/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/ac-service-exporter.xml b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/ac-service-exporter.xml new file mode 100644 index 0000000000..1ab7f93f89 --- /dev/null +++ b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/ac-service-exporter.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/ac-service-importer.xml b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/ac-service-importer.xml new file mode 100644 index 0000000000..1c1740ad8f --- /dev/null +++ b/org.springframework.integration.osgi/src/test/java/org/springframework/integration/osgi/demo/ac-service-importer.xml @@ -0,0 +1,18 @@ + + + + + + + + + +