You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by da...@apache.org on 2007/09/30 23:02:25 UTC

svn commit: r580786 - in /cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main: java/org/apache/cocoon/springosgi/config/ java/org/apache/cocoon/springosgi/service/exporter/ resources/org/apache/cocoon/springosgi/config/

Author: danielf
Date: Sun Sep 30 14:02:24 2007
New Revision: 580786

URL: http://svn.apache.org/viewvc?rev=580786&view=rev
Log:
Spring-OSGi doesn't contain any support for beans with prototype scope. This is  mainly because there is no direct mapping between the prototype scope and any OSGi concepts. In OSGi services are either singletons at global level or at bundle level. The only reasonable mapping, AFAICS, is to export a factory bean as an OSGi service.

As Cocoon uses prototypes in many places I have extended Spring-OSGi with support for exporting a factory bean instead of the bean by setting the attribute "factory-export" to true. In this case a SmartFactoryBean is exported and the classes of the contained bean is put in the "org.springframework.osgi.beanclasses" property.

The import part is not written yet.

The code contains a lot of copy and modify from Spring-OSGi as some of the involved classes wasn't really extendible. The idea is to try to contribute it back to Spring-OSGi.

Added:
    cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/ServiceBeanDefinitionParser.java   (with props)
    cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/service/exporter/
    cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/service/exporter/OsgiServiceFactoryBean.java   (with props)
Modified:
    cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/OsgiNamespaceHandler.java
    cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/resources/org/apache/cocoon/springosgi/config/spring-osgi.xsd

Modified: cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/OsgiNamespaceHandler.java
URL: http://svn.apache.org/viewvc/cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/OsgiNamespaceHandler.java?rev=580786&r1=580785&r2=580786&view=diff
==============================================================================
--- cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/OsgiNamespaceHandler.java (original)
+++ cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/OsgiNamespaceHandler.java Sun Sep 30 14:02:24 2007
@@ -31,5 +31,7 @@
 	 */
 	public void init() {
         registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
+
+        registerBeanDefinitionParser("service", new ServiceBeanDefinitionParser());
 	}
 }

Added: cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/ServiceBeanDefinitionParser.java
URL: http://svn.apache.org/viewvc/cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/ServiceBeanDefinitionParser.java?rev=580786&view=auto
==============================================================================
--- cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/ServiceBeanDefinitionParser.java (added)
+++ cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/ServiceBeanDefinitionParser.java Sun Sep 30 14:02:24 2007
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2002-2005 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.apache.cocoon.springosgi.config;
+
+import java.util.Set;
+
+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.xml.AbstractBeanDefinitionParser;
+import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.core.Conventions;
+import org.springframework.osgi.config.ParserUtils;
+import org.springframework.osgi.config.ParserUtils.AttributeCallback;
+import org.apache.cocoon.springosgi.service.exporter.OsgiServiceFactoryBean;
+import org.springframework.util.xml.DomUtils;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Copied from Spring-OSGi as it is package local and is needed to be extended for exporting factories. 
+ * 
+ * BeanDefinitionParser for service element found in the osgi namespace.
+ * 
+ * @author Costin Leau
+ * @author Hal Hildebrand
+ * @author Andy Piper
+ */
+class ServiceBeanDefinitionParser extends AbstractBeanDefinitionParser {
+    private static final String TARGET_BEAN_NAME = "targetBeanName";
+
+    public static final String ACTIVATION_ID = "activation-method";
+
+    public static final String DEACTIVATION_ID = "deactivation-method";
+
+    public static final String INTERFACES_ID = "interfaces";
+
+    public static final String INTERFACE = "interface";
+
+    public static final String PROPS_ID = "service-properties";
+
+    public static final String REF = "ref";
+    
+    public static final String FACTORY_EXPORT = "factory-export";
+
+    protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
+        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(OsgiServiceFactoryBean.class);
+
+        // parse attributes
+        ParserUtils.parseCustomAttributes(element, builder, new AttributeCallback() {
+
+            /*
+             * (non-Javadoc)
+             * 
+             * @see org.springframework.osgi.config.ParserUtils.AttributeCallback#process(org.w3c.dom.Element,
+             * org.w3c.dom.Attr)
+             */
+            public void process(Element parent, Attr attribute, BeanDefinitionBuilder bldr) {
+                String name = attribute.getLocalName();
+
+                if (INTERFACE.equals(name)) {
+                    bldr.addPropertyValue(INTERFACES_ID, attribute.getValue());
+                }
+                else if (REF.equals(name)) {
+                    ;
+                }
+                // fallback mechanism
+                else {
+                    bldr.addPropertyValue(Conventions.attributeNameToPropertyName(name), attribute.getValue());
+                }
+            }
+        });
+
+        // determine nested/referred beans
+        Object target = null;
+        if (element.hasAttribute(REF))
+            target = new RuntimeBeanReference(element.getAttribute(REF));
+
+        NodeList nl = element.getChildNodes();
+
+        // parse all sub elements
+        for (int i = 0; i < nl.getLength(); i++) {
+            Node node = nl.item(i);
+            if (node instanceof Element) {
+                Element subElement = (Element) node;
+
+                // osgi:interfaces
+                if (INTERFACES_ID.equals(subElement.getLocalName())) {
+                    // check shortcut
+                    if (element.hasAttribute(INTERFACE)) {
+                        parserContext.getReaderContext().error(
+                                "either 'interface' attribute or <intefaces> sub-element has be specified", element);
+                    }
+                    Set interfaces = parserContext.getDelegate().parseSetElement(subElement,
+                            builder.getBeanDefinition());
+                    builder.addPropertyValue(INTERFACES_ID, interfaces);
+                }
+
+                // osgi:service-properties
+                else if (PROPS_ID.equals(subElement.getLocalName())) {
+                    if (DomUtils.getChildElementsByTagName(subElement, BeanDefinitionParserDelegate.ENTRY_ELEMENT).size()>0) {
+                        Object props = parserContext.getDelegate().parseMapElement(subElement, builder.getRawBeanDefinition());
+                        builder.addPropertyValue(Conventions.attributeNameToPropertyName(PROPS_ID), props);
+                    }
+                    else if (DomUtils.getChildElementsByTagName(subElement, BeanDefinitionParserDelegate.PROP_ELEMENT).size()>0) {
+                        Object props = parserContext.getDelegate().parsePropsElement(subElement);
+                        builder.addPropertyValue(Conventions.attributeNameToPropertyName(PROPS_ID), props);
+                    }
+                    else {
+                        parserContext.getReaderContext().error("Invalid service property type", subElement);
+                    }
+                }
+                // nested bean reference/declaration
+                else {
+                    if (element.hasAttribute(REF))
+                        parserContext.getReaderContext().error(
+                                "nested bean definition/reference cannot be used when attribute 'ref' is specified",
+                                element);
+                    target = parserContext.getDelegate().parsePropertySubElement(subElement,
+                            builder.getBeanDefinition());
+                }
+            }
+        }
+
+        // do we have a bean reference ?
+        if (target instanceof RuntimeBeanReference) {
+            builder.addPropertyValue(TARGET_BEAN_NAME, ((RuntimeBeanReference) target).getBeanName());
+        }
+        
+        // handle factory exports: no target and not allowed for nested bean definitions
+        if (element.hasAttribute(FACTORY_EXPORT) && "true".equals(element.getAttribute(FACTORY_EXPORT))) {
+            if (!element.hasAttribute(REF))
+                parserContext.getReaderContext().error(
+                        "nested bean definition/reference cannot be used when attribute 'factory-export' is specified",
+                        element);
+        } else {
+            builder.addPropertyValue("target", target);
+        }
+        
+        return builder.getBeanDefinition();
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#shouldGenerateIdAsFallback()
+     */
+    protected boolean shouldGenerateIdAsFallback() {
+        return true;
+    }
+
+}
\ No newline at end of file

Propchange: cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/ServiceBeanDefinitionParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/config/ServiceBeanDefinitionParser.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/service/exporter/OsgiServiceFactoryBean.java
URL: http://svn.apache.org/viewvc/cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/service/exporter/OsgiServiceFactoryBean.java?rev=580786&view=auto
==============================================================================
--- cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/service/exporter/OsgiServiceFactoryBean.java (added)
+++ cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/service/exporter/OsgiServiceFactoryBean.java Sun Sep 30 14:02:24 2007
@@ -0,0 +1,635 @@
+/*
+ * Copyright 2002-2006 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.apache.cocoon.springosgi.service.exporter;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.SmartFactoryBean;
+import org.springframework.beans.factory.config.BeanReferenceFactoryBean;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.core.Constants;
+import org.springframework.core.Ordered;
+import org.springframework.osgi.context.BundleContextAware;
+import org.springframework.osgi.context.OsgiBundleScope;
+import org.springframework.osgi.internal.service.exporter.AbstractListenerAwareExporter;
+import org.springframework.osgi.internal.util.DebugUtils;
+import org.springframework.osgi.service.BeanNameServicePropertiesResolver;
+import org.springframework.osgi.service.OsgiServicePropertiesResolver;
+import org.springframework.osgi.service.exporter.ExportClassLoadingOptions;
+import org.springframework.osgi.service.interceptor.OsgiServiceTCCLInvoker;
+import org.springframework.osgi.util.ClassUtils;
+import org.springframework.osgi.util.MapBasedDictionary;
+import org.springframework.osgi.util.OsgiServiceUtils;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Copied from Spring-OSGi to be able to extend it with functionality for exporting
+ * factories instead of beans.
+ * 
+ * FactoryBean that transparently publishes other beans in the same application
+ * context as OSGi services returning the ServiceRegistration for the given
+ * object.
+ * 
+ * <p/> The service properties used when publishing the service are determined
+ * by the OsgiServicePropertiesResolver. The default implementation uses
+ * <ul>
+ * <li>BundleSymbolicName=&lt;bundle symbolic name&gt;</li>
+ * <li>BundleVersion=&lt;bundle version&gt;</li>
+ * <li>org.springframework.osgi.beanname="&lt;bean name&gt;</li>
+ * </ul>
+ * 
+ * @author Adrian Colyer
+ * @author Costin Leau
+ * @author Hal Hildebrand
+ * @author Andy Piper
+ * 
+ */
+public class OsgiServiceFactoryBean extends AbstractListenerAwareExporter implements BeanFactoryAware, DisposableBean,
+        BundleContextAware, FactoryBean, Ordered, BeanClassLoaderAware, BeanNameAware {
+
+    /**
+     * ServiceFactory used for publishing the service beans. Acts as a a wrapper
+     * around special beans (such as ServiceFactory) and delegates to the
+     * container each time a bundle requests the service for the first time.
+     * 
+     */
+    private class PublishingServiceFactory implements ServiceFactory {
+
+        // used if the published bean is itself a ServiceFactory
+        private ServiceFactory serviceFactory;
+
+        private Class[] classes;
+
+        protected PublishingServiceFactory(Class[] classes) {
+            this.classes = classes;
+        }
+
+        public Object getService(Bundle bundle, ServiceRegistration serviceRegistration) {
+
+            Object bean = (target != null ? target : beanFactory.getBean(targetBeanName));
+
+            // if we get a ServiceFactory, call its method
+            if (bean instanceof ServiceFactory) {
+                serviceFactory = (ServiceFactory) bean;
+                bean = serviceFactory.getService(bundle, serviceRegistration);
+            }
+
+            // FIXME: what's the spec on this one?
+            if (StringUtils.hasText(activationMethod)) {
+                Method m = BeanUtils.resolveSignature(activationMethod, bean.getClass());
+                try {
+                    ReflectionUtils.invokeMethod(m, bean);
+                }
+                catch (RuntimeException ex) {
+                    log.error("Activation method for [" + bean + "] threw an exception", ex);
+                }
+            }
+
+            if (contextClassloaderManagementStrategy == ExportClassLoadingOptions.SERVICE_PROVIDER) {
+                return wrapWithClassLoaderManagingProxy(bean, classes);
+            }
+            else {
+                return bean;
+            }
+        }
+
+        public void ungetService(Bundle bundle, ServiceRegistration serviceRegistration, Object bean) {
+            // FIXME: what's the spec on this one?
+            if (StringUtils.hasText(deactivationMethod)) {
+                Method m = BeanUtils.resolveSignature(deactivationMethod, bean.getClass());
+                try {
+                    ReflectionUtils.invokeMethod(m, bean);
+                }
+                catch (RuntimeException ex) {
+                    log.error("Deactivation method for [" + bean + "] threw an exception", ex);
+                }
+            }
+
+            if (serviceFactory != null)
+                serviceFactory.ungetService(bundle, serviceRegistration, bean);
+        }
+    }
+
+    /**
+     * Decorating {@link org.osgi.framework.ServiceFactory} used for supporting
+     * 'bundle' scoped beans.
+     * 
+     * @author Costin Leau
+     * 
+     */
+    private class BundleScopeServiceFactory implements ServiceFactory {
+        private ServiceFactory decoratedServiceFactory;
+
+        private Runnable destructionCallback;
+
+        private Class[] classes;
+
+        public BundleScopeServiceFactory(ServiceFactory serviceFactory, Class[] classes) {
+            Assert.notNull(serviceFactory);
+            this.decoratedServiceFactory = serviceFactory;
+            this.classes = classes;
+        }
+
+        public Object getService(Bundle bundle, ServiceRegistration registration) {
+            // inform OsgiBundleScope (just place a boolean)
+            OsgiBundleScope.CALLING_BUNDLE.set(Boolean.TRUE);
+            try {
+                Object obj = decoratedServiceFactory.getService(bundle, registration);
+                // retrieve destructionCallback if any
+                Object callback = OsgiBundleScope.CALLING_BUNDLE.get();
+                if (callback != null && callback instanceof Runnable)
+                    this.destructionCallback = (Runnable) callback;
+                if (contextClassloaderManagementStrategy == ExportClassLoadingOptions.SERVICE_PROVIDER) {
+                    obj = wrapWithClassLoaderManagingProxy(obj, classes);
+                }
+                return obj;
+            }
+            finally {
+                // clean ThreadLocal
+                OsgiBundleScope.CALLING_BUNDLE.set(null);
+            }
+        }
+
+        public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+            decoratedServiceFactory.ungetService(bundle, registration, service);
+            if (destructionCallback != null)
+                destructionCallback.run();
+        }
+
+    }
+
+    private static final Log log = LogFactory.getLog(OsgiServiceFactoryBean.class);
+
+    public static final int AUTO_EXPORT_DISABLED = 0;
+
+    public static final int AUTO_EXPORT_INTERFACES = 1;
+
+    public static final int AUTO_EXPORT_CLASS_HIERARCHY = 2;
+
+    public static final int AUTO_EXPORT_ALL = AUTO_EXPORT_INTERFACES | AUTO_EXPORT_CLASS_HIERARCHY;
+
+    private static final String AUTO_EXPORT_PREFIX = "AUTO_EXPORT_";
+
+    private static final Constants EXPORTING_OPTIONS = new Constants(OsgiServiceFactoryBean.class);
+
+    public static final Object BEAN_CLASSES_PROPERTY_KEY = "org.springframework.osgi.beanclasses";
+    
+    private BundleContext bundleContext;
+
+    private OsgiServicePropertiesResolver propertiesResolver;
+
+    private BeanFactory beanFactory;
+
+    private ServiceRegistration serviceRegistration;
+
+    private Map serviceProperties;
+
+    private int ranking;
+
+    private String targetBeanName;
+
+    private Class[] interfaces;
+
+    private int autoExportMode = AUTO_EXPORT_DISABLED;
+
+    private String activationMethod;
+
+    private String deactivationMethod;
+
+    private int contextClassloaderManagementStrategy = ExportClassLoadingOptions.UNMANAGED;
+
+    private Object target;
+
+    private int order = Ordered.LOWEST_PRECEDENCE;
+
+    private ClassLoader classLoader;
+
+    /** exporter bean name */
+    private String beanName;
+
+    private boolean factoryExport = false;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+     */
+    public void afterPropertiesSet() throws Exception {
+        Assert.notNull(beanFactory, "required property 'beanFactory' has not been set");
+        Assert.notNull(bundleContext, "required property 'bundleContext' has not been set");
+
+        // Create a factory bean that creates the bean with targetBeanName using the current bean factory 
+        if (factoryExport) {
+            BeanReferenceFactoryBean factory = new BeanReferenceFactoryBean();
+            factory.setTargetBeanName(targetBeanName);
+            factory.setBeanFactory(beanFactory);
+            target = factory;
+        }
+        
+        Assert.notNull(target, "'target' property is required");
+
+        if (propertiesResolver == null) {
+            propertiesResolver = new BeanNameServicePropertiesResolver();
+            ((BeanNameServicePropertiesResolver) propertiesResolver).setBundleContext(bundleContext);
+        }
+
+        // sanity check
+        if (interfaces == null)
+            interfaces = new Class[0];
+
+        super.afterPropertiesSet();
+    }
+
+    /**
+     * Proxy the target object with a proxy that manages the context
+     * classloader.
+     * 
+     * @param target
+     * @return
+     */
+    private Object wrapWithClassLoaderManagingProxy(final Object target, Class[] interfaces) {
+        ProxyFactory factory = new ProxyFactory();
+
+        // mold the proxy
+        for (int i = 0; i < interfaces.length; i++) {
+            factory.addInterface(interfaces[i]);
+        }
+
+        factory.addAdvice(new OsgiServiceTCCLInvoker(target, classLoader));
+
+        try {
+            return factory.getProxy(classLoader);
+        }
+        catch (NoClassDefFoundError ncdfe) {
+            if (log.isWarnEnabled()) {
+                DebugUtils.debugNoClassDefFoundWhenProxying(ncdfe, bundleContext, this.interfaces);
+            }
+            throw ncdfe;
+        }
+    }
+
+    private Dictionary mergeServiceProperties(String beanName) {
+        MapBasedDictionary props = new MapBasedDictionary(propertiesResolver.getServiceProperties(beanName));
+
+        props.putAll((Map) props);
+
+        // add service properties
+        if (serviceProperties != null)
+            props.putAll(serviceProperties);
+
+        if (ranking != 0) {
+            props.put(org.osgi.framework.Constants.SERVICE_RANKING, new Integer(ranking));
+        }
+        return props;
+    }
+
+    /**
+     * Return an array of classes for the given bean that have been discovered
+     * using the autoExportMode.
+     * 
+     * @param clazz
+     * @return
+     */
+    protected Class[] autoDetectClassesForPublishing(Class clazz) {
+
+        Class[] classes;
+
+        switch (autoExportMode) {
+        case AUTO_EXPORT_ALL:
+            classes = org.springframework.osgi.util.ClassUtils.getClassHierarchy(clazz,
+                org.springframework.osgi.util.ClassUtils.INCLUDE_ALL_CLASSES);
+            break;
+        case AUTO_EXPORT_CLASS_HIERARCHY:
+            classes = org.springframework.osgi.util.ClassUtils.getClassHierarchy(clazz,
+                org.springframework.osgi.util.ClassUtils.INCLUDE_CLASS_HIERARCHY);
+            break;
+        case AUTO_EXPORT_INTERFACES:
+            classes = org.springframework.osgi.util.ClassUtils.getClassHierarchy(clazz,
+                org.springframework.osgi.util.ClassUtils.INCLUDE_INTERFACES);
+            break;
+        default:
+            classes = new Class[0];
+            break;
+        }
+        if (log.isTraceEnabled())
+            log.trace("autodetect mode [" + autoExportMode + "] discovered on class [" + clazz + "] classes "
+                    + ObjectUtils.nullSafeToString(classes));
+
+        return classes;
+    }
+
+    /**
+     * Publish the given object as an OSGi service. It simply assembles the
+     * classes required for publishing and then delegates the actual
+     * registration to a dedicated method.
+     * 
+     * @param beanClass
+     * @param serviceProperties
+     */
+    public void registerService() {
+
+        // get the type of the bean and not type of the factory for factory export
+        Class serviceClass =
+            factoryExport ? ((SmartFactoryBean)target).getObjectType() : target.getClass();
+        // if we have a nested bean / non-Spring managed object
+        String beanName = (!StringUtils.hasText(targetBeanName) ? ObjectUtils.getIdentityHexString(target)
+                : targetBeanName);
+
+        Dictionary serviceProperties = mergeServiceProperties(beanName);
+
+        Class[] intfs = interfaces;
+        Class[] autoDetectedClasses = autoDetectClassesForPublishing(serviceClass);
+
+        // filter duplicates
+        Set classes = new LinkedHashSet(intfs.length + autoDetectedClasses.length);
+
+        CollectionUtils.mergeArrayIntoCollection(intfs, classes);
+        CollectionUtils.mergeArrayIntoCollection(autoDetectedClasses, classes);
+
+        Class[] mergedClasses = (Class[]) classes.toArray(new Class[classes.size()]);
+
+         ServiceRegistration reg = registerService(mergedClasses, serviceProperties);
+        
+         serviceRegistration = notifyListeners((Map) serviceProperties, reg);
+    }
+
+    /**
+     * Registration method.
+     * 
+     * @param classes
+     * @param serviceProperties
+     * @return the ServiceRegistration
+     */
+    protected ServiceRegistration registerService(Class[] classes, Dictionary serviceProperties) {
+        Assert.notEmpty(
+            classes,
+            "at least one class has to be specified for exporting (if autoExport is enabled then maybe the object doesn't implement any interface)");
+
+        Class beanClass = (target == null || factoryExport ? beanFactory.getType(targetBeanName) : target.getClass());
+
+        // filter classes based on visibility
+        ClassLoader beanClassLoader = ClassUtils.getClassLoader(beanClass);
+
+        Class[] visibleClasses = ClassUtils.getVisibleClasses(classes, beanClassLoader);
+
+        // create an array of classnames (used for registering the service)
+        String[] names = org.springframework.osgi.util.ClassUtils.toStringArray(visibleClasses);
+
+        // sort the names in alphabetical order (eases debugging)
+        Arrays.sort(names);
+        
+        // For factory export the classes of the bean is put in a special property in the service 
+        // properties and just the factory class is used as class in the registration
+        if (factoryExport) {
+            serviceProperties.put(BEAN_CLASSES_PROPERTY_KEY, names);
+            Class[] factoryClass = new Class[] {SmartFactoryBean.class};
+            names = org.springframework.osgi.util.ClassUtils.toStringArray(factoryClass);
+        }
+
+        log.info("Publishing service under classes [" + ObjectUtils.nullSafeToString(names) + "]");
+
+        ServiceFactory serviceFactory = new PublishingServiceFactory(visibleClasses);
+
+        if (isBeanBundleScoped())
+            serviceFactory = new BundleScopeServiceFactory(serviceFactory, visibleClasses);
+
+        return bundleContext.registerService(names, serviceFactory, serviceProperties);
+    }
+
+    protected boolean isBeanBundleScoped() {
+        boolean bundleScoped = false;
+        // if we do have a bundle scope, use ServiceFactory decoration
+        if (targetBeanName != null) {
+            if (beanFactory instanceof ConfigurableListableBeanFactory) {
+                String beanScope = ((ConfigurableListableBeanFactory) beanFactory).getMergedBeanDefinition(
+                    targetBeanName).getScope();
+                bundleScoped = OsgiBundleScope.SCOPE_NAME.equals(beanScope);
+            }
+            else
+                // if for some reason, the passed in BeanFactory can't be
+                // queried for scopes and we do
+                // have a bean reference, apply scoped decoration.
+                bundleScoped = true;
+        }
+        return bundleScoped;
+    }
+
+    public void setBeanClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    public Object getObject() throws Exception {
+        return serviceRegistration;
+    }
+
+    public Class getObjectType() {
+        return (serviceRegistration != null ? serviceRegistration.getClass() : ServiceRegistration.class);
+    }
+
+    public boolean isSingleton() {
+        return false;
+    }
+
+    public void destroy() {
+        // stop published service
+        stop();
+    }
+
+    public void unregisterService() {
+        unregisterService(serviceRegistration);
+        serviceRegistration = null;
+    }
+
+    /**
+     * Unregisters (literally stops) a service.
+     * 
+     * @param registration
+     */
+    protected void unregisterService(ServiceRegistration registration) {
+        if (OsgiServiceUtils.unregisterService(registration)) {
+            log.info("Unregistered service [" + registration + "]");
+        }
+    }
+
+    /**
+     * Set the context classloader management strategy to use when invoking
+     * operations on the exposed target bean
+     * @param classloaderManagementOption
+     */
+    public void setContextClassloader(String classloaderManagementOption) {
+        this.contextClassloaderManagementStrategy = ExportClassLoadingOptions.getFromString(classloaderManagementOption);
+    }
+
+    /**
+     * Export the given object as an OSGi service. Normally used when the
+     * exported service is a nested bean or an object not managed by the Spring
+     * container.
+     * 
+     * @param target The target to set.
+     */
+    public void setTarget(Object target) {
+        this.target = target;
+    }
+
+    /**
+     * Set the autoexport mode to use. This allows the exporter to use the
+     * target class hierarchy and/or interfaces for registering the OSGi
+     * service. By default, autoExport is disabled (@link
+     * {@link #AUTO_EXPORT_DISABLED}
+     * 
+     * @see #setAutoExportName(String)
+     * @see #AUTO_EXPORT_DISABLED
+     * @see #AUTO_EXPORT_INTERFACES
+     * @see #AUTO_EXPORT_CLASS_HIERARCHY
+     * @see #AUTO_EXPORT_ALL
+     * 
+     * @param autoExportMode the auto export mode as an int
+     */
+    public void setAutoExportNumber(int autoExportMode) {
+        if (!EXPORTING_OPTIONS.getValues(AUTO_EXPORT_PREFIX).contains(new Integer(autoExportMode)))
+            throw new IllegalArgumentException("invalid autoExportMode");
+        this.autoExportMode = autoExportMode;
+    }
+
+    /**
+     * Set the autoexport mode to use.
+     * @see #setAutoExportNumber(int)
+     * 
+     * @param autoExportMode
+     */
+    public void setAutoExport(String autoExportMode) {
+        if (autoExportMode != null) {
+            if (!autoExportMode.startsWith(AUTO_EXPORT_PREFIX))
+                autoExportMode = AUTO_EXPORT_PREFIX + autoExportMode;
+            this.autoExportMode = EXPORTING_OPTIONS.asNumber(autoExportMode).intValue();
+        }
+    }
+
+    public String getActivationMethod() {
+        return activationMethod;
+    }
+
+    public void setActivationMethod(String activationMethod) {
+        this.activationMethod = activationMethod;
+    }
+
+    public String getDeactivationMethod() {
+        return deactivationMethod;
+    }
+
+    public void setDeactivationMethod(String deactivationMethod) {
+        this.deactivationMethod = deactivationMethod;
+    }
+
+    public Map getServiceProperties() {
+        return serviceProperties;
+    }
+
+    public void setServiceProperties(Map serviceProperties) {
+        this.serviceProperties = serviceProperties;
+    }
+
+    public int getRanking() {
+        return ranking;
+    }
+
+    public void setRanking(int ranking) {
+        this.ranking = ranking;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
+     */
+    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+        this.beanFactory = beanFactory;
+    }
+
+    public void setBundleContext(BundleContext context) {
+        this.bundleContext = context;
+    }
+
+    /**
+     * @return Returns the resolver.
+     */
+    public OsgiServicePropertiesResolver getResolver() {
+        return this.propertiesResolver;
+    }
+
+    /**
+     * @param resolver The resolver to set.
+     */
+    public void setResolver(OsgiServicePropertiesResolver resolver) {
+        this.propertiesResolver = resolver;
+    }
+
+    public void setTargetBeanName(String name) {
+        this.targetBeanName = name;
+    }
+
+    public void setInterfaces(Class[] serviceInterfaces) {
+        this.interfaces = serviceInterfaces;
+    }
+
+    /**
+     * @return Returns the order.
+     */
+    public int getOrder() {
+        return order;
+    }
+
+    /**
+     * @param order The order to set.
+     */
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+
+    public void setBeanName(String name) {
+        this.beanName = name;
+    }
+
+    public void setFactoryExport(boolean factoryExport) {
+        this.factoryExport = factoryExport;
+    }
+}

Propchange: cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/service/exporter/OsgiServiceFactoryBean.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/java/org/apache/cocoon/springosgi/service/exporter/OsgiServiceFactoryBean.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/resources/org/apache/cocoon/springosgi/config/spring-osgi.xsd
URL: http://svn.apache.org/viewvc/cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/resources/org/apache/cocoon/springosgi/config/spring-osgi.xsd?rev=580786&r1=580785&r2=580786&view=diff
==============================================================================
--- cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/resources/org/apache/cocoon/springosgi/config/spring-osgi.xsd (original)
+++ cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main/resources/org/apache/cocoon/springosgi/config/spring-osgi.xsd Sun Sep 30 14:02:24 2007
@@ -48,7 +48,17 @@
         <xsd:complexContent>
             <xsd:extension base="springosgi:Treference">
                 <xsd:attribute name="key-property" type="xsd:string" use="optional"/>
-                <xsd:attribute name="strip-prefix" type="xsd:string" use="optional"/>
+                <xsd:attribute name="strip-prefix" type="xsd:string" use="optional" default="true"/>
+            </xsd:extension>
+        </xsd:complexContent>
+    </xsd:complexType>
+
+    <xsd:element name="service" type="Tservice"/>
+
+    <xsd:complexType name="Tservice">
+        <xsd:complexContent>
+            <xsd:extension base="springosgi:Tservice">
+                <xsd:attribute name="factory-export" type="xsd:boolean" use="optional" default="false"/>
             </xsd:extension>
         </xsd:complexContent>
     </xsd:complexType>



Re: svn commit: r580786 - in /cocoon/whiteboard/osgi/core/cocoon-spring-osgi/cocoon-spring-osgi-impl/src/main: java/org/apache/cocoon/springosgi/config/ java/org/apache/cocoon/springosgi/service/exporter/ resources/org/apache/cocoon/springosgi/config/

Posted by Joerg Heinicke <jo...@gmx.de>.
On 30.09.2007 17:02 Uhr, danielf@apache.org wrote:

> Author: danielf
> Date: Sun Sep 30 14:02:24 2007
> New Revision: 580786
> 
> URL: http://svn.apache.org/viewvc?rev=580786&view=rev
> Log: Spring-OSGi doesn't contain any support for beans with prototype
> scope. This is  mainly because there is no direct mapping between the
> prototype scope and any OSGi concepts. In OSGi services are either
> singletons at global level or at bundle level. The only reasonable
> mapping, AFAICS, is to export a factory bean as an OSGi service.
> 
> As Cocoon uses prototypes in many places I have extended Spring-OSGi
> with support for exporting a factory bean instead of the bean by
> setting the attribute "factory-export" to true. In this case a
> SmartFactoryBean is exported and the classes of the contained bean is
> put in the "org.springframework.osgi.beanclasses" property.
> 
> The import part is not written yet.
> 
> The code contains a lot of copy and modify from Spring-OSGi as some
> of the involved classes wasn't really extendible. The idea is to try
> to contribute it back to Spring-OSGi.

I don't know if it applies here at all, but in standard Spring to get
access to the factory bean instead of the bean itself you prefix the
bean name with an ampersand. Is it reasonable to go with that approach 
here as well compared to your new attribute "factory-export"?

Joerg