Code review:

- cleaned up the code to use more recent Java constructs, such as foreach
  loops;
- cleaned up the test cases to make them a bit more readable and easier to
  maintain by folding some duplicate code into helper methods;
- fixed a corner case in which an AutoConf resource drops a configuration while
  updating existing ones at the same time. A NPE could be thrown in this
  situation due ConfigurationAdmin not being present;
- fixed a corner case in which a resource is dropped twice. A IOException could
  be thrown;
- moved some really specific MetaType code to a utility class;
- added some test cases for PersistencyManager;
- applied the code formatting guidelines for Apache Felix.



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1724802 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/deploymentadmin/autoconf/pom.xml b/deploymentadmin/autoconf/pom.xml
index f6b83d7..0406d3a 100644
--- a/deploymentadmin/autoconf/pom.xml
+++ b/deploymentadmin/autoconf/pom.xml
@@ -85,9 +85,19 @@
                         <Bundle-Name>Apache Felix AutoConf Resource Processor</Bundle-Name>
                         <Bundle-Description>A customizer bundle that publishes a Resource Processor service that processes configuration resources shipped in a Deployment Package.</Bundle-Description>
                         <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
-                        <Private-Package>org.apache.felix.deployment.rp.autoconf, org.apache.felix.metatype, org.apache.felix.metatype.internal.l10n, org.apache.felix.metatype.internal, org.kxml2.io;-split-package:=merge-first, org.xmlpull.v1;-split-package:=merge-first, org.osgi.service.metatype;-split-package:=merge-first</Private-Package>
-                        <Import-Package>org.apache.felix.dm,org.osgi.service.deploymentadmin,org.osgi.service.deploymentadmin.spi,org.osgi.service.event,org.osgi.service.log,*</Import-Package>
-                        <Export-Package>org.osgi.service.deploymentadmin.spi;version="1.0",org.osgi.service.metatype;version="1.2"</Export-Package>
+                        <Private-Package>
+                        	org.apache.felix.deployment.rp.autoconf,
+                        	org.apache.felix.metatype,
+                        	org.apache.felix.metatype.internal.l10n,
+                        	org.apache.felix.metatype.internal,
+                        	org.kxml2.io; -split-package:=merge-first,
+                        	org.xmlpull.v1; -split-package:=merge-first,
+                        	org.osgi.service.metatype; -split-package:=merge-first
+                       	</Private-Package>
+                        <Export-Package>
+                        	org.osgi.service.deploymentadmin.spi; -split-package:=merge-last;version="1.0",
+                        	org.osgi.service.metatype; -split-package:=merge-last;version="1.2"
+                       	</Export-Package>
                         <DeploymentPackage-Customizer>true</DeploymentPackage-Customizer>
                         <Deployment-ProvidesResourceProcessor>org.osgi.deployment.rp.autoconf</Deployment-ProvidesResourceProcessor>
                     </instructions>
diff --git a/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/Activator.java b/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/Activator.java
index dda7900..0a31cfa 100644
--- a/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/Activator.java
+++ b/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/Activator.java
@@ -38,7 +38,7 @@
     public void init(BundleContext context, DependencyManager manager) throws Exception {
     	Dictionary properties = new Properties();
         properties.put(Constants.SERVICE_PID, "org.osgi.deployment.rp.autoconf");
-        
+
         AutoConfResourceProcessor processor = new AutoConfResourceProcessor();
         manager.add(createComponent()
             .setInterface(ResourceProcessor.class.getName(), properties)
diff --git a/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/AutoConfResource.java b/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/AutoConfResource.java
index 8d093f9..ca7e936 100644
--- a/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/AutoConfResource.java
+++ b/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/AutoConfResource.java
@@ -18,95 +18,146 @@
  */
 package org.apache.felix.deployment.rp.autoconf;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.Serializable;
 import java.util.Dictionary;
 
-public class AutoConfResource implements Serializable {
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
 
-	private static final long serialVersionUID = 1L;
-	
-	private final String m_pid;
-	private final String m_factoryPid;
-	private final Dictionary m_properties;
-	private final String m_bundleLoc;
-	private final boolean m_merge;
-	private final String m_name;
-	private final String m_filter;
+public class AutoConfResource implements Serializable
+{
+    private static final long serialVersionUID = 1L;
 
-	private String m_alias = null;
+    private final String m_pid;
+    private final String m_factoryPid;
+    private final Dictionary m_properties;
+    private final String m_bundleLoc;
+    private final boolean m_merge;
+    private final String m_name;
 
-	
-	public AutoConfResource(String name, String pid, String factoryPid, String bundleLocation, boolean merge, Dictionary properties, String filter) {
-		m_name = name;
-		m_pid = pid;
+    private transient Filter m_filter;
+    private String m_alias = null;
+
+    public AutoConfResource(String name, String pid, String factoryPid, String bundleLocation, boolean merge, Dictionary properties, Filter filter)
+    {
+        m_name = name;
+        m_pid = pid;
         m_filter = filter;
-		m_factoryPid = (factoryPid == null) ? "" : factoryPid;
-		m_bundleLoc = bundleLocation;
-		m_merge = merge;
-		m_properties = properties;
-	}
+        m_factoryPid = (factoryPid == null) ? "" : factoryPid;
+        m_bundleLoc = bundleLocation;
+        m_merge = merge;
+        m_properties = properties;
+    }
 
-	public String getName() {
-		return m_name;
-	}
-	
-	public String getPid() {
-		return m_pid;
-	}
-	
-	public String getFilter() {
+    public String getName()
+    {
+        return m_name;
+    }
+
+    public String getPid()
+    {
+        return m_pid;
+    }
+
+    public Filter getFilter()
+    {
         return m_filter;
     }
 
-	/**
-	 * Returns empty string if this configuration is not a factory configuration, otherwise the factory
-	 * PID is returned.
-	 * 
-	 * @return Empty string if this is not a factory configuration resource, else the factory PID is returned.
-	 */
-	public String getFactoryPid() {
-		return m_factoryPid;
-	}
+    /**
+     * Returns empty string if this configuration is not a factory configuration, otherwise the factory
+     * PID is returned.
+     * 
+     * @return Empty string if this is not a factory configuration resource, else the factory PID is returned.
+     */
+    public String getFactoryPid()
+    {
+        return m_factoryPid;
+    }
 
-	public Dictionary getProperties() {
-		return m_properties;
-	}
-	
-	public String getBundleLocation() {
-		return m_bundleLoc;
-	}
-	
-	public boolean isMerge() {
-		return m_merge;
-	}
-	
-	public boolean isFactoryConfig() {
-		return !(m_factoryPid == null || "".equals(m_factoryPid));
-	}
-	
-	public void setGeneratedPid(String alias) {
-		m_alias = alias;
-	}
-	
-	public String getGeneratedPid() {
-		if (m_alias == null) {
-			throw new IllegalStateException("Must set an alias first.");
-		}
-		return m_alias;
-	}
-	
-	/**
-	 * Determine if the specified <code>AutoConfResource</code> is meant to be used for the same <code>Configuration</code> as this object.
-	 *  
-	 * @param resource The <code>AutoConfResource</code> to compare with.
-	 * @return Returns <code>true</code> if the two resources are meant to be used for the same <code>Configuration</code> object, false otherwise.
-	 */
-	public boolean equalsTargetConfiguration(AutoConfResource resource) {
-		if (isFactoryConfig()) {
-			return m_pid.equals(resource.getPid()) && m_factoryPid.equals(resource.getFactoryPid());
-		}
-		else {
-			return m_pid.equals(resource.getPid());
-		}
-	}
+    public Dictionary getProperties()
+    {
+        return m_properties;
+    }
+
+    public String getBundleLocation()
+    {
+        return m_bundleLoc;
+    }
+
+    public boolean isMerge()
+    {
+        return m_merge;
+    }
+
+    public boolean isFactoryConfig()
+    {
+        return !(m_factoryPid == null || "".equals(m_factoryPid));
+    }
+
+    public void setGeneratedPid(String alias)
+    {
+        m_alias = alias;
+    }
+
+    public String getGeneratedPid()
+    {
+        if (m_alias == null)
+        {
+            throw new IllegalStateException("Must set an alias first.");
+        }
+        return m_alias;
+    }
+
+    /**
+     * Determine if the specified <code>AutoConfResource</code> is meant to be used for the same <code>Configuration</code> as this object.
+     *  
+     * @param resource The <code>AutoConfResource</code> to compare with.
+     * @return Returns <code>true</code> if the two resources are meant to be used for the same <code>Configuration</code> object, false otherwise.
+     */
+    public boolean equalsTargetConfiguration(AutoConfResource resource)
+    {
+        if (isFactoryConfig())
+        {
+            return m_pid.equals(resource.getPid()) && m_factoryPid.equals(resource.getFactoryPid());
+        }
+        else
+        {
+            return m_pid.equals(resource.getPid());
+        }
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException
+    {
+        out.defaultWriteObject();
+        if (m_filter != null)
+        {
+            out.writeUTF(m_filter.toString());
+        }
+        else
+        {
+            out.writeUTF("");
+        }
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+        String filter = in.readUTF();
+        if (!"".equals(filter))
+        {
+            try
+            {
+                m_filter = FrameworkUtil.createFilter(filter);
+            }
+            catch (InvalidSyntaxException e)
+            {
+                throw new IOException("Unable to parse serialized filter: " + e.getMessage());
+            }
+        }
+    }
 }
diff --git a/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/AutoConfResourceProcessor.java b/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/AutoConfResourceProcessor.java
index f68b306..f158b8e 100644
--- a/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/AutoConfResourceProcessor.java
+++ b/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/AutoConfResourceProcessor.java
@@ -18,33 +18,32 @@
  */
 package org.apache.felix.deployment.rp.autoconf;
 
+import static org.osgi.service.deploymentadmin.spi.ResourceProcessorException.CODE_OTHER_ERROR;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.Set;
-import java.util.Vector;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.DependencyManager;
-import org.apache.felix.metatype.Attribute;
 import org.apache.felix.metatype.Designate;
+import org.apache.felix.metatype.DesignateObject;
 import org.apache.felix.metatype.MetaData;
 import org.apache.felix.metatype.MetaDataReader;
 import org.apache.felix.metatype.OCD;
 import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
 import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.cm.Configuration;
@@ -56,238 +55,104 @@
 import org.osgi.service.event.EventConstants;
 import org.osgi.service.event.EventHandler;
 import org.osgi.service.log.LogService;
-import org.osgi.service.metatype.AttributeDefinition;
 import org.osgi.service.metatype.MetaTypeInformation;
 import org.osgi.service.metatype.MetaTypeService;
 import org.osgi.service.metatype.ObjectClassDefinition;
 
-public class AutoConfResourceProcessor implements ResourceProcessor, EventHandler {
-    private static final int CODE_OTHER_ERROR = ResourceProcessorException.CODE_OTHER_ERROR;
-    private static final String LOCATION_PREFIX = "osgi-dp:";
+public class AutoConfResourceProcessor implements ResourceProcessor, EventHandler
+{
     public static final String CONFIGURATION_ADMIN_FILTER_ATTRIBUTE = "filter";
 
-	// dependencies injected by Dependency Manager
-	private volatile LogService m_log;
-	private volatile ConfigurationAdmin m_configAdmin;
-	private volatile MetaTypeService m_metaService;
-	private volatile BundleContext m_bc;
+    private static final String LOCATION_PREFIX = "osgi-dp:";
+
+    // dependencies injected by Dependency Manager
+    private volatile LogService m_log;
+    private volatile MetaTypeService m_metaService;
     private volatile DependencyManager m_dm;
-    
-	private Component m_component;
-	
-	private final Object m_lock = new Object(); // protects the members below
+    // Locally managed
+    private Component m_component;
+    private PersistencyManager m_persistencyManager;
 
-	private DeploymentSession m_session = null;
-	private final Map m_toBeInstalled = new HashMap(); // Map<String, List<AutoConfResource>>
-	private final Map m_toBeDeleted = new HashMap();
-	
-	private PersistencyManager m_persistencyManager;
+    private final Object m_lock; // protects the members below
+    private final Map<String, List<AutoConfResource>> m_toBeInstalled;
+    private final Map<String, List<AutoConfResource>> m_toBeDeleted;
+    private final AtomicReference<DeploymentSession> m_sessionRef;
+    private final List<ConfigurationAdminTask> m_configurationAdminTasks;
+    private final List<PostCommitTask> m_postCommitTasks;
 
-	public void start() throws IOException {
-		File root = m_bc.getDataFile("");
-		if (root == null) {
-			throw new IOException("No file system support");
-		}
-		m_persistencyManager = new PersistencyManager(root);
-	}
-	
-    public void begin(DeploymentSession session) {
+    public AutoConfResourceProcessor()
+    {
+        m_lock = new Object();
+        m_sessionRef = new AtomicReference<DeploymentSession>();
+        m_toBeInstalled = new HashMap<String, List<AutoConfResource>>();
+        m_toBeDeleted = new HashMap<String, List<AutoConfResource>>();
+        m_configurationAdminTasks = new ArrayList<ConfigurationAdminTask>();
+        m_postCommitTasks = new ArrayList<PostCommitTask>();
+    }
+
+    /**
+     * Called by Felix DM for the component created in {@link #commit()}.
+     */
+    public void addConfigurationAdmin(ServiceReference ref, ConfigurationAdmin ca)
+    {
+        m_log.log(LogService.LOG_DEBUG, "found configuration admin " + ref);
+
+        List<ConfigurationAdminTask> configAdminTasks;
+        synchronized (m_lock)
+        {
+            configAdminTasks = new ArrayList<ConfigurationAdminTask>(m_configurationAdminTasks);
+        }
+
+        for (ConfigurationAdminTask task : configAdminTasks)
+        {
+            try
+            {
+                Filter filter = task.getFilter();
+                if ((filter == null) || (filter != null && filter.match(ref)))
+                {
+                    task.run(m_persistencyManager, ca);
+                }
+            }
+            catch (Exception e)
+            {
+                m_log.log(LogService.LOG_ERROR, "Exception during configuration to " + ca + ". Trying to continue.", e);
+            }
+        }
+
+        m_log.log(LogService.LOG_DEBUG, "found configuration admin " + ref + " done");
+    }
+
+    public void begin(DeploymentSession session)
+    {
         m_log.log(LogService.LOG_DEBUG, "beginning session " + session);
-        synchronized (m_lock) {
-            if (m_session != null) {
+
+        synchronized (m_lock)
+        {
+            DeploymentSession current = m_sessionRef.get();
+            if (current != null)
+            {
                 throw new IllegalArgumentException("Trying to begin new deployment session while already in one.");
             }
-            if (session == null) {
+            if (session == null)
+            {
                 throw new IllegalArgumentException("Trying to begin new deployment session with a null session.");
             }
-            if (!m_toBeInstalled.isEmpty() || !m_toBeDeleted.isEmpty() || !m_configurationAdminTasks.isEmpty() || !m_postCommitTasks.isEmpty() || m_component != null) {
+            if (!m_toBeInstalled.isEmpty() || !m_toBeDeleted.isEmpty() || !m_configurationAdminTasks.isEmpty() || !m_postCommitTasks.isEmpty() || m_component != null)
+            {
                 throw new IllegalStateException("State not reset correctly at start of session.");
             }
-            m_session = session;
+            m_sessionRef.set(session);
         }
     }
- 
-    public void process(String name, InputStream stream) throws ResourceProcessorException {
-        m_log.log(LogService.LOG_DEBUG, "processing " + name);
-        // initial validation
-        synchronized (m_lock) {
-            if (m_session == null) {
-                throw new ResourceProcessorException(CODE_OTHER_ERROR, "Can not process resource without a Deployment Session");
-            }
-        }
-        MetaDataReader reader = new MetaDataReader();
-        MetaData data = null;
-        try {
-            data = reader.parse(stream);
-        }
-        catch (IOException e) {
-            throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unable to process resource.", e);
-        }
-        if (data == null) {
-            throw new ResourceProcessorException(CODE_OTHER_ERROR, "Supplied configuration is not conform the metatype xml specification.");
-        }
-        // process resources
-        String filter = null;
-        Map optionalAttributes = data.getOptionalAttributes();
-        if (optionalAttributes != null) {
-            filter = (String) optionalAttributes.get(AutoConfResourceProcessor.CONFIGURATION_ADMIN_FILTER_ATTRIBUTE);
-        }
-        // add to session data
-        if (!m_toBeInstalled.containsKey(name)) {
-            m_toBeInstalled.put(name, new ArrayList());
-        }
-        List designates = data.getDesignates();
-        if (designates == null || designates.isEmpty()) {
-            // if there are no designates, there's nothing to process
-            m_log.log(LogService.LOG_INFO, "No designates found in the resource, so there's nothing to process.");
-            return;
-        }
-        Map localOcds = data.getObjectClassDefinitions();
-        if (localOcds == null) {
-            localOcds = Collections.EMPTY_MAP;
-        }
-        Iterator i = designates.iterator();
-        while (i.hasNext()) {
-            Designate designate = (Designate) i.next();
-            
-            // check object
-            if (designate.getObject() == null) {
-                throw new ResourceProcessorException(CODE_OTHER_ERROR, "Designate Object child missing or invalid");
-            }
-            
-            // check attributes
-            if (designate.getObject().getAttributes() == null || designate.getObject().getAttributes().size() == 0) {
-                throw new ResourceProcessorException(CODE_OTHER_ERROR, "Object Attributes child missing or invalid");
-            }
-            
-            // check ocdRef
-            String ocdRef = designate.getObject().getOcdRef();
-            if (ocdRef == null || "".equals(ocdRef)) {
-                throw new ResourceProcessorException(CODE_OTHER_ERROR, "Object ocdRef attribute missing or invalid");
-            }
 
-            // determine OCD
-            ObjectClassDefinition ocd = null;
-            OCD localOcd = (OCD) localOcds.get(ocdRef);
-            // ask meta type service for matching OCD if no local OCD has been defined
-            ocd = (localOcd != null) ? new ObjectClassDefinitionImpl(localOcd) : getMetaTypeOCD(data, designate);
-            if (ocd == null) {
-                throw new ResourceProcessorException(CODE_OTHER_ERROR, "No Object Class Definition found with id=" + ocdRef);
-            }
-            // determine configuration data based on the values and their type definition
-            Dictionary dict = getProperties(designate, ocd);
-            if (dict == null) {
-                // designate does not match it's definition, but was marked optional, ignore it
-                continue;
-            }
-            List resources = (List) m_toBeInstalled.get(name);
-            resources.add(new AutoConfResource(name, designate.getPid(), designate.getFactoryPid(), designate.getBundleLocation(), designate.isMerge(), dict, filter));
-        }
-        m_log.log(LogService.LOG_DEBUG, "processing " + name + " done");
+    public void cancel()
+    {
+        m_log.log(LogService.LOG_DEBUG, "cancel");
+        rollback();
     }
 
-    public void dropped(String name) throws ResourceProcessorException {
-        m_log.log(LogService.LOG_DEBUG, "dropped " + name);
-        synchronized (m_lock) {
-        	if (m_session == null) {
-        		throw new ResourceProcessorException(CODE_OTHER_ERROR, "Can not process resource without a Deployment Session");
-        	}
-        }
-    	try {
-    		List resources = m_persistencyManager.load(name);
-    		if (!m_toBeDeleted.containsKey(name)) {
-    			m_toBeDeleted.put(name, new ArrayList());
-    		}
-    		((List) m_toBeDeleted.get(name)).addAll(resources);
-    	}
-    	catch (IOException ioe) {
-    		throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unable to drop resource: " + name, ioe);
-    	}
-        m_log.log(LogService.LOG_DEBUG, "dropped " + name + " done");
-    }
-
-    public void dropAllResources() throws ResourceProcessorException {
-        m_log.log(LogService.LOG_DEBUG, "drop all resources");
-        synchronized (m_lock) {
-        	if (m_session == null) {
-        		throw new ResourceProcessorException(CODE_OTHER_ERROR, "Can not drop all resources without a Deployment Session");
-        	}
-        }
-
-    	File basedir = m_bc.getDataFile("");
-    	if (basedir != null && basedir.isDirectory()) {
-    		String[] files = basedir.list();
-    		for (int i = 0; i < files.length; i++) {
-    			dropped(files[i]);
-    		}
-    	}
-    	else {
-    		throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unable to drop resources, data area is not accessible");
-    	}
-        m_log.log(LogService.LOG_DEBUG, "drop all resources done");
-    }
-    
-    private List m_configurationAdminTasks = new ArrayList();
-    private List m_postCommitTasks = new ArrayList();
-
-    public void prepare() throws ResourceProcessorException {
-        m_log.log(LogService.LOG_DEBUG, "prepare");
-        synchronized (m_lock) {
-        	if (m_session == null) {
-        		throw new ResourceProcessorException(CODE_OTHER_ERROR, "Can not process resource without a Deployment Session");
-        	}
-        }
-    	try {
-            m_log.log(LogService.LOG_DEBUG, "prepare delete");
-    		// delete dropped resources
-    		for (Iterator i = m_toBeDeleted.keySet().iterator(); i.hasNext();) {
-    			String name = (String) i.next();
-    			List resources = (List) m_toBeDeleted.get(name);
-    			for (Iterator j = resources.iterator(); j.hasNext();) {
-    				AutoConfResource resource = (AutoConfResource) j.next();
-    				m_configurationAdminTasks.add(new DropResourceTask(resource));
-    			}
-    			m_postCommitTasks.add(new DeleteResourceTask(name));
-    		}
-
-            m_log.log(LogService.LOG_DEBUG, "prepare install/update");
-    		// install new/updated resources
-    		for (Iterator j = m_toBeInstalled.keySet().iterator(); j.hasNext();) {
-    			String name = (String) j.next();
-    			List existingResources = null;
-    			try {
-    				existingResources = m_persistencyManager.load(name);
-    			}
-    			catch (IOException ioe) {
-    				throw new ResourceProcessorException(ResourceProcessorException.CODE_PREPARE, "Unable to read existing resources for resource " + name, ioe);
-    			}
-    			List resources = (List) m_toBeInstalled.get(name);
-    			for (Iterator iterator = resources.iterator(); iterator.hasNext();) {
-    				AutoConfResource resource = (AutoConfResource) iterator.next();
-    				m_configurationAdminTasks.add(new InstallOrUpdateResourceTask(resource));
-    			}
-    			// remove existing configurations that were not in the new version of the resource
-    			for (Iterator i = existingResources.iterator(); i.hasNext();) {
-    				AutoConfResource existingResource = (AutoConfResource) i.next();
-    				Configuration configuration = null;
-    				if (existingResource.isFactoryConfig()) {
-    					configuration = m_configAdmin.getConfiguration(existingResource.getGeneratedPid(), existingResource.getBundleLocation());
-    				} else {
-    					configuration = m_configAdmin.getConfiguration(existingResource.getPid(), existingResource.getBundleLocation());
-    				}
-    				configuration.delete();
-    			}
-    			m_postCommitTasks.add(new StoreResourceTask(name, resources));
-    		}
-    	}
-    	catch (IOException ioe) {
-    		m_toBeInstalled.clear();
-    		throw new ResourceProcessorException(ResourceProcessorException.CODE_PREPARE, "Unable to prepare for commit for resource", ioe);
-    	}
-        m_log.log(LogService.LOG_DEBUG, "prepare done");
-    }
-
-    public synchronized void commit() {
+    public void commit()
+    {
         m_log.log(LogService.LOG_DEBUG, "commit");
 
         Dictionary properties = new Properties();
@@ -298,144 +163,387 @@
             .setCallbacks(null, null, null, null)
             .setAutoConfig(Component.class, false)
             .add(m_dm.createServiceDependency()
-                .setService(ConfigurationAdmin.class)
-                .setCallbacks("addConfigurationAdmin", null)
-                .setRequired(false));
+                    .setService(ConfigurationAdmin.class)
+                    .setCallbacks("addConfigurationAdmin", null)
+                    .setRequired(false)
+        );
         m_dm.add(m_component);
 
         m_log.log(LogService.LOG_DEBUG, "commit done");
     }
-    
-    public void addConfigurationAdmin(ServiceReference ref, ConfigurationAdmin ca) {
-        m_log.log(LogService.LOG_DEBUG, "found configuration admin " + ref);
-        Iterator iterator = m_configurationAdminTasks.iterator();
-        while (iterator.hasNext()) {
-            ConfigurationAdminTask task = (ConfigurationAdminTask) iterator.next();
-            try {
-                Filter filter = null;
-                String filterString = task.getFilter();
-                if (filterString != null) {
-                    try {
-                        filter = m_bc.createFilter(filterString);
-                    }
-                    catch (InvalidSyntaxException e) {
-                        m_log.log(LogService.LOG_ERROR, "Could not parse filter, ignoring it: " + filterString, e);
-                    }
-                }
-                if (filter == null || filter != null && filter.match(ref)) {
-                    task.run(m_persistencyManager, ca);
-                }
-            }
-            catch (Exception e) {
-                m_log.log(LogService.LOG_ERROR, "Exception during configuration to " + ca + ". Trying to continue.", e);
-            }
+
+    public void dropAllResources() throws ResourceProcessorException
+    {
+        m_log.log(LogService.LOG_DEBUG, "drop all resources");
+
+        assertInDeploymentSession("Can not drop all resources without a Deployment Session");
+
+        for (String name : m_persistencyManager.getResourceNames())
+        {
+            dropped(name);
         }
-        m_log.log(LogService.LOG_DEBUG, "found configuration admin " + ref + " done");
+
+        m_log.log(LogService.LOG_DEBUG, "drop all resources done");
     }
-    
-    public void postcommit() {
+
+    public void dropped(String name) throws ResourceProcessorException
+    {
+        m_log.log(LogService.LOG_DEBUG, "dropped " + name);
+
+        assertInDeploymentSession("Can not drop resource without a Deployment Session");
+
+        Map<String, List<AutoConfResource>> toBeDeleted;
+        synchronized (m_lock)
+        {
+            toBeDeleted = new HashMap<String, List<AutoConfResource>>(m_toBeDeleted);
+        }
+
+        try
+        {
+            List<AutoConfResource> resources = m_persistencyManager.load(name);
+
+            if (!toBeDeleted.containsKey(name))
+            {
+                toBeDeleted.put(name, new ArrayList());
+            }
+            toBeDeleted.get(name).addAll(resources);
+        }
+        catch (IOException ioe)
+        {
+            throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unable to drop resource: " + name, ioe);
+        }
+
+        synchronized (m_lock)
+        {
+            m_toBeDeleted.putAll(toBeDeleted);
+        }
+
+        m_log.log(LogService.LOG_DEBUG, "dropped " + name + " done");
+    }
+
+    public void handleEvent(Event event)
+    {
+        // regardless of the outcome, we simply invoke postcommit
+        postcommit();
+    }
+
+    public void postcommit()
+    {
         m_log.log(LogService.LOG_DEBUG, "post commit");
-        Iterator iterator = m_postCommitTasks.iterator();
-        while (iterator.hasNext()) {
-            PostCommitTask task = (PostCommitTask) iterator.next();
-            try {
+
+        List<PostCommitTask> postCommitTasks;
+        synchronized (m_lock)
+        {
+            postCommitTasks = new ArrayList<PostCommitTask>(m_postCommitTasks);
+        }
+
+        for (PostCommitTask task : postCommitTasks)
+        {
+            try
+            {
                 task.run(m_persistencyManager);
             }
-            catch (Exception e) {
+            catch (Exception e)
+            {
                 m_log.log(LogService.LOG_ERROR, "Exception during post commit wrap-up. Trying to continue.", e);
             }
         }
+
         endSession();
+
         m_log.log(LogService.LOG_DEBUG, "post commit done");
     }
 
-    private void endSession() {
-        if (m_component != null) {
-        	m_dm.remove(m_component);
-            m_component = null;
+    public void prepare() throws ResourceProcessorException
+    {
+        m_log.log(LogService.LOG_DEBUG, "prepare");
+
+        assertInDeploymentSession("Can not prepare resource without a Deployment Session");
+
+        Map<String, List<AutoConfResource>> toBeDeleted;
+        Map<String, List<AutoConfResource>> toBeInstalled;
+        synchronized (m_lock)
+        {
+            toBeDeleted = new HashMap<String, List<AutoConfResource>>(m_toBeDeleted);
+            toBeInstalled = new HashMap<String, List<AutoConfResource>>(m_toBeInstalled);
         }
-        m_toBeInstalled.clear();
-        m_toBeDeleted.clear();
-        m_postCommitTasks.clear();
-        m_configurationAdminTasks.clear();
-        m_session = null;
+
+        List<ConfigurationAdminTask> configAdminTasks = new ArrayList<ConfigurationAdminTask>();
+        List<PostCommitTask> postCommitTasks = new ArrayList<PostCommitTask>();
+
+        m_log.log(LogService.LOG_DEBUG, "prepare delete");
+        // delete dropped resources
+        for (Map.Entry<String, List<AutoConfResource>> entry : toBeDeleted.entrySet())
+        {
+            String name = entry.getKey();
+            for (AutoConfResource resource : entry.getValue())
+            {
+                configAdminTasks.add(new DropResourceTask(resource));
+            }
+            postCommitTasks.add(new DeleteResourceTask(name));
+        }
+
+        m_log.log(LogService.LOG_DEBUG, "prepare install/update");
+        // install new/updated resources
+        for (Map.Entry<String, List<AutoConfResource>> entry : toBeInstalled.entrySet())
+        {
+            String name = entry.getKey();
+
+            List<AutoConfResource> existingResources = null;
+            try
+            {
+                existingResources = m_persistencyManager.load(name);
+            }
+            catch (IOException ioe)
+            {
+                throw new ResourceProcessorException(ResourceProcessorException.CODE_PREPARE, "Unable to read existing resources for resource " + name, ioe);
+            }
+
+            List<AutoConfResource> resources = entry.getValue();
+            for (AutoConfResource resource : resources)
+            {
+                // When updating existing configurations, make sure that we delete the ones that have become obsolete... 
+                if (existingResources != null)
+                {
+                    Iterator<AutoConfResource> iter = existingResources.iterator();
+                    while (iter.hasNext())
+                    {
+                        AutoConfResource existing = iter.next();
+                        if (existing.equalsTargetConfiguration(resource))
+                        {
+                            iter.remove();
+                        }
+                    }
+                }
+
+                configAdminTasks.add(new InstallOrUpdateResourceTask(resource));
+            }
+            // remove existing configurations that were not in the new version of the resource
+            for (AutoConfResource existingResource : existingResources)
+            {
+                configAdminTasks.add(new DropResourceTask(existingResource));
+            }
+
+            postCommitTasks.add(new StoreResourceTask(name, resources));
+        }
+
+        synchronized (m_lock)
+        {
+            m_configurationAdminTasks.addAll(configAdminTasks);
+            m_postCommitTasks.addAll(postCommitTasks);
+        }
+
+        m_log.log(LogService.LOG_DEBUG, "prepare done");
     }
 
-    public void rollback() {
+    public void process(String name, InputStream stream) throws ResourceProcessorException
+    {
+        m_log.log(LogService.LOG_DEBUG, "processing " + name);
+
+        // initial validation
+        assertInDeploymentSession("Can not process resource without a Deployment Session");
+
+        Map<String, List<AutoConfResource>> toBeInstalled;
+        synchronized (m_lock)
+        {
+            toBeInstalled = new HashMap<String, List<AutoConfResource>>(m_toBeInstalled);
+        }
+
+        MetaData data = parseAutoConfResource(stream);
+        // process resources
+        Filter filter = getFilter(data);
+
+        // add to session data
+        if (!toBeInstalled.containsKey(name))
+        {
+            toBeInstalled.put(name, new ArrayList<AutoConfResource>());
+        }
+
+        List<Designate> designates = data.getDesignates();
+        if (designates == null || designates.isEmpty())
+        {
+            // if there are no designates, there's nothing to process
+            m_log.log(LogService.LOG_INFO, "No designates found in the resource, so there's nothing to process.");
+            return;
+        }
+
+        Map<String, OCD> localOcds = data.getObjectClassDefinitions();
+        if (localOcds == null)
+        {
+            localOcds = Collections.emptyMap();
+        }
+
+        for (Designate designate : designates)
+        {
+            // check object
+            DesignateObject objectDef = designate.getObject();
+            if (objectDef == null)
+            {
+                throw new ResourceProcessorException(CODE_OTHER_ERROR, "Designate Object child missing or invalid");
+            }
+
+            // check attributes
+            if (objectDef.getAttributes() == null || objectDef.getAttributes().isEmpty())
+            {
+                throw new ResourceProcessorException(CODE_OTHER_ERROR, "Object Attributes child missing or invalid");
+            }
+
+            // check ocdRef
+            String ocdRef = objectDef.getOcdRef();
+            if (ocdRef == null || "".equals(ocdRef))
+            {
+                throw new ResourceProcessorException(CODE_OTHER_ERROR, "Object ocdRef attribute missing or invalid");
+            }
+
+            // determine OCD
+            ObjectClassDefinition ocd = null;
+            OCD localOcd = localOcds.get(ocdRef);
+            // ask meta type service for matching OCD if no local OCD has been defined
+            ocd = (localOcd != null) ? new ObjectClassDefinitionImpl(localOcd) : getMetaTypeOCD(data, designate);
+            if (ocd == null)
+            {
+                throw new ResourceProcessorException(CODE_OTHER_ERROR, "No Object Class Definition found with id=" + ocdRef);
+            }
+
+            // determine configuration data based on the values and their type definition
+            Dictionary dict = MetaTypeUtil.getProperties(designate, ocd);
+            if (dict == null)
+            {
+                // designate does not match it's definition, but was marked optional, ignore it
+                continue;
+            }
+
+            AutoConfResource resource = new AutoConfResource(name, designate.getPid(), designate.getFactoryPid(), designate.getBundleLocation(), designate.isMerge(), dict, filter);
+            
+            toBeInstalled.get(name).add(resource);
+        }
+
+        synchronized (m_lock)
+        {
+            m_toBeInstalled.putAll(toBeInstalled);
+        }
+
+        m_log.log(LogService.LOG_DEBUG, "processing " + name + " done");
+    }
+
+    public void rollback()
+    {
         m_log.log(LogService.LOG_DEBUG, "rollback");
-    	Set keys = m_toBeInstalled.keySet();
-    	for (Iterator i = keys.iterator(); i.hasNext();) {
-    		List configs = (List) m_toBeInstalled.get(i.next());
-    		for (Iterator j = configs.iterator(); j.hasNext();) {
-    			AutoConfResource resource = (AutoConfResource) j.next();
-    			String name = resource.getName();
-    			try {
-    				dropped(name);
-    			}
-    			catch (ResourceProcessorException e) {
-    				m_log.log(LogService.LOG_ERROR, "Unable to roll back resource '" + name + "', reason: " + e.getMessage() + ", caused by: " + e.getCause().getMessage());
-    			}
-    			break;
-    		}
-    	}
-    	endSession();
+
+        Map<String, List<AutoConfResource>> toBeInstalled;
+        synchronized (m_lock)
+        {
+            toBeInstalled = new HashMap<String, List<AutoConfResource>>(m_toBeInstalled);
+        }
+
+        for (Map.Entry<String, List<AutoConfResource>> entry : toBeInstalled.entrySet())
+        {
+            for (AutoConfResource resource : entry.getValue())
+            {
+                String name = resource.getName();
+                try
+                {
+                    dropped(name);
+                }
+                catch (ResourceProcessorException e)
+                {
+                    m_log.log(LogService.LOG_ERROR, "Unable to roll back resource '" + name + "', reason: " + e.getMessage() + ", caused by: " + e.getCause().getMessage());
+                }
+                break;
+            }
+        }
+
+        endSession();
+
         m_log.log(LogService.LOG_DEBUG, "rollback done");
     }
 
-    public void cancel() {
-        m_log.log(LogService.LOG_DEBUG, "cancel");
-    	rollback();
+    /**
+     * Called by Felix DM when starting this component.
+     */
+    public void start() throws IOException
+    {
+        File root = m_dm.getBundleContext().getDataFile("");
+        if (root == null)
+        {
+            throw new IOException("No file system support");
+        }
+        m_persistencyManager = new PersistencyManager(root);
     }
 
-    /**
-     * Determines the actual configuration data based on the specified designate and object class definition
-     * 
-     * @param designate The designate object containing the values for the properties
-     * @param ocd The object class definition
-     * @return A dictionary containing data as described in the designate and ocd objects, or <code>null</code> if the designate does not match it's
-     * definition and the designate was marked as optional.
-     * @throws ResourceProcessorException If the designate does not match the ocd and the designate is not marked as optional.
-     */
-    private Dictionary getProperties(Designate designate, ObjectClassDefinition ocd) throws ResourceProcessorException {
-    	Dictionary properties = new Hashtable();
-    	AttributeDefinition[] attributeDefs = ocd.getAttributeDefinitions(ObjectClassDefinition.ALL);
-    	List attributes = designate.getObject().getAttributes();
+    private void assertInDeploymentSession(String msg) throws ResourceProcessorException
+    {
+        synchronized (m_lock)
+        {
+            DeploymentSession current = m_sessionRef.get();
+            if (current == null)
+            {
+                throw new ResourceProcessorException(CODE_OTHER_ERROR, msg);
+            }
+        }
+    }
 
-    	for (Iterator i = attributes.iterator(); i.hasNext();) {
-    		Attribute attribute = (Attribute) i.next();
+    private void endSession()
+    {
+        if (m_component != null)
+        {
+            m_dm.remove(m_component);
+            m_component = null;
+        }
+        synchronized (m_lock)
+        {
+            m_toBeInstalled.clear();
+            m_toBeDeleted.clear();
+            m_postCommitTasks.clear();
+            m_configurationAdminTasks.clear();
+            m_sessionRef.set(null);
+        }
+    }
 
-    		String adRef = attribute.getAdRef();
-    		boolean found = false;
-    		for(int j = 0; j < attributeDefs.length; j++) {
-    			AttributeDefinition ad = attributeDefs[j];
-    			if (adRef.equals(ad.getID())) {
-    				// found attribute definition
-    				Object value = getValue(attribute, ad);
-    				if (value == null) {
-    					if (designate.isOptional()) {
-    						properties = null;
-    						break;
-    					}
-    					else {
-    						throw new ResourceProcessorException(CODE_OTHER_ERROR, "Could not match attribute to it's definition: adref=" + adRef);
-    					}
-    				}
-    				properties.put(adRef, value);
-    				found = true;
-    				break;
-    			}
-    		}
-    		if (!found) {
-    			if (designate.isOptional()) {
-    				properties = null;
-    				break;
-    			} else {
-    				throw new ResourceProcessorException(CODE_OTHER_ERROR, "Could not find attribute definition: adref=" + adRef);
-    			}
-    		}
-    	}
+    private Bundle getBundle(String bundleLocation, boolean isFactory) throws ResourceProcessorException
+    {
+        Bundle bundle = null;
+        if (!isFactory)
+        {
+            // singleton configuration, no foreign bundles allowed, use source deployment package to find specified bundle
+            if (bundleLocation.startsWith(LOCATION_PREFIX))
+            {
+                DeploymentSession session = m_sessionRef.get();
+                bundle = session.getSourceDeploymentPackage().getBundle(bundleLocation.substring(LOCATION_PREFIX.length()));
+            }
+        }
+        else
+        {
+            // factory configuration, foreign bundles allowed, use bundle context to find the specified bundle
+            Bundle[] bundles = m_dm.getBundleContext().getBundles();
+            for (int i = 0; i < bundles.length; i++)
+            {
+                String location = bundles[i].getLocation();
+                if (bundleLocation.equals(location))
+                {
+                    bundle = bundles[i];
+                    break;
+                }
+            }
+        }
+        return bundle;
+    }
 
-    	return properties;
+    private Filter getFilter(MetaData data) throws ResourceProcessorException
+    {
+        Map optionalAttributes = data.getOptionalAttributes();
+        if (optionalAttributes != null)
+        {
+            try
+            {
+                return FrameworkUtil.createFilter((String) optionalAttributes.get(AutoConfResourceProcessor.CONFIGURATION_ADMIN_FILTER_ATTRIBUTE));
+            }
+            catch (InvalidSyntaxException e)
+            {
+                throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unable to create filter!", e);
+            }
+        }
+        return null;
     }
 
     /**
@@ -446,198 +554,112 @@
      * @return
      * @throws ResourceProcessorException
      */
-    private ObjectClassDefinition getMetaTypeOCD(MetaData data, Designate designate) throws ResourceProcessorException {
-    	ObjectClassDefinition ocd = null;
-    	String ocdRef = designate.getObject().getOcdRef();
-    	Bundle bundle = getBundle(designate.getBundleLocation(), isFactoryConfig(designate));
-    	if (bundle != null) {
-    		MetaTypeInformation mti = m_metaService.getMetaTypeInformation(bundle);
-    		if (mti != null) {
-    			String pid = isFactoryConfig(designate) ? pid = designate.getFactoryPid() : designate.getPid();
-    			try {
-    				ObjectClassDefinition tempOcd = mti.getObjectClassDefinition(pid, null);
-    				// tempOcd will always have a value, if pid was not known IAE will be thrown
-    				if (ocdRef.equals(tempOcd.getID())) {
-    					ocd = tempOcd;
-    				}
-    			}
-    			catch (IllegalArgumentException iae) {
-    				// let null be returned
-    			}
-    		}
-    	}
-    	return ocd;
+    private ObjectClassDefinition getMetaTypeOCD(MetaData data, Designate designate) throws ResourceProcessorException
+    {
+        boolean isFactoryConfig = isFactoryConfig(designate);
+
+        Bundle bundle = getBundle(designate.getBundleLocation(), isFactoryConfig);
+        if (bundle == null)
+        {
+            return null;
+        }
+
+        MetaTypeInformation mti = m_metaService.getMetaTypeInformation(bundle);
+        if (mti == null)
+        {
+            return null;
+        }
+
+        String pid = isFactoryConfig ? pid = designate.getFactoryPid() : designate.getPid();
+        try
+        {
+            ObjectClassDefinition tempOcd = mti.getObjectClassDefinition(pid, null);
+            // tempOcd will always have a value, if pid was not known IAE will be thrown
+            String ocdRef = designate.getObject().getOcdRef();
+            if (ocdRef.equals(tempOcd.getID()))
+            {
+                return tempOcd;
+            }
+        }
+        catch (IllegalArgumentException iae)
+        {
+            // let null be returned
+        }
+
+        return null;
     }
 
-    private boolean isFactoryConfig(Designate designate) {
-    	String factoryPid = designate.getFactoryPid();
-    	return (factoryPid != null && !"".equals(factoryPid));
+    private boolean isFactoryConfig(Designate designate)
+    {
+        String factoryPid = designate.getFactoryPid();
+        return (factoryPid != null && !"".equals(factoryPid));
     }
 
-    private Bundle getBundle(String bundleLocation, boolean isFactory) throws ResourceProcessorException {
-    	Bundle bundle = null;
-    	if (!isFactory) {
-    		// singleton configuration, no foreign bundles allowed, use source deployment package to find specified bundle
-    		if (bundleLocation.startsWith(LOCATION_PREFIX)) {
-    			bundle = m_session.getSourceDeploymentPackage().getBundle(bundleLocation.substring(LOCATION_PREFIX.length()));
-    		}
-    	}
-    	else {
-    		// factory configuration, foreign bundles allowed, use bundle context to find the specified bundle
-    		Bundle[] bundles = m_bc.getBundles();                                                                               
-    		for (int i = 0; i < bundles.length; i++) {
-    			String location = bundles[i].getLocation();
-    			if (bundleLocation.equals(location)) {
-    				bundle = bundles[i];
-    				break;
-    			}
-    		}
-    	}
-    	return bundle;
-    }
-
-    /**
-     * Determines the value of an attribute based on an attribute definition
-     * 
-     * @param attribute The attribute containing value(s)
-     * @param ad The attribute definition
-     * @return An <code>Object</code> reflecting what was specified in the attribute and it's definition or <code>null</code> if the value did not match it's definition.
-     * @throws ResourceProcessorException in case we're unable to parse the value of an attribute.
-     */
-    private Object getValue(Attribute attribute, AttributeDefinition ad) throws ResourceProcessorException {
-    	if (attribute == null || ad == null || !attribute.getAdRef().equals(ad.getID())) {
-    		// wrong attribute or definition
-    		return null;
-    	}
-    	String[] content = attribute.getContent();
-
-    	// verify correct type of the value(s)
-    	int type = ad.getType();
-    	Object[] typedContent = null;
-    	try {
-    		for (int i = 0; i < content.length; i++) {
-    			String value = content[i];
-    			switch (type) {
-	    			case AttributeDefinition.BOOLEAN:
-	    				typedContent = (typedContent == null) ? new Boolean[content.length] : typedContent;
-	    				typedContent[i] = Boolean.valueOf(value);	
-	    				break;
-	    			case AttributeDefinition.BYTE:
-	    				typedContent = (typedContent == null) ? new Byte[content.length] : typedContent;
-	    				typedContent[i] = Byte.valueOf(value);
-	    				break;
-	    			case AttributeDefinition.CHARACTER:
-	    				typedContent = (typedContent == null) ? new Character[content.length] : typedContent;
-	    				char[] charArray = value.toCharArray();
-	    				if (charArray.length == 1) {
-	    					typedContent[i] = new Character(charArray[0]);
-	    				}
-	    				else {
-	    		            throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unable to parse value for definition: adref=" + ad.getID());
-	    				}
-	    				break;
-	    			case AttributeDefinition.DOUBLE:
-	    				typedContent = (typedContent == null) ? new Double[content.length] : typedContent;
-	    				typedContent[i] = Double.valueOf(value);
-	    				break;
-	    			case AttributeDefinition.FLOAT:
-	    				typedContent = (typedContent == null) ? new Float[content.length] : typedContent;
-	    				typedContent[i] = Float.valueOf(value);
-	    				break;
-	    			case AttributeDefinition.INTEGER:
-	    				typedContent = (typedContent == null) ? new Integer[content.length] : typedContent;
-	    				typedContent[i] = Integer.valueOf(value);
-	    				break;
-	    			case AttributeDefinition.LONG:
-	    				typedContent = (typedContent == null) ? new Long[content.length] : typedContent;
-	    				typedContent[i] = Long.valueOf(value);
-	    				break;
-	    			case AttributeDefinition.SHORT:
-	    				typedContent = (typedContent == null) ? new Short[content.length] : typedContent;
-	    				typedContent[i] = Short.valueOf(value);
-	    				break;
-	    			case AttributeDefinition.STRING:
-	    				typedContent = (typedContent == null) ? new String[content.length] : typedContent;
-	    				typedContent[i] = value;
-	    				break;
-	    			default:
-	    				// unsupported type
-                        throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unsupported value-type for definition: adref=" + ad.getID());
-    			}
-    		}
-    	}
-    	catch (NumberFormatException nfe) {
-            throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unable to parse value for definition: adref=" + ad.getID());
-    	}
-
-    	// verify cardinality of value(s)
-    	int cardinality = ad.getCardinality();
-    	Object result = null;
-    	if (cardinality == 0) {
-    		if (typedContent.length == 1) {
-    			result = typedContent[0];
-    		}
-    		else {
-    			result = null;
-    		}
-    	}
-    	else if (cardinality == Integer.MIN_VALUE) {
-    		result = new Vector(Arrays.asList(typedContent));
-    	}
-    	else if (cardinality == Integer.MAX_VALUE) {
-    		result = typedContent;
-    	}
-    	else if (cardinality < 0) {
-            if (typedContent.length <= Math.abs(cardinality)) {
-    			result = new Vector(Arrays.asList(typedContent));
-    		}
-    		else {
-    			result = null;
-    		}
-    	}
-    	else if (cardinality > 0) {
-    		if (typedContent.length <= cardinality) {
-    			result = typedContent;
-    		}
-    		else {
-    			result = null;
-    		}
-    	}
-    	return result;
-    }
-
-    public void handleEvent(Event event) {
-    	// regardless of the outcome, we simply invoke postcommit
-    	postcommit();
+    private MetaData parseAutoConfResource(InputStream stream) throws ResourceProcessorException
+    {
+        MetaDataReader reader = new MetaDataReader();
+        MetaData data = null;
+        try
+        {
+            data = reader.parse(stream);
+        }
+        catch (IOException e)
+        {
+            throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unable to process resource.", e);
+        }
+        if (data == null)
+        {
+            throw new ResourceProcessorException(CODE_OTHER_ERROR, "Supplied configuration is not conform the metatype xml specification.");
+        }
+        return data;
     }
 }
 
-interface ConfigurationAdminTask {
-    public String getFilter();
+interface ConfigurationAdminTask
+{
+    public Filter getFilter();
+
     public void run(PersistencyManager persistencyManager, ConfigurationAdmin configAdmin) throws Exception;
 }
 
-interface PostCommitTask {
-    public void run(PersistencyManager manager) throws Exception;
+class DeleteResourceTask implements PostCommitTask
+{
+    private final String m_name;
+
+    public DeleteResourceTask(String name)
+    {
+        m_name = name;
+    }
+
+    public void run(PersistencyManager manager) throws Exception
+    {
+        manager.delete(m_name);
+    }
 }
 
-class DropResourceTask implements ConfigurationAdminTask {
+class DropResourceTask implements ConfigurationAdminTask
+{
     private final AutoConfResource m_resource;
 
-    public DropResourceTask(AutoConfResource resource) {
+    public DropResourceTask(AutoConfResource resource)
+    {
         m_resource = resource;
     }
-    
-    public String getFilter() {
+
+    public Filter getFilter()
+    {
         return m_resource.getFilter();
     }
 
-    public void run(PersistencyManager persistencyManager, ConfigurationAdmin configAdmin) throws Exception {
+    public void run(PersistencyManager persistencyManager, ConfigurationAdmin configAdmin) throws Exception
+    {
         String pid;
-        if (m_resource.isFactoryConfig()) {
+        if (m_resource.isFactoryConfig())
+        {
             pid = m_resource.getGeneratedPid();
         }
-        else {
+        else
+        {
             pid = m_resource.getPid();
         }
         Configuration configuration = configAdmin.getConfiguration(pid, m_resource.getBundleLocation());
@@ -645,69 +667,87 @@
     }
 }
 
-class InstallOrUpdateResourceTask implements ConfigurationAdminTask {
+class InstallOrUpdateResourceTask implements ConfigurationAdminTask
+{
     private final AutoConfResource m_resource;
 
-    public InstallOrUpdateResourceTask(AutoConfResource resource) {
+    public InstallOrUpdateResourceTask(AutoConfResource resource)
+    {
         m_resource = resource;
     }
 
-    public String getFilter() {
+    public Filter getFilter()
+    {
         return m_resource.getFilter();
     }
 
-    public void run(PersistencyManager persistencyManager, ConfigurationAdmin configAdmin) throws Exception {
+    public void run(PersistencyManager persistencyManager, ConfigurationAdmin configAdmin) throws Exception
+    {
         String name = m_resource.getName();
         Dictionary properties = m_resource.getProperties();
         String bundleLocation = m_resource.getBundleLocation();
         Configuration configuration = null;
 
         List existingResources = null;
-        try {
+        try
+        {
             existingResources = persistencyManager.load(name);
         }
-        catch (IOException ioe) {
+        catch (IOException ioe)
+        {
             throw new ResourceProcessorException(ResourceProcessorException.CODE_PREPARE, "Unable to read existing resources for resource " + name, ioe);
         }
-        
+
         // update configuration
-        if (m_resource.isFactoryConfig()) {
+        if (m_resource.isFactoryConfig())
+        {
             // check if this is an factory config instance update
-            for (Iterator i = existingResources.iterator(); i.hasNext();) {
+            for (Iterator i = existingResources.iterator(); i.hasNext();)
+            {
                 AutoConfResource existingResource = (AutoConfResource) i.next();
-                if (m_resource.equalsTargetConfiguration(existingResource)) {
+                if (m_resource.equalsTargetConfiguration(existingResource))
+                {
                     // existing instance found
                     configuration = configAdmin.getConfiguration(existingResource.getGeneratedPid(), bundleLocation);
                     existingResources.remove(existingResource);
                     break;
                 }
             }
-            if (configuration == null) {
+            if (configuration == null)
+            {
                 // no existing instance, create new
                 configuration = configAdmin.createFactoryConfiguration(m_resource.getFactoryPid(), bundleLocation);
             }
             m_resource.setGeneratedPid(configuration.getPid());
         }
-        else {
-            for (Iterator i = existingResources.iterator(); i.hasNext();) {
+        else
+        {
+            for (Iterator i = existingResources.iterator(); i.hasNext();)
+            {
                 AutoConfResource existingResource = (AutoConfResource) i.next();
-                if (m_resource.getPid().equals(existingResource.getPid())) {
+                if (m_resource.getPid().equals(existingResource.getPid()))
+                {
                     // existing resource found
                     existingResources.remove(existingResource);
                     break;
                 }
             }
             configuration = configAdmin.getConfiguration(m_resource.getPid(), bundleLocation);
-            if (!bundleLocation.equals(configuration.getBundleLocation())) {
+            if (!bundleLocation.equals(configuration.getBundleLocation()))
+            {
                 // an existing configuration exists that is bound to a different location, which is not allowed
-                throw new ResourceProcessorException(ResourceProcessorException.CODE_PREPARE, "Existing configuration was bound to " + configuration.getBundleLocation() + " instead of " + bundleLocation);
+                throw new ResourceProcessorException(ResourceProcessorException.CODE_PREPARE,
+                    "Existing configuration was bound to " + configuration.getBundleLocation() + " instead of " + bundleLocation);
             }
         }
-        if (m_resource.isMerge()) {
+        if (m_resource.isMerge())
+        {
             Dictionary existingProperties = configuration.getProperties();
-            if (existingProperties != null) {
+            if (existingProperties != null)
+            {
                 Enumeration keys = existingProperties.keys();
-                while (keys.hasMoreElements()) {
+                while (keys.hasMoreElements())
+                {
                     Object key = keys.nextElement();
                     properties.put(key, existingProperties.get(key));
                 }
@@ -717,28 +757,24 @@
     }
 }
 
-class DeleteResourceTask implements PostCommitTask {
-    private final String m_name;
-
-    public DeleteResourceTask(String name) {
-        m_name = name;
-    }
-
-    public void run(PersistencyManager manager) throws Exception {
-        manager.delete(m_name);
-    }
+interface PostCommitTask
+{
+    public void run(PersistencyManager manager) throws Exception;
 }
 
-class StoreResourceTask implements PostCommitTask {
+class StoreResourceTask implements PostCommitTask
+{
     private final String m_name;
     private final List m_resources;
 
-    public StoreResourceTask(String name, List resources) {
+    public StoreResourceTask(String name, List resources)
+    {
         m_name = name;
         m_resources = resources;
     }
 
-    public void run(PersistencyManager manager) throws Exception {
+    public void run(PersistencyManager manager) throws Exception
+    {
         manager.store(m_name, m_resources);
     }
 }
\ No newline at end of file
diff --git a/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/MetaTypeUtil.java b/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/MetaTypeUtil.java
new file mode 100644
index 0000000..7abbb91
--- /dev/null
+++ b/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/MetaTypeUtil.java
@@ -0,0 +1,233 @@
+/*
+ * 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.felix.deployment.rp.autoconf;
+
+import static org.osgi.service.deploymentadmin.spi.ResourceProcessorException.CODE_OTHER_ERROR;
+
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.felix.metatype.Attribute;
+import org.apache.felix.metatype.Designate;
+import org.osgi.service.deploymentadmin.spi.ResourceProcessorException;
+import org.osgi.service.metatype.AttributeDefinition;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * Convenience methods to work with MetaType structures.
+ */
+public class MetaTypeUtil
+{
+    private MetaTypeUtil()
+    {
+        // Nop
+    }
+
+    /**
+     * Determines the actual configuration data based on the specified designate and object class definition
+     * 
+     * @param designate The designate object containing the values for the properties
+     * @param ocd The object class definition
+     * @return A dictionary containing data as described in the designate and ocd objects, or <code>null</code> if the designate does not match it's
+     * definition and the designate was marked as optional.
+     * @throws ResourceProcessorException If the designate does not match the ocd and the designate is not marked as optional.
+     */
+    public static Dictionary getProperties(Designate designate, ObjectClassDefinition ocd) throws ResourceProcessorException
+    {
+        Dictionary properties = new Hashtable();
+        AttributeDefinition[] attributeDefs = ocd.getAttributeDefinitions(ObjectClassDefinition.ALL);
+
+        List<Attribute> attributes = designate.getObject().getAttributes();
+        for (Attribute attribute : attributes)
+        {
+            String adRef = attribute.getAdRef();
+            boolean found = false;
+            for (int j = 0; j < attributeDefs.length; j++)
+            {
+                AttributeDefinition ad = attributeDefs[j];
+                if (adRef.equals(ad.getID()))
+                {
+                    // found attribute definition
+                    Object value = getValue(attribute, ad);
+                    if (value == null)
+                    {
+                        if (designate.isOptional())
+                        {
+                            properties = null;
+                            break;
+                        }
+                        else
+                        {
+                            throw new ResourceProcessorException(CODE_OTHER_ERROR, "Could not match attribute to it's definition: adref=" + adRef);
+                        }
+                    }
+                    properties.put(adRef, value);
+                    found = true;
+                    break;
+                }
+            }
+            if (!found)
+            {
+                if (designate.isOptional())
+                {
+                    properties = null;
+                    break;
+                }
+                else
+                {
+                    throw new ResourceProcessorException(CODE_OTHER_ERROR, "Could not find attribute definition: adref=" + adRef);
+                }
+            }
+        }
+
+        return properties;
+    }
+
+    /**
+     * Determines the value of an attribute based on an attribute definition
+     * 
+     * @param attribute The attribute containing value(s)
+     * @param ad The attribute definition
+     * @return An <code>Object</code> reflecting what was specified in the attribute and it's definition or <code>null</code> if the value did not match it's definition.
+     * @throws ResourceProcessorException in case we're unable to parse the value of an attribute.
+     */
+    private static Object getValue(Attribute attribute, AttributeDefinition ad) throws ResourceProcessorException
+    {
+        if (attribute == null || ad == null || !attribute.getAdRef().equals(ad.getID()))
+        {
+            // wrong attribute or definition
+            return null;
+        }
+        String[] content = attribute.getContent();
+
+        // verify correct type of the value(s)
+        int type = ad.getType();
+        Object[] typedContent = null;
+        try
+        {
+            for (int i = 0; i < content.length; i++)
+            {
+                String value = content[i];
+                switch (type)
+                {
+                    case AttributeDefinition.BOOLEAN:
+                        typedContent = (typedContent == null) ? new Boolean[content.length] : typedContent;
+                        typedContent[i] = Boolean.valueOf(value);
+                        break;
+                    case AttributeDefinition.BYTE:
+                        typedContent = (typedContent == null) ? new Byte[content.length] : typedContent;
+                        typedContent[i] = Byte.valueOf(value);
+                        break;
+                    case AttributeDefinition.CHARACTER:
+                        typedContent = (typedContent == null) ? new Character[content.length] : typedContent;
+                        char[] charArray = value.toCharArray();
+                        if (charArray.length == 1)
+                        {
+                            typedContent[i] = new Character(charArray[0]);
+                        }
+                        else
+                        {
+                            throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unable to parse value for definition: adref=" + ad.getID());
+                        }
+                        break;
+                    case AttributeDefinition.DOUBLE:
+                        typedContent = (typedContent == null) ? new Double[content.length] : typedContent;
+                        typedContent[i] = Double.valueOf(value);
+                        break;
+                    case AttributeDefinition.FLOAT:
+                        typedContent = (typedContent == null) ? new Float[content.length] : typedContent;
+                        typedContent[i] = Float.valueOf(value);
+                        break;
+                    case AttributeDefinition.INTEGER:
+                        typedContent = (typedContent == null) ? new Integer[content.length] : typedContent;
+                        typedContent[i] = Integer.valueOf(value);
+                        break;
+                    case AttributeDefinition.LONG:
+                        typedContent = (typedContent == null) ? new Long[content.length] : typedContent;
+                        typedContent[i] = Long.valueOf(value);
+                        break;
+                    case AttributeDefinition.SHORT:
+                        typedContent = (typedContent == null) ? new Short[content.length] : typedContent;
+                        typedContent[i] = Short.valueOf(value);
+                        break;
+                    case AttributeDefinition.STRING:
+                        typedContent = (typedContent == null) ? new String[content.length] : typedContent;
+                        typedContent[i] = value;
+                        break;
+                    default:
+                        // unsupported type
+                        throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unsupported value-type for definition: adref=" + ad.getID());
+                }
+            }
+        }
+        catch (NumberFormatException nfe)
+        {
+            throw new ResourceProcessorException(CODE_OTHER_ERROR, "Unable to parse value for definition: adref=" + ad.getID());
+        }
+
+        // verify cardinality of value(s)
+        int cardinality = ad.getCardinality();
+        Object result = null;
+        if (cardinality == 0)
+        {
+            if (typedContent.length == 1)
+            {
+                result = typedContent[0];
+            }
+            else
+            {
+                result = null;
+            }
+        }
+        else if (cardinality == Integer.MIN_VALUE)
+        {
+            result = new Vector(Arrays.asList(typedContent));
+        }
+        else if (cardinality == Integer.MAX_VALUE)
+        {
+            result = typedContent;
+        }
+        else if (cardinality < 0)
+        {
+            if (typedContent.length <= Math.abs(cardinality))
+            {
+                result = new Vector(Arrays.asList(typedContent));
+            }
+            else
+            {
+                result = null;
+            }
+        }
+        else if (cardinality > 0)
+        {
+            if (typedContent.length <= cardinality)
+            {
+                result = typedContent;
+            }
+            else
+            {
+                result = null;
+            }
+        }
+        return result;
+    }
+}
diff --git a/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/PersistencyManager.java b/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/PersistencyManager.java
index d69f917..cd41d30 100644
--- a/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/PersistencyManager.java
+++ b/deploymentadmin/autoconf/src/main/java/org/apache/felix/deployment/rp/autoconf/PersistencyManager.java
@@ -19,6 +19,7 @@
 package org.apache.felix.deployment.rp.autoconf;
 
 import java.io.File;
+import java.io.FileFilter;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -28,104 +29,155 @@
 import java.util.ArrayList;
 import java.util.List;
 
-public class PersistencyManager {
-	
-	private final File m_root;
+public class PersistencyManager
+{
+    private static final FileFilter FILES_ONLY_FILTER = new FileFilter()
+    {
+        public boolean accept(File pathname)
+        {
+            return pathname.isFile();
+        }
+    };
 
-	public PersistencyManager(File root) {
-		m_root = root;
-	}
+    private final File m_root;
 
-	/**
-	 * Stores a resource.
-	 * 
-	 * @param name Name of the resource.
-	 * @param configs List of <code>AutoConfResource</code>s representing the specified resource.
-	 * @throws IOException If the resource could not be stored.
-	 */
-	public void store(String name, List configs) throws IOException {
-		File targetDir = m_root;
-		name = name.replace('/', File.separatorChar);
-		
-		if (name.startsWith(File.separator)) {
-			name = name.substring(1);
-		}
-		int lastSeparator = name.lastIndexOf(File.separator);
-		File target = null;
-		if (lastSeparator != -1) {
-			targetDir = new File(targetDir, name.substring(0, lastSeparator));
-			targetDir.mkdirs();
-		}
-		target = new File(targetDir, name.substring(lastSeparator + 1));
+    public PersistencyManager(File root)
+    {
+        m_root = root;
+    }
 
-		ObjectOutputStream out = null;
-		try {
-			out = new ObjectOutputStream(new FileOutputStream(target));
-			out.writeObject(configs);
-		}
-		finally {
-			if (out != null) {
-				try {
-					out.close();
-				} catch (Exception e) {
-					// not much we can do
-				}
-			}
-		}
-	}
+    /**
+     * Deletes a resource.
+     * 
+     * @param name Name of the resource.
+     * @throws IOException If the resource could not be deleted.
+     */
+    public void delete(String name) throws IOException
+    {
+        name = name.replace('/', File.separatorChar);
+        File target = new File(m_root, name);
+        if (target.exists() && !target.delete())
+        {
+            throw new IOException("Unable to delete file: " + target.getAbsolutePath());
+        }
+        while (target.getParentFile().list().length == 0 && !target.getParentFile().getAbsolutePath().equals(m_root.getAbsolutePath()))
+        {
+            target = target.getParentFile();
+            target.delete();
+        }
+    }
 
-	/**
-	 * Deletes a resource.
-	 * 
-	 * @param name Name of the resource.
-	 * @throws IOException If the resource could not be deleted.
-	 */
-	public void delete(String name) throws IOException {
-		name = name.replace('/', File.separatorChar);
-		File target = new File(m_root, name);
-		if (!target.delete()) {
-			throw new IOException("Unable to delete file: " + target.getAbsolutePath());
-		}
-		while (target.getParentFile().list().length == 0 && !target.getParentFile().getAbsolutePath().equals(m_root.getAbsolutePath())) {
-			target = target.getParentFile();
-			target.delete();
-		}
-	}
+    /**
+     * Returns the names of all persisted resources.
+     * @return a list of resource names, never <code>null</code>.
+     */
+    public List<String> getResourceNames()
+    {
+        List<String> result = new ArrayList<String>();
 
-	/**
-	 * Loads a stored resource.
-	 * 
-	 * @param name Name of the resource.
-	 * @return List of <code>AutoConfResource</code>s representing the specified resource, if the resource is unknown an empty list is returned.
-	 * @throws IOException If the resource could not be properly read.
-	 */
-	public List load(String name) throws IOException {
-		name = name.replace('/', File.separatorChar);
-		List resources = new ArrayList();
-		File resourcesFile = new File(m_root, name);
-		if (resourcesFile.exists()) {
-			ObjectInputStream in = null;
-			try {
-				in = new ObjectInputStream(new FileInputStream(resourcesFile));
-				resources = (List) in.readObject();
-			} 
-			catch (FileNotFoundException fnfe) {
-				throw new IOException("Resource does not exist: " + name);
-			} 
-			catch (ClassNotFoundException cnfe) {
-				throw new IOException("Unable to recreate persisted object from file: " + name);
-			} 
-			finally {
-				if (in != null) {
-					try {
-						in.close();
-					} 
-					catch (Exception e) {
-						// not much we can do
-					}
-				}
-			}
-		}
-		return resources;
-	}
+        File[] list = m_root.listFiles(FILES_ONLY_FILTER);
+        if (list != null && list.length > 0)
+        {
+            for (File resource : list)
+            {
+                result.add(resource.getName());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Loads a stored resource.
+     * 
+     * @param name Name of the resource.
+     * @return List of <code>AutoConfResource</code>s representing the specified resource, if the resource is unknown an empty list is returned.
+     * @throws IOException If the resource could not be properly read.
+     */
+    public List<AutoConfResource> load(String name) throws IOException
+    {
+        List<AutoConfResource> resources = new ArrayList<AutoConfResource>();
+        name = name.replace('/', File.separatorChar);
+        File resourcesFile = new File(m_root, name);
+        if (!resourcesFile.exists())
+        {
+            return resources;
+        }
+
+        ObjectInputStream in = null;
+        try
+        {
+            in = new ObjectInputStream(new FileInputStream(resourcesFile));
+            resources = (List<AutoConfResource>) in.readObject();
+        }
+        catch (FileNotFoundException e)
+        {
+            throw new IOException("Resource does not exist: " + name);
+        }
+        catch (ClassNotFoundException e)
+        {
+            throw new IOException("Unable to recreate persisted object from file: " + name);
+        }
+        finally
+        {
+            if (in != null)
+            {
+                try
+                {
+                    in.close();
+                }
+                catch (Exception e)
+                {
+                    // not much we can do
+                }
+            }
+        }
+        return resources;
+    }
+
+    /**
+     * Stores a resource.
+     * 
+     * @param name Name of the resource.
+     * @param configs List of <code>AutoConfResource</code>s representing the specified resource.
+     * @throws IOException If the resource could not be stored.
+     */
+    public void store(String name, List<AutoConfResource> configs) throws IOException
+    {
+        File targetDir = m_root;
+        name = name.replace('/', File.separatorChar);
+
+        if (name.startsWith(File.separator))
+        {
+            name = name.substring(1);
+        }
+        int lastSeparator = name.lastIndexOf(File.separator);
+        File target = null;
+        if (lastSeparator != -1)
+        {
+            targetDir = new File(targetDir, name.substring(0, lastSeparator));
+            targetDir.mkdirs();
+        }
+        target = new File(targetDir, name.substring(lastSeparator + 1));
+
+        ObjectOutputStream out = null;
+        try
+        {
+            out = new ObjectOutputStream(new FileOutputStream(target));
+            out.writeObject(configs);
+        }
+        finally
+        {
+            if (out != null)
+            {
+                try
+                {
+                    out.close();
+                }
+                catch (Exception e)
+                {
+                    // not much we can do
+                }
+            }
+        }
+    }
 }
diff --git a/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/AutoConfResourceProcessorTest.java b/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/AutoConfResourceProcessorTest.java
index fd8d06b..271520c 100644
--- a/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/AutoConfResourceProcessorTest.java
+++ b/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/AutoConfResourceProcessorTest.java
@@ -21,7 +21,10 @@
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.Dictionary;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.Properties;
 
 import junit.framework.TestCase;
@@ -40,316 +43,349 @@
 import org.osgi.service.deploymentadmin.spi.ResourceProcessorException;
 import org.osgi.service.log.LogService;
 
-public class AutoConfResourceProcessorTest extends TestCase {
-    /** Make sure the processor does not accept a 'null' session. */
-    public void testNullSession() throws Exception {
-        AutoConfResourceProcessor p = new AutoConfResourceProcessor();
-        try {
-            p.begin(null);
-            fail("Should have gotten an exception when trying to begin with null session.");
+public class AutoConfResourceProcessorTest extends TestCase
+{
+    private static class ConfigurationAdminImpl implements ConfigurationAdmin
+    {
+        private final String[] m_expectedPIDs;
+        private final String[] m_expectedFactoryPIDs;
+        private final Map<String, ConfigurationImpl> m_configs;
+
+        public ConfigurationAdminImpl(String... expectedPIDs)
+        {
+            this(expectedPIDs, new String[0]);
         }
-        catch (Exception e) {
-            // expected
+
+        public ConfigurationAdminImpl(String[] expectedPIDs, String[] expectedFactoryPIDs)
+        {
+            m_expectedPIDs = expectedPIDs;
+            m_expectedFactoryPIDs = expectedFactoryPIDs;
+
+            m_configs = new LinkedHashMap<String, ConfigurationImpl>();
         }
-    }
-    
-    /** Go through a simple session, containing two empty configurations. */
-    public void testSimpleSession() throws Exception {
-        AutoConfResourceProcessor p = new AutoConfResourceProcessor();
-        Utils.configureObject(p, LogService.class);
-        Utils.configureObject(p, DependencyManager.class, new DependencyManager((BundleContext) Utils.createNullObject(BundleContext.class)) {
-        	public void remove(Component service) {
-        	}
-        });
-        File tempDir = File.createTempFile("persistence", "dir");
-        tempDir.delete();
-        tempDir.mkdirs();
-        
-        System.out.println("Temporary dir: " + tempDir);
-        
-        Utils.configureObject(p, PersistencyManager.class, new PersistencyManager(tempDir));
-        Session s = new Session();
-        p.begin(s);
-        Utils.configureObject(p, Component.class, Utils.createMockObjectAdapter(Component.class, new Object() {
-            public DependencyManager getDependencyManager() {
-                return new DependencyManager((BundleContext) Utils.createNullObject(BundleContext.class));
+
+        public Configuration createFactoryConfiguration(String factoryPid) throws IOException
+        {
+            return createFactoryConfiguration(factoryPid, null);
+        }
+
+        public Configuration createFactoryConfiguration(String factoryPid, String location) throws IOException
+        {
+            if (!isExpected(m_expectedFactoryPIDs, factoryPid))
+            {
+                throw new IOException("Unexpected factory PID: " + factoryPid);
             }
-        }));
-        p.process("a", new ByteArrayInputStream("<MetaData />".getBytes()));
-        p.process("b", new ByteArrayInputStream("<MetaData />".getBytes()));
-        p.prepare();
-        p.commit();
-        p.postcommit();
-        Utils.removeDirectoryWithContent(tempDir);
+            // This should be unique enough for our use cases...
+            String pid = String.format("pid%d", m_configs.size());
+
+            ConfigurationImpl config = m_configs.get(pid);
+            if (config == null)
+            {
+                config = new ConfigurationImpl(factoryPid, pid, location);
+                m_configs.put(pid, config);
+            }
+            config.setBundleLocation(location);
+            return config;
+        }
+
+        public Configuration getConfiguration(String pid) throws IOException
+        {
+            return getConfiguration(pid, null);
+        }
+
+        public Configuration getConfiguration(String pid, String location) throws IOException
+        {
+            if (!isExpected(m_expectedPIDs, pid))
+            {
+                throw new IOException("Unexpected PID: " + pid);
+            }
+
+            ConfigurationImpl config = m_configs.get(pid);
+            if (config == null)
+            {
+                config = new ConfigurationImpl(null, pid, location);
+                m_configs.put(pid, config);
+            }
+            config.setBundleLocation(location);
+            return config;
+        }
+
+        public Configuration[] listConfigurations(String filter) throws IOException, InvalidSyntaxException
+        {
+            return null;
+        }
+
+        private boolean isExpected(String[] expectedPIDs, String actualPID)
+        {
+            for (String expectedPID : expectedPIDs)
+            {
+                if (actualPID.equals(expectedPID))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
     }
 
-    /** Go through a simple session, containing two empty configurations. */
-    public void testSimpleInstallAndUninstallSession() throws Throwable {
-        AutoConfResourceProcessor p = new AutoConfResourceProcessor();
-        Utils.configureObject(p, LogService.class);
-        Utils.configureObject(p, DependencyManager.class, new DependencyManager((BundleContext) Utils.createNullObject(BundleContext.class)) {
-        	public void remove(Component service) {
-        	}
-        });
-        Logger logger = new Logger();
-        Utils.configureObject(p, LogService.class, logger);
-        File tempDir = File.createTempFile("persistence", "dir");
-        tempDir.delete();
-        tempDir.mkdirs();
-        
-        System.out.println("Temporary dir: " + tempDir);
-        
-        Utils.configureObject(p, PersistencyManager.class, new PersistencyManager(tempDir));
-        Session s = new Session();
-        p.begin(s);
-        Utils.configureObject(p, Component.class, Utils.createMockObjectAdapter(Component.class, new Object() {
-            public DependencyManager getDependencyManager() {
-                return new DependencyManager((BundleContext) Utils.createNullObject(BundleContext.class));
+    private static class ConfigurationImpl implements Configuration
+    {
+        private final String m_factoryPID;
+        private final String m_pid;
+        private String m_bundleLocation;
+        private Dictionary m_properties;
+        private boolean m_deleted;
+
+        public ConfigurationImpl(String factoryPid, String pid, String bundleLocation)
+        {
+            m_factoryPID = factoryPid;
+            m_pid = pid;
+            m_bundleLocation = bundleLocation;
+        }
+
+        public void delete() throws IOException
+        {
+            m_deleted = true;
+        }
+
+        public String getBundleLocation()
+        {
+            return m_bundleLocation;
+        }
+
+        public String getFactoryPid()
+        {
+            return m_factoryPID;
+        }
+
+        public String getPid()
+        {
+            return m_pid;
+        }
+
+        public Dictionary getProperties()
+        {
+            return m_properties;
+        }
+
+        public void setBundleLocation(String bundleLocation)
+        {
+            if (m_bundleLocation != null && !m_bundleLocation.equals(bundleLocation))
+            {
+                throw new RuntimeException("Configuration already bound to location: " + m_bundleLocation + " (trying to set to: " + bundleLocation + ")");
             }
-        }));
-        p.process("a", new ByteArrayInputStream("<MetaData />".getBytes()));
-        p.prepare();
-        p.commit();
-        p.postcommit();
-        logger.failOnException();
-        s = new Session();
-        p.begin(s);
-        p.dropped("a");
-        p.prepare();
-        p.commit();
-        p.postcommit();
-        logger.failOnException();
-        Utils.removeDirectoryWithContent(tempDir);
+            m_bundleLocation = bundleLocation;
+        }
+
+        public void update() throws IOException
+        {
+        }
+
+        public void update(Dictionary properties) throws IOException
+        {
+            m_properties = properties;
+        }
     }
-    
-    /** Go through a simple session, containing two empty configurations. */
-    public void testBasicConfigurationSession() throws Throwable {
-        AutoConfResourceProcessor p = new AutoConfResourceProcessor();
-        Logger logger = new Logger();
-        Utils.configureObject(p, LogService.class, logger);
-        Utils.configureObject(p, DependencyManager.class, new DependencyManager((BundleContext) Utils.createNullObject(BundleContext.class)) {
-        	public void remove(Component service) {
-        	}
-        });
-        File tempDir = File.createTempFile("persistence", "dir");
-        tempDir.delete();
-        tempDir.mkdirs();
-        
-        System.out.println("Temporary dir: " + tempDir);
-        
-        Utils.configureObject(p, PersistencyManager.class, new PersistencyManager(tempDir));
-        Session s = new Session();
-        p.begin(s);
-        Utils.configureObject(p, Component.class, Utils.createMockObjectAdapter(Component.class, new Object() {
-            public DependencyManager getDependencyManager() {
-                return new DependencyManager((BundleContext) Utils.createNullObject(BundleContext.class));
+
+    /** Dummy session. */
+    private static class DeploymentSessionImpl implements DeploymentSession
+    {
+        public File getDataFile(Bundle bundle)
+        {
+            return null;
+        }
+
+        public DeploymentPackage getSourceDeploymentPackage()
+        {
+            return null;
+        }
+
+        public DeploymentPackage getTargetDeploymentPackage()
+        {
+            return null;
+        }
+
+        @Override
+        public String toString()
+        {
+            return "Test DeploymentSession @ 0x" + System.identityHashCode(this);
+        }
+    }
+
+    private static class LogServiceImpl implements LogService
+    {
+        private static final String[] LEVEL = { "", "[ERROR]", "[WARN ]", "[INFO ]", "[DEBUG]" };
+        private Throwable m_exception;
+
+        public void failOnException() throws Throwable
+        {
+            if (m_exception != null)
+            {
+                throw m_exception;
             }
-        }));
-        String config =
-            "<MetaData xmlns:metatype='http://www.osgi.org/xmlns/metatype/v1.0.0'>\n" + 
-            "  <OCD name='ocd' id='ocd'>\n" + 
-            "    <AD id='name' type='STRING' cardinality='0' />\n" + 
-            "  </OCD>\n" + 
-            "  <Designate pid='simple' bundle='osgi-dp:location'>\n" + 
-            "    <Object ocdref='ocd'>\n" + 
-            "      <Attribute adref='name'>\n" + 
-            "        <Value><![CDATA[test]]></Value>\n" + 
-            "      </Attribute>\n" + 
-            "    </Object>\n" + 
-            "  </Designate>\n" + 
+        }
+
+        public void log(int level, String message)
+        {
+            System.out.println(LEVEL[level] + " - " + message);
+        }
+
+        public void log(int level, String message, Throwable exception)
+        {
+            System.out.println(LEVEL[level] + " - " + message + " - " + exception.getMessage());
+            m_exception = exception;
+        }
+
+        public void log(ServiceReference sr, int level, String message)
+        {
+            System.out.println(LEVEL[level] + " - " + message);
+        }
+
+        public void log(ServiceReference sr, int level, String message, Throwable exception)
+        {
+            System.out.println(LEVEL[level] + " - " + message + " - " + exception.getMessage());
+            m_exception = exception;
+        }
+    }
+
+    private static class ServiceReferenceImpl implements ServiceReference
+    {
+        private final Properties m_properties;
+
+        public ServiceReferenceImpl()
+        {
+            this(new Properties());
+        }
+
+        public ServiceReferenceImpl(Properties properties)
+        {
+            m_properties = properties;
+        }
+
+        public int compareTo(Object reference)
+        {
+            return 0;
+        }
+
+        public Bundle getBundle()
+        {
+            return null;
+        }
+
+        public Object getProperty(String key)
+        {
+            return m_properties.get(key);
+        }
+
+        public String[] getPropertyKeys()
+        {
+            return Collections.list(m_properties.keys()).toArray(new String[0]);
+        }
+
+        public Bundle[] getUsingBundles()
+        {
+            return null;
+        }
+
+        public boolean isAssignableTo(Bundle bundle, String className)
+        {
+            return false;
+        }
+
+        @Override
+        public String toString()
+        {
+            return "Test ConfigAdmin @ 0x" + System.identityHashCode(this);
+        }
+    }
+
+    private File m_tempDir;
+    private LogServiceImpl m_logger;
+
+    /** Go through a simple session, containing two empty configurations. */
+    public void testBasicConfigurationSession() throws Throwable
+    {
+        AutoConfResourceProcessor p = createAutoConfRP();
+
+        createNewSession(p);
+        String config = "<MetaData xmlns:metatype='http://www.osgi.org/xmlns/metatype/v1.0.0'>\n" +
+            "  <OCD name='ocd' id='ocd'>\n" +
+            "    <AD id='name' type='STRING' cardinality='0' />\n" +
+            "  </OCD>\n" +
+            "  <Designate pid='simple' bundle='osgi-dp:location'>\n" +
+            "    <Object ocdref='ocd'>\n" +
+            "      <Attribute adref='name'>\n" +
+            "        <Value><![CDATA[test]]></Value>\n" +
+            "      </Attribute>\n" +
+            "    </Object>\n" +
+            "  </Designate>\n" +
             "</MetaData>\n";
         p.process("basic", new ByteArrayInputStream(config.getBytes()));
         p.prepare();
         p.commit();
-        p.addConfigurationAdmin(null, new ConfigurationAdmin() {
-            public Configuration[] listConfigurations(String filter) throws IOException, InvalidSyntaxException {
-                return null;
-            }
-            
-            public Configuration getConfiguration(String pid, String location) throws IOException {
-                return new ConfigurationImpl();
-            }
-            
-            public Configuration getConfiguration(String pid) throws IOException {
-                return null;
-            }
-            
-            public Configuration createFactoryConfiguration(String factoryPid, String location) throws IOException {
-                return null;
-            }
-            
-            public Configuration createFactoryConfiguration(String factoryPid) throws IOException {
-                return null;
-            }
-        });
+        p.addConfigurationAdmin(new ServiceReferenceImpl(), new ConfigurationAdminImpl("simple"));
         p.postcommit();
-        logger.failOnException();
-        Utils.removeDirectoryWithContent(tempDir);
+        m_logger.failOnException();
     }
 
     /** Go through a simple session, containing two empty configurations. */
-    public void testFilteredConfigurationSession() throws Throwable {
-        AutoConfResourceProcessor p = new AutoConfResourceProcessor();
-        Logger logger = new Logger();
-        Utils.configureObject(p, LogService.class, logger);
-        BundleContext mockBC = (BundleContext) Utils.createMockObjectAdapter(BundleContext.class, new Object() {
-            public Filter createFilter(String condition) {
-                return (Filter) Utils.createMockObjectAdapter(Filter.class, new Object() {
-                    public boolean match(ServiceReference ref) {
-                        Object id = ref.getProperty("id");
-                        if (id != null && id.equals(Integer.valueOf(42))) {
-                            return true;
-                        }
-                        return false;
-                    }
-                    public void remove(Component service) {
-                    }
-                });
-            }
-        });
-		Utils.configureObject(p, BundleContext.class, mockBC);
-        Utils.configureObject(p, DependencyManager.class, new DependencyManager(mockBC) {
-        	public void remove(Component service) {
-        	}
-        });
-        File tempDir = File.createTempFile("persistence", "dir");
-        tempDir.delete();
-        tempDir.mkdirs();
-        
-        System.out.println("Temporary dir: " + tempDir);
-        
-        Utils.configureObject(p, PersistencyManager.class, new PersistencyManager(tempDir));
-        Session s = new Session();
-        p.begin(s);
-        Utils.configureObject(p, Component.class, Utils.createMockObjectAdapter(Component.class, new Object() {
-            public DependencyManager getDependencyManager() {
-                return new DependencyManager((BundleContext) Utils.createNullObject(BundleContext.class));
-            }
-        }));
-        String config =
-            "<MetaData xmlns:metatype='http://www.osgi.org/xmlns/metatype/v1.0.0' filter='(id=42)'>\n" + 
-            "  <OCD name='ocd' id='ocd'>\n" + 
-            "    <AD id='name' type='STRING' cardinality='0' />\n" + 
-            "  </OCD>\n" + 
-            "  <Designate pid='simple' bundle='osgi-dp:location'>\n" + 
-            "    <Object ocdref='ocd'>\n" + 
-            "      <Attribute adref='name'>\n" + 
-            "        <Value><![CDATA[test]]></Value>\n" + 
-            "      </Attribute>\n" + 
-            "    </Object>\n" + 
-            "  </Designate>\n" + 
+    public void testFilteredConfigurationSession() throws Throwable
+    {
+        AutoConfResourceProcessor p = createAutoConfRP();
+
+        createNewSession(p);
+        String config = "<MetaData xmlns:metatype='http://www.osgi.org/xmlns/metatype/v1.0.0' filter='(id=42)'>\n" +
+            "  <OCD name='ocd' id='ocd'>\n" +
+            "    <AD id='name' type='STRING' cardinality='0' />\n" +
+            "  </OCD>\n" +
+            "  <Designate pid='simple' bundle='osgi-dp:location'>\n" +
+            "    <Object ocdref='ocd'>\n" +
+            "      <Attribute adref='name'>\n" +
+            "        <Value><![CDATA[test]]></Value>\n" +
+            "      </Attribute>\n" +
+            "    </Object>\n" +
+            "  </Designate>\n" +
             "</MetaData>\n";
         p.process("basic", new ByteArrayInputStream(config.getBytes()));
         p.prepare();
         p.commit();
+
         Properties props = new Properties();
         props.put("id", Integer.valueOf(42));
-        final Configuration configuration = new ConfigurationImpl();
-        p.addConfigurationAdmin(new Reference(props), new ConfigurationAdmin() {
-            public Configuration[] listConfigurations(String filter) throws IOException, InvalidSyntaxException {
-                return null;
-            }
-            
-            public Configuration getConfiguration(String pid, String location) throws IOException {
-                return configuration;
-            }
-            
-            public Configuration getConfiguration(String pid) throws IOException {
-                return null;
-            }
-            
-            public Configuration createFactoryConfiguration(String factoryPid, String location) throws IOException {
-                return null;
-            }
-            
-            public Configuration createFactoryConfiguration(String factoryPid) throws IOException {
-                return null;
-            }
-        });
-        
-        final Configuration emptyConfiguration = new ConfigurationImpl();
-        p.addConfigurationAdmin(new Reference(new Properties()), new ConfigurationAdmin() {
-            public Configuration[] listConfigurations(String filter) throws IOException, InvalidSyntaxException {
-                return null;
-            }
-            
-            public Configuration getConfiguration(String pid, String location) throws IOException {
-                return emptyConfiguration;
-            }
-            
-            public Configuration getConfiguration(String pid) throws IOException {
-                return null;
-            }
-            
-            public Configuration createFactoryConfiguration(String factoryPid, String location) throws IOException {
-                return null;
-            }
-            
-            public Configuration createFactoryConfiguration(String factoryPid) throws IOException {
-                return null;
-            }
-        });        
+
+        ConfigurationAdminImpl ca1 = new ConfigurationAdminImpl("simple");
+        ConfigurationAdminImpl ca2 = new ConfigurationAdminImpl();
+
+        p.addConfigurationAdmin(new ServiceReferenceImpl(props), ca1);
+        p.addConfigurationAdmin(new ServiceReferenceImpl(), ca2);
         p.postcommit();
-        logger.failOnException();
-        assertEquals("test", configuration.getProperties().get("name"));
-        assertNull(emptyConfiguration.getProperties());
-        Utils.removeDirectoryWithContent(tempDir);
+
+        m_logger.failOnException();
+
+        assertEquals("test", ca1.m_configs.get("simple").getProperties().get("name"));
+        assertTrue(ca2.m_configs.isEmpty());
     }
 
     /** Go through a simple session, containing two empty configurations. */
-    public void testMissingMandatoryValueInConfig() throws Throwable {
-        AutoConfResourceProcessor p = new AutoConfResourceProcessor();
-        Logger logger = new Logger();
-        Utils.configureObject(p, LogService.class, logger);
-        BundleContext mockBC = (BundleContext) Utils.createMockObjectAdapter(BundleContext.class, new Object() {
-            public Filter createFilter(String condition) {
-                return (Filter) Utils.createMockObjectAdapter(Filter.class, new Object() {
-                    public boolean match(ServiceReference ref) {
-                        Object id = ref.getProperty("id");
-                        if (id != null && id.equals(Integer.valueOf(42))) {
-                            return true;
-                        }
-                        return false;
-                    }
-                    public void remove(Component service) {
-                    }
-                });
-            }
-        });
-        Utils.configureObject(p, BundleContext.class, mockBC);
-        Utils.configureObject(p, DependencyManager.class, new DependencyManager(mockBC) {
-            public void remove(Component service) {
-            }
-        });
-        File tempDir = File.createTempFile("persistence", "dir");
-        tempDir.delete();
-        tempDir.mkdirs();
-        
-        System.out.println("Temporary dir: " + tempDir);
-        
-        Utils.configureObject(p, PersistencyManager.class, new PersistencyManager(tempDir));
-        Session s = new Session();
-        p.begin(s);
-        Utils.configureObject(p, Component.class, Utils.createMockObjectAdapter(Component.class, new Object() {
-            public DependencyManager getDependencyManager() {
-                return new DependencyManager((BundleContext) Utils.createNullObject(BundleContext.class));
-            }
-        }));
+    public void testMissingMandatoryValueInConfig() throws Throwable
+    {
+        AutoConfResourceProcessor p = createAutoConfRP();
 
-        String config =
-            "<MetaData xmlns:metatype='http://www.osgi.org/xmlns/metatype/v1.1.0' filter='(id=42)'>\n" + 
-            "  <OCD name='ocd' id='ocd'>\n" + 
-            "    <AD id='name' type='Integer' />\n" + 
-            "  </OCD>\n" + 
-            "  <Designate pid='simple' bundle='osgi-dp:location'>\n" + 
-            "    <Object ocdref='ocd'>\n" + 
-            "      <Attribute adref='name'>\n" + 
-            "        <Value><![CDATA[]]></Value>\n" + 
-            "      </Attribute>\n" + 
-            "    </Object>\n" + 
-            "  </Designate>\n" + 
+        createNewSession(p);
+
+        String config = "<MetaData xmlns:metatype='http://www.osgi.org/xmlns/metatype/v1.1.0' filter='(id=42)'>\n" +
+            "  <OCD name='ocd' id='ocd'>\n" +
+            "    <AD id='name' type='Integer' />\n" +
+            "  </OCD>\n" +
+            "  <Designate pid='simple' bundle='osgi-dp:location'>\n" +
+            "    <Object ocdref='ocd'>\n" +
+            "      <Attribute adref='name'>\n" +
+            "        <Value><![CDATA[]]></Value>\n" +
+            "      </Attribute>\n" +
+            "    </Object>\n" +
+            "  </Designate>\n" +
             "</MetaData>\n";
-        
+
         try
         {
             p.process("missing-value", new ByteArrayInputStream(config.getBytes()));
@@ -360,114 +396,232 @@
             // Ok; expected...
             assertEquals("Unable to parse value for definition: adref=name", e.getMessage());
         }
-        Utils.removeDirectoryWithContent(tempDir);
     }
 
-    private static class ConfigurationImpl implements Configuration {
-        private String m_bundleLocation = "osgi-dp:location";
-        private Dictionary m_properties;
-
-        public String getPid() {
-            return null;
+    /** Make sure the processor does not accept a 'null' session. */
+    public void testNullSession() throws Exception
+    {
+        AutoConfResourceProcessor p = new AutoConfResourceProcessor();
+        try
+        {
+            p.begin(null);
+            fail("Should have gotten an exception when trying to begin with null session.");
         }
-
-        public Dictionary getProperties() {
-            return m_properties;
-        }
-
-        public void update(Dictionary properties) throws IOException {
-            m_properties = properties;
-        }
-
-        public void delete() throws IOException {
-        }
-
-        public String getFactoryPid() {
-            return null;
-        }
-
-        public void update() throws IOException {
-        }
-
-        public void setBundleLocation(String bundleLocation) {
-            m_bundleLocation = bundleLocation;
-        }
-
-        public String getBundleLocation() {
-            return m_bundleLocation;
+        catch (Exception e)
+        {
+            // expected
         }
     }
 
-    /** Dummy session. */
-    private static class Session implements DeploymentSession {
-        public DeploymentPackage getTargetDeploymentPackage() {
-            return null;
-        }
-        public DeploymentPackage getSourceDeploymentPackage() {
-            return null;
-        }
-        public File getDataFile(Bundle bundle) {
-            return null;
-        }
+    /** Go through a simple session, containing two empty configurations. */
+    public void testSimpleInstallAndUninstallSession() throws Throwable
+    {
+        AutoConfResourceProcessor p = createAutoConfRP();
+
+        createNewSession(p);
+
+        p.process("a", new ByteArrayInputStream("<MetaData />".getBytes()));
+        p.prepare();
+        p.commit();
+        p.postcommit();
+        m_logger.failOnException();
+
+        createNewSession(p);
+
+        p.dropAllResources();
+        p.prepare();
+        p.commit();
+        p.postcommit();
+        m_logger.failOnException();
     }
-    
-    private static class Logger implements LogService {
-        private static final String[] LEVEL = { "", "[ERROR]", "[WARN ]", "[INFO ]", "[DEBUG]" };
-        private Throwable m_exception;
-        
-        public void log(int level, String message) {
-            System.out.println(LEVEL[level] + " - " + message);
-        }
 
-        public void log(int level, String message, Throwable exception) {
-            System.out.println(LEVEL[level] + " - " + message + " - " + exception.getMessage());
-            m_exception = exception;
-        }
+    /** Go through a simple session, containing two empty configurations. */
+    public void testSimpleSession() throws Throwable
+    {
+        AutoConfResourceProcessor p = createAutoConfRP();
 
-        public void log(ServiceReference sr, int level, String message) {
-            System.out.println(LEVEL[level] + " - " + message);
-        }
+        createNewSession(p);
+        p.process("a", new ByteArrayInputStream("<MetaData />".getBytes()));
+        p.process("b", new ByteArrayInputStream("<MetaData />".getBytes()));
+        p.prepare();
+        p.commit();
+        p.postcommit();
+        m_logger.failOnException();
+    }
 
-        public void log(ServiceReference sr, int level, String message, Throwable exception) {
-            System.out.println(LEVEL[level] + " - " + message + " - " + exception.getMessage());
-            m_exception = exception;
-        }
-        
-        public void failOnException() throws Throwable {
-            if (m_exception != null) {
-                throw m_exception;
+    /** Tests that we can update an existing configuration and properly handling deleted & updated configurations. */
+    public void testUpdateConfigurationSession() throws Throwable
+    {
+        AutoConfResourceProcessor p = createAutoConfRP();
+
+        createNewSession(p);
+
+        String config1 = "<MetaData xmlns:metatype='http://www.osgi.org/xmlns/metatype/v1.0.0'>" +
+            "<OCD name='ocd1' id='ocd1'>" +
+            "  <AD id='nameA' type='STRING' cardinality='0' />" +
+            "</OCD>" +
+            "<OCD name='ocd2' id='ocd2'>" +
+            "  <AD id='nameB' type='STRING' cardinality='0' />" +
+            "</OCD>" +
+            "<Designate pid='pid2' bundle='osgi-dp:location2'>" +
+            "  <Object ocdref='ocd2'>" +
+            "    <Attribute adref='nameB'>" +
+            "      <Value><![CDATA[test2]]></Value>" +
+            "    </Attribute>" +
+            "  </Object>" +
+            "</Designate>" +
+            "<Designate pid='pid1' bundle='osgi-dp:location1'>" +
+            "  <Object ocdref='ocd1'>" +
+            "    <Attribute adref='nameA'>" +
+            "      <Value><![CDATA[test1]]></Value>" +
+            "    </Attribute>" +
+            "  </Object>" +
+            "</Designate>" +
+            "</MetaData>";
+
+        ConfigurationAdminImpl ca = new ConfigurationAdminImpl("pid1", "pid2", "pid3");
+
+        p.process("update", new ByteArrayInputStream(config1.getBytes()));
+        p.prepare();
+        p.commit();
+        p.addConfigurationAdmin(new ServiceReferenceImpl(), ca);
+        p.postcommit();
+        m_logger.failOnException();
+
+        assertEquals(2, ca.m_configs.size());
+        assertTrue(ca.m_configs.containsKey("pid1"));
+        assertFalse(ca.m_configs.get("pid1").m_deleted);
+        assertEquals("test1", ca.m_configs.get("pid1").getProperties().get("nameA"));
+
+        assertTrue(ca.m_configs.containsKey("pid2"));
+        assertFalse(ca.m_configs.get("pid2").m_deleted);
+        assertEquals("test2", ca.m_configs.get("pid2").getProperties().get("nameB"));
+
+        String config2 = "<MetaData xmlns:metatype='http://www.osgi.org/xmlns/metatype/v1.0.0'>" +
+            "<OCD name='ocd3' id='ocd3'>" +
+            "  <AD id='nameC' type='STRING' cardinality='0' />" +
+            "</OCD>" +
+            "<OCD name='ocd2' id='ocd2'>" +
+            "  <AD id='nameB' type='STRING' cardinality='0' />" +
+            "</OCD>" +
+            "<Designate pid='pid2' bundle='osgi-dp:location2'>" +
+            "  <Object ocdref='ocd2'>" +
+            "    <Attribute adref='nameB'>" +
+            "      <Value><![CDATA[test4]]></Value>" +
+            "    </Attribute>" +
+            "  </Object>" +
+            "</Designate>" +
+            "<Designate pid='pid3' bundle='osgi-dp:location3'>" +
+            "  <Object ocdref='ocd3'>" +
+            "    <Attribute adref='nameC'>" +
+            "      <Value><![CDATA[test3]]></Value>" +
+            "    </Attribute>" +
+            "  </Object>" +
+            "</Designate>" +
+            "</MetaData>";
+
+        createNewSession(p);
+
+        p.process("update", new ByteArrayInputStream(config2.getBytes()));
+        p.prepare();
+        p.commit();
+        p.addConfigurationAdmin(new ServiceReferenceImpl(), ca);
+        p.postcommit();
+        m_logger.failOnException();
+
+        assertEquals(3, ca.m_configs.size());
+        assertTrue(ca.m_configs.containsKey("pid1"));
+        assertTrue(ca.m_configs.get("pid1").m_deleted);
+        assertEquals("test1", ca.m_configs.get("pid1").getProperties().get("nameA"));
+
+        assertTrue(ca.m_configs.containsKey("pid2"));
+        assertFalse(ca.m_configs.get("pid2").m_deleted);
+        assertEquals("test4", ca.m_configs.get("pid2").getProperties().get("nameB"));
+
+        assertTrue(ca.m_configs.containsKey("pid3"));
+        assertFalse(ca.m_configs.get("pid3").m_deleted);
+        assertEquals("test3", ca.m_configs.get("pid3").getProperties().get("nameC"));
+    }
+
+    @Override
+    protected void setUp() throws IOException
+    {
+        m_tempDir = File.createTempFile("persistence", "dir");
+        m_tempDir.delete();
+        m_tempDir.mkdirs();
+
+        m_logger = new LogServiceImpl();
+    }
+
+    @Override
+    protected void tearDown() throws Exception
+    {
+        Utils.removeDirectoryWithContent(m_tempDir);
+    }
+
+    private AutoConfResourceProcessor createAutoConfRP()
+    {
+        AutoConfResourceProcessor p = new AutoConfResourceProcessor();
+        Utils.configureObject(p, LogService.class, m_logger);
+        Utils.configureObject(p, DependencyManager.class, createMockDM());
+        Utils.configureObject(p, PersistencyManager.class, new PersistencyManager(m_tempDir));
+        return p;
+    }
+
+    @SuppressWarnings("unused")
+    private BundleContext createMockBundleContext()
+    {
+        return Utils.createMockObjectAdapter(BundleContext.class, new Object()
+        {
+            public Filter createFilter(String condition)
+            {
+                return Utils.createMockObjectAdapter(Filter.class, new Object()
+                {
+                    public boolean match(ServiceReference ref)
+                    {
+                        Object id = ref.getProperty("id");
+                        if (id != null && id.equals(Integer.valueOf(42)))
+                        {
+                            return true;
+                        }
+                        return false;
+                    }
+
+                    public void remove(Component service)
+                    {
+                    }
+                });
             }
-        }
+        });
     }
-    private static class Reference implements ServiceReference {
-        private final Dictionary m_properties;
 
-        public Reference(Dictionary properties) {
-            m_properties = properties;
-        }
+    @SuppressWarnings("unused")
+    private Component createMockComponent()
+    {
+        return Utils.createMockObjectAdapter(Component.class, new Object()
+        {
+            public DependencyManager getDependencyManager()
+            {
+                return new DependencyManager(createMockBundleContext());
+            }
+        });
+    }
 
-        public Object getProperty(String key) {
-            return m_properties.get(key);
-        }
+    private DependencyManager createMockDM()
+    {
+        return new DependencyManager(createMockBundleContext())
+        {
+            public void remove(Component service)
+            {
+            }
+        };
+    }
 
-        public String[] getPropertyKeys() {
-            return null;
-        }
-
-        public Bundle getBundle() {
-            return null;
-        }
-
-        public Bundle[] getUsingBundles() {
-            return null;
-        }
-
-        public boolean isAssignableTo(Bundle bundle, String className) {
-            return false;
-        }
-
-        public int compareTo(Object reference) {
-            return 0;
-        }
+    private DeploymentSession createNewSession(AutoConfResourceProcessor p)
+    {
+        DeploymentSessionImpl s = new DeploymentSessionImpl();
+        p.begin(s);
+        Utils.configureObject(p, Component.class, createMockComponent());
+        return s;
     }
 }
diff --git a/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/DefaultNullObject.java b/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/DefaultNullObject.java
index 46e8d11..e6b827c 100644
--- a/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/DefaultNullObject.java
+++ b/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/DefaultNullObject.java
@@ -21,14 +21,14 @@
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 
-
 /**
  * Default null object implementation. Uses a dynamic proxy. Null objects are used
  * as placeholders for services that are not available.
  * 
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-public class DefaultNullObject implements InvocationHandler {
+public class DefaultNullObject implements InvocationHandler
+{
     private static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;
     private static final Byte DEFAULT_BYTE = new Byte((byte) 0);
     private static final Short DEFAULT_SHORT = new Short((short) 0);
@@ -36,35 +36,44 @@
     private static final Long DEFAULT_LONG = new Long(0);
     private static final Float DEFAULT_FLOAT = new Float(0.0f);
     private static final Double DEFAULT_DOUBLE = new Double(0.0);
-    
+
     /**
      * Invokes a method on this null object. The method will return a default
      * value without doing anything.
      */
-    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+    {
         Class returnType = method.getReturnType();
-        if (returnType.equals(Boolean.class) || returnType.equals(Boolean.TYPE)) {
+        if (returnType.equals(Boolean.class) || returnType.equals(Boolean.TYPE))
+        {
             return DEFAULT_BOOLEAN;
         }
-        else if (returnType.equals(Byte.class) || returnType.equals(Byte.TYPE)) {
+        else if (returnType.equals(Byte.class) || returnType.equals(Byte.TYPE))
+        {
             return DEFAULT_BYTE;
-        } 
-        else if (returnType.equals(Short.class) || returnType.equals(Short.TYPE)) {
+        }
+        else if (returnType.equals(Short.class) || returnType.equals(Short.TYPE))
+        {
             return DEFAULT_SHORT;
-        } 
-        else if (returnType.equals(Integer.class) || returnType.equals(Integer.TYPE)) {
+        }
+        else if (returnType.equals(Integer.class) || returnType.equals(Integer.TYPE))
+        {
             return DEFAULT_INT;
-        } 
-        else if (returnType.equals(Long.class) || returnType.equals(Long.TYPE)) {
+        }
+        else if (returnType.equals(Long.class) || returnType.equals(Long.TYPE))
+        {
             return DEFAULT_LONG;
-        } 
-        else if (returnType.equals(Float.class) || returnType.equals(Float.TYPE)) {
+        }
+        else if (returnType.equals(Float.class) || returnType.equals(Float.TYPE))
+        {
             return DEFAULT_FLOAT;
-        } 
-        else if (returnType.equals(Double.class) || returnType.equals(Double.TYPE)) {
+        }
+        else if (returnType.equals(Double.class) || returnType.equals(Double.TYPE))
+        {
             return DEFAULT_DOUBLE;
-        } 
-        else {
+        }
+        else
+        {
             return null;
         }
     }
diff --git a/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/PersistencyManagerTest.java b/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/PersistencyManagerTest.java
new file mode 100644
index 0000000..07bfe30
--- /dev/null
+++ b/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/PersistencyManagerTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.felix.deployment.rp.autoconf;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Hashtable;
+
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for {@link PersistencyManager}.
+ */
+public class PersistencyManagerTest extends TestCase
+{
+    private File m_tempDir;
+
+    public void testHandleNonExistingDirectory() throws Exception
+    {
+        PersistencyManager pm = new PersistencyManager(new File("/does/not/exist"));
+        assertNotNull(pm);
+
+        assertEquals(0, pm.getResourceNames().size());
+    }
+
+    public void testHandleEmptyExistingDirectory() throws Exception
+    {
+        PersistencyManager pm = new PersistencyManager(m_tempDir);
+        assertNotNull(pm);
+
+        assertEquals(0, pm.getResourceNames().size());
+    }
+
+    public void testLoadNonExistingResource() throws Exception
+    {
+        PersistencyManager pm = new PersistencyManager(m_tempDir);
+        assertEquals(0, pm.load("doesNotExist").size());
+    }
+
+    public void testSaveResourceWithoutFilter() throws Exception
+    {
+        AutoConfResource res1 = new AutoConfResource("res1", "pid1", null, "osgi-dp:locationA", false, new Hashtable<String, Object>(), null);
+        AutoConfResource res2 = new AutoConfResource("res2", "pid2", null, "osgi-dp:locationB", false, new Hashtable<String, Object>(), null);
+
+        PersistencyManager pm = new PersistencyManager(m_tempDir);
+        pm.store("test1", Arrays.asList(res1, res2));
+
+        assertEquals(2, pm.load("test1").size());
+    }
+
+    public void testSaveResourceWithFilter() throws Exception
+    {
+        Filter f = FrameworkUtil.createFilter("(name=test)");
+
+        AutoConfResource res1 = new AutoConfResource("res1", "pid1", null, "osgi-dp:locationA", false, new Hashtable<String, Object>(), f);
+        AutoConfResource res2 = new AutoConfResource("res2", "pid2", null, "osgi-dp:locationB", false, new Hashtable<String, Object>(), null);
+
+        PersistencyManager pm = new PersistencyManager(m_tempDir);
+        pm.store("test1", Arrays.asList(res1, res2));
+
+        assertEquals(2, pm.load("test1").size());
+    }
+
+    @Override
+    protected void setUp() throws IOException
+    {
+        m_tempDir = File.createTempFile("persistence", "dir");
+        m_tempDir.delete();
+        m_tempDir.mkdirs();
+    }
+
+    @Override
+    protected void tearDown() throws Exception
+    {
+        Utils.removeDirectoryWithContent(m_tempDir);
+    }
+
+}
diff --git a/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/Utils.java b/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/Utils.java
index a199404..a13d5e7 100644
--- a/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/Utils.java
+++ b/deploymentadmin/autoconf/src/test/java/org/apache/felix/deployment/rp/autoconf/Utils.java
@@ -28,14 +28,16 @@
 /**
  * Utility class that injects dependencies. Can be used to unit test service implementations.
  */
-public class Utils {
+public class Utils
+{
     /**
      * Configures an object to use a null object for the specified service interface.
      *
      * @param object the object
      * @param iface the service interface
      */
-    public static void configureObject(Object object, Class iface) {
+    public static void configureObject(Object object, Class iface)
+    {
         configureObject(object, iface, createNullObject(iface));
     }
 
@@ -45,8 +47,9 @@
      * @param iface the service interface
      * @return a null object
      */
-    public static Object createNullObject(Class iface) {
-        return Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new DefaultNullObject());
+    public static <T> T createNullObject(Class<T> iface)
+    {
+        return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new DefaultNullObject());
     }
 
     /**
@@ -57,19 +60,24 @@
      * @param handler the handler to pass invocations to.
      * @return an adapter that will try to pass on received invocations to the given handler
      */
-    public static Object createMockObjectAdapter(Class iface, final Object handler) {
-        return Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new DefaultNullObject() {
-
-            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-                try {
+    public static <T> T createMockObjectAdapter(Class<T> iface, final Object handler)
+    {
+        return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new DefaultNullObject()
+        {
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+            {
+                try
+                {
                     Method bridge = handler.getClass().getMethod(method.getName(), method.getParameterTypes());
                     bridge.setAccessible(true);
                     return bridge.invoke(handler, args);
                 }
-                catch (NoSuchMethodException ex) {
+                catch (NoSuchMethodException ex)
+                {
                     return super.invoke(proxy, method, args);
                 }
-                catch (InvocationTargetException ex) {
+                catch (InvocationTargetException ex)
+                {
                     throw ex.getCause();
                 }
             }
@@ -83,21 +91,28 @@
      * @param iface the service interface
      * @param instance the implementation
      */
-    public static void configureObject(Object object, Class iface, Object instance) {
+    public static void configureObject(Object object, Class iface, Object instance)
+    {
         Class serviceClazz = object.getClass();
 
-        while (serviceClazz != null) {
+        while (serviceClazz != null)
+        {
             Field[] fields = serviceClazz.getDeclaredFields();
             AccessibleObject.setAccessible(fields, true);
-            for (int j = 0; j < fields.length; j++) {
-                if (fields[j].getType().equals(iface)) {
-                    try {
+            for (int j = 0; j < fields.length; j++)
+            {
+                if (fields[j].getType().equals(iface))
+                {
+                    try
+                    {
                         // synchronized makes sure the field is actually written to immediately
-                        synchronized (new Object()) {
+                        synchronized (new Object())
+                        {
                             fields[j].set(object, instance);
                         }
                     }
-                    catch (Exception e) {
+                    catch (Exception e)
+                    {
                         throw new IllegalStateException("Could not set field " + fields[j].getName() + " on " + object);
                     }
                 }
@@ -105,20 +120,24 @@
             serviceClazz = serviceClazz.getSuperclass();
         }
     }
-    
+
     /**
      * Remove the given directory and all it's files and subdirectories
      * 
      * @param directory the name of the directory to remove
      */
-    public static void removeDirectoryWithContent(File directory) {
-        if ((directory == null) || !directory.exists()) {
+    public static void removeDirectoryWithContent(File directory)
+    {
+        if ((directory == null) || !directory.exists())
+        {
             return;
         }
         File[] filesAndSubDirs = directory.listFiles();
-        for (int i=0; i < filesAndSubDirs.length; i++) {
+        for (int i = 0; i < filesAndSubDirs.length; i++)
+        {
             File file = filesAndSubDirs[i];
-            if (file.isDirectory()) {
+            if (file.isDirectory())
+            {
                 removeDirectoryWithContent(file);
             }
             // else just remove the file