Bug 475424 - ResolutionException not printed during debug of dynamic import resolution
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/AbstractTest.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/AbstractTest.java
index 5991c98..f49b085 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/AbstractTest.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/AbstractTest.java
@@ -15,6 +15,7 @@
 import java.util.*;
 import org.eclipse.osgi.container.*;
 import org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory;
+import org.eclipse.osgi.service.debug.DebugOptions;
 import org.eclipse.osgi.tests.container.dummys.*;
 import org.eclipse.osgi.util.ManifestElement;
 import org.junit.*;
@@ -48,6 +49,10 @@
 		return new DummyContainerAdaptor(new DummyCollisionHook(false), Collections.<String, String> emptyMap(), new DummyResolverHookFactory(hook));
 	}
 
+	protected DummyContainerAdaptor createDummyAdaptor(DebugOptions debugOptions) {
+		return new DummyContainerAdaptor(new DummyCollisionHook(false), Collections.<String, String> emptyMap(), new DummyResolverHookFactory(), debugOptions);
+	}
+
 	protected Bundle getBundle() {
 		return ((BundleReference) getClass().getClassLoader()).getBundle();
 	}
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java
index cc4c275..2981cdb 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java
@@ -20,6 +20,7 @@
 import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent;
 import org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory;
 import org.eclipse.osgi.container.namespaces.EclipsePlatformNamespace;
+import org.eclipse.osgi.internal.debug.Debug;
 import org.eclipse.osgi.report.resolution.ResolutionReport;
 import org.eclipse.osgi.tests.container.dummys.*;
 import org.eclipse.osgi.tests.container.dummys.DummyModuleDatabase.DummyContainerEvent;
@@ -1560,6 +1561,42 @@
 		Assert.assertEquals("c should not resolve.", State.INSTALLED, uses_c.getState());
 	}
 
+	@Test
+	public void testUses1Dynamic() throws BundleException, IOException {
+		DummyContainerAdaptor adaptor = createDummyAdaptor(new DummyDebugOptions(Collections.singletonMap("org.eclipse.osgi/resolver/report", "true")));
+		ModuleContainer container = adaptor.getContainer();
+
+		Module systemBundle = installDummyModule("system.bundle.MF", Constants.SYSTEM_BUNDLE_LOCATION, container);
+
+		container.resolve(Arrays.asList(systemBundle), true);
+		Module uses_a = installDummyModule("uses.a.MF", "a", container);
+		Module uses_b = installDummyModule("uses.b.MF", "b", container);
+		Module uses_c_dynamic = installDummyModule("uses.c.dynamic.MF", "c", container);
+
+		container.resolve(null, false);
+
+		Assert.assertEquals("a should resolve.", State.RESOLVED, uses_a.getState());
+		Assert.assertEquals("b should resolve.", State.RESOLVED, uses_b.getState());
+		Assert.assertEquals("c should resolve.", State.RESOLVED, uses_c_dynamic.getState());
+
+		ModuleWire dynamicWire = container.resolveDynamic("uses1", uses_c_dynamic.getCurrentRevision());
+		Assert.assertNotNull("No dynamic wire.", dynamicWire);
+
+		PrintStream originalOut = Debug.out;
+		ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+		PrintStream testOut = new PrintStream(bytesOut);
+		Debug.out = testOut;
+		try {
+			dynamicWire = container.resolveDynamic("uses2", uses_c_dynamic.getCurrentRevision());
+			Assert.assertNull("Dynamic wire found.", dynamicWire);
+		} finally {
+			Debug.out = originalOut;
+			testOut.close();
+		}
+		String traceOutput = bytesOut.toString();
+		Assert.assertTrue("Wrong traceOutput: " + traceOutput, traceOutput.startsWith("org.osgi.service.resolver.ResolutionException"));
+	}
+
 	/*
 	 * Test that split packages are handled ok with uses constraints
 	 */
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/dummys/DummyContainerAdaptor.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/dummys/DummyContainerAdaptor.java
index 07dc9e8..4d91c7d 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/dummys/DummyContainerAdaptor.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/dummys/DummyContainerAdaptor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012 IBM Corporation and others.
+ * Copyright (c) 2012, 2015 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,13 +10,13 @@
  *******************************************************************************/
 package org.eclipse.osgi.tests.container.dummys;
 
-import org.eclipse.osgi.tests.container.dummys.DummyModuleDatabase.DummyContainerEvent;
-import org.eclipse.osgi.tests.container.dummys.DummyModuleDatabase.DummyModuleEvent;
-
 import java.util.EnumSet;
 import java.util.Map;
 import org.eclipse.osgi.container.*;
 import org.eclipse.osgi.container.Module.Settings;
+import org.eclipse.osgi.service.debug.DebugOptions;
+import org.eclipse.osgi.tests.container.dummys.DummyModuleDatabase.DummyContainerEvent;
+import org.eclipse.osgi.tests.container.dummys.DummyModuleDatabase.DummyModuleEvent;
 import org.osgi.framework.FrameworkListener;
 import org.osgi.framework.hooks.resolver.ResolverHookFactory;
 
@@ -26,16 +26,22 @@
 	private final DummyModuleDatabase moduleDatabase;
 	private final ModuleContainer container;
 	private final ResolverHookFactory resolverHookFactory;
+	private final DebugOptions debugOptions;
 
 	public DummyContainerAdaptor(ModuleCollisionHook collisionHook, Map<String, String> configuration) {
 		this(collisionHook, configuration, new DummyResolverHookFactory());
 	}
 
 	public DummyContainerAdaptor(ModuleCollisionHook collisionHook, Map<String, String> configuration, ResolverHookFactory resolverHookFactory) {
+		this(collisionHook, configuration, resolverHookFactory, null);
+	}
+
+	public DummyContainerAdaptor(ModuleCollisionHook collisionHook, Map<String, String> configuration, ResolverHookFactory resolverHookFactory, DebugOptions debugOptions) {
 		this.collisionHook = collisionHook;
 		this.configuration = configuration;
 		this.resolverHookFactory = resolverHookFactory;
 		this.moduleDatabase = new DummyModuleDatabase(this);
+		this.debugOptions = debugOptions;
 		this.container = new ModuleContainer(this, moduleDatabase);
 	}
 
@@ -82,4 +88,10 @@
 	public void publishModuleEvent(ModuleEvent type, Module module, Module origin) {
 		moduleDatabase.addEvent(new DummyModuleEvent(module, type, module.getState()));
 	}
+
+	@Override
+	public DebugOptions getDebugOptions() {
+		return this.debugOptions;
+	}
+
 }
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/dummys/DummyDebugOptions.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/dummys/DummyDebugOptions.java
new file mode 100755
index 0000000..debf784
--- /dev/null
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/dummys/DummyDebugOptions.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2015 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.tests.container.dummys;
+
+import java.io.File;
+import java.util.Map;
+import org.eclipse.osgi.service.debug.DebugOptions;
+import org.eclipse.osgi.service.debug.DebugTrace;
+
+public class DummyDebugOptions implements DebugOptions {
+	private final Map<String, String> options;
+
+	public DummyDebugOptions(Map<String, String> options) {
+		this.options = options;
+	}
+
+	@Override
+	public boolean getBooleanOption(String option, boolean defaultValue) {
+		String value = options.get(option);
+		return value == null ? defaultValue : Boolean.parseBoolean(value);
+	}
+
+	@Override
+	public String getOption(String option) {
+		return options.get(option);
+	}
+
+	@Override
+	public String getOption(String option, String defaultValue) {
+		String value = options.get(option);
+		return value == null ? defaultValue : value;
+	}
+
+	@Override
+	public int getIntegerOption(String option, int defaultValue) {
+		String value = options.get(option);
+		try {
+			return Integer.parseInt(value);
+		} catch (NumberFormatException e) {
+			return defaultValue;
+		}
+	}
+
+	@Override
+	public Map<String, String> getOptions() {
+		return options;
+	}
+
+	@Override
+	public void setOption(String option, String value) {
+		options.put(option, value);
+	}
+
+	@Override
+	public void setOptions(Map<String, String> options) {
+		this.options.clear();
+		this.options.putAll(options);
+	}
+
+	@Override
+	public void removeOption(String option) {
+		this.options.remove(option);
+	}
+
+	@Override
+	public boolean isDebugEnabled() {
+		return true;
+	}
+
+	@Override
+	public void setDebugEnabled(boolean value) {
+		// nothing
+	}
+
+	@Override
+	public void setFile(File newFile) {
+		// nothing
+	}
+
+	@Override
+	public File getFile() {
+		// nothing
+		return null;
+	}
+
+	@Override
+	public DebugTrace newDebugTrace(String bundleSymbolicName) {
+		// nothing
+		return null;
+	}
+
+	@Override
+	public DebugTrace newDebugTrace(String bundleSymbolicName, Class<?> traceEntryClass) {
+		// nothing
+		return null;
+	}
+
+}
diff --git a/bundles/org.eclipse.osgi.tests/test_files/containerTests/uses.c.dynamic.MF b/bundles/org.eclipse.osgi.tests/test_files/containerTests/uses.c.dynamic.MF
new file mode 100755
index 0000000..b880d65
--- /dev/null
+++ b/bundles/org.eclipse.osgi.tests/test_files/containerTests/uses.c.dynamic.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: uses.c
+Bundle-Version: 1.0
+DynamicImport-Package: 
+ uses1; version="[1.0,2.0)",
+ uses2; version="[2.0,3.0)"
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java
index a3bec34..cbde028 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java
@@ -20,7 +20,7 @@
 import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
 import org.eclipse.osgi.internal.framework.EquinoxContainer;
 import org.eclipse.osgi.internal.messages.Msg;
-import org.eclipse.osgi.report.resolution.*;
+import org.eclipse.osgi.report.resolution.ResolutionReport;
 import org.eclipse.osgi.report.resolution.ResolutionReport.Entry;
 import org.eclipse.osgi.report.resolution.ResolutionReport.Entry.Type;
 import org.eclipse.osgi.service.debug.DebugOptions;
@@ -900,6 +900,9 @@
 					}
 					report = reportBuilder.build(result, re);
 					if (DEBUG_REPORT) {
+						if (report.getResolutionException() != null) {
+							Debug.printStackTrace(report.getResolutionException());
+						}
 						Set<Resource> resources = report.getEntries().keySet();
 						if (!resources.isEmpty()) {
 							Debug.println("RESOLVER: Resolution report"); //$NON-NLS-1$