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 <[email protected]> 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 "[email protected]" <[email protected]>.
