You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:42:25 UTC

[sling-org-apache-sling-installer-factory-configuration] 01/11: SLING-1999 : Split Installer Core and Configuration Support

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.installer.factory.configuration-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-factory-configuration.git

commit fc46c463f23d86b17170700789115c95150d0a19
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Tue Mar 1 09:48:27 2011 +0000

    SLING-1999 : Split Installer Core and Configuration Support
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/installer/factories/configuration@1075751 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |  83 ++++++++++
 .../configuration/impl/AbstractConfigTask.java     | 100 +++++++++++
 .../factories/configuration/impl/Activator.java    |  48 ++++++
 .../configuration/impl/ConfigInstallTask.java      |  81 +++++++++
 .../configuration/impl/ConfigRemoveTask.java       |  72 ++++++++
 .../configuration/impl/ConfigTaskCreator.java      | 182 +++++++++++++++++++++
 .../factories/configuration/impl/ConfigUtil.java   | 128 +++++++++++++++
 .../configuration/impl/ServicesListener.java       | 174 ++++++++++++++++++++
 8 files changed, 868 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..17a3d65
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,83 @@
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>10</version>
+        <relativePath>../../../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.sling.installer.factory.configuration</artifactId>
+    <version>0.9.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>Apache Sling Configuration Admin Installer</name>
+    <description> 
+        Provides support for configurations to the Apache Sling OSGi installer
+    </description>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/installer/factories/configuration</connection>
+        <developerConnection> scm:svn:https://svn.apache.org/repos/asf/sling/trunk/installer/factories/configuration</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/installer/factories/configuration/</url>
+    </scm>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>
+                            org.apache.sling.installer.factories.configuration.impl.Activator
+                        </Bundle-Activator>
+                        <Private-Package>
+                            org.apache.sling.installer.factories.configuration.impl.*
+                        </Private-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.installer.core</artifactId>
+            <version>3.1.3-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/AbstractConfigTask.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/AbstractConfigTask.java
new file mode 100644
index 0000000..011e193
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/AbstractConfigTask.java
@@ -0,0 +1,100 @@
+/*
+ * 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.sling.installer.factories.configuration.impl;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.sling.installer.api.tasks.InstallTask;
+import org.apache.sling.installer.api.tasks.TaskResourceGroup;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Base class for configuration-related tasks
+ */
+abstract class AbstractConfigTask extends InstallTask {
+
+    /** Configuration PID */
+    protected final String configPid;
+
+    /** Factory PID or null */
+    protected final String factoryPid;
+
+    /** Configuration admin. */
+    private final ConfigurationAdmin configAdmin;
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    AbstractConfigTask(final TaskResourceGroup r, final ConfigurationAdmin configAdmin) {
+        super(r);
+        this.configAdmin = configAdmin;
+        this.configPid = (String)getResource().getAttribute(Constants.SERVICE_PID);
+        this.factoryPid = (String)getResource().getAttribute(ConfigurationAdmin.SERVICE_FACTORYPID);
+    }
+
+    protected Logger getLogger() {
+        return this.logger;
+    }
+
+    /**
+     * Get the configuration admin - if available
+     */
+    protected ConfigurationAdmin getConfigurationAdmin() {
+        return this.configAdmin;
+    }
+
+    protected String getCompositePid() {
+        return (factoryPid == null ? "" : factoryPid + ".") + configPid;
+    }
+
+    protected Dictionary<String, Object> getDictionary() {
+        // Copy dictionary and add pseudo-properties
+        final Dictionary<String, Object> d = this.getResource().getDictionary();
+        if ( d == null ) {
+            return null;
+        }
+
+        final Dictionary<String, Object> result = new Hashtable<String, Object>();
+        final Enumeration<String> e = d.keys();
+        while(e.hasMoreElements()) {
+            final String key = e.nextElement();
+            result.put(key, d.get(key));
+        }
+
+        result.put(ConfigTaskCreator.CONFIG_PATH_KEY, getResource().getURL());
+        if ( this.factoryPid != null ) {
+            result.put(ConfigTaskCreator.ALIAS_KEY, configPid);
+        }
+
+        return result;
+    }
+
+    protected Configuration getConfiguration(final ConfigurationAdmin ca,
+                                             final boolean createIfNeeded)
+    throws IOException, InvalidSyntaxException {
+        return ConfigUtil.getConfiguration(ca, this.factoryPid, this.configPid, createIfNeeded, true);
+    }
+}
diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/Activator.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/Activator.java
new file mode 100644
index 0000000..ac16697
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/Activator.java
@@ -0,0 +1,48 @@
+/*
+ * 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.sling.installer.factories.configuration.impl;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator registers the configuration support service.
+ */
+public class Activator implements BundleActivator {
+
+    /** Services listener. */
+    private ServicesListener listener;
+
+    /**
+     * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+     */
+    public void start(final BundleContext context) throws Exception {
+        this.listener = new ServicesListener(context);
+    }
+
+    /**
+     * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+     */
+    public void stop(final BundleContext context) {
+        if ( this.listener != null ) {
+            this.listener.deactivate();
+            this.listener = null;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigInstallTask.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigInstallTask.java
new file mode 100644
index 0000000..bc9308e
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigInstallTask.java
@@ -0,0 +1,81 @@
+/*
+ * 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.sling.installer.factories.configuration.impl;
+
+import org.apache.sling.installer.api.tasks.InstallationContext;
+import org.apache.sling.installer.api.tasks.ResourceState;
+import org.apache.sling.installer.api.tasks.TaskResourceGroup;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Task to install a configuration
+ */
+public class ConfigInstallTask extends AbstractConfigTask {
+
+    private static final String CONFIG_INSTALL_ORDER = "20-";
+
+    public ConfigInstallTask(final TaskResourceGroup r, final ConfigurationAdmin configAdmin) {
+        super(r, configAdmin);
+    }
+
+    @Override
+    public String getSortKey() {
+        return CONFIG_INSTALL_ORDER + getCompositePid();
+    }
+
+    @SuppressWarnings("unchecked")
+	@Override
+    public void execute(final InstallationContext ctx) {
+        final ConfigurationAdmin ca = this.getConfigurationAdmin();
+
+        // Get or create configuration, but do not
+        // update if the new one has the same values.
+        boolean created = false;
+        try {
+            Configuration config = getConfiguration(ca, false);
+            if (config == null) {
+                created = true;
+                config = getConfiguration(ca, true);
+            } else {
+    			if (ConfigUtil.isSameData(config.getProperties(), getResource().getDictionary())) {
+    			    this.getLogger().debug("Configuration {} already installed with same data, update request ignored: {}",
+    	                        config.getPid(), getResource());
+    				config = null;
+    			}
+            }
+
+            if (config != null) {
+                if (config.getBundleLocation() != null) {
+                    config.setBundleLocation(null);
+                }
+                config.update(getDictionary());
+                ctx.log("Installed configuration {} from resource {}", config.getPid(), getResource());
+                this.setFinishedState(ResourceState.INSTALLED);
+                this.getLogger().debug("Configuration " + config.getPid()
+                            + " " + (created ? "created" : "updated")
+                            + " from " + getResource());
+            } else {
+                this.setFinishedState(ResourceState.IGNORED);
+            }
+        } catch (Exception e) {
+            this.getLogger().debug("Exception during installation of config " + this.getResource() + " : " + e.getMessage() + ". Retrying later.", e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigRemoveTask.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigRemoveTask.java
new file mode 100644
index 0000000..78050b4
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigRemoveTask.java
@@ -0,0 +1,72 @@
+/*
+ * 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.sling.installer.factories.configuration.impl;
+
+import org.apache.sling.installer.api.tasks.InstallationContext;
+import org.apache.sling.installer.api.tasks.ResourceState;
+import org.apache.sling.installer.api.tasks.TaskResourceGroup;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/** Remove a Configuration */
+public class ConfigRemoveTask extends AbstractConfigTask {
+
+    private static final String CONFIG_REMOVE_ORDER = "10-";
+
+    public ConfigRemoveTask(final TaskResourceGroup r,
+            final ConfigurationAdmin configAdmin) {
+        super(r, configAdmin);
+    }
+
+    @Override
+    public String getSortKey() {
+        return CONFIG_REMOVE_ORDER + getCompositePid();
+    }
+
+    /**
+     * @see org.apache.sling.installer.api.tasks.InstallTask#execute(org.apache.sling.installer.api.tasks.InstallationContext)
+     */
+    @SuppressWarnings("unchecked")
+    public void execute(final InstallationContext ctx) {
+        final ConfigurationAdmin ca = this.getConfigurationAdmin();
+
+        try {
+            final Configuration cfg = getConfiguration(ca, false);
+            if (cfg == null) {
+                this.getLogger().debug("Cannot delete config , pid={} not found, ignored ({})", getCompositePid(), getResource());
+                this.setFinishedState(ResourceState.IGNORED);
+            } else {
+                if ( cfg.getProperties().get(ConfigTaskCreator.CONFIG_PATH_KEY) == null ) {
+                    this.getLogger().debug("Configuration has not been installed by this resource. Not removing!");
+                    this.setFinishedState(ResourceState.IGNORED);
+                } else if ( !ConfigUtil.isSameData(cfg.getProperties(), this.getResource().getDictionary()) ) {
+                    this.getLogger().debug("Configuration has changed after it has been installed. Not removing!");
+                    this.setFinishedState(ResourceState.IGNORED);
+                } else {
+                    this.getLogger().debug("Deleting config {} ({})", getCompositePid(), getResource());
+                    cfg.delete();
+                    ctx.log("Deleted configuration {} from resource {}", getCompositePid(), getResource());
+                    this.setFinishedState(ResourceState.UNINSTALLED);
+                }
+            }
+        } catch (Exception e) {
+            this.getLogger().debug("Exception during removal of config " + this.getResource() + " : " + e.getMessage() + ". Retrying later.", e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java
new file mode 100644
index 0000000..535b5f8
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java
@@ -0,0 +1,182 @@
+/*
+ * 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.sling.installer.factories.configuration.impl;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sling.installer.api.InstallableResource;
+import org.apache.sling.installer.api.ResourceChangeListener;
+import org.apache.sling.installer.api.tasks.InstallTask;
+import org.apache.sling.installer.api.tasks.InstallTaskFactory;
+import org.apache.sling.installer.api.tasks.RegisteredResource;
+import org.apache.sling.installer.api.tasks.ResourceState;
+import org.apache.sling.installer.api.tasks.ResourceTransformer;
+import org.apache.sling.installer.api.tasks.TaskResource;
+import org.apache.sling.installer.api.tasks.TaskResourceGroup;
+import org.apache.sling.installer.api.tasks.TransformationResult;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationEvent;
+import org.osgi.service.cm.ConfigurationListener;
+
+/**
+ * Task creator for configurations.
+ */
+public class ConfigTaskCreator
+    implements InstallTaskFactory, ConfigurationListener, ResourceTransformer {
+
+    public static final String ALIAS_KEY = "org.apache.sling.installer.osgi.factoryaliaspid";
+    public static final String CONFIG_PATH_KEY = "org.apache.sling.installer.osgi.path";
+
+    /** Configuration admin. */
+    private ConfigurationAdmin configAdmin;
+
+    /** Resource change listener. */
+    private ResourceChangeListener changeListener;
+
+    public ConfigTaskCreator(final ResourceChangeListener listener, final ConfigurationAdmin configAdmin) {
+        this.changeListener = listener;
+        this.configAdmin = configAdmin;
+    }
+
+    /**
+     * Create a task to install or uninstall a configuration.
+     *
+	 * @see org.apache.sling.installer.api.tasks.InstallTaskFactory#createTask(org.apache.sling.installer.api.tasks.TaskResourceGroup)
+	 */
+	public InstallTask createTask(final TaskResourceGroup group) {
+        final TaskResource toActivate = group.getActiveResource();
+        if ( !toActivate.getType().equals(InstallableResource.TYPE_CONFIG) ) {
+            return null;
+        }
+
+        final InstallTask result;
+		if (toActivate.getState() == ResourceState.UNINSTALL) {
+		    result = new ConfigRemoveTask(group, this.configAdmin);
+		} else {
+	        result = new ConfigInstallTask(group, this.configAdmin);
+		}
+		return result;
+	}
+
+    /**
+     * @see org.osgi.service.cm.ConfigurationListener#configurationEvent(org.osgi.service.cm.ConfigurationEvent)
+     */
+    @SuppressWarnings("unchecked")
+    public void configurationEvent(final ConfigurationEvent event) {
+        final String id = (event.getFactoryPid() == null ? "" : event.getFactoryPid() + ".") + event.getPid();
+        if ( event.getType() == ConfigurationEvent.CM_DELETED ) {
+            this.changeListener.resourceRemoved(InstallableResource.TYPE_CONFIG, id);
+        } else {
+            try {
+                final Configuration config = ConfigUtil.getConfiguration(configAdmin,
+                        event.getFactoryPid(),
+                        event.getPid(),
+                        false, false);
+                if ( config != null ) {
+                    final Dictionary<String, Object> dict = ConfigUtil.cleanConfiguration(config.getProperties());
+                    this.changeListener.resourceAddedOrUpdated(InstallableResource.TYPE_CONFIG, id, null, dict);
+                }
+            } catch ( final Exception ignore) {
+                // ignore for now (TODO)
+            }
+        }
+    }
+
+    /**
+     * @see org.apache.sling.installer.api.tasks.ResourceTransformer#transform(org.apache.sling.installer.api.tasks.RegisteredResource)
+     */
+    public TransformationResult[] transform(final RegisteredResource resource) {
+        if ( resource.getType().equals(InstallableResource.TYPE_PROPERTIES) ) {
+            return checkConfiguration(resource);
+        }
+        return null;
+    }
+
+    /**
+     * Check if the registered resource is a configuration
+     * @param resource The resource
+     */
+    private TransformationResult[] checkConfiguration(final RegisteredResource resource) {
+        final String url = resource.getURL();
+        String lastIdPart = url;
+        final int pos = lastIdPart.lastIndexOf('/');
+        if ( pos != -1 ) {
+            lastIdPart = lastIdPart.substring(pos + 1);
+        }
+
+        final String pid;
+        // remove extension if known
+        if ( isConfigExtension(getExtension(lastIdPart)) ) {
+            final int lastDot = lastIdPart.lastIndexOf('.');
+            pid = lastIdPart.substring(0, lastDot);
+        } else {
+            pid = lastIdPart;
+        }
+
+        // split pid and factory pid alias
+        final String factoryPid;
+        final String configPid;
+        int n = pid.indexOf('-');
+        if (n > 0) {
+            configPid = pid.substring(n + 1);
+            factoryPid = pid.substring(0, n);
+        } else {
+            factoryPid = null;
+            configPid = pid;
+        }
+
+        final Map<String, Object> attr = new HashMap<String, Object>();
+
+        attr.put(Constants.SERVICE_PID, configPid);
+        // Factory?
+        if (factoryPid != null) {
+            attr.put(ConfigurationAdmin.SERVICE_FACTORYPID, factoryPid);
+        }
+
+        final TransformationResult tr = new TransformationResult();
+        final String id = (factoryPid == null ? "" : factoryPid + ".") + configPid;
+        tr.setId(id);
+        tr.setResourceType(InstallableResource.TYPE_CONFIG);
+        tr.setAttributes(attr);
+
+        return new TransformationResult[] {tr};
+    }
+
+    /**
+     * Compute the extension
+     */
+    private static String getExtension(String url) {
+        final int pos = url.lastIndexOf('.');
+        return (pos < 0 ? "" : url.substring(pos+1));
+    }
+
+    private static boolean isConfigExtension(String extension) {
+        if ( extension.equals("cfg")
+                || extension.equals("config")
+                || extension.equals("xml")
+                || extension.equals("properties")) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigUtil.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigUtil.java
new file mode 100644
index 0000000..321bdd9
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigUtil.java
@@ -0,0 +1,128 @@
+/*
+ * 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.sling.installer.factories.configuration.impl;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Utilities for configuration handling
+ */
+abstract class ConfigUtil {
+
+    /** Configuration properties to ignore when comparing configs */
+    private static final Set<String> IGNORED_PROPERTIES = new HashSet<String>();
+    static {
+        IGNORED_PROPERTIES.add(Constants.SERVICE_PID);
+        IGNORED_PROPERTIES.add(ConfigTaskCreator.CONFIG_PATH_KEY);
+        IGNORED_PROPERTIES.add(ConfigTaskCreator.ALIAS_KEY);
+    }
+
+    private static Set<String> collectKeys(final Dictionary<String, Object>a) {
+        final Set<String> keys = new HashSet<String>();
+        final Enumeration<String> aI = a.keys();
+        while (aI.hasMoreElements() ) {
+            final String key = aI.nextElement();
+            if ( !IGNORED_PROPERTIES.contains(key) ) {
+                keys.add(key);
+            }
+        }
+        return keys;
+    }
+
+    /** True if a and b represent the same config data, ignoring "non-configuration" keys in the dictionaries */
+    public static boolean isSameData(Dictionary<String, Object>a, Dictionary<String, Object>b) {
+        boolean result = false;
+        if (a != null && b != null) {
+            final Set<String> keysA = collectKeys(a);
+            final Set<String> keysB = collectKeys(b);
+            if ( keysA.size() == keysB.size() && keysA.containsAll(keysB) ) {
+                for(final String key : keysA ) {
+                    if ( !a.get(key).equals(b.get(key)) ) {
+                        return result;
+                    }
+                }
+                result = true;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Remove all ignored properties
+     */
+    public static Dictionary<String, Object> cleanConfiguration(final Dictionary<String, Object> config) {
+        final Dictionary<String, Object> cleanedConfig = new Hashtable<String, Object>();
+        final Enumeration<String> e = config.keys();
+        while(e.hasMoreElements()) {
+            final String key = e.nextElement();
+            if ( !IGNORED_PROPERTIES.contains(key) ) {
+                cleanedConfig.put(key, config.get(key));
+            }
+        }
+
+        return cleanedConfig;
+    }
+
+    public static Configuration getConfiguration(final ConfigurationAdmin ca,
+            final String factoryPid,
+            final String configPid,
+            final boolean createIfNeeded,
+            final boolean useAliasForFactory)
+    throws IOException, InvalidSyntaxException {
+        Configuration result = null;
+
+        if (factoryPid == null) {
+            if (createIfNeeded) {
+                result = ca.getConfiguration(configPid, null);
+            } else {
+                String filter = "(" + Constants.SERVICE_PID + "=" + configPid
+                        + ")";
+                Configuration[] configs = ca.listConfigurations(filter);
+                if (configs != null && configs.length > 0) {
+                    result = configs[0];
+                }
+            }
+        } else {
+            Configuration configs[] = ca.listConfigurations("(&("
+                    + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + factoryPid
+                    + ")(" + (useAliasForFactory ? ConfigTaskCreator.ALIAS_KEY : Constants.SERVICE_PID) + "=" + configPid
+                    + "))");
+
+            if (configs == null || configs.length == 0) {
+                if (createIfNeeded) {
+                    result = ca.createFactoryConfiguration(factoryPid, null);
+                }
+            } else {
+                result = configs[0];
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ServicesListener.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ServicesListener.java
new file mode 100644
index 0000000..ec987e3
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ServicesListener.java
@@ -0,0 +1,174 @@
+/*
+ * 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.sling.installer.factories.configuration.impl;
+
+import java.util.Hashtable;
+
+import org.apache.sling.installer.api.ResourceChangeListener;
+import org.apache.sling.installer.api.tasks.InstallTaskFactory;
+import org.apache.sling.installer.api.tasks.ResourceTransformer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationListener;
+
+/**
+ * The <code>ServicesListener</code> listens for the required services
+ * and starts/stops the scanners based on the availability of the
+ * services.
+ */
+public class ServicesListener {
+
+    /** Vendor of all registered services. */
+    public static final String VENDOR = "The Apache Software Foundation";
+
+    /** The bundle context. */
+    private final BundleContext bundleContext;
+
+    /** The listener for the change list handler. */
+    private final Listener changeHandlerListener;
+
+    /** The listener for the configuration admin. */
+    private final Listener configAdminListener;
+
+    /** Registration the service. */
+    private ServiceRegistration configTaskCreatorRegistration;
+
+    private ConfigTaskCreator configTaskCreator;
+
+    public ServicesListener(final BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+        this.changeHandlerListener = new Listener(ResourceChangeListener.class.getName());
+        this.configAdminListener = new Listener(ConfigurationAdmin.class.getName());
+        this.changeHandlerListener.start();
+        this.configAdminListener.start();
+    }
+
+    public synchronized void notifyChange() {
+        // check if all services are available
+        final ResourceChangeListener listener = (ResourceChangeListener)this.changeHandlerListener.getService();
+        final ConfigurationAdmin configAdmin = (ConfigurationAdmin)this.configAdminListener.getService();
+
+        if ( configAdmin != null && listener != null ) {
+            if ( configTaskCreator == null ) {
+                final Hashtable<String, String> props = new Hashtable<String, String>();
+                props.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Configuration Install Task Factory");
+                props.put(Constants.SERVICE_VENDOR, VENDOR);
+
+                this.configTaskCreator = new ConfigTaskCreator(listener, configAdmin);
+                // start and register osgi installer service
+                final String [] serviceInterfaces = {
+                        InstallTaskFactory.class.getName(),
+                        ConfigurationListener.class.getName(),
+                        ResourceTransformer.class.getName()
+                };
+                configTaskCreatorRegistration = this.bundleContext.registerService(serviceInterfaces, configTaskCreator, props);
+            }
+        } else {
+            this.stop();
+        }
+    }
+
+    private void stop() {
+        // unregister
+        if ( this.configTaskCreatorRegistration != null ) {
+            this.configTaskCreatorRegistration.unregister();
+            this.configTaskCreatorRegistration = null;
+        }
+        this.configTaskCreator = null;
+    }
+
+    /**
+     * Deactivate this listener.
+     */
+    public void deactivate() {
+        this.changeHandlerListener.deactivate();
+        this.configAdminListener.deactivate();
+        this.stop();
+    }
+
+    protected final class Listener implements ServiceListener {
+
+        private final String serviceName;
+
+        private ServiceReference reference;
+        private Object service;
+
+        public Listener(final String serviceName) {
+            this.serviceName = serviceName;
+        }
+
+        public void start() {
+            this.retainService();
+            try {
+                bundleContext.addServiceListener(this, "("
+                        + Constants.OBJECTCLASS + "=" + serviceName + ")");
+            } catch (final InvalidSyntaxException ise) {
+                // this should really never happen
+                throw new RuntimeException("Unexpected exception occured.", ise);
+            }
+        }
+
+        public void deactivate() {
+            bundleContext.removeServiceListener(this);
+        }
+
+        public synchronized Object getService() {
+            return this.service;
+        }
+        private synchronized void retainService() {
+            if ( this.reference == null ) {
+                this.reference = bundleContext.getServiceReference(this.serviceName);
+                if ( this.reference != null ) {
+                    this.service = bundleContext.getService(this.reference);
+                    if ( this.service == null ) {
+                        this.reference = null;
+                    } else {
+                        notifyChange();
+                    }
+                }
+            }
+        }
+
+        private synchronized void releaseService() {
+            if ( this.reference != null ) {
+                this.service = null;
+                bundleContext.ungetService(this.reference);
+                this.reference = null;
+                notifyChange();
+            }
+        }
+
+        /**
+         * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
+         */
+        public void serviceChanged(ServiceEvent event) {
+            if (event.getType() == ServiceEvent.REGISTERED && this.service == null ) {
+                this.retainService();
+            } else if ( event.getType() == ServiceEvent.UNREGISTERING && this.service != null ) {
+                this.releaseService();
+            }
+        }
+    }
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.