You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2014/07/18 18:34:13 UTC
[04/13] git commit: promote snake-yaml dependency and Yamls utility
to utils package,
and use yaml parsing for map coercion to be much more flexible in terms of
string-to-map coercion
promote snake-yaml dependency and Yamls utility to utils package, and use yaml parsing for map coercion to be much more flexible in terms of string-to-map coercion
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/a4f7a4c0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/a4f7a4c0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/a4f7a4c0
Branch: refs/heads/master
Commit: a4f7a4c0e3db5220e936bcffb5d52c3731b22175
Parents: f94c2ae
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Jul 17 23:29:12 2014 -0400
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Jul 18 09:53:52 2014 -0400
----------------------------------------------------------------------
camp/camp-base/pom.xml | 5 -
.../java/io/brooklyn/camp/spi/pdp/Artifact.java | 3 +-
.../camp/spi/pdp/ArtifactRequirement.java | 3 +-
.../java/io/brooklyn/camp/spi/pdp/Service.java | 3 +-
.../camp/spi/pdp/ServiceCharacteristic.java | 3 +-
.../brooklyn/camp/spi/resolve/PdpProcessor.java | 2 +-
.../main/java/io/brooklyn/util/yaml/Yamls.java | 89 +-------------
.../pdp/DeploymentPlanToyInterpreterTest.java | 2 +-
.../java/brooklyn/util/flags/TypeCoercions.java | 85 +++++++++-----
.../util/internal/TypeCoercionsTest.java | 72 ++++++++++--
usage/launcher/pom.xml | 7 ++
utils/common/pom.xml | 5 +
.../src/main/java/brooklyn/util/yaml/Yamls.java | 115 +++++++++++++++++++
13 files changed, 253 insertions(+), 141 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/pom.xml
----------------------------------------------------------------------
diff --git a/camp/camp-base/pom.xml b/camp/camp-base/pom.xml
index b80ef0c..e8638b1 100644
--- a/camp/camp-base/pom.xml
+++ b/camp/camp-base/pom.xml
@@ -57,11 +57,6 @@
<artifactId>commons-compress</artifactId>
<version>${commons-compress.version}</version>
</dependency>
- <dependency>
- <groupId>org.yaml</groupId>
- <artifactId>snakeyaml</artifactId>
- <version>${snakeyaml.version}</version>
- </dependency>
<!-- just for logging, not exported -->
<!--
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
index 72f356e..697a3ba 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Artifact.java
@@ -18,8 +18,6 @@
*/
package io.brooklyn.camp.spi.pdp;
-import io.brooklyn.util.yaml.Yamls;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -27,6 +25,7 @@ import java.util.Map;
import org.apache.commons.lang3.builder.ToStringBuilder;
import brooklyn.util.collections.MutableMap;
+import brooklyn.util.yaml.Yamls;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
index 50f5335..da9936a 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ArtifactRequirement.java
@@ -18,13 +18,12 @@
*/
package io.brooklyn.camp.spi.pdp;
-import io.brooklyn.util.yaml.Yamls;
-
import java.util.Map;
import org.apache.commons.lang3.builder.ToStringBuilder;
import brooklyn.util.collections.MutableMap;
+import brooklyn.util.yaml.Yamls;
import com.google.common.collect.ImmutableMap;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
index 241b80c..5921176 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/Service.java
@@ -18,8 +18,6 @@
*/
package io.brooklyn.camp.spi.pdp;
-import io.brooklyn.util.yaml.Yamls;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -27,6 +25,7 @@ import java.util.Map;
import org.apache.commons.lang3.builder.ToStringBuilder;
import brooklyn.util.collections.MutableMap;
+import brooklyn.util.yaml.Yamls;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
index cc5227d..8b27e2a 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/pdp/ServiceCharacteristic.java
@@ -18,13 +18,12 @@
*/
package io.brooklyn.camp.spi.pdp;
-import io.brooklyn.util.yaml.Yamls;
-
import java.util.Map;
import org.apache.commons.lang3.builder.ToStringBuilder;
import brooklyn.util.collections.MutableMap;
+import brooklyn.util.yaml.Yamls;
import com.google.common.collect.ImmutableMap;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpProcessor.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpProcessor.java b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpProcessor.java
index 897ff23..3252aaf 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpProcessor.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/camp/spi/resolve/PdpProcessor.java
@@ -27,7 +27,6 @@ import io.brooklyn.camp.spi.pdp.DeploymentPlan;
import io.brooklyn.camp.spi.pdp.Service;
import io.brooklyn.camp.spi.resolve.interpret.PlanInterpretationContext;
import io.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode;
-import io.brooklyn.util.yaml.Yamls;
import java.io.InputStream;
import java.io.Reader;
@@ -41,6 +40,7 @@ import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.yaml.snakeyaml.error.YAMLException;
import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.yaml.Yamls;
import com.google.common.annotations.VisibleForTesting;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/main/java/io/brooklyn/util/yaml/Yamls.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/main/java/io/brooklyn/util/yaml/Yamls.java b/camp/camp-base/src/main/java/io/brooklyn/util/yaml/Yamls.java
index d275ce8..44974f0 100644
--- a/camp/camp-base/src/main/java/io/brooklyn/util/yaml/Yamls.java
+++ b/camp/camp-base/src/main/java/io/brooklyn/util/yaml/Yamls.java
@@ -18,90 +18,7 @@
*/
package io.brooklyn.util.yaml;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.Iterables;
-
-public class Yamls {
-
- private static final Logger log = LoggerFactory.getLogger(Yamls.class);
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- public static <T> T getAs(Object x, Class<T> type) {
- if (x==null) return null;
- if (x instanceof Iterable || x instanceof Iterator) {
- List result = new ArrayList();
- Iterator xi;
- if (Iterator.class.isAssignableFrom(x.getClass())) {
- xi = (Iterator)x;
- } else {
- xi = ((Iterable)x).iterator();
- }
- while (xi.hasNext()) {
- result.add( xi.next() );
- }
- if (type.isAssignableFrom(Iterable.class)) return (T)result;
- if (type.isAssignableFrom(Iterator.class)) return (T)result.iterator();
- if (type.isAssignableFrom(List.class)) return (T)result;
- x = Iterables.getOnlyElement(result);
- }
- // TODO more coercion?
- return (T)x;
- }
-
- @SuppressWarnings("rawtypes")
- public static void dump(int depth, Object r) {
- if (r instanceof Iterable) {
- for (Object ri : ((Iterable)r))
- dump(depth+1, ri);
- } else if (r instanceof Map) {
- for (Object re: ((Map)r).entrySet()) {
- for (int i=0; i<depth; i++) System.out.print(" ");
- System.out.println(((Entry)re).getKey()+":");
- dump(depth+1, ((Entry)re).getValue());
- }
- } else {
- for (int i=0; i<depth; i++) System.out.print(" ");
- if (r==null) System.out.println("<null>");
- else System.out.println("<"+r.getClass().getSimpleName()+">"+" "+r);
- }
- }
-
- /** simplifies new Yaml().loadAll, and converts to list to prevent single-use iterable bug in yaml */
- @SuppressWarnings("unchecked")
- public static Iterable<Object> parseAll(String yaml) {
- Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml);
- return (List<Object>) getAs(result, List.class);
- }
-
- /** as {@link #parseAll(String)} */
- @SuppressWarnings("unchecked")
- public static Iterable<Object> parseAll(Reader yaml) {
- Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml);
- return (List<Object>) getAs(result, List.class);
- }
-
- public static Object removeMultinameAttribute(Map<String,Object> obj, String ...equivalentNames) {
- Object result = null;
- for (String name: equivalentNames) {
- Object candidate = obj.remove(name);
- if (candidate!=null) {
- if (result==null) result = candidate;
- else if (!result.equals(candidate)) {
- log.warn("Different values for attributes "+Arrays.toString(equivalentNames)+"; " +
- "preferring '"+result+"' to '"+candidate+"'");
- }
- }
- }
- return result;
- }
+/** @deprecated since 0.7.0 use {@link brooklyn.util.yaml.Yamls} */
+@Deprecated
+public class Yamls extends brooklyn.util.yaml.Yamls {
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/camp/camp-base/src/test/java/io/brooklyn/camp/spi/pdp/DeploymentPlanToyInterpreterTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-base/src/test/java/io/brooklyn/camp/spi/pdp/DeploymentPlanToyInterpreterTest.java b/camp/camp-base/src/test/java/io/brooklyn/camp/spi/pdp/DeploymentPlanToyInterpreterTest.java
index de922a1..29f8abf 100644
--- a/camp/camp-base/src/test/java/io/brooklyn/camp/spi/pdp/DeploymentPlanToyInterpreterTest.java
+++ b/camp/camp-base/src/test/java/io/brooklyn/camp/spi/pdp/DeploymentPlanToyInterpreterTest.java
@@ -21,7 +21,6 @@ package io.brooklyn.camp.spi.pdp;
import io.brooklyn.camp.BasicCampPlatform;
import io.brooklyn.camp.spi.resolve.PlanInterpreter.PlanInterpreterAdapter;
import io.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode;
-import io.brooklyn.util.yaml.Yamls;
import java.util.Map;
@@ -32,6 +31,7 @@ import org.testng.annotations.Test;
import brooklyn.util.stream.Streams;
import brooklyn.util.text.Strings;
+import brooklyn.util.yaml.Yamls;
@Test
public class DeploymentPlanToyInterpreterTest {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
index 1ee45ae..7e92ed5 100644
--- a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
+++ b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
@@ -18,7 +18,9 @@
*/
package brooklyn.util.flags;
-import java.io.IOException;
+import groovy.lang.Closure;
+import groovy.time.TimeDuration;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -39,23 +41,9 @@ import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
-import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.CaseFormat;
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Splitter;
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Table;
-import com.google.common.net.HostAndPort;
-import com.google.common.primitives.Primitives;
-import com.google.common.reflect.TypeToken;
-
import brooklyn.entity.basic.ClosureEntityFactory;
import brooklyn.entity.basic.ConfigurableEntityFactory;
import brooklyn.entity.basic.ConfigurableEntityFactoryFromEntityFactory;
@@ -70,10 +58,23 @@ import brooklyn.util.net.Cidr;
import brooklyn.util.net.Networking;
import brooklyn.util.net.UserAndHostAndPort;
import brooklyn.util.text.StringEscapes.JavaStringEscapes;
+import brooklyn.util.text.Strings;
import brooklyn.util.time.Duration;
-import groovy.lang.Closure;
-import groovy.time.TimeDuration;
+import brooklyn.util.yaml.Yamls;
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Table;
+import com.google.common.net.HostAndPort;
+import com.google.common.primitives.Primitives;
+import com.google.common.reflect.TypeToken;
+
+@SuppressWarnings("rawtypes")
public class TypeCoercions {
private static final Logger log = LoggerFactory.getLogger(TypeCoercions.class);
@@ -103,7 +104,7 @@ public class TypeCoercions {
}
/** @see #coerce(Object, Class) */
- @SuppressWarnings({ "unchecked", "rawtypes" })
+ @SuppressWarnings({ "unchecked" })
public static <T> T coerce(Object value, TypeToken<T> targetTypeToken) {
if (value==null) return null;
// does not actually cast generified contents; that is left to the caller
@@ -433,12 +434,14 @@ public class TypeCoercions {
}
});
registerAdapter(Collection.class, Set.class, new Function<Collection,Set>() {
+ @SuppressWarnings("unchecked")
@Override
public Set apply(Collection input) {
return new LinkedHashSet(input);
}
});
registerAdapter(Collection.class, List.class, new Function<Collection,List>() {
+ @SuppressWarnings("unchecked")
@Override
public List apply(Collection input) {
return new ArrayList(input);
@@ -485,12 +488,14 @@ public class TypeCoercions {
}
});
registerAdapter(Closure.class, ConfigurableEntityFactory.class, new Function<Closure,ConfigurableEntityFactory>() {
+ @SuppressWarnings("unchecked")
@Override
public ConfigurableEntityFactory apply(Closure input) {
return new ClosureEntityFactory(input);
}
});
registerAdapter(EntityFactory.class, ConfigurableEntityFactory.class, new Function<EntityFactory,ConfigurableEntityFactory>() {
+ @SuppressWarnings("unchecked")
@Override
public ConfigurableEntityFactory apply(EntityFactory input) {
if (input instanceof ConfigurableEntityFactory) return (ConfigurableEntityFactory)input;
@@ -498,6 +503,7 @@ public class TypeCoercions {
}
});
registerAdapter(Closure.class, EntityFactory.class, new Function<Closure,EntityFactory>() {
+ @SuppressWarnings("unchecked")
@Override
public EntityFactory apply(Closure input) {
return new ClosureEntityFactory(input);
@@ -530,6 +536,7 @@ public class TypeCoercions {
}
});
registerAdapter(Object.class, TimeDuration.class, new Function<Object,TimeDuration>() {
+ @SuppressWarnings("deprecation")
@Override
public TimeDuration apply(final Object input) {
log.warn("deprecated automatic coercion of Object to TimeDuration (set breakpoint in TypeCoercions to inspect, convert to Duration)");
@@ -627,20 +634,40 @@ public class TypeCoercions {
registerAdapter(String.class, Map.class, new Function<String,Map>() {
@Override
public Map apply(final String input) {
- // Auto-detect JSON. This allows complex data structures to be received over the REST API.
+ Exception error = null;
+
+ // first try wrapping in braces if needed
+ if (!input.trim().startsWith("{")) {
+ try {
+ return apply("{ "+input+" }");
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ // prefer this error
+ error = e;
+ // fall back to parsing without braces, e.g. if it's multiline
+ }
+ }
+
try {
- if (!input.isEmpty() && input.charAt(0) == '{') {
- return new ObjectMapper().readValue(input, Map.class);
+ return Yamls.getAs( Yamls.parseAll(input), Map.class );
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ if (error!=null && input.indexOf('\n')==-1) {
+ // prefer the original error if it wasn't braced and wasn't multiline
+ e = error;
}
- } catch (IOException e) {
- // just fall through to the map parsing
+ throw new IllegalArgumentException("Cannot parse string as map with flexible YAML parsing; "+
+ (e instanceof ClassCastException ? "yaml treats it as a string" :
+ (e instanceof IllegalArgumentException && Strings.isNonEmpty(e.getMessage())) ? e.getMessage() :
+ ""+e) );
}
-
- // TODO would be nice to accept YAML for complex data structures too, but it's not as simple as JSON to auto-detect.
-
- // Simple map parsing - supports "key1=value1,key2=value2" style input
- // TODO we should respect quoted strings etc
- return ImmutableMap.copyOf(Splitter.on(",").trimResults().omitEmptyStrings().withKeyValueSeparator("=").split(input));
+
+ // NB: previously we supported this also, when we did json above;
+ // yaml support is better as it supports quotes (and better than json because it allows dropping quotes)
+ // snake-yaml, our parser, also accepts key=value -- although i'm not sure this is strictly yaml compliant;
+ // our tests will catch it if snake behaviour changes, and we can reinstate this
+ // (but note it doesn't do quotes; see http://code.google.com/p/guava-libraries/issues/detail?id=412 for that):
+// return ImmutableMap.copyOf(Splitter.on(",").trimResults().omitEmptyStrings().withKeyValueSeparator("=").split(input));
}
});
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java b/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java
index 05b4c72..6efffa2 100644
--- a/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java
+++ b/core/src/test/java/brooklyn/util/internal/TypeCoercionsTest.java
@@ -32,17 +32,17 @@ import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.Test;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.util.flags.ClassCoercionException;
+import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.text.StringPredicates;
+
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.TypeToken;
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.util.flags.ClassCoercionException;
-import brooklyn.util.flags.TypeCoercions;
-import brooklyn.util.text.StringPredicates;
-
public class TypeCoercionsTest {
private static final Logger log = LoggerFactory.getLogger(TypeCoercionsTest.class);
@@ -191,15 +191,65 @@ public class TypeCoercionsTest {
}
@Test
- public void testStringToMapCoercion() {
- Map<?,?> s = TypeCoercions.coerce("a=1,b=2,c=3", Map.class);
- Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", "2", "c", "3"));
+ public void testJsonStringToMapCoercion() {
+ Map<?,?> s = TypeCoercions.coerce("{ \"a\" : \"1\", b : 2 }", Map.class);
+ Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", 2));
}
@Test
- public void testJsonStringToMapCoercion() {
- Map<?,?> s = TypeCoercions.coerce("{ \"a\" : \"1\", \"b\" : \"2\", \"c\" : \"3\" }", Map.class);
- Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", "2", "c", "3"));
+ public void testJsonStringWithoutQuotesToMapCoercion() {
+ Map<?,?> s = TypeCoercions.coerce("{ a : 1 }", Map.class);
+ Assert.assertEquals(s, ImmutableMap.of("a", 1));
+ }
+
+ @Test
+ public void testJsonComplexTypesToMapCoercion() {
+ Map<?,?> s = TypeCoercions.coerce("{ a : [1, \"2\", '\"3\"'], b: { c: d, 'e': \"f\" } }", Map.class);
+ Assert.assertEquals(s, ImmutableMap.of("a", ImmutableList.<Object>of(1, "2", "\"3\""),
+ "b", ImmutableMap.of("c", "d", "e", "f")));
+ }
+
+ @Test
+ public void testJsonStringWithoutBracesToMapCoercion() {
+ Map<?,?> s = TypeCoercions.coerce("a : 1", Map.class);
+ Assert.assertEquals(s, ImmutableMap.of("a", 1));
+ }
+
+ @Test
+ public void testJsonStringWithoutBracesWithMultipleToMapCoercion() {
+ Map<?,?> s = TypeCoercions.coerce("a : 1, b : 2", Map.class);
+ Assert.assertEquals(s, ImmutableMap.of("a", 1, "b", 2));
+ }
+
+ @Test
+ public void testKeyEqualsValueStringToMapCoercion() {
+ Map<?,?> s = TypeCoercions.coerce("a=1,b=2", Map.class);
+ Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", "2"));
+ }
+
+ @Test(expectedExceptions=IllegalArgumentException.class)
+ public void testJsonStringWithoutBracesOrSpaceDisallowedAsMapCoercion() {
+ // yaml requires spaces after the colon
+ Map<?,?> s = TypeCoercions.coerce("a:1,b:2", Map.class);
+ Assert.assertEquals(s, ImmutableMap.of("a", 1, "b", 2));
+ }
+
+ @Test
+ public void testEqualsInBracesMapCoercion() {
+ Map<?,?> s = TypeCoercions.coerce("{ a = 1, b = '2' }", Map.class);
+ Assert.assertEquals(s, ImmutableMap.of("a", 1, "b", "2"));
+ }
+
+ @Test
+ public void testKeyEqualsOrColonValueWithBracesStringToMapCoercion() {
+ Map<?,?> s = TypeCoercions.coerce("{ a=1, b: 2 }", Map.class);
+ Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", 2));
+ }
+
+ @Test
+ public void testKeyEqualsOrColonValueWithoutBracesStringToMapCoercion() {
+ Map<?,?> s = TypeCoercions.coerce("a=1, b: 2", Map.class);
+ Assert.assertEquals(s, ImmutableMap.of("a", "1", "b", 2));
}
@Test
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/usage/launcher/pom.xml
----------------------------------------------------------------------
diff --git a/usage/launcher/pom.xml b/usage/launcher/pom.xml
index 57d3e24..1fb4fcd 100644
--- a/usage/launcher/pom.xml
+++ b/usage/launcher/pom.xml
@@ -86,6 +86,13 @@
</dependency>
<dependency>
<groupId>io.brooklyn</groupId>
+ <artifactId>brooklyn-software-base</artifactId>
+ <version>${project.version}</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.brooklyn</groupId>
<artifactId>brooklyn-software-webapp</artifactId>
<version>${project.version}</version>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/utils/common/pom.xml
----------------------------------------------------------------------
diff --git a/utils/common/pom.xml b/utils/common/pom.xml
index 24dec9f..54de72a 100644
--- a/utils/common/pom.xml
+++ b/utils/common/pom.xml
@@ -56,6 +56,11 @@
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <version>${snakeyaml.version}</version>
+ </dependency>
<dependency>
<groupId>org.testng</groupId>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a4f7a4c0/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java b/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java
new file mode 100644
index 0000000..a22432f
--- /dev/null
+++ b/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 brooklyn.util.yaml;
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Iterables;
+
+public class Yamls {
+
+ private static final Logger log = LoggerFactory.getLogger(Yamls.class);
+
+ /** returns the given yaml object (map or list or primitive) as the given yaml-supperted type
+ * (map or list or primitive e.g. string, number, boolean).
+ * <p>
+ * if the object is an iterable containing a single element, and the type is not an iterable,
+ * this will attempt to unwrap it.
+ *
+ * @throws IllegalArgumentException if the input is an iterable not containing a single element,
+ * and the cast is requested to a non-iterable type
+ * @throws ClassCastException if cannot be casted */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static <T> T getAs(Object x, Class<T> type) {
+ if (x==null) return null;
+ if (x instanceof Iterable || x instanceof Iterator) {
+ List result = new ArrayList();
+ Iterator xi;
+ if (Iterator.class.isAssignableFrom(x.getClass())) {
+ xi = (Iterator)x;
+ } else {
+ xi = ((Iterable)x).iterator();
+ }
+ while (xi.hasNext()) {
+ result.add( xi.next() );
+ }
+ if (type.isAssignableFrom(Iterable.class)) return (T)result;
+ if (type.isAssignableFrom(Iterator.class)) return (T)result.iterator();
+ if (type.isAssignableFrom(List.class)) return (T)result;
+ x = Iterables.getOnlyElement(result);
+ }
+ return (T)x;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static void dump(int depth, Object r) {
+ if (r instanceof Iterable) {
+ for (Object ri : ((Iterable)r))
+ dump(depth+1, ri);
+ } else if (r instanceof Map) {
+ for (Object re: ((Map)r).entrySet()) {
+ for (int i=0; i<depth; i++) System.out.print(" ");
+ System.out.println(((Entry)re).getKey()+":");
+ dump(depth+1, ((Entry)re).getValue());
+ }
+ } else {
+ for (int i=0; i<depth; i++) System.out.print(" ");
+ if (r==null) System.out.println("<null>");
+ else System.out.println("<"+r.getClass().getSimpleName()+">"+" "+r);
+ }
+ }
+
+ /** simplifies new Yaml().loadAll, and converts to list to prevent single-use iterable bug in yaml */
+ @SuppressWarnings("unchecked")
+ public static Iterable<Object> parseAll(String yaml) {
+ Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml);
+ return (List<Object>) getAs(result, List.class);
+ }
+
+ /** as {@link #parseAll(String)} */
+ @SuppressWarnings("unchecked")
+ public static Iterable<Object> parseAll(Reader yaml) {
+ Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml);
+ return (List<Object>) getAs(result, List.class);
+ }
+
+ public static Object removeMultinameAttribute(Map<String,Object> obj, String ...equivalentNames) {
+ Object result = null;
+ for (String name: equivalentNames) {
+ Object candidate = obj.remove(name);
+ if (candidate!=null) {
+ if (result==null) result = candidate;
+ else if (!result.equals(candidate)) {
+ log.warn("Different values for attributes "+Arrays.toString(equivalentNames)+"; " +
+ "preferring '"+result+"' to '"+candidate+"'");
+ }
+ }
+ }
+ return result;
+ }
+}