ONOS-423 - Throw a specific exception when resources are exhausted

Create an exception to throw when no resources are available
Simple, Hazelcast and Distributed link resource stores throw ResourceAllocationException
Unit tests for successful and unsuccessful bandwidth and lambda allocations

Change-Id: If062d10d2233935dd59efabfa5f37a446e275a5b
diff --git a/core/api/src/main/java/org/onosproject/net/resource/ResourceAllocationException.java b/core/api/src/main/java/org/onosproject/net/resource/ResourceAllocationException.java
new file mode 100644
index 0000000..88c5ff0
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/resource/ResourceAllocationException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.net.resource;
+
+/**
+ * Exception thrown for resource allocation errors.
+ */
+public class ResourceAllocationException extends ResourceException {
+    public ResourceAllocationException() {
+        super();
+    }
+
+    public ResourceAllocationException(String message) {
+        super(message);
+    }
+
+    public ResourceAllocationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/resource/ResourceException.java b/core/api/src/main/java/org/onosproject/net/resource/ResourceException.java
new file mode 100644
index 0000000..d527206
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/resource/ResourceException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.net.resource;
+
+/**
+ * Represents a resource related error.
+ */
+public class ResourceException extends RuntimeException {
+
+    /**
+     * Constructs an exception with no message and no underlying cause.
+     */
+    public ResourceException() {
+    }
+
+    /**
+     * Constructs an exception with the specified message.
+     *
+     * @param message the message describing the specific nature of the error
+     */
+    public ResourceException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs an exception with the specified message and the underlying cause.
+     *
+     * @param message the message describing the specific nature of the error
+     * @param cause   the underlying cause of this error
+     */
+    public ResourceException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/DistributedLinkResourceStore.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/DistributedLinkResourceStore.java
index 2e7dfa1..bca6cdf 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/DistributedLinkResourceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/DistributedLinkResourceStore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.PositionalParameterStringFormatter;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.Link;
 import org.onosproject.net.LinkKey;
@@ -42,6 +43,7 @@
 import org.onosproject.net.resource.LinkResourceEvent;
 import org.onosproject.net.resource.LinkResourceStore;
 import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.resource.ResourceAllocationException;
 import org.onosproject.net.resource.ResourceType;
 import org.onosproject.store.serializers.KryoSerializer;
 import org.onosproject.store.serializers.StoreSerializer;
@@ -384,19 +386,26 @@
                 BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
                 double bwLeft = bw.bandwidth().toDouble();
                 bwLeft -= ((BandwidthResourceAllocation) req).bandwidth().toDouble();
+                BandwidthResourceAllocation bwReq = ((BandwidthResourceAllocation) req);
                 if (bwLeft < 0) {
-                    checkState(bwLeft >= 0,
-                               "There's no Bandwidth left on %s. %s",
-                               link, bwLeft);
+                    throw new ResourceAllocationException(
+                            PositionalParameterStringFormatter.format(
+                                    "Unable to allocate bandwidth for link {} "
+                                            + " requested amount is {} current allocation is {}",
+                                    link,
+                                    bwReq.bandwidth().toDouble(),
+                                    bw));
                 }
             } else if (req instanceof LambdaResourceAllocation) {
-
+                final LambdaResourceAllocation lambdaAllocation = (LambdaResourceAllocation) req;
                 // check if allocation should be accepted
                 if (!avail.contains(req)) {
                     // requested lambda was not available
-                    checkState(avail.contains(req),
-                               "Allocating %s on %s failed",
-                               req, link);
+                    throw new ResourceAllocationException(
+                            PositionalParameterStringFormatter.format(
+                                    "Unable to allocate lambda for link {} lamdba is {}",
+                                    link,
+                                    lambdaAllocation.lambda().toInt()));
                 }
             }
         }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/HazelcastLinkResourceStore.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/HazelcastLinkResourceStore.java
index 1294f0b..d5c6588 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/HazelcastLinkResourceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/HazelcastLinkResourceStore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.PositionalParameterStringFormatter;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.Link;
 import org.onosproject.net.LinkKey;
@@ -43,6 +44,7 @@
 import org.onosproject.net.resource.LinkResourceEvent;
 import org.onosproject.net.resource.LinkResourceStore;
 import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.resource.ResourceAllocationException;
 import org.onosproject.net.resource.ResourceType;
 import org.onosproject.store.StoreDelegate;
 import org.onosproject.store.hz.AbstractHazelcastStore;
@@ -330,20 +332,27 @@
                 }
                 BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
                 double bwLeft = bw.bandwidth().toDouble();
-                bwLeft -= ((BandwidthResourceAllocation) req).bandwidth().toDouble();
+                BandwidthResourceAllocation bwReq = ((BandwidthResourceAllocation) req);
+                bwLeft -= bwReq.bandwidth().toDouble();
                 if (bwLeft < 0) {
-                    checkState(bwLeft >= 0,
-                               "There's no Bandwidth left on %s. %s",
-                               link, bwLeft);
+                    throw new ResourceAllocationException(
+                            PositionalParameterStringFormatter.format(
+                                    "Unable to allocate bandwidth for link {} "
+                                        + " requested amount is {} current allocation is {}",
+                                    link,
+                                    bwReq.bandwidth().toDouble(),
+                                    bw));
                 }
             } else if (req instanceof LambdaResourceAllocation) {
-
+                LambdaResourceAllocation lambdaAllocation = (LambdaResourceAllocation) req;
                 // check if allocation should be accepted
                 if (!avail.contains(req)) {
                     // requested lambda was not available
-                    checkState(avail.contains(req),
-                               "Allocating %s on %s failed",
-                               req, link);
+                    throw new ResourceAllocationException(
+                            PositionalParameterStringFormatter.format(
+                                "Unable to allocate lambda for link {} lamdba is {}",
+                                    link,
+                                    lambdaAllocation.lambda().toInt()));
                 }
             }
         }
diff --git a/core/store/dist/src/test/java/org/onosproject/store/resource/impl/HazelcastLinkResourceStoreTest.java b/core/store/dist/src/test/java/org/onosproject/store/resource/impl/HazelcastLinkResourceStoreTest.java
index 7779435..c15018f 100644
--- a/core/store/dist/src/test/java/org/onosproject/store/resource/impl/HazelcastLinkResourceStoreTest.java
+++ b/core/store/dist/src/test/java/org/onosproject/store/resource/impl/HazelcastLinkResourceStoreTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,17 +27,25 @@
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DefaultLink;
 import org.onosproject.net.Link;
+import org.onosproject.net.intent.IntentId;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.resource.Bandwidth;
 import org.onosproject.net.resource.BandwidthResourceAllocation;
+import org.onosproject.net.resource.DefaultLinkResourceAllocations;
+import org.onosproject.net.resource.DefaultLinkResourceRequest;
+import org.onosproject.net.resource.Lambda;
 import org.onosproject.net.resource.LambdaResourceAllocation;
 import org.onosproject.net.resource.LinkResourceAllocations;
+import org.onosproject.net.resource.LinkResourceRequest;
 import org.onosproject.net.resource.LinkResourceStore;
 import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.resource.ResourceAllocationException;
 import org.onosproject.net.resource.ResourceType;
 import org.onosproject.store.hz.StoreService;
 import org.onosproject.store.hz.TestStoreManager;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.hazelcast.config.Config;
 import com.hazelcast.core.Hazelcast;
 
@@ -191,4 +199,103 @@
         }
 
     }
+
+    /**
+     * Tests a successful bandwidth allocation.
+     */
+    @Test
+    public void testSuccessfulBandwidthAllocation() {
+        final Link link = newLink("of:1", 1, "of:2", 2);
+
+        final LinkResourceRequest request =
+                DefaultLinkResourceRequest.builder(IntentId.valueOf(1),
+                        ImmutableSet.of(link))
+                .build();
+        final ResourceAllocation allocation =
+                new BandwidthResourceAllocation(Bandwidth.valueOf(900000.0));
+        final Set<ResourceAllocation> allocationSet = ImmutableSet.of(allocation);
+
+        final LinkResourceAllocations allocations =
+                new DefaultLinkResourceAllocations(request, ImmutableMap.of(link, allocationSet));
+
+        store.allocateResources(allocations);
+    }
+
+    /**
+     * Tests a unsuccessful bandwidth allocation.
+     */
+    @Test
+    public void testUnsuccessfulBandwidthAllocation() {
+        final Link link = newLink("of:1", 1, "of:2", 2);
+
+        final LinkResourceRequest request =
+                DefaultLinkResourceRequest.builder(IntentId.valueOf(1),
+                        ImmutableSet.of(link))
+                        .build();
+        final ResourceAllocation allocation =
+                new BandwidthResourceAllocation(Bandwidth.valueOf(9000000.0));
+        final Set<ResourceAllocation> allocationSet = ImmutableSet.of(allocation);
+
+        final LinkResourceAllocations allocations =
+                new DefaultLinkResourceAllocations(request, ImmutableMap.of(link, allocationSet));
+
+        boolean gotException = false;
+        try {
+            store.allocateResources(allocations);
+        } catch (ResourceAllocationException rae) {
+            assertEquals(true, rae.getMessage().contains("Unable to allocate bandwidth for link"));
+            gotException = true;
+        }
+        assertEquals(true, gotException);
+    }
+
+    /**
+     * Tests a successful bandwidth allocation.
+     */
+    @Test
+    public void testSuccessfulLambdaAllocation() {
+        final Link link = newLink("of:1", 1, "of:2", 2);
+
+        final LinkResourceRequest request =
+                DefaultLinkResourceRequest.builder(IntentId.valueOf(1),
+                        ImmutableSet.of(link))
+                        .build();
+        final ResourceAllocation allocation =
+                new BandwidthResourceAllocation(Bandwidth.valueOf(900000.0));
+        final Set<ResourceAllocation> allocationSet = ImmutableSet.of(allocation);
+
+        final LinkResourceAllocations allocations =
+                new DefaultLinkResourceAllocations(request, ImmutableMap.of(link, allocationSet));
+
+        store.allocateResources(allocations);
+    }
+
+    /**
+     * Tests a unsuccessful bandwidth allocation.
+     */
+    @Test
+    public void testUnsuccessfulLambdaAllocation() {
+        final Link link = newLink("of:1", 1, "of:2", 2);
+
+        final LinkResourceRequest request =
+                DefaultLinkResourceRequest.builder(IntentId.valueOf(1),
+                        ImmutableSet.of(link))
+                        .build();
+        final ResourceAllocation allocation =
+                new LambdaResourceAllocation(Lambda.valueOf(33));
+        final Set<ResourceAllocation> allocationSet = ImmutableSet.of(allocation);
+
+        final LinkResourceAllocations allocations =
+                new DefaultLinkResourceAllocations(request, ImmutableMap.of(link, allocationSet));
+        store.allocateResources(allocations);
+
+        boolean gotException = false;
+        try {
+            store.allocateResources(allocations);
+        } catch (ResourceAllocationException rae) {
+            assertEquals(true, rae.getMessage().contains("Unable to allocate lambda for link"));
+            gotException = true;
+        }
+        assertEquals(true, gotException);
+    }
 }
diff --git a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleLinkResourceStore.java b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleLinkResourceStore.java
index a0d877e..64e18e2 100644
--- a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleLinkResourceStore.java
+++ b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleLinkResourceStore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.PositionalParameterStringFormatter;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.Annotations;
 import org.onosproject.net.Link;
@@ -38,6 +39,7 @@
 import org.onosproject.net.resource.LinkResourceEvent;
 import org.onosproject.net.resource.LinkResourceStore;
 import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.resource.ResourceAllocationException;
 import org.onosproject.net.resource.ResourceType;
 import org.slf4j.Logger;
 
@@ -143,13 +145,30 @@
                 double requestedBandwidth =
                         ((BandwidthResourceAllocation) res).bandwidth().toDouble();
                 double newBandwidth = ba.bandwidth().toDouble() - requestedBandwidth;
-                checkState(newBandwidth >= 0.0);
+                if (newBandwidth < 0.0) {
+                    throw new ResourceAllocationException(
+                            PositionalParameterStringFormatter.format(
+                            "Unable to allocate bandwidth for link {} "
+                            + "requested amount is {} current allocation is {}",
+                                    link,
+                                    requestedBandwidth,
+                                    ba));
+                }
                 freeRes.remove(ba);
                 freeRes.add(new BandwidthResourceAllocation(
                         Bandwidth.valueOf(newBandwidth)));
                 break;
             case LAMBDA:
-                checkState(freeRes.remove(res));
+                final boolean lambdaAvailable = freeRes.remove(res);
+                if (!lambdaAvailable) {
+                    int requestedLambda =
+                            ((LambdaResourceAllocation) res).lambda().toInt();
+                    throw new ResourceAllocationException(
+                            PositionalParameterStringFormatter.format(
+                                    "Unable to allocate lambda for link {} lambda is {}",
+                                    link,
+                                    requestedLambda));
+                }
                 break;
             default:
                 break;
diff --git a/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleLinkResourceStoreTest.java b/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleLinkResourceStoreTest.java
index b4796b6..9638f3f 100644
--- a/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleLinkResourceStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleLinkResourceStoreTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
  */
 package org.onosproject.store.trivial.impl;
 
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -27,15 +28,21 @@
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DefaultLink;
 import org.onosproject.net.Link;
+import org.onosproject.net.intent.IntentId;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.resource.Bandwidth;
 import org.onosproject.net.resource.BandwidthResourceAllocation;
+import org.onosproject.net.resource.Lambda;
 import org.onosproject.net.resource.LambdaResourceAllocation;
 import org.onosproject.net.resource.LinkResourceAllocations;
 import org.onosproject.net.resource.LinkResourceStore;
 import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.resource.ResourceAllocationException;
+import org.onosproject.net.resource.ResourceRequest;
 import org.onosproject.net.resource.ResourceType;
 
+import com.google.common.collect.ImmutableSet;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -63,7 +70,7 @@
      * @param port2 destination port
      * @return created {@link Link} object
      */
-    private Link newLink(String dev1, int port1, String dev2, int port2) {
+    private static Link newLink(String dev1, int port1, String dev2, int port2) {
         Annotations annotations = DefaultAnnotations.builder()
                 .set(AnnotationKeys.OPTICAL_WAVES, "80")
                 .set(AnnotationKeys.BANDWIDTH, "1000000")
@@ -167,4 +174,133 @@
         assertNotNull(res);
         assertEquals(80, res.size());
     }
+
+    public static class MockLinkResourceBandwidthAllocations implements LinkResourceAllocations {
+        final double allocationAmount;
+
+        MockLinkResourceBandwidthAllocations(Double allocationAmount) {
+            this.allocationAmount = allocationAmount;
+        }
+        @Override
+        public Set<ResourceAllocation> getResourceAllocation(Link link) {
+            final ResourceAllocation allocation =
+                    new BandwidthResourceAllocation(Bandwidth.valueOf(allocationAmount));
+            final Set<ResourceAllocation> allocations = new HashSet<>();
+            allocations.add(allocation);
+            return allocations;
+        }
+
+        @Override
+        public IntentId intendId() {
+            return null;
+        }
+
+        @Override
+        public Collection<Link> links() {
+            return ImmutableSet.of(newLink("of:1", 1, "of:2", 2));
+        }
+
+        @Override
+        public Set<ResourceRequest> resources() {
+            return null;
+        }
+
+        @Override
+        public ResourceType type() {
+            return null;
+        }
+    }
+
+    public static class MockLinkResourceLambdaAllocations implements LinkResourceAllocations {
+        final int allocatedLambda;
+
+        MockLinkResourceLambdaAllocations(int allocatedLambda) {
+            this.allocatedLambda = allocatedLambda;
+        }
+        @Override
+        public Set<ResourceAllocation> getResourceAllocation(Link link) {
+            final ResourceAllocation allocation =
+                    new LambdaResourceAllocation(Lambda.valueOf(allocatedLambda));
+            final Set<ResourceAllocation> allocations = new HashSet<>();
+            allocations.add(allocation);
+            return allocations;
+        }
+
+        @Override
+        public IntentId intendId() {
+            return null;
+        }
+
+        @Override
+        public Collection<Link> links() {
+            return ImmutableSet.of(newLink("of:1", 1, "of:2", 2));
+        }
+
+        @Override
+        public Set<ResourceRequest> resources() {
+            return null;
+        }
+
+        @Override
+        public ResourceType type() {
+            return null;
+        }
+    }
+
+    /**
+     * Tests a successful bandwidth allocation.
+     */
+    @Test
+    public void testSuccessfulBandwidthAllocation() {
+        final LinkResourceAllocations allocations =
+                new MockLinkResourceBandwidthAllocations(900.0);
+        store.allocateResources(allocations);
+    }
+
+    /**
+     * Tests an unsuccessful bandwidth allocation.
+     */
+    @Test
+    public void testUnsuccessfulBandwidthAllocation() {
+        final LinkResourceAllocations allocations =
+                new MockLinkResourceBandwidthAllocations(2000000000.0);
+        boolean gotException = false;
+        try {
+            store.allocateResources(allocations);
+        } catch (ResourceAllocationException rae) {
+            assertEquals(true, rae.getMessage().contains("Unable to allocate bandwidth for link"));
+            gotException = true;
+        }
+        assertEquals(true, gotException);
+    }
+
+    /**
+     * Tests a successful lambda allocation.
+     */
+    @Test
+    public void testSuccessfulLambdaAllocation() {
+        final LinkResourceAllocations allocations =
+                new MockLinkResourceLambdaAllocations(1);
+        store.allocateResources(allocations);
+    }
+
+    /**
+     * Tests an unsuccessful lambda allocation.
+     */
+    @Test
+    public void testUnsuccessfulLambdaAllocation() {
+        final LinkResourceAllocations allocations =
+                new MockLinkResourceLambdaAllocations(1);
+        store.allocateResources(allocations);
+
+        boolean gotException = false;
+
+        try {
+            store.allocateResources(allocations);
+        } catch (ResourceAllocationException rae) {
+            assertEquals(true, rae.getMessage().contains("Unable to allocate lambda for link"));
+            gotException = true;
+        }
+        assertEquals(true, gotException);
+    }
 }
diff --git a/utils/misc/src/main/java/org/onlab/util/PositionalParameterStringFormatter.java b/utils/misc/src/main/java/org/onlab/util/PositionalParameterStringFormatter.java
new file mode 100644
index 0000000..89b2b75
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/util/PositionalParameterStringFormatter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed 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.onlab.util;
+
+/**
+ * Allows slf4j style formatting of parameters into a string.
+ */
+public final class PositionalParameterStringFormatter {
+
+    /**
+     * Hide default constructor.
+     */
+    private PositionalParameterStringFormatter() {
+    }
+
+    /**
+     * Formats a string using slf4j style positional parameter replacement.
+     * Instances of "{}" in the source string are replaced in order by the
+     * specified parameter values as strings.
+     *
+     * @param source original string to format
+     * @param parameters list of parameters that will be substituted
+     * @return formatted string
+     */
+    public static String format(String source, Object... parameters) {
+        String current = source;
+        for (Object parameter : parameters) {
+            if (!current.contains("{}")) {
+                return current;
+            }
+            current = current.replaceFirst("\\{\\}", parameter.toString());
+        }
+        return current;
+    }
+}