SLING-4272 : Issues in handling of configurations wrt update handling and write back
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1648985 13f79535-47bb-0310-9956-ffa450edef68
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
index a5f1e11..2b27df6 100644
--- 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
@@ -22,6 +22,7 @@
import org.apache.sling.installer.api.tasks.ResourceState;
import org.apache.sling.installer.api.tasks.TaskResourceGroup;
import org.apache.sling.installer.factories.configuration.ConfigurationConstants;
+import org.apache.sling.installer.factories.configuration.impl.Coordinator.Operation;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
@@ -44,7 +45,7 @@
@SuppressWarnings("unchecked")
@Override
public void execute(final InstallationContext ctx) {
- synchronized ( ConfigTaskCreator.getLock() ) {
+ synchronized ( Coordinator.SHARED ) {
// Get or create configuration, but do not
// update if the new one has the same values.
boolean created = false;
@@ -81,6 +82,8 @@
this.getLogger().debug("Configuration " + config.getPid()
+ " " + (created ? "created" : "updated")
+ " from " + getResource());
+ final Operation op = new Coordinator.Operation(config.getPid(), config.getFactoryPid(), false);
+ Coordinator.SHARED.add(op);
} else {
this.setFinishedState(ResourceState.IGNORED, this.getCompositeAliasPid());
}
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
index 9cdc7b5..e0bf6ea 100644
--- 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
@@ -45,7 +45,7 @@
@Override
@SuppressWarnings("unchecked")
public void execute(final InstallationContext ctx) {
- synchronized ( ConfigTaskCreator.getLock() ) {
+ synchronized ( Coordinator.SHARED ) {
try {
final Configuration cfg = getConfiguration();
if (cfg == null) {
@@ -54,12 +54,16 @@
if ( !ConfigUtil.isSameData(cfg.getProperties(), this.getResource().getDictionary()) ) {
this.getLogger().debug("Configuration has changed after it has been installed!");
} else {
+ final Coordinator.Operation op = new Coordinator.Operation(cfg.getPid(), cfg.getFactoryPid(), true);
+
this.getLogger().debug("Deleting config {} ({})", getCompositePid(), getResource());
cfg.delete();
ctx.log("Deleted configuration {} from resource {}", getCompositePid(), getResource());
+
+ Coordinator.SHARED.add(op);
}
}
- } catch (Exception e) {
+ } catch (final Exception e) {
this.getLogger().debug("Exception during removal of config " + this.getResource() + " : " + e.getMessage() + ". Retrying later.", e);
}
// we always set to uninstalled as the resource really has been deleted
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
index bb01629..23a59e0 100644
--- 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
@@ -39,6 +39,8 @@
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ConfigurationEvent;
import org.osgi.service.cm.ConfigurationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Task creator for configurations.
@@ -46,6 +48,9 @@
public class ConfigTaskCreator
implements InstallTaskFactory, ConfigurationListener, ResourceTransformer {
+ /** Logger. */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
/** Configuration admin. */
private final ConfigurationAdmin configAdmin;
@@ -91,7 +96,7 @@
*/
@SuppressWarnings("unchecked")
public void configurationEvent(final ConfigurationEvent event) {
- synchronized ( ConfigTaskCreator.getLock() ) {
+ synchronized ( Coordinator.SHARED ) {
final String id;
final String pid;
if (event.getFactoryPid() == null ) {
@@ -103,13 +108,19 @@
id = event.getFactoryPid() + '.' + event.getPid();
}
if ( event.getType() == ConfigurationEvent.CM_DELETED ) {
- this.changeListener.resourceRemoved(InstallableResource.TYPE_CONFIG, id);
+ final Coordinator.Operation op = Coordinator.SHARED.get(event.getPid(), event.getFactoryPid(), true);
+ if ( op == null ) {
+ this.changeListener.resourceRemoved(InstallableResource.TYPE_CONFIG, id);
+ } else {
+ this.logger.debug("Ignoring configuration event for {}:{}", event.getPid(), event.getFactoryPid());
+ }
} else if ( event.getType() == ConfigurationEvent.CM_UPDATED ) {
try {
final Configuration config = ConfigUtil.getConfiguration(configAdmin,
event.getFactoryPid(),
event.getPid());
- if ( config != null ) {
+ final Coordinator.Operation op = Coordinator.SHARED.get(event.getPid(), event.getFactoryPid(), false);
+ if ( config != null && op == null ) {
final boolean persist = ConfigUtil.toBoolean(config.getProperties().get(ConfigurationConstants.PROPERTY_PERSISTENCE), true);
if ( persist ) {
final Dictionary<String, Object> dict = ConfigUtil.cleanConfiguration(config.getProperties());
@@ -129,6 +140,8 @@
}
this.changeListener.resourceAddedOrUpdated(InstallableResource.TYPE_CONFIG, id, null, dict, attrs);
}
+ } else {
+ this.logger.debug("Ignoring configuration event for {}:{}", event.getPid(), event.getFactoryPid());
}
} catch ( final Exception ignore) {
// ignore for now
@@ -242,10 +255,4 @@
}
return path.replace('\\', '/');
}
-
- private static final Object LOCK = new Object();
-
- public static Object getLock() {
- return LOCK;
- }
}
diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/Coordinator.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/Coordinator.java
new file mode 100644
index 0000000..c2c4b17
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/Coordinator.java
@@ -0,0 +1,131 @@
+/*
+ * 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.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Coordinator service.
+ *
+ * All operations should be synced on the {@link #SHARED} instance.
+ */
+public class Coordinator {
+
+ /**
+ * Shared instance for syncing and keeping track of operations.
+ */
+ public static final Coordinator SHARED = new Coordinator();
+
+ /**
+ * Entries expire after this number of milliseconds (defaults to 5 secs)
+ */
+ private static final long EXPIRY = 5000;
+
+ /**
+ * An operation
+ */
+ public static final class Operation {
+ public final String pid;
+ public final String factoryPid;
+ public final boolean isDelete;
+ public final long created;
+
+ public Operation(final String pid, final String factoryPid, final boolean isDelete) {
+ created = System.currentTimeMillis();
+ this.pid = pid;
+ this.factoryPid = factoryPid;
+ this.isDelete = isDelete;
+ }
+
+ @Override
+ public String toString() {
+ return "Operation [pid=" + pid + ", factoryPid=" + factoryPid
+ + ", isDelete=" + isDelete + ", created=" + created + "]";
+ }
+ }
+
+ /**
+ * Logger
+ */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /**
+ * The list of operations.
+ */
+ private final List<Operation> operations = new ArrayList<Coordinator.Operation>();
+
+ /**
+ * Private constructor
+ */
+ private Coordinator() {
+ // private constructor
+ }
+
+ public void add(final Operation op) {
+ this.cleanup();
+ this.operations.add(op);
+ logger.debug("Adding {}", op);
+ }
+
+ public Operation get(final String pid, final String factoryPid, final boolean isDelete) {
+ this.cleanup();
+ logger.debug("Searching {} : {} - {}", new Object[] {pid, factoryPid, isDelete});
+ Operation result = null;
+ final Iterator<Operation> i = this.operations.iterator();
+ while ( i.hasNext() ) {
+ final Operation op = i.next();
+ if ( op.isDelete == isDelete ) {
+ if ( op.pid.equals(pid) ) {
+ if ( (op.factoryPid == null && factoryPid == null)
+ || (op.factoryPid != null && op.factoryPid.equals(factoryPid)) ) {
+ result = op;
+ i.remove();
+ break;
+ }
+ }
+ }
+ }
+ logger.debug("Result ({} : {} - {}) : {}", new Object[] {pid, factoryPid, isDelete, result});
+ return result;
+ }
+
+ /**
+ * Clean up the list of operations.
+ * Remove all entries which are older then the {@link #EXPIRY}
+ */
+ private void cleanup() {
+ final long time = System.currentTimeMillis() - EXPIRY;
+ final Iterator<Operation> i = this.operations.iterator();
+ while ( i.hasNext() ) {
+ final Operation op = i.next();
+ if ( op.created <= time ) {
+ logger.debug("Deleting expired {}", op);
+ i.remove();
+ } else {
+ break;
+ }
+ }
+ }
+}