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