To combat typos, warn if there are identical adaptation services in a group.

We check both service names (keys) and service URIs for uniqueness.

There is no special runtime treatment for duplicates because they might
be configured that way intentionally.

=== modified file 'src/adaptation/ServiceGroups.cc'
--- src/adaptation/ServiceGroups.cc	2009-06-18 05:53:09 +0000
+++ src/adaptation/ServiceGroups.cc	2009-06-27 22:03:29 +0000
@@ -38,14 +38,15 @@
 {
     // 1) warn if services have different methods or vectoring point
     // 2) warn if all-same services have different bypass status
+    // 3) warn if there are seemingly identical services in the group
     // TODO: optimize by remembering ServicePointers rather than IDs
 
     String baselineKey;
     bool baselineBypass = false;
-    for (Store::iterator i = services.begin(); i != services.end(); ++i) {
-        const String &sid = *i;
+    for (Pos pos = 0; has(pos); ++pos) {
         // TODO: quit on all errors
-        ServicePointer service = FindService(sid);
+        const String &sid = services[pos];
+        ServicePointer service = at(pos);
         if (service != NULL) {
             if (method == methodNone) { 
                 // optimization: cache values that should be the same
@@ -53,11 +54,13 @@
                 point = service->cfg().point;
             } else {
                 if (method != service->cfg().method)
-                    finalizeError("Inconsistent service method for", sid);
+                    finalizeMsg("Inconsistent service method for", sid, true);
                 if (point != service->cfg().point)
-                    finalizeError("Inconsistent vectoring point for", sid);
+                    finalizeMsg("Inconsistent vectoring point for", sid, true);
             }
 
+            checkUniqueness(pos);
+
             if (allServicesSame) { 
                 if (!baselineKey.size()) {
                     baselineKey = service->cfg().key;
@@ -70,17 +73,39 @@
                 }
             }
         } else { 
-            finalizeError("Unknown adaptation name", sid);
+            finalizeMsg("ERROR: Unknown adaptation name", sid, true);
         }
     }
     debugs(93,7, HERE << "finalized " << kind << ": " << id);
 }
 
-void
-Adaptation::ServiceGroup::finalizeError(const char *msg, const String &culprit)
-{
-    debugs(93,0, "ERROR: " << msg << ' ' << culprit << " in " << kind <<
-        " '" << id << "'");
+/// checks that the service name or URI is not repeated later in the group
+void
+Adaptation::ServiceGroup::checkUniqueness(const Pos checkedPos) const
+{
+    ServicePointer checkedService = at(checkedPos);
+    if (!checkedService) // should not happen but be robust
+        return;
+
+    for (Pos p = checkedPos + 1; has(p); ++p) {
+        ServicePointer s = at(p);
+        if (s != NULL && s->cfg().key == checkedService->cfg().key)
+            finalizeMsg("duplicate service name", s->cfg().key, false);
+        else
+        if (s != NULL && s->cfg().uri == checkedService->cfg().uri)
+            finalizeMsg("duplicate service URI", s->cfg().uri, false);
+    }
+}
+
+/// emits a formatted warning or error message at the appropriate dbg level
+void
+Adaptation::ServiceGroup::finalizeMsg(const char *msg, const String &culprit,
+    bool error) const
+{
+    const int level = error ? DBG_CRITICAL : DBG_IMPORTANT;
+    const char *pfx = error ? "ERROR: " : "WARNING: ";
+    debugs(93,level, pfx << msg << ' ' << culprit << " in " << kind << " '" <<
+        id << "'");
 }
 
 Adaptation::ServicePointer Adaptation::ServiceGroup::at(const Pos pos) const {

=== modified file 'src/adaptation/ServiceGroups.h'
--- src/adaptation/ServiceGroups.h	2009-06-18 05:53:09 +0000
+++ src/adaptation/ServiceGroups.h	2009-06-27 21:58:20 +0000
@@ -48,7 +48,9 @@
 private:
     ServicePointer at(const Pos pos) const;
     bool findService(const ServiceFilter &filter, Pos &pos) const;
-    void finalizeError(const char *msg, const String &culprit);
+
+    void checkUniqueness(const Pos checkedPos) const;
+    void finalizeMsg(const char *msg, const String &culprit, bool error) const;
 
 public:
     String kind;


