Improve resolver messages for constraint violations. (FELIX-2841)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1071050 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index aa2827a..bdd8a6e 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -3420,10 +3420,6 @@
                 catch (BundleException ex)
                 {
                     result = false;
-                    m_logger.log(targets[i],
-                        Logger.LOG_WARNING,
-                        "Unable to resolve bundle " + targets[i].getBundleId(),
-                        ex);
                 }
             }
 
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
index ecfe761..53281f3 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
@@ -34,8 +34,8 @@
 import org.apache.felix.framework.capabilityset.CapabilitySet;
 import org.apache.felix.framework.capabilityset.Directive;
 import org.apache.felix.framework.capabilityset.Requirement;
+import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.manifestparser.RequirementImpl;
-import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
 
 public class ResolverImpl implements Resolver
@@ -102,11 +102,6 @@
                     catch (ResolveException ex)
                     {
                         rethrow = ex;
-                        m_logger.log(
-                            module.getBundle(),
-                            Logger.LOG_DEBUG,
-                            "Current candidate permutation failed, will try another if possible.",
-                            ex);
                     }
                 }
                 while ((rethrow != null)
@@ -179,11 +174,6 @@
                     catch (ResolveException ex)
                     {
                         rethrow = ex;
-                        m_logger.log(
-                            module.getBundle(),
-                            Logger.LOG_DEBUG,
-                            "Current candidate permutation failed, will try another if possible.",
-                            ex);
                     }
                 }
                 while ((rethrow != null)
@@ -886,15 +876,27 @@
                         permutate(candidateMap, sourceBlame.m_reqs.get(0), m_importPermutations);
                         // Report conflict.
                         ResolveException ex = new ResolveException(
-                            "Constraint violation for package '"
-                            + entry.getKey() + "' when resolving module "
-                            + module + " between an import "
-                            + sourceBlame + " and a fragment import "
-                            + blame, module, blame.m_reqs.get(0));
+                            "Unable to resolve module "
+                            + module.getSymbolicName()
+                            + " [" + module
+                            + "] because it is exposed to package '"
+                            + entry.getKey()
+                            + "' from "
+                            + sourceBlame.m_cap.getModule().getSymbolicName()
+                            + " [" + sourceBlame.m_cap.getModule()
+                            + "] and "
+                            + blame.m_cap.getModule().getSymbolicName()
+                            + " [" + blame.m_cap.getModule()
+                            + "] via two dependency chains.\n\nChain 1:\n"
+                            + toStringBlame(sourceBlame)
+                            + "\n\nChain 2:\n"
+                            + toStringBlame(blame),
+                            null,
+                            null);
                         m_logger.log(
-                            module.getBundle(),
                             Logger.LOG_DEBUG,
-                            "Conflicting fragment import",
+                            "Candidate permutation failed due to a conflict with a "
+                            + "fragment import; will try another if possible.",
                             ex);
                         throw ex;
                     }
@@ -922,11 +924,18 @@
                     rethrow = (rethrow != null)
                         ? rethrow
                         : new ResolveException(
-                            "Constraint violation for package '"
-                            + pkgName + "' when resolving module "
-                            + module + " between existing export "
-                            + exportBlame + " and uses constraint "
-                            + usedBlame, null, null);
+                            "Unable to resolve module "
+                            + module.getSymbolicName()
+                            + " [" + module
+                            + "] because it exports package '"
+                            + pkgName
+                            + "' and is also exposed to it from "
+                            + usedBlame.m_cap.getModule().getSymbolicName()
+                            + " [" + usedBlame.m_cap.getModule()
+                            + "] via the following dependency chain:\n\n"
+                            + toStringBlame(usedBlame),
+                            null,
+                            null);
 
                     mutated = (mutated != null)
                         ? mutated
@@ -967,12 +976,10 @@
                 {
                     m_usesPermutations.add(permutation);
                 }
-                Bundle bundle =
-                    (rethrow.getModule() != null) ? rethrow.getModule().getBundle() : null;
                 m_logger.log(
-                    bundle,
                     Logger.LOG_DEBUG,
-                    "Conflict between an export and import",
+                    "Candidate permutation failed due to a conflict between "
+                    + "an export and import; will try another if possible.",
                     rethrow);
                 throw rethrow;
             }
@@ -1000,11 +1007,23 @@
                         rethrow = (rethrow != null)
                             ? rethrow
                             : new ResolveException(
-                                "Constraint violation for package '"
-                                + pkgName + "' when resolving module "
-                                + module + " between existing import "
-                                + importBlame + " and uses constraint "
-                                + usedBlame, null, null);
+                                "Unable to resolve module "
+                                + module.getSymbolicName()
+                                + " [" + module
+                                + "] because it is exposed to package '"
+                                + pkgName
+                                + "' from "
+                                + importBlame.m_cap.getModule().getSymbolicName()
+                                + " [" + importBlame.m_cap.getModule()
+                                + "] and "
+                                + usedBlame.m_cap.getModule().getSymbolicName()
+                                + " [" + usedBlame.m_cap.getModule()
+                                + "] via two dependency chains.\n\nChain 1:\n"
+                                + toStringBlame(importBlame)
+                                + "\n\nChain 2:\n"
+                                + toStringBlame(usedBlame),
+                                null,
+                                null);
 
                         mutated = (mutated != null)
                             ? mutated
@@ -1066,13 +1085,10 @@
                         permutateIfNeeded(candidateMap, req, m_importPermutations);
                     }
 
-                    Bundle bundle =
-                        (rethrow.getModule() != null)
-                            ? rethrow.getModule().getBundle() : null;
                     m_logger.log(
-                        bundle,
                         Logger.LOG_DEBUG,
-                        "Conflict between imports",
+                        "Candidate permutation failed due to a conflict between "
+                        + "imports; will try another if possible.",
                         rethrow);
                     throw rethrow;
                 }
@@ -1498,6 +1514,74 @@
         }
     }
 
+    private static String toStringBlame(Blame blame)
+    {
+        StringBuffer sb = new StringBuffer();
+        if ((blame.m_reqs != null) && !blame.m_reqs.isEmpty())
+        {
+            for (int i = 0; i < blame.m_reqs.size(); i++)
+            {
+                Requirement req = blame.m_reqs.get(i);
+                sb.append("  ");
+                sb.append(req.getModule().getSymbolicName());
+                sb.append(" [");
+                sb.append(req.getModule().toString());
+                sb.append("]\n    import: ");
+                sb.append(req.getFilter().toString());
+                sb.append("\n     |");
+                sb.append("\n    export: ");
+                if ((i + 1) < blame.m_reqs.size())
+                {
+                    Capability export = Util.getSatisfyingCapability(
+                        blame.m_reqs.get(i + 1).getModule(),
+                        blame.m_reqs.get(i));
+                    sb.append(export.getAttribute(Capability.PACKAGE_ATTR).toString());
+                    Capability usedCap;
+                    if ((i + 2) < blame.m_reqs.size())
+                    {
+                        usedCap = Util.getSatisfyingCapability(
+                            blame.m_reqs.get(i + 2).getModule(),
+                            blame.m_reqs.get(i + 1));
+                    }
+                    else
+                    {
+                        usedCap = Util.getSatisfyingCapability(
+                            blame.m_cap.getModule(),
+                            blame.m_reqs.get(i + 1));
+                    }
+                    sb.append("; uses:=");
+                    sb.append(usedCap.getAttribute(Capability.PACKAGE_ATTR).getValue());
+                    sb.append("\n");
+                }
+                else
+                {
+                    Capability export = Util.getSatisfyingCapability(
+                        blame.m_cap.getModule(),
+                        blame.m_reqs.get(i));
+                    sb.append(export.getAttribute(Capability.PACKAGE_ATTR).toString());
+                    if (!export.getAttribute(Capability.PACKAGE_ATTR).getValue()
+                        .equals(blame.m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue()))
+                    {
+                        sb.append("; uses:=");
+                        sb.append(blame.m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue());
+                        sb.append("\n    export: ");
+                        sb.append(blame.m_cap.getAttribute(Capability.PACKAGE_ATTR).toString());
+                    }
+                    sb.append("\n  ");
+                    sb.append(blame.m_cap.getModule().getSymbolicName());
+                    sb.append(" [");
+                    sb.append(blame.m_cap.getModule().toString());
+                    sb.append("]");
+                }
+            }
+        }
+        else
+        {
+            sb.append(blame.m_cap.getModule().toString());
+        }
+        return sb.toString();
+    }
+
     private static class Packages
     {
         private final Module m_module;