You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by gn...@apache.org on 2012/07/24 01:06:02 UTC

svn commit: r1364831 - in /aries/trunk/blueprint/blueprint-cm: ./ src/main/java/org/apache/aries/blueprint/compendium/cm/ src/test/ src/test/java/ src/test/java/org/ src/test/java/org/apache/ src/test/java/org/apache/aries/ src/test/java/org/apache/ari...

Author: gnodet
Date: Mon Jul 23 23:06:01 2012
New Revision: 1364831

URL: http://svn.apache.org/viewvc?rev=1364831&view=rev
Log:
[ARIES-878] Threading issues with <cm:managed-properties />

Added:
    aries/trunk/blueprint/blueprint-cm/src/test/
    aries/trunk/blueprint/blueprint-cm/src/test/java/
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/BaseTest.java
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/Foo.java
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/FooInterface.java
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/Helper.java
    aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.java
    aries/trunk/blueprint/blueprint-cm/src/test/resources/
    aries/trunk/blueprint/blueprint-cm/src/test/resources/log4j.properties
    aries/trunk/blueprint/blueprint-cm/src/test/resources/org/
    aries/trunk/blueprint/blueprint-cm/src/test/resources/org/apache/
    aries/trunk/blueprint/blueprint-cm/src/test/resources/org/apache/aries/
    aries/trunk/blueprint/blueprint-cm/src/test/resources/org/apache/aries/blueprint/
    aries/trunk/blueprint/blueprint-cm/src/test/resources/org/apache/aries/blueprint/compendium/
    aries/trunk/blueprint/blueprint-cm/src/test/resources/org/apache/aries/blueprint/compendium/cm/
    aries/trunk/blueprint/blueprint-cm/src/test/resources/org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.xml
Modified:
    aries/trunk/blueprint/blueprint-cm/pom.xml
    aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/CmManagedProperties.java
    aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/CmNamespaceHandler.java
    aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/ManagedObjectManager.java

Modified: aries/trunk/blueprint/blueprint-cm/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/pom.xml?rev=1364831&r1=1364830&r2=1364831&view=diff
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/pom.xml (original)
+++ aries/trunk/blueprint/blueprint-cm/pom.xml Mon Jul 23 23:06:01 2012
@@ -101,6 +101,48 @@
             <artifactId>slf4j-api</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>com.googlecode.pojosr</groupId>
+            <artifactId>de.kalpatec.pojosr.framework</artifactId>
+            <version>0.1.6</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.swissbox</groupId>
+            <artifactId>pax-swissbox-tinybundles</artifactId>
+            <version>1.3.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.7</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>1.5.11</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <version>1.5.11</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.proxy</groupId>
+            <artifactId>org.apache.aries.proxy.impl</artifactId>
+            <version>1.0.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configadmin</artifactId>
+            <version>1.2.8</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>

Modified: aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/CmManagedProperties.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/CmManagedProperties.java?rev=1364831&r1=1364830&r2=1364831&view=diff
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/CmManagedProperties.java (original)
+++ aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/CmManagedProperties.java Mon Jul 23 23:06:01 2012
@@ -150,17 +150,12 @@ public class CmManagedProperties impleme
 
     public void updated(final Dictionary props) {
         LOGGER.debug("Configuration updated for bean={} / pid={}", beanName, persistentId);
-        // Run in a separate thread to avoid re-entrance
-        new Thread() {
-            public void run() {
-                synchronized (lock) {
-                    properties = props;
-                    for (Object bean : beans) {
-                        inject(bean, false);
-                    }
-                }
+        synchronized (lock) {
+            properties = props;
+            for (Object bean : beans) {
+                inject(bean, false);
             }
-        }.start();
+        }
     }
 
     public Object beforeInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanData) {

Modified: aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/CmNamespaceHandler.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/CmNamespaceHandler.java?rev=1364831&r1=1364830&r2=1364831&view=diff
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/CmNamespaceHandler.java (original)
+++ aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/CmNamespaceHandler.java Mon Jul 23 23:06:01 2012
@@ -24,19 +24,13 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Properties;
 import java.util.Set;
 
-import org.apache.aries.blueprint.ext.PlaceholdersUtils;
-import org.w3c.dom.CharacterData;
-import org.w3c.dom.Comment;
-import org.w3c.dom.Element;
-import org.w3c.dom.EntityReference;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
 import org.apache.aries.blueprint.ComponentDefinitionRegistry;
 import org.apache.aries.blueprint.NamespaceHandler;
 import org.apache.aries.blueprint.ParserContext;
+import org.apache.aries.blueprint.ext.PlaceholdersUtils;
 import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
 import org.apache.aries.blueprint.mutable.MutableCollectionMetadata;
 import org.apache.aries.blueprint.mutable.MutableComponentMetadata;
@@ -46,7 +40,6 @@ import org.apache.aries.blueprint.mutabl
 import org.apache.aries.blueprint.mutable.MutableReferenceMetadata;
 import org.apache.aries.blueprint.mutable.MutableValueMetadata;
 import org.apache.aries.blueprint.utils.ServiceListener;
-import org.osgi.framework.BundleContext;
 import org.osgi.service.blueprint.container.ComponentDefinitionException;
 import org.osgi.service.blueprint.reflect.BeanMetadata;
 import org.osgi.service.blueprint.reflect.BeanProperty;
@@ -63,6 +56,12 @@ import org.osgi.service.blueprint.reflec
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.w3c.dom.CharacterData;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * Namespace handler for the Config Admin service.
@@ -240,8 +239,7 @@ public class CmNamespaceHandler implemen
             Node node = nl.item(i);
             if (node instanceof Element) {
                 Element e = (Element) node;
-                if (BLUEPRINT_CM_NAMESPACE_1_0.equals(e.getNamespaceURI())
-                        || BLUEPRINT_CM_NAMESPACE_1_1.equals(e.getNamespaceURI())) {
+                if (isCmNamespace(e.getNamespaceURI())) {
                     if (nodeNameEquals(e, DEFAULT_PROPERTIES_ELEMENT)) {
                         if (defaultsRef != null) {
                             throw new ComponentDefinitionException("Only one of " + DEFAULTS_REF_ATTRIBUTE + " attribute or " + DEFAULT_PROPERTIES_ELEMENT + " element is allowed");
@@ -249,9 +247,7 @@ public class CmNamespaceHandler implemen
                         Metadata props = parseDefaultProperties(context, metadata, e);
                         metadata.addProperty("defaultProperties", props);
                     }
-                } else if (BLUEPRINT_EXT_NAMESPACE_V1_0.equals(e.getNamespaceURI())
-                    || BLUEPRINT_EXT_NAMESPACE_V1_1.equals(e.getNamespaceURI())
-                    || BLUEPRINT_EXT_NAMESPACE_V1_2.equals(e.getNamespaceURI())) {
+                } else if (isExtNamespace(e.getNamespaceURI())) {
                     if (nodeNameEquals(e, LOCATION_ELEMENT)) {
                         locations.add(getTextValue(e));
                     }
@@ -299,8 +295,7 @@ public class CmNamespaceHandler implemen
             Node node = nl.item(i);
             if (node instanceof Element) {
                 Element e = (Element) node;
-                if (BLUEPRINT_CM_NAMESPACE_1_0.equals(e.getNamespaceURI())
-                        || BLUEPRINT_CM_NAMESPACE_1_1.equals(e.getNamespaceURI())) {
+                if (isCmNamespace(e.getNamespaceURI())) {
                     if (nodeNameEquals(e, PROPERTY_ELEMENT)) {
                         BeanProperty prop = context.parseElement(BeanProperty.class, enclosingComponent, e);
                         props.addEntry(createValue(context, prop.getName(), String.class.getName()), prop.getValue());
@@ -364,12 +359,20 @@ public class CmNamespaceHandler implemen
                         MapMetadata map = context.parseElement(MapMetadata.class,
                             factoryMetadata, e);
                         factoryMetadata.addProperty("serviceProperties", map);
+                        NodeList enl = e.getChildNodes();
+                        for (int j = 0; j < enl.getLength(); j++) {
+                            Node enode = enl.item(j);
+                            if (enode instanceof Element) {
+                                if (isCmNamespace(enode.getNamespaceURI()) && nodeNameEquals(enode, CM_PROPERTIES_ELEMENT)) {
+                                    decorateCmProperties(context, (Element) enode, factoryMetadata);
+                                }
+                            }
+                        }
                     } else if (nodeNameEquals(e, REGISTRATION_LISTENER_ELEMENT)) {
                         listeners.add(context.parseElement(RegistrationListener.class,
                             factoryMetadata, e));
                     }
-                } else if (BLUEPRINT_CM_NAMESPACE_1_0.equals(e.getNamespaceURI())
-                        || BLUEPRINT_CM_NAMESPACE_1_1.equals(e.getNamespaceURI())) {
+                } else if (isCmNamespace(e.getNamespaceURI())) {
                     if (nodeNameEquals(e, MANAGED_COMPONENT_ELEMENT)) {
                         MutableBeanMetadata managedComponent = context.parseElement(MutableBeanMetadata.class, null, e);
                         generateIdIfNeeded(context, managedComponent);
@@ -482,7 +485,7 @@ public class CmNamespaceHandler implemen
     }
     
     private MutableReferenceMetadata createConfigurationAdminRef(ParserContext context) {
-        return createServiceRef(context, ConfigurationAdmin.class, "(objectClass=" + ConfigurationAdmin.class.getName() + ")");
+        return createServiceRef(context, ConfigurationAdmin.class, null);
     }
     
     private static ValueMetadata createValue(ParserContext context, String value) {
@@ -552,6 +555,17 @@ public class CmNamespaceHandler implemen
         return BLUEPRINT_NAMESPACE.equals(ns);
     }
 
+    public static boolean isCmNamespace(String uri) {
+        return BLUEPRINT_CM_NAMESPACE_1_0.equals(uri)
+                || BLUEPRINT_CM_NAMESPACE_1_1.equals(uri);
+    }
+
+    public static boolean isExtNamespace(String uri) {
+        return BLUEPRINT_EXT_NAMESPACE_V1_0.equals(uri)
+                || BLUEPRINT_EXT_NAMESPACE_V1_1.equals(uri)
+                || BLUEPRINT_EXT_NAMESPACE_V1_2.equals(uri);
+    }
+
     public String getId(ParserContext context, Element element) {
         if (element.hasAttribute(ID_ATTRIBUTE)) {
             return element.getAttribute(ID_ATTRIBUTE);

Modified: aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/ManagedObjectManager.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/ManagedObjectManager.java?rev=1364831&r1=1364830&r2=1364831&view=diff
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/ManagedObjectManager.java (original)
+++ aries/trunk/blueprint/blueprint-cm/src/main/java/org/apache/aries/blueprint/compendium/cm/ManagedObjectManager.java Mon Jul 23 23:06:01 2012
@@ -18,12 +18,11 @@
  */
 package org.apache.aries.blueprint.compendium.cm;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Properties;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.apache.aries.util.AriesFrameworkUtil;
 import org.osgi.framework.ServiceRegistration;
@@ -69,17 +68,20 @@ public class ManagedObjectManager {
     private static class ConfigurationWatcher implements ManagedService {
 
         private ServiceRegistration registration;
-        private List<ManagedObject> list = Collections.synchronizedList(new ArrayList<ManagedObject>());
+        private List<ManagedObject> list = new CopyOnWriteArrayList<ManagedObject>();
         
         public ConfigurationWatcher() {
         }
         
-        public void updated(Dictionary props) throws ConfigurationException {
-            synchronized (list) {
-                for (ManagedObject cm : list) {
-                    cm.updated(props);
+        public void updated(final Dictionary props) throws ConfigurationException {
+            // Run in a separate thread to avoid re-entrance
+            new Thread() {
+                public void run() {
+                    for (ManagedObject cm : list) {
+                        cm.updated(props);
+                    }
                 }
-            }
+            }.start();
         }
         
         private void setRegistration(ServiceRegistration registration) {

Added: aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/BaseTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/BaseTest.java?rev=1364831&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/BaseTest.java (added)
+++ aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/BaseTest.java Mon Jul 23 23:06:01 2012
@@ -0,0 +1,111 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.blueprint.compendium.cm;
+
+import org.junit.After;
+import org.junit.Before;
+import org.ops4j.pax.swissbox.tinybundles.core.TinyBundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.blueprint.container.BlueprintContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class BaseTest {
+
+    private BundleContext bundleContext;
+    protected transient Logger log = LoggerFactory.getLogger(getClass());
+
+    @Before
+    public void setUp() throws Exception {
+        String symbolicName = getClass().getSimpleName();
+
+        TinyBundle cmBundle = Helper.createTestBundle("blueprint-cm", "1.0.0.SNAPSHOT", "OSGI-INF/blueprint/blueprint-cm.xml");
+        TinyBundle testBundle = Helper.createTestBundle(symbolicName, "1.0.0.SNAPSHOT", getBlueprintDescriptor());
+
+        this.bundleContext = Helper.createBundleContext(getBundleFilter(), new TinyBundle[] { cmBundle, testBundle });
+
+        // must wait for blueprint container to be published then the namespace parser is complete and we are ready for testing
+        log.debug("Waiting for BlueprintContainer to be published with symbolicName: {}", symbolicName);
+        getOsgiService(BlueprintContainer.class, "(osgi.blueprint.container.symbolicname=" + symbolicName + ")");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        Helper.disposeBundleContext(bundleContext);
+    }
+
+    /**
+     * Return the system bundle context
+     * @return
+     */
+    protected BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    /**
+     * Gets the bundle descriptor from the classpath.
+     * <p/>
+     * Return the location(s) of the bundle descriptors from the classpath.
+     * Separate multiple locations by comma, or return a single location.
+     * <p/>
+     * For example override this method and return <tt>OSGI-INF/blueprint/camel-context.xml</tt>
+     *
+     * @return the location of the bundle descriptor file.
+     */
+    protected String getBlueprintDescriptor() {
+        return null;
+    }
+
+    /**
+     * Gets filter expression of bundle descriptors.
+     * Modify this method if you wish to change default behavior.
+     *
+     * @return filter expression for OSGi bundles.
+     */
+    protected String getBundleFilter() {
+        return Helper.BUNDLE_FILTER;
+    }
+
+    /**
+     * Gets test bundle version.
+     * Modify this method if you wish to change default behavior.
+     *
+     * @return test bundle version
+     */
+    protected String getBundleVersion() {
+        return Helper.BUNDLE_VERSION;
+    }
+
+    protected <T> T getOsgiService(Class<T> type) {
+        return Helper.getOsgiService(bundleContext, type);
+    }
+
+    protected <T> T getOsgiService(Class<T> type, long timeout) {
+        return Helper.getOsgiService(bundleContext, type, timeout);
+    }
+
+    protected <T> T getOsgiService(Class<T> type, String filter) {
+        return Helper.getOsgiService(bundleContext, type, filter);
+    }
+
+    protected <T> T getOsgiService(Class<T> type, String filter, long timeout) {
+        return Helper.getOsgiService(bundleContext, type, filter, timeout);
+    }
+
+}

Added: aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/Foo.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/Foo.java?rev=1364831&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/Foo.java (added)
+++ aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/Foo.java Mon Jul 23 23:06:01 2012
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.blueprint.compendium.cm;
+
+import java.util.Properties;
+
+public class Foo implements FooInterface {
+
+    public Foo() {
+    }
+
+    private int a;
+    private String b;
+    private Properties props;
+
+    public int getA() {
+        return a;
+    }
+
+    public void setA(int i) {
+        a = i;
+    }
+
+    public String getB() {
+        return b;
+    }
+
+    public void setB(String i) {
+        b = i;
+    }
+
+    public Properties getProps() {
+        return props;
+    }
+
+    public void setProps(Properties props) {
+        this.props = props;
+    }
+}
+

Added: aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/FooInterface.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/FooInterface.java?rev=1364831&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/FooInterface.java (added)
+++ aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/FooInterface.java Mon Jul 23 23:06:01 2012
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.blueprint.compendium.cm;
+
+import java.util.Properties;
+
+public interface FooInterface {
+
+    Properties getProps();
+
+}

Added: aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/Helper.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/Helper.java?rev=1364831&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/Helper.java (added)
+++ aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/Helper.java Mon Jul 23 23:06:01 2012
@@ -0,0 +1,781 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.blueprint.compendium.cm;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.jar.JarInputStream;
+
+import de.kalpatec.pojosr.framework.PojoServiceRegistryFactoryImpl;
+import de.kalpatec.pojosr.framework.launch.BundleDescriptor;
+import de.kalpatec.pojosr.framework.launch.ClasspathScanner;
+import de.kalpatec.pojosr.framework.launch.PojoServiceRegistry;
+import de.kalpatec.pojosr.framework.launch.PojoServiceRegistryFactory;
+import org.ops4j.pax.swissbox.tinybundles.core.TinyBundle;
+import org.ops4j.pax.swissbox.tinybundles.core.TinyBundles;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public final class Helper {
+
+    public static final long DEFAULT_TIMEOUT = 30000;
+    public static final String BUNDLE_FILTER = "(Bundle-SymbolicName=*)";
+    public static final String BUNDLE_VERSION = "1.0.0";
+    private static final transient Logger LOG = LoggerFactory.getLogger(Helper.class);
+
+    private Helper() {
+    }
+
+    public static BundleContext createBundleContext(String name, String descriptors, boolean includeTestBundle) throws Exception {
+        return createBundleContext(name, descriptors, includeTestBundle, BUNDLE_FILTER, BUNDLE_VERSION);
+    }
+
+    public static BundleContext createBundleContext(String name, String descriptors, boolean includeTestBundle,
+                                                    String bundleFilter, String testBundleVersion) throws Exception {
+        TinyBundle bundle = null;
+
+        if (includeTestBundle) {
+            // add ourselves as a bundle
+            bundle = createTestBundle(name, testBundleVersion, descriptors);
+        }
+
+        return createBundleContext(bundleFilter, new TinyBundle[] { bundle });
+    }
+
+    public static BundleContext createBundleContext(String bundleFilter, TinyBundle[] testBundles) throws Exception {
+        deleteDirectory("target/bundles");
+        createDirectory("target/bundles");
+
+        // ensure pojosr stores bundles in an unique target directory
+        System.setProperty("org.osgi.framework.storage", "target/bundles/" + System.currentTimeMillis());
+
+        // get the bundles
+        List<BundleDescriptor> bundles = getBundleDescriptors(bundleFilter);
+
+        if (testBundles != null) {
+            for (TinyBundle bundle : testBundles) {
+                File tmp = File.createTempFile("test-", ".jar", new File("target/bundles/"));
+                tmp.delete();
+                bundles.add(getBundleDescriptor(tmp.getPath(), bundle));
+            }
+        }
+
+        if (LOG.isDebugEnabled()) {
+            for (int i = 0; i < bundles.size(); i++) {
+                BundleDescriptor desc = bundles.get(i);
+                LOG.debug("Bundle #{} -> {}", i, desc);
+            }
+        }
+
+        // setup pojosr to use our bundles
+        Map<String, List<BundleDescriptor>> config = new HashMap<String, List<BundleDescriptor>>();
+        config.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, bundles);
+
+        // create pojorsr osgi service registry
+        PojoServiceRegistry reg = new PojoServiceRegistryFactoryImpl().newPojoServiceRegistry(config);
+        return reg.getBundleContext();
+    }
+
+    public static void disposeBundleContext(BundleContext bundleContext) throws BundleException {
+        try {
+            if (bundleContext != null) {
+                bundleContext.getBundle().stop();
+            }
+        } finally {
+            System.clearProperty("org.osgi.framework.storage");
+        }
+    }
+
+    public static <T> T getOsgiService(BundleContext bundleContext, Class<T> type, long timeout) {
+        return getOsgiService(bundleContext, type, null, timeout);
+    }
+
+    public static <T> T getOsgiService(BundleContext bundleContext, Class<T> type) {
+        return getOsgiService(bundleContext, type, null, DEFAULT_TIMEOUT);
+    }
+
+    public static <T> T getOsgiService(BundleContext bundleContext, Class<T> type, String filter) {
+        return getOsgiService(bundleContext, type, filter, DEFAULT_TIMEOUT);
+    }
+
+    public static <T> ServiceReference getOsgiServiceReference(BundleContext bundleContext, Class<T> type, String filter, long timeout) {
+        ServiceTracker tracker = null;
+        try {
+            String flt;
+            if (filter != null) {
+                if (filter.startsWith("(")) {
+                    flt = "(&(" + Constants.OBJECTCLASS + "=" + type.getName() + ")" + filter + ")";
+                } else {
+                    flt = "(&(" + Constants.OBJECTCLASS + "=" + type.getName() + ")(" + filter + "))";
+                }
+            } else {
+                flt = "(" + Constants.OBJECTCLASS + "=" + type.getName() + ")";
+            }
+            Filter osgiFilter = FrameworkUtil.createFilter(flt);
+            tracker = new ServiceTracker(bundleContext, osgiFilter, null);
+            tracker.open(true);
+            // Note that the tracker is not closed to keep the reference
+            // This is buggy, as the service reference may change i think
+            Object svc = tracker.waitForService(timeout);
+            if (svc == null) {
+                Dictionary<?, ?> dic = bundleContext.getBundle().getHeaders();
+                System.err.println("Test bundle headers: " + explode(dic));
+
+                for (ServiceReference ref : asCollection(bundleContext.getAllServiceReferences(null, null))) {
+                    System.err.println("ServiceReference: " + ref + ", bundle: " + ref.getBundle() + ", symbolicName: " + ref.getBundle().getSymbolicName());
+                }
+
+                for (ServiceReference ref : asCollection(bundleContext.getAllServiceReferences(null, flt))) {
+                    System.err.println("Filtered ServiceReference: " + ref + ", bundle: " + ref.getBundle() + ", symbolicName: " + ref.getBundle().getSymbolicName());
+                }
+
+                throw new RuntimeException("Gave up waiting for service " + flt);
+            }
+            return tracker.getServiceReference();
+        } catch (InvalidSyntaxException e) {
+            throw new IllegalArgumentException("Invalid filter", e);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static <T> T getOsgiService(BundleContext bundleContext, Class<T> type, String filter, long timeout) {
+        ServiceTracker tracker = null;
+        try {
+            String flt;
+            if (filter != null) {
+                if (filter.startsWith("(")) {
+                    flt = "(&(" + Constants.OBJECTCLASS + "=" + type.getName() + ")" + filter + ")";
+                } else {
+                    flt = "(&(" + Constants.OBJECTCLASS + "=" + type.getName() + ")(" + filter + "))";
+                }
+            } else {
+                flt = "(" + Constants.OBJECTCLASS + "=" + type.getName() + ")";
+            }
+            Filter osgiFilter = FrameworkUtil.createFilter(flt);
+            tracker = new ServiceTracker(bundleContext, osgiFilter, null);
+            tracker.open(true);
+            // Note that the tracker is not closed to keep the reference
+            // This is buggy, as the service reference may change i think
+            Object svc = tracker.waitForService(timeout);
+            if (svc == null) {
+                Dictionary<?, ?> dic = bundleContext.getBundle().getHeaders();
+                System.err.println("Test bundle headers: " + explode(dic));
+
+                for (ServiceReference ref : asCollection(bundleContext.getAllServiceReferences(null, null))) {
+                    System.err.println("ServiceReference: " + ref + ", bundle: " + ref.getBundle() + ", symbolicName: " + ref.getBundle().getSymbolicName());
+                }
+
+                for (ServiceReference ref : asCollection(bundleContext.getAllServiceReferences(null, flt))) {
+                    System.err.println("Filtered ServiceReference: " + ref + ", bundle: " + ref.getBundle() + ", symbolicName: " + ref.getBundle().getSymbolicName());
+                }
+
+                throw new RuntimeException("Gave up waiting for service " + flt);
+            }
+            return type.cast(svc);
+        } catch (InvalidSyntaxException e) {
+            throw new IllegalArgumentException("Invalid filter", e);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected static TinyBundle createTestBundle(String name, String version, String descriptors) throws FileNotFoundException, MalformedURLException {
+        TinyBundle bundle = TinyBundles.newBundle();
+        for (URL url : getBlueprintDescriptors(descriptors)) {
+            LOG.info("Using Blueprint XML file: " + url.getFile());
+            bundle.add("OSGI-INF/blueprint/blueprint-" + url.getFile().replace("/", "-"), url);
+        }
+        bundle.set("Manifest-Version", "2")
+                .set("Bundle-ManifestVersion", "2")
+                .set("Bundle-SymbolicName", name)
+                .set("Bundle-Version", version);
+        return bundle;
+    }
+
+    /**
+     * Explode the dictionary into a <code>,</code> delimited list of <code>key=value</code> pairs.
+     */
+    private static String explode(Dictionary<?, ?> dictionary) {
+        Enumeration<?> keys = dictionary.keys();
+        StringBuffer result = new StringBuffer();
+        while (keys.hasMoreElements()) {
+            Object key = keys.nextElement();
+            result.append(String.format("%s=%s", key, dictionary.get(key)));
+            if (keys.hasMoreElements()) {
+                result.append(", ");
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * Provides an iterable collection of references, even if the original array is <code>null</code>.
+     */
+    private static Collection<ServiceReference> asCollection(ServiceReference[] references) {
+        return references  == null ? new ArrayList<ServiceReference>(0) : Arrays.asList(references);
+    }
+
+    /**
+     * Gets list of bundle descriptors.
+     * @param bundleFilter Filter expression for OSGI bundles.
+     *
+     * @return List pointers to OSGi bundles.
+     * @throws Exception If looking up the bundles fails.
+     */
+    private static List<BundleDescriptor> getBundleDescriptors(final String bundleFilter) throws Exception {
+        return new ClasspathScanner().scanForBundles(bundleFilter);
+    }
+
+    /**
+     * Gets the bundle descriptors as {@link URL} resources.
+     *
+     * @param descriptors the bundle descriptors, can be separated by comma
+     * @return the bundle descriptors.
+     * @throws FileNotFoundException is thrown if a bundle descriptor cannot be found
+     */
+    private static Collection<URL> getBlueprintDescriptors(String descriptors) throws FileNotFoundException, MalformedURLException {
+        List<URL> answer = new ArrayList<URL>();
+        String descriptor = descriptors;
+        if (descriptor != null) {
+            // there may be more resources separated by comma
+            Iterator<Object> it = createIterator(descriptor);
+            while (it.hasNext()) {
+                String s = (String) it.next();
+                LOG.trace("Resource descriptor: {}", s);
+
+                // remove leading / to be able to load resource from the classpath
+                s = stripLeadingSeparator(s);
+
+                // if there is wildcards for *.xml then we need to find the urls from the package
+                if (s.endsWith("*.xml")) {
+                    String packageName = s.substring(0, s.length() - 5);
+                    // remove trailing / to be able to load resource from the classpath
+                    Enumeration<URL> urls = loadResourcesAsURL(packageName);
+                    while (urls.hasMoreElements()) {
+                        URL url = urls.nextElement();
+                        File dir = new File(url.getFile());
+                        if (dir.isDirectory()) {
+                            File[] files = dir.listFiles();
+                            if (files != null) {
+                                for (File file : files) {
+                                    if (file.isFile() && file.exists() && file.getName().endsWith(".xml")) {
+                                        String name = packageName + file.getName();
+                                        LOG.debug("Resolving resource: {}", name);
+                                        URL xmlUrl = loadResourceAsURL(name);
+                                        if (xmlUrl != null) {
+                                            answer.add(xmlUrl);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    LOG.debug("Resolving resource: {}", s);
+                    URL url = resolveMandatoryResourceAsUrl(s);
+                    if (url == null) {
+                        throw new FileNotFoundException("Resource " + s + " not found");
+                    }
+                    answer.add(url);
+                }
+            }
+        } else {
+            throw new IllegalArgumentException("No bundle descriptor configured. Override getBlueprintDescriptor() or getBlueprintDescriptors() method");
+        }
+
+        if (answer.isEmpty()) {
+            throw new IllegalArgumentException("Cannot find any resources in classpath from descriptor " + descriptors);
+        }
+        return answer;
+    }
+
+    private static BundleDescriptor getBundleDescriptor(String path, TinyBundle bundle) throws Exception {
+        File file = new File(path);
+        FileOutputStream fos = new FileOutputStream(file, true);
+        try {
+            copy(bundle.build(), fos);
+        } finally {
+            close(fos);
+        }
+
+        FileInputStream fis = null;
+        JarInputStream jis = null;
+        try {
+            fis = new FileInputStream(file);
+            jis = new JarInputStream(fis);
+            Map<String, String> headers = new HashMap<String, String>();
+            for (Map.Entry<Object, Object> entry : jis.getManifest().getMainAttributes().entrySet()) {
+                headers.put(entry.getKey().toString(), entry.getValue().toString());
+            }
+
+            return new BundleDescriptor(
+                    bundle.getClass().getClassLoader(),
+                    new URL("jar:" + file.toURI().toString() + "!/"),
+                    headers);
+        } finally {
+            close(fis, jis);
+        }
+    }
+
+    /**
+     * Closes the given resource if it is available, logging any closing exceptions to the given log.
+     *
+     * @param closeable the object to close
+     * @param name the name of the resource
+     * @param log the log to use when reporting closure warnings, will use this class's own {@link Logger} if <tt>log == null</tt>
+     */
+    public static void close(Closeable closeable, String name, Logger log) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (IOException e) {
+                if (log == null) {
+                    // then fallback to use the own Logger
+                    log = LOG;
+                }
+                if (name != null) {
+                    log.warn("Cannot close: " + name + ". Reason: " + e.getMessage(), e);
+                } else {
+                    log.warn("Cannot close. Reason: " + e.getMessage(), e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Closes the given resource if it is available.
+     *
+     * @param closeable the object to close
+     * @param name the name of the resource
+     */
+    public static void close(Closeable closeable, String name) {
+        close(closeable, name, LOG);
+    }
+
+    /**
+     * Closes the given resource if it is available.
+     *
+     * @param closeable the object to close
+     */
+    public static void close(Closeable closeable) {
+        close(closeable, null, LOG);
+    }
+
+    /**
+     * Closes the given resources if they are available.
+     *
+     * @param closeables the objects to close
+     */
+    public static void close(Closeable... closeables) {
+        for (Closeable closeable : closeables) {
+            close(closeable);
+        }
+    }
+
+    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+    private static final String DEFAULT_DELIMITER = ",";
+
+    public static int copy(InputStream input, OutputStream output) throws IOException {
+        return copy(input, output, DEFAULT_BUFFER_SIZE);
+    }
+
+    public static int copy(final InputStream input, final OutputStream output, int bufferSize) throws IOException {
+        int avail = input.available();
+        if (avail > 262144) {
+            avail = 262144;
+        }
+        if (avail > bufferSize) {
+            bufferSize = avail;
+        }
+
+        final byte[] buffer = new byte[bufferSize];
+        int n = input.read(buffer);
+        int total = 0;
+        while (-1 != n) {
+            output.write(buffer, 0, n);
+            total += n;
+            n = input.read(buffer);
+        }
+        output.flush();
+        return total;
+    }
+
+    /**
+     * Creates an iterator over the value if the value is a collection, an
+     * Object[], a String with values separated by comma,
+     * or a primitive type array; otherwise to simplify the caller's code,
+     * we just create a singleton collection iterator over a single value
+     * <p/>
+     * Will default use comma for String separating String values.
+     * This method does <b>not</b> allow empty values
+     *
+     * @param value  the value
+     * @return the iterator
+     */
+    public static Iterator<Object> createIterator(Object value) {
+        return createIterator(value, DEFAULT_DELIMITER);
+    }
+
+    /**
+     * Creates an iterator over the value if the value is a collection, an
+     * Object[], a String with values separated by the given delimiter,
+     * or a primitive type array; otherwise to simplify the caller's
+     * code, we just create a singleton collection iterator over a single value
+     * <p/>
+     * This method does <b>not</b> allow empty values
+     *
+     * @param value      the value
+     * @param delimiter  delimiter for separating String values
+     * @return the iterator
+     */
+    public static Iterator<Object> createIterator(Object value, String delimiter) {
+        return createIterator(value, delimiter, false);
+    }
+
+    /**
+     * Creates an iterator over the value if the value is a collection, an
+     * Object[], a String with values separated by the given delimiter,
+     * or a primitive type array; otherwise to simplify the caller's
+     * code, we just create a singleton collection iterator over a single value
+     *
+     * @param value             the value
+     * @param delimiter         delimiter for separating String values
+     * @param allowEmptyValues  whether to allow empty values
+     * @return the iterator
+     */
+    @SuppressWarnings("unchecked")
+    public static Iterator<Object> createIterator(Object value, String delimiter, final boolean allowEmptyValues) {
+        if (value == null) {
+            return Collections.emptyList().iterator();
+        } else if (value instanceof Iterator) {
+            return (Iterator<Object>)value;
+        } else if (value instanceof Iterable) {
+            return ((Iterable<Object>)value).iterator();
+        } else if (value.getClass().isArray()) {
+            // TODO we should handle primitive array types?
+            List<Object> list = Arrays.asList((Object[])value);
+            return list.iterator();
+        } else if (value instanceof NodeList) {
+            // lets iterate through DOM results after performing XPaths
+            final NodeList nodeList = (NodeList) value;
+            return cast(new Iterator<Node>() {
+                int idx = -1;
+
+                public boolean hasNext() {
+                    return (idx + 1) < nodeList.getLength();
+                }
+
+                public Node next() {
+                    idx++;
+                    return nodeList.item(idx);
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+            });
+        } else if (value instanceof String) {
+            final String s = (String) value;
+
+            // this code is optimized to only use a Scanner if needed, eg there is a delimiter
+
+            if (delimiter != null && s.contains(delimiter)) {
+                // use a scanner if it contains the delimiter
+                Scanner scanner = new Scanner((String)value);
+
+                if (DEFAULT_DELIMITER.equals(delimiter)) {
+                    // we use the default delimiter which is a comma, then cater for bean expressions with OGNL
+                    // which may have balanced parentheses pairs as well.
+                    // if the value contains parentheses we need to balance those, to avoid iterating
+                    // in the middle of parentheses pair, so use this regular expression (a bit hard to read)
+                    // the regexp will split by comma, but honor parentheses pair that may include commas
+                    // as well, eg if value = "bean=foo?method=killer(a,b),bean=bar?method=great(a,b)"
+                    // then the regexp will split that into two:
+                    // -> bean=foo?method=killer(a,b)
+                    // -> bean=bar?method=great(a,b)
+                    // http://stackoverflow.com/questions/1516090/splitting-a-title-into-separate-parts
+                    delimiter = ",(?!(?:[^\\(,]|[^\\)],[^\\)])+\\))";
+                }
+
+                scanner.useDelimiter(delimiter);
+                return cast(scanner);
+            } else {
+                // use a plain iterator that returns the value as is as there are only a single value
+                return cast(new Iterator<String>() {
+                    int idx = -1;
+
+                    public boolean hasNext() {
+                        return idx + 1 == 0 && (allowEmptyValues || isNotEmpty(s));
+                    }
+
+                    public String next() {
+                        idx++;
+                        return s;
+                    }
+
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                });
+            }
+        } else {
+            return Collections.singletonList(value).iterator();
+        }
+    }
+
+    /**
+     * Tests whether the value is <b>not</b> <tt>null</tt> or an empty string.
+     *
+     * @param value  the value, if its a String it will be tested for text length as well
+     * @return true if <b>not</b> empty
+     */
+    public static boolean isNotEmpty(Object value) {
+        if (value == null) {
+            return false;
+        } else if (value instanceof String) {
+            String text = (String) value;
+            return text.trim().length() > 0;
+        } else {
+            return true;
+        }
+    }
+
+    public static <T> Iterator<T> cast(Iterator<?> p) {
+        return (Iterator<T>) p;
+    }
+
+    /**
+     * Strip any leading separators
+     */
+    public static String stripLeadingSeparator(String name) {
+        if (name == null) {
+            return null;
+        }
+        while (name.startsWith("/") || name.startsWith(File.separator)) {
+            name = name.substring(1);
+        }
+        return name;
+    }
+
+    /**
+     * Attempts to load the given resources from the given package name using the thread context
+     * class loader or the class loader used to load this class
+     *
+     * @param packageName the name of the package to load its resources
+     * @return the URLs for the resources or null if it could not be loaded
+     */
+    public static Enumeration<URL> loadResourcesAsURL(String packageName) {
+        Enumeration<URL> url = null;
+
+        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+        if (contextClassLoader != null) {
+            try {
+                url = contextClassLoader.getResources(packageName);
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+        if (url == null) {
+            try {
+                url = Helper.class.getClassLoader().getResources(packageName);
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+
+        return url;
+    }
+
+    /**
+     * Attempts to load the given resource as a stream using the thread context
+     * class loader or the class loader used to load this class
+     *
+     * @param name the name of the resource to load
+     * @return the stream or null if it could not be loaded
+     */
+    public static URL loadResourceAsURL(String name) {
+        URL url = null;
+
+        String resolvedName = resolveUriPath(name);
+        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+        if (contextClassLoader != null) {
+            url = contextClassLoader.getResource(resolvedName);
+        }
+        if (url == null) {
+            url = Helper.class.getClassLoader().getResource(resolvedName);
+        }
+
+        return url;
+    }
+
+    /**
+     * Helper operation used to remove relative path notation from
+     * resources.  Most critical for resources on the Classpath
+     * as resource loaders will not resolve the relative paths correctly.
+     *
+     * @param name the name of the resource to load
+     * @return the modified or unmodified string if there were no changes
+     */
+    private static String resolveUriPath(String name) {
+        String answer = name;
+        if (answer.indexOf("//") > -1) {
+            answer = answer.replaceAll("//", "/");
+        }
+        if (answer.indexOf("../") > -1) {
+            answer = answer.replaceAll("[A-Za-z0-9]*/\\.\\./", "");
+        }
+        if (answer.indexOf("./") > -1) {
+            answer = answer.replaceAll("\\./", "");
+        }
+        return answer;
+    }
+
+    /**
+     * Resolves the mandatory resource.
+     *
+     * @param uri uri of the resource
+     * @return the resource as an {@link InputStream}.  Remember to close this stream after usage.
+     * @throws java.io.FileNotFoundException is thrown if the resource file could not be found
+     * @throws java.net.MalformedURLException if the URI is malformed
+     */
+    public static URL resolveMandatoryResourceAsUrl(String uri) throws FileNotFoundException, MalformedURLException {
+        if (uri.startsWith("file:")) {
+            // check if file exists first
+            String name = after(uri, "file:");
+            File file = new File(name);
+            if (!file.exists()) {
+                throw new FileNotFoundException("File " + file + " not found");
+            }
+            return new URL(uri);
+        } else if (uri.startsWith("http:")) {
+            return new URL(uri);
+        } else if (uri.startsWith("classpath:")) {
+            uri = after(uri, "classpath:");
+        }
+
+        // load from classpath by default
+        URL url = loadResourceAsURL(uri);
+        if (url == null) {
+            throw new FileNotFoundException("Cannot find resource in classpath for URI: " + uri);
+        } else {
+            return url;
+        }
+    }
+
+    public static String after(String text, String after) {
+        if (!text.contains(after)) {
+            return null;
+        }
+        return text.substring(text.indexOf(after) + after.length());
+    }
+
+    /**
+     * Recursively delete a directory, useful to zapping test data
+     *
+     * @param file the directory to be deleted
+     * @return <tt>false</tt> if error deleting directory
+     */
+    public static boolean deleteDirectory(String file) {
+        return deleteDirectory(new File(file));
+    }
+
+    /**
+     * Recursively delete a directory, useful to zapping test data
+     *
+     * @param file the directory to be deleted
+     * @return <tt>false</tt> if error deleting directory
+     */
+    public static boolean deleteDirectory(File file) {
+        int tries = 0;
+        int maxTries = 5;
+        boolean exists = true;
+        while (exists && (tries < maxTries)) {
+            recursivelyDeleteDirectory(file);
+            tries++;
+            exists = file.exists();
+            if (exists) {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+            }
+        }
+        return !exists;
+    }
+
+    private static void recursivelyDeleteDirectory(File file) {
+        if (!file.exists()) {
+            return;
+        }
+
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();
+            for (File child : files) {
+                recursivelyDeleteDirectory(child);
+            }
+        }
+        boolean success = file.delete();
+        if (!success) {
+            LOG.warn("Deletion of file: " + file.getAbsolutePath() + " failed");
+        }
+    }
+
+    /**
+     * create the directory
+     *
+     * @param file the directory to be created
+     */
+    public static void createDirectory(String file) {
+        File dir = new File(file);
+        dir.mkdirs();
+    }
+
+
+}

Added: aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.java?rev=1364831&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.java (added)
+++ aries/trunk/blueprint/blueprint-cm/src/test/java/org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.java Mon Jul 23 23:06:01 2012
@@ -0,0 +1,138 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.blueprint.compendium.cm;
+
+import java.util.Hashtable;
+
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class ManagedServiceFactoryTest extends BaseTest {
+
+    @Override
+    protected String getBlueprintDescriptor() {
+        return "org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.xml";
+    }
+
+    @Test
+    public void test1() throws Exception {
+        ConfigurationAdmin ca = getOsgiService(ConfigurationAdmin.class);
+        Configuration cf = ca.createFactoryConfiguration("blueprint-sample-managed-service-factory", null);
+        Hashtable<String,String> props = new Hashtable<String,String>();
+        props.put("a", "5");
+        cf.update(props);
+
+        BundleContext context = getBundleContext();
+        ServiceReference sr = Helper.getOsgiServiceReference(context, Foo.class, "(key=foo1)", Helper.DEFAULT_TIMEOUT);
+        assertNotNull(sr);
+        Foo foo = (Foo) context.getService(sr);
+        assertNotNull(foo);
+        assertEquals(5, foo.getA());
+        assertEquals("default", foo.getB());
+        assertEquals("5", sr.getProperty("a"));
+        assertNull(sr.getProperty("b"));
+
+        props = new Hashtable<String,String>();
+        props.put("b", "foo");
+        cf.update(props);
+        Thread.sleep(500);
+
+        // No update of bean after creation
+        assertEquals(5, foo.getA());
+        assertEquals("default", foo.getB());
+
+        // Only initial update of service properties
+        assertEquals("5", sr.getProperty("a"));
+        assertNull(sr.getProperty("b"));
+    }
+
+    @Test
+    public void test2() throws Exception {
+        ConfigurationAdmin ca = getOsgiService(ConfigurationAdmin.class);
+        Configuration cf = ca.createFactoryConfiguration("blueprint-sample-managed-service-factory2", null);
+        Hashtable<String,String> props = new Hashtable<String,String>();
+        props.put("a", "5");
+        cf.update(props);
+
+        BundleContext context = getBundleContext();
+        ServiceReference sr = Helper.getOsgiServiceReference(context, Foo.class, "(key=foo2)", Helper.DEFAULT_TIMEOUT);
+        assertNotNull(sr);
+
+        Foo foo = (Foo) context.getService(sr);
+        assertNotNull(foo);
+        assertEquals(5, foo.getA());
+        assertEquals("default", foo.getB());
+        assertNull(sr.getProperty("a"));
+        assertNull(sr.getProperty("b"));
+
+        props = new Hashtable<String,String>();
+        props.put("b", "foo");
+        cf.update(props);
+
+        // Update after creation
+        Thread.sleep(500);
+        assertEquals(5, foo.getA());
+        assertEquals("foo", foo.getB());
+
+        // No update of service properties
+        assertNull(sr.getProperty("a"));
+        assertNull(sr.getProperty("b"));
+    }
+
+    @Test
+    public void test3() throws Exception {
+        ConfigurationAdmin ca = getOsgiService(ConfigurationAdmin.class);
+        Configuration cf = ca.createFactoryConfiguration("blueprint-sample-managed-service-factory3", null);
+        Hashtable<String,String> props = new Hashtable<String,String>();
+        props.put("a", "5");
+        cf.update(props);
+
+        BundleContext context = getBundleContext();
+        ServiceReference sr = Helper.getOsgiServiceReference(context, Foo.class, "(key=foo3)", Helper.DEFAULT_TIMEOUT);
+        assertNotNull(sr);
+
+        Foo foo = (Foo) context.getService(sr);
+        assertNotNull(foo);
+        assertEquals(5, foo.getA());
+        assertEquals("default", foo.getB());
+        assertEquals("5", sr.getProperty("a"));
+        assertNull(sr.getProperty("b"));
+
+        props = new Hashtable<String,String>();
+        props.put("b", "foo");
+        cf.update(props);
+
+        // Update after creation
+        Thread.sleep(500);
+        assertEquals(5, foo.getA());
+        assertEquals("foo", foo.getB());
+
+        // Update of service properties
+        assertEquals("5", sr.getProperty("a"));
+        assertEquals("foo", sr.getProperty("b"));
+    }
+
+}

Added: aries/trunk/blueprint/blueprint-cm/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/src/test/resources/log4j.properties?rev=1364831&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/src/test/resources/log4j.properties (added)
+++ aries/trunk/blueprint/blueprint-cm/src/test/resources/log4j.properties Mon Jul 23 23:06:01 2012
@@ -0,0 +1,42 @@
+## ------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements.  See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License.  You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ------------------------------------------------------------------------
+
+#
+# The logging properties used for testing
+#
+log4j.rootLogger=DEBUG, out, file
+
+#log4j.logger.de.kalpatec.pojosr=DEBUG
+#log4j.logger.org.apache.camel.test.blueprint=DEBUG
+#log4j.logger.org.apache.camel=DEBUG
+#log4j.logger.org.apache.camel.blueprint=TRACE
+
+# CONSOLE appender not used by default
+log4j.appender.out=org.apache.log4j.ConsoleAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+# MDC
+#log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %-10.10X{camel.breadcrumbId} - %-10.10X{camel.exchangeId} - %-10.10X{camel.correlationId} - %-10.10X{camel.routeId} - %m%n
+
+# File appender
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.file=target/camel-test-blueprint.log
+log4j.appender.file.append=true
+log4j.appender.file.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+# MDC
+#log4j.appender.file.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %-10.10X{camel.breadcrumbId} - %-10.10X{camel.exchangeId} - %-10.10X{camel.correlationId} - %-10.10X{camel.routeId} - %m%n

Added: aries/trunk/blueprint/blueprint-cm/src/test/resources/org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.xml
URL: http://svn.apache.org/viewvc/aries/trunk/blueprint/blueprint-cm/src/test/resources/org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.xml?rev=1364831&view=auto
==============================================================================
--- aries/trunk/blueprint/blueprint-cm/src/test/resources/org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.xml (added)
+++ aries/trunk/blueprint/blueprint-cm/src/test/resources/org/apache/aries/blueprint/compendium/cm/ManagedServiceFactoryTest.xml Mon Jul 23 23:06:01 2012
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements. See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version
+        2.0 (the "License"); you may not use this file except in compliance
+        with the License. You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0 Unless required by
+        applicable law or agreed to in writing, software distributed under the
+        License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+        CONDITIONS OF ANY KIND, either express or implied. See the License for
+        the specific language governing permissions and limitations under the
+        License.
+    -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0">
+
+    <cm:managed-service-factory id="managed-service-factory"
+                                factory-pid="blueprint-sample-managed-service-factory"
+                                interface="org.apache.aries.blueprint.compendium.cm.Foo">
+        <service-properties>
+            <entry key="key" value="foo1" />
+            <cm:cm-properties persistent-id="" />
+        </service-properties>
+        <cm:managed-component class="org.apache.aries.blueprint.compendium.cm.Foo">
+            <cm:managed-properties persistent-id="" />
+            <property name="a" value="1" />
+            <property name="b" value="default" />
+        </cm:managed-component>
+    </cm:managed-service-factory>
+
+    <cm:managed-service-factory id="managed-service-factory2"
+                                factory-pid="blueprint-sample-managed-service-factory2"
+                                interface="org.apache.aries.blueprint.compendium.cm.Foo">
+        <service-properties>
+            <entry key="key" value="foo2" />
+        </service-properties>
+        <cm:managed-component class="org.apache.aries.blueprint.compendium.cm.Foo">
+            <cm:managed-properties persistent-id="" update-strategy="container-managed" />
+            <property name="a" value="1" />
+            <property name="b" value="default" />
+        </cm:managed-component>
+    </cm:managed-service-factory>
+
+    <cm:managed-service-factory id="managed-service-factory3"
+                                factory-pid="blueprint-sample-managed-service-factory3"
+                                interface="org.apache.aries.blueprint.compendium.cm.Foo">
+        <service-properties>
+            <entry key="key" value="foo3" />
+            <cm:cm-properties persistent-id="" update="true"/>
+        </service-properties>
+        <cm:managed-component class="org.apache.aries.blueprint.compendium.cm.Foo">
+            <cm:managed-properties persistent-id="" update-strategy="container-managed" />
+            <property name="a" value="1" />
+            <property name="b" value="default" />
+        </cm:managed-component>
+    </cm:managed-service-factory>
+
+</blueprint>