Return-Path: X-Original-To: apmail-sling-commits-archive@www.apache.org Delivered-To: apmail-sling-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 91D48107B7 for ; Fri, 2 Jan 2015 09:32:41 +0000 (UTC) Received: (qmail 84194 invoked by uid 500); 2 Jan 2015 09:32:42 -0000 Delivered-To: apmail-sling-commits-archive@sling.apache.org Received: (qmail 84137 invoked by uid 500); 2 Jan 2015 09:32:42 -0000 Mailing-List: contact commits-help@sling.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@sling.apache.org Delivered-To: mailing list commits@sling.apache.org Received: (qmail 84127 invoked by uid 99); 2 Jan 2015 09:32:42 -0000 Received: from eris.apache.org (HELO hades.apache.org) (140.211.11.105) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 02 Jan 2015 09:32:42 +0000 Received: from hades.apache.org (localhost [127.0.0.1]) by hades.apache.org (ASF Mail Server at hades.apache.org) with ESMTP id 1CF54AC08CB; Fri, 2 Jan 2015 09:32:42 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1648985 - in /sling/trunk/installer: core/src/main/java/org/apache/sling/installer/core/impl/ factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ it/src/test/java/org/apache/sling/installer/it/ Date: Fri, 02 Jan 2015 09:32:41 -0000 To: commits@sling.apache.org From: cziegeler@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20150102093242.1CF54AC08CB@hades.apache.org> Author: cziegeler Date: Fri Jan 2 09:32:41 2015 New Revision: 1648985 URL: http://svn.apache.org/r1648985 Log: SLING-4272 : Issues in handling of configurations wrt update handling and write back Added: sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/Coordinator.java (with props) Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/EntityResourceList.java sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigInstallTask.java sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigRemoveTask.java sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/ConfigInstallTest.java sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/OsgiInstallerTestBase.java Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/EntityResourceList.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/EntityResourceList.java?rev=1648985&r1=1648984&r2=1648985&view=diff ============================================================================== --- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/EntityResourceList.java (original) +++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/EntityResourceList.java Fri Jan 2 09:32:41 2015 @@ -211,7 +211,7 @@ public class EntityResourceList implemen if ( second.getDictionary() != null && second.getDictionary().get(InstallableResource.RESOURCE_IS_TEMPLATE) != null ) { // second resource is a template! Do not install - ((RegisteredResourceImpl)second).setState(ResourceState.INSTALLED); + ((RegisteredResourceImpl)second).setState(ResourceState.IGNORED); } else if ( state == ResourceState.UNINSTALLED ) { // first resource got uninstalled, go back to second if (second.getState() == ResourceState.IGNORED || second.getState() == ResourceState.INSTALLED) { Modified: sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigInstallTask.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigInstallTask.java?rev=1648985&r1=1648984&r2=1648985&view=diff ============================================================================== --- sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigInstallTask.java (original) +++ sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigInstallTask.java Fri Jan 2 09:32:41 2015 @@ -22,6 +22,7 @@ import org.apache.sling.installer.api.ta 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 @@ public class ConfigInstallTask extends A @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 @@ public class ConfigInstallTask extends A 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()); } Modified: sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigRemoveTask.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigRemoveTask.java?rev=1648985&r1=1648984&r2=1648985&view=diff ============================================================================== --- sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigRemoveTask.java (original) +++ sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigRemoveTask.java Fri Jan 2 09:32:41 2015 @@ -45,7 +45,7 @@ public class ConfigRemoveTask extends Ab @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 @@ public class ConfigRemoveTask extends Ab 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 Modified: sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java?rev=1648985&r1=1648984&r2=1648985&view=diff ============================================================================== --- sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java (original) +++ sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java Fri Jan 2 09:32:41 2015 @@ -39,6 +39,8 @@ import org.osgi.service.cm.Configuration 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 @@ import org.osgi.service.cm.Configuration 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 @@ public class ConfigTaskCreator */ @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 @@ public class ConfigTaskCreator 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 dict = ConfigUtil.cleanConfiguration(config.getProperties()); @@ -129,6 +140,8 @@ public class ConfigTaskCreator } 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 @@ public class ConfigTaskCreator } return path.replace('\\', '/'); } - - private static final Object LOCK = new Object(); - - public static Object getLock() { - return LOCK; - } } Added: sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/Coordinator.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/Coordinator.java?rev=1648985&view=auto ============================================================================== --- sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/Coordinator.java (added) +++ sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/Coordinator.java Fri Jan 2 09:32:41 2015 @@ -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 operations = new ArrayList(); + + /** + * 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 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 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; + } + } + } +} Propchange: sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/Coordinator.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/installer/factories/configuration/src/main/java/org/apache/sling/installer/factories/configuration/impl/Coordinator.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url Modified: sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/ConfigInstallTest.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/ConfigInstallTest.java?rev=1648985&r1=1648984&r2=1648985&view=diff ============================================================================== --- sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/ConfigInstallTest.java (original) +++ sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/ConfigInstallTest.java Fri Jan 2 09:32:41 2015 @@ -151,7 +151,7 @@ public class ConfigInstallTest extends O } @Test - public void testInstallUpdateRemoveConfig() throws Exception { + public void testInstallUpdateRemoveConfigResource() throws Exception { final Dictionary cfgData = new Hashtable(); cfgData.put("foo", "bar"); final String cfgPid = getClass().getSimpleName() + "." + uniqueID(); @@ -189,7 +189,7 @@ public class ConfigInstallTest extends O } @Test - public void testInstallUpdateRemoveTemplateConfig() throws Exception { + public void testInstallUpdateRemoveTemplateConfigResource() throws Exception { final Dictionary cfgData = new Hashtable(); cfgData.put("foo", "bar"); cfgData.put(InstallableResource.RESOURCE_IS_TEMPLATE, "true"); @@ -228,6 +228,85 @@ public class ConfigInstallTest extends O } @Test + public void testInstallUpdateRemoveConfigFactoryResource() throws Exception { + final Dictionary cfgData = new Hashtable(); + cfgData.put("foo", "bar"); + final String cfgFactoryPid = getClass().getSimpleName() + "." + uniqueID(); + final String alias = "alias" + uniqueID(); + assertNull("Factory config " + cfgFactoryPid + " must not be found before test", findFactoryConfiguration(cfgFactoryPid)); + + // install factory config + final InstallableResource rsrc = new InstallableResource("/configA/" + cfgFactoryPid + "-" + alias, + null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10); + installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null); + + // get factory config + final Configuration cfg = waitForFactoryConfiguration("After installing", cfgFactoryPid, TIMEOUT, true); + assertEquals("Config value must match", "bar", cfg.getProperties().get("foo")); + + // create second factory configuration with same alias + final Dictionary secondData = new Hashtable(); + secondData.put("foo", "bla"); + final InstallableResource rsrc2 = new InstallableResource("/configB/" + cfgFactoryPid + "-" + alias, + null, secondData, null, InstallableResource.TYPE_PROPERTIES, 20); + installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc2}, null); + + sleep(200); + + // get updated factory config + final Configuration secondCfg = waitForFactoryConfiguration("After updating", cfgFactoryPid, TIMEOUT, true); + assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo")); + + // remove factory config + installer.updateResources(URL_SCHEME, null, new String[] {"/configB/" + cfgFactoryPid + "-" + alias}); + + sleep(200); + + final Configuration origCfg = waitForFactoryConfiguration("After deleting", cfgFactoryPid, TIMEOUT, true); + assertEquals("Config value must match", "bar", origCfg.getProperties().get("foo")); + } + + @Test + public void testInstallUpdateRemoveTemplateConfigFactoryResource() throws Exception { + final Dictionary cfgData = new Hashtable(); + cfgData.put("foo", "bar"); + cfgData.put(InstallableResource.RESOURCE_IS_TEMPLATE, "true"); + final String cfgFactoryPid = getClass().getSimpleName() + "." + uniqueID(); + final String alias = "alias" + uniqueID(); + assertNull("Factory config " + cfgFactoryPid + " must not be found before test", findFactoryConfiguration(cfgFactoryPid)); + + // install factory config + final InstallableResource rsrc = new InstallableResource("/configA/" + cfgFactoryPid + "-" + alias, + null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10); + installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null); + + // get factory config + final Configuration cfg = waitForFactoryConfiguration("After installing", cfgFactoryPid, TIMEOUT, true); + assertEquals("Config value must match", "bar", cfg.getProperties().get("foo")); + + // create second factory configuration + final Dictionary secondData = new Hashtable(); + secondData.put("foo", "bla"); + final InstallableResource rsrc2 = new InstallableResource("/configB/" + cfgFactoryPid + "-" + alias, + null, secondData, null, InstallableResource.TYPE_PROPERTIES, 20); + installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc2}, null); + + sleep(200); + + // get updated factory config + final Configuration secondCfg = waitForFactoryConfiguration("After updating", cfgFactoryPid, TIMEOUT, true); + assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo")); + + // remove config + installer.updateResources(URL_SCHEME, null, new String[] {"/configB/" + cfgFactoryPid + "-" + alias}); + + sleep(200); + + final Configuration noCfg = waitForFactoryConfiguration("After deleting", cfgFactoryPid, TIMEOUT, false); + assertNull("Factory configuration should be removed", noCfg); + } + + @Test public void testDeferredConfigInstall() throws Exception { // get config admin bundle and wait for service final Bundle configAdmin = this.getConfigAdminBundle(); Modified: sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/OsgiInstallerTestBase.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/OsgiInstallerTestBase.java?rev=1648985&r1=1648984&r2=1648985&view=diff ============================================================================== --- sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/OsgiInstallerTestBase.java (original) +++ sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/OsgiInstallerTestBase.java Fri Jan 2 09:32:41 2015 @@ -187,23 +187,51 @@ public class OsgiInstallerTestBase imple } } - protected Configuration findConfiguration(String pid) throws Exception { + /** + * Encode the value for the ldap filter: \, *, (, and ) should be escaped. + */ + private static String encode(final String value) { + return value.replace("\\", "\\\\") + .replace("*", "\\*") + .replace("(", "\\(") + .replace(")", "\\)"); + } + + /** + * Find the configuration with the given pid. + */ + protected Configuration findConfiguration(final String pid) throws Exception { final ConfigurationAdmin ca = this.waitForConfigAdmin(true); if (ca != null) { - final Configuration[] cfgs = ca.listConfigurations(null); - if (cfgs != null) { - for(Configuration cfg : cfgs) { - try { - if(cfg.getPid().equals(pid)) { - return cfg; - } - } catch (IllegalStateException e) {} - } + final Configuration[] cfgs = ca.listConfigurations("(" + Constants.SERVICE_PID + "=" + encode(pid) + ")"); + if (cfgs != null && cfgs.length > 0 ) { + if ( cfgs.length == 1 ) { + return cfgs[0]; + } + throw new IllegalStateException("More than one configuration for " + pid); } } return null; } + /** + * Find the configuration with the given factory pid. + */ + protected Configuration findFactoryConfiguration(final String factoryPid) throws Exception { + final ConfigurationAdmin ca = this.waitForConfigAdmin(true); + if (ca != null) { + final Configuration[] cfgs = ca.listConfigurations("(" + + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + encode(factoryPid) + ")"); + if (cfgs != null && cfgs.length > 0 ) { + if ( cfgs.length == 1 ) { + return cfgs[0]; + } + throw new IllegalStateException("More than one factory configuration for " + factoryPid); + } + } + return null; + } + protected void waitForCondition(String info, long timeoutMsec, Condition c) throws Exception { final long end = System.currentTimeMillis() + timeoutMsec; do { @@ -261,6 +289,40 @@ public class OsgiInstallerTestBase imple } return result; } + + protected Configuration waitForFactoryConfiguration(final String info, + final String factoryPid, + final long timeoutMsec, + final boolean shouldBePresent) + throws Exception { + String msg; + if (info == null) { + msg = ""; + } else { + msg = info + ": "; + } + + Configuration result = null; + final long start = System.currentTimeMillis(); + final long end = start + timeoutMsec; + log(LogService.LOG_DEBUG, "Starting factory config check at " + start + "; ending by " + end); + do { + result = findFactoryConfiguration(factoryPid); + if ((shouldBePresent && result != null) || + (!shouldBePresent && result == null)) { + break; + } + log(LogService.LOG_DEBUG, "Config check failed at " + System.currentTimeMillis() + "; sleeping"); + sleep(25); + } while(System.currentTimeMillis() < end); + + if (shouldBePresent && result == null) { + fail(msg + "Factory Configuration not found (" + factoryPid + ")"); + } else if (!shouldBePresent && result != null) { + fail(msg + "Factory Configuration is still present (" + factoryPid + ")"); + } + return result; + } protected Bundle findBundle(String symbolicName) { for(Bundle b : bundleContext.getBundles()) {