You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by gg...@apache.org on 2015/07/20 14:52:00 UTC
[02/14] camel git commit: [CAMEL-8948] Precise synchronization with
BP container of the test bundle
[CAMEL-8948] Precise synchronization with BP container of the test bundle
Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/692e479b
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/692e479b
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/692e479b
Branch: refs/heads/master
Commit: 692e479b940b1198df3cec386c16aa4fd071e16c
Parents: 9aa1ab9
Author: Grzegorz Grzybek <gr...@gmail.com>
Authored: Fri Jul 17 21:40:16 2015 +0200
Committer: Grzegorz Grzybek <gr...@gmail.com>
Committed: Mon Jul 20 14:51:37 2015 +0200
----------------------------------------------------------------------
.../test/blueprint/CamelBlueprintHelper.java | 52 ++++++++++-
.../blueprint/CamelBlueprintTestSupport.java | 91 +++++++-------------
.../org/apache/camel/test/blueprint/Main.java | 3 +-
3 files changed, 82 insertions(+), 64 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/692e479b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintHelper.java
----------------------------------------------------------------------
diff --git a/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintHelper.java b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintHelper.java
index 42592c3..4e84211 100644
--- a/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintHelper.java
+++ b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintHelper.java
@@ -35,6 +35,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.jar.JarInputStream;
import org.apache.camel.impl.DefaultClassResolver;
@@ -58,6 +61,9 @@ import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.blueprint.container.BlueprintEvent;
+import org.osgi.service.blueprint.container.BlueprintListener;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.util.tracker.ServiceTracker;
@@ -107,6 +113,9 @@ public final class CamelBlueprintHelper {
String uid = "" + System.currentTimeMillis();
String tempDir = "target/bundles/" + uid;
System.setProperty("org.osgi.framework.storage", tempDir);
+ // explicitly set this to "false" - we will not depend on the order of starting bundles,
+ // (and running their BP containers) but we will have to do more synchornization
+ System.setProperty("org.apache.aries.blueprint.synchronous", "false");
createDirectory(tempDir);
// use another directory for the jar of the bundle as it cannot be in the same directory
@@ -169,7 +178,8 @@ public final class CamelBlueprintHelper {
// pick up persistent file configuration
@SuppressWarnings({"unchecked", "rawtypes"})
public static void setPersistentFileForConfigAdmin(BundleContext bundleContext, String pid,
- String fileName, Dictionary props) throws IOException {
+ String fileName, final Dictionary props,
+ String symbolicName, Set<Long> bpEvents) throws IOException, InterruptedException {
if (pid != null) {
if (fileName == null) {
throw new IllegalArgumentException("The persistent file should not be null");
@@ -186,9 +196,20 @@ public final class CamelBlueprintHelper {
if (configAdmin != null) {
// ensure we update
// we *have to* use "null" as 2nd arg to have correct bundle location for Configuration object
- Configuration config = configAdmin.getConfiguration(pid, null);
+ final Configuration config = configAdmin.getConfiguration(pid, null);
LOG.info("Updating ConfigAdmin {} by overriding properties {}", config, props);
- config.update(props);
+ // we will have update and in consequence, BP container reload, let's wait for it to
+ // be CREATED again
+ CamelBlueprintHelper.waitForBlueprintContainer(bpEvents, bundleContext, symbolicName, BlueprintEvent.CREATED, new Runnable() {
+ @Override
+ public void run() {
+ try {
+ config.update(props);
+ } catch (IOException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ });
}
}
@@ -226,6 +247,7 @@ public final class CamelBlueprintHelper {
// Note that the tracker is not closed to keep the reference
// This is buggy, as the service reference may change i think
Object svc = tracker.waitForService(timeout);
+
if (svc == null) {
Dictionary<?, ?> dic = bundleContext.getBundle().getHeaders();
LOG.warn("Test bundle headers: " + explode(dic));
@@ -248,6 +270,30 @@ public final class CamelBlueprintHelper {
}
}
+ /**
+ * Synchronization method to wait for particular state of BlueprintContainer under test.
+ */
+ public static void waitForBlueprintContainer(final Set<Long> eventHistory, BundleContext context, final String symbolicName, final int bpEvent, final Runnable runAndWait) throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ ServiceRegistration<BlueprintListener> registration = context.registerService(BlueprintListener.class, new BlueprintListener() {
+ @Override
+ public void blueprintEvent(BlueprintEvent event) {
+ if (event.getType() == bpEvent && event.getBundle().getSymbolicName().equals(symbolicName)) {
+ // we skip events that we've already seen
+ // it works with BP container reloads if next CREATE state is at least 1ms after previous one
+ if (eventHistory == null || eventHistory.add(event.getTimestamp())) {
+ latch.countDown();
+ }
+ }
+ }
+ }, null);
+ if (runAndWait != null) {
+ runAndWait.run();
+ }
+ latch.await(CamelBlueprintHelper.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
+ registration.unregister();
+ }
+
protected static TinyBundle createTestBundle(String name, String version, String descriptors) throws FileNotFoundException, MalformedURLException {
TinyBundle bundle = TinyBundles.newBundle();
for (URL url : getBlueprintDescriptors(descriptors)) {
http://git-wip-us.apache.org/repos/asf/camel/blob/692e479b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintTestSupport.java
----------------------------------------------------------------------
diff --git a/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintTestSupport.java b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintTestSupport.java
index 1bc22a5..87f7256 100644
--- a/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintTestSupport.java
+++ b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintTestSupport.java
@@ -17,7 +17,9 @@
package org.apache.camel.test.blueprint;
import java.io.File;
+import java.io.IOException;
import java.util.Dictionary;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -25,8 +27,6 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import org.apache.camel.CamelContext;
import org.apache.camel.component.properties.PropertiesComponent;
@@ -38,13 +38,9 @@ import org.junit.AfterClass;
import org.junit.Before;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.blueprint.container.BlueprintContainer;
import org.osgi.service.blueprint.container.BlueprintEvent;
-import org.osgi.service.blueprint.container.BlueprintListener;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.service.cm.ConfigurationEvent;
-import org.osgi.service.cm.ConfigurationListener;
/**
* Base class for OSGi Blueprint unit tests with Camel.
@@ -103,7 +99,7 @@ public abstract class CamelBlueprintTestSupport extends CamelTestSupport {
}
// must reuse props as we can do both load from .cfg file and override afterwards
- Dictionary props = new Properties();
+ final Dictionary props = new Properties();
// load configuration file
String[] file = loadConfigAdminConfigurationFile();
@@ -111,79 +107,54 @@ public abstract class CamelBlueprintTestSupport extends CamelTestSupport {
throw new IllegalArgumentException("The returned String[] from loadConfigAdminConfigurationFile must be of length 2, was " + file.length);
}
+ // if blueprint XML uses <cm:property-placeholder> (any update-strategy and any default properties)
+ // - org.apache.aries.blueprint.compendium.cm.ManagedObjectManager.register() is called
+ // - ManagedServiceUpdate is scheduled in felix.cm
+ // - org.apache.felix.cm.impl.ConfigurationImpl.setDynamicBundleLocation() is called
+ // - CM_LOCATION_CHANGED event is fired
+ // - if BP was alredy created, it's <cm:property-placeholder> receives the event and
+ // - org.apache.aries.blueprint.compendium.cm.CmPropertyPlaceholder.updated() is called,
+ // but no BP reload occurs
+ // we will however wait for BP container of the test bundle to become CREATED for the first time
+ // each configadmin update *may* lead to reload of BP container, if it uses <cm:property-placeholder>
+ // with update-strategy="reload"
+
+ // we will gather timestamps of BP events. We don't want to be fooled but repeated events related
+ // to the same state of BP container
+ Set<Long> bpEvents = new HashSet<>();
+
+ CamelBlueprintHelper.waitForBlueprintContainer(bpEvents, answer, symbolicName, BlueprintEvent.CREATED, null);
+
if (file != null) {
if (!new File(file[0]).exists()) {
throw new IllegalArgumentException("The provided file \"" + file[0] + "\" from loadConfigAdminConfigurationFile doesn't exist");
}
- CamelBlueprintHelper.setPersistentFileForConfigAdmin(answer, file[1], file[0], props);
+ CamelBlueprintHelper.setPersistentFileForConfigAdmin(answer, file[1], file[0], props, symbolicName, bpEvents);
}
// allow end user to override properties
String pid = useOverridePropertiesWithConfigAdmin(props);
if (pid != null) {
- // we will update the configuration now. As OSGi is highly asynchronous, we need to make the tests as repeatable as possible
- // the problem is when blueprint container defines cm:property-placeholder with update-strategy="reload"
- // updating the configuration leads to (felix framework + aries blueprint):
- // 1. schedule org.apache.felix.cm.impl.ConfigurationManager.UpdateConfiguration object to run in config admin thread
- // 2. this thread calls org.apache.felix.cm.impl.ConfigurationImpl#tryBindLocation()
- // 3. org.osgi.service.cm.ConfigurationEvent#CM_LOCATION_CHANGED is send
- // 4. org.apache.aries.blueprint.compendium.cm.ManagedObjectManager.ConfigurationWatcher#updated() is invoked
- // 5. new Thread().start() is called
- // 6. org.apache.aries.blueprint.compendium.cm.ManagedObject#updated() is called
- // 7. org.apache.aries.blueprint.compendium.cm.CmPropertyPlaceholder#updated() is called
- // 8. new Thread().start() is called
- // 9. org.apache.aries.blueprint.services.ExtendedBlueprintContainer#reload() is called which destroys everything in BP container
- // 10. finally reload of BP container is scheduled (in yet another thread)
- //
- // if we start/use camel context between point 9 and 10 we may get many different errors described in https://issues.apache.org/jira/browse/ARIES-961
-
- // to synchronize this (main) thread of execution with the asynchronous series of events, we can register the following listener.
- // this way be sure that we got to point 3
- final CountDownLatch latch = new CountDownLatch(2);
- answer.registerService(ConfigurationListener.class, new ConfigurationListener() {
- @Override
- public void configurationEvent(ConfigurationEvent event) {
- if (event.getType() == ConfigurationEvent.CM_LOCATION_CHANGED) {
- latch.countDown();
- }
- // when we update the configuration, BP container will be reloaded as well
- // hoping that we get the event after *second* restart, let's register the listener
- answer.registerService(BlueprintListener.class, new BlueprintListener() {
- @Override
- public void blueprintEvent(BlueprintEvent event) {
- if (event.getType() == BlueprintEvent.CREATED && event.getBundle().getSymbolicName().equals(symbolicName)) {
- latch.countDown();
- }
- }
- }, null);
- }
- }, null);
-
+ // we will update the configuration again
ConfigurationAdmin configAdmin = CamelBlueprintHelper.getOsgiService(answer, ConfigurationAdmin.class);
// passing null as second argument ties the configuration to correct bundle.
// using single-arg method causes:
// *ERROR* Cannot use configuration xxx.properties for [org.osgi.service.cm.ManagedService, id=N, bundle=N/jar:file:xyz.jar!/]: No visibility to configuration bound to felix-connect
- Configuration config = configAdmin.getConfiguration(pid, null);
+ final Configuration config = configAdmin.getConfiguration(pid, null);
if (config == null) {
throw new IllegalArgumentException("Cannot find configuration with pid " + pid + " in OSGi ConfigurationAdmin service.");
}
log.info("Updating ConfigAdmin {} by overriding properties {}", config, props);
- config.update(props);
-
- latch.await(CamelBlueprintHelper.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
- } else {
- // let's wait for BP container to start
- final CountDownLatch latch = new CountDownLatch(1);
- answer.registerService(BlueprintListener.class, new BlueprintListener() {
+ CamelBlueprintHelper.waitForBlueprintContainer(bpEvents, answer, symbolicName, BlueprintEvent.CREATED, new Runnable() {
@Override
- public void blueprintEvent(BlueprintEvent event) {
- if (event.getType() == BlueprintEvent.CREATED && event.getBundle().getSymbolicName().equals(symbolicName)) {
- latch.countDown();
+ public void run() {
+ try {
+ config.update(props);
+ } catch (IOException e) {
+ throw new RuntimeException(e.getMessage(), e);
}
}
- }, null);
-
- latch.await(CamelBlueprintHelper.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
+ });
}
return answer;
http://git-wip-us.apache.org/repos/asf/camel/blob/692e479b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/Main.java
----------------------------------------------------------------------
diff --git a/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/Main.java b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/Main.java
index ba6bcf3..338ad7a 100644
--- a/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/Main.java
+++ b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/Main.java
@@ -97,7 +97,8 @@ public class Main extends MainSupport {
}
LOG.debug("Starting Blueprint XML file: " + descriptors);
bundleContext = createBundleContext(bundleName);
- CamelBlueprintHelper.setPersistentFileForConfigAdmin(bundleContext, configAdminPid, configAdminFileName, new Properties());
+ CamelBlueprintHelper.setPersistentFileForConfigAdmin(bundleContext, configAdminPid, configAdminFileName, new Properties(),
+ bundleName, null);
camelContext = CamelBlueprintHelper.getOsgiService(bundleContext, CamelContext.class);
if (camelContext == null) {
throw new IllegalArgumentException("Cannot find CamelContext in blueprint XML file: " + descriptors);