You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/10/14 15:08:19 UTC

[3/4] incubator-brooklyn git commit: Not anonymous inner classes in functions

Not anonymous inner classes in functions

- Anonymous inner classes makes persistence very brittle.
- Adds more tests.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/f22b3eb4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/f22b3eb4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/f22b3eb4

Branch: refs/heads/master
Commit: f22b3eb4319d7c9d6e2604b0266bef8287889b86
Parents: 74207bd
Author: Aled Sage <al...@gmail.com>
Authored: Wed Oct 7 10:20:46 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Mon Oct 12 10:18:15 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/core/entity/EntityFunctions.java   | 178 ++++++++-
 .../brooklyn/feed/http/HttpValueFunctions.java  |   3 +
 .../brooklyn/feed/http/JsonFunctions.java       | 385 ++++++++++++++-----
 .../brooklyn/feed/ssh/SshValueFunctions.java    |  80 +++-
 .../brooklyn/feed/http/JsonFunctionsTest.java   |  15 +-
 .../feed/ssh/SshValueFunctionsTest.java         |  43 +++
 .../brooklyn/feed/jmx/JmxValueFunctions.java    |  55 ++-
 .../feed/jmx/JmxValueFunctionsTest.java         | 120 ++++++
 .../brooklyn/util/guava/MaybeFunctions.java     |  52 ++-
 .../brooklyn/util/math/MathFunctions.java       | 204 +++++++++-
 .../brooklyn/util/text/StringFunctions.java     | 301 +++++++++++++--
 .../brooklyn/util/guava/MaybeFunctionsTest.java |  47 +++
 .../brooklyn/util/math/MathFunctionsTest.java   |  16 +-
 .../brooklyn/util/text/StringFunctionsTest.java |  41 +-
 14 files changed, 1342 insertions(+), 198 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/core/src/main/java/org/apache/brooklyn/core/entity/EntityFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/EntityFunctions.java b/core/src/main/java/org/apache/brooklyn/core/entity/EntityFunctions.java
index 331fb7d..a4459ed 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/EntityFunctions.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/EntityFunctions.java
@@ -43,7 +43,10 @@ import com.google.common.collect.Iterables;
 
 public class EntityFunctions {
 
-    public static <T> Function<Entity, T> attribute(final AttributeSensor<T> attribute) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <T> Function<Entity, T> attributeOld(final AttributeSensor<T> attribute) {
+        // TODO PERSISTENCE WORKAROUND
         class GetEntityAttributeFunction implements Function<Entity, T> {
             @Override public T apply(Entity input) {
                 return (input == null) ? null : input.getAttribute(attribute);
@@ -52,7 +55,10 @@ public class EntityFunctions {
         return new GetEntityAttributeFunction();
     }
     
-    public static <T> Function<Entity, T> config(final ConfigKey<T> key) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <T> Function<Entity, T> configOld(final ConfigKey<T> key) {
+        // TODO PERSISTENCE WORKAROUND
         class GetEntityConfigFunction implements Function<Entity, T> {
             @Override public T apply(Entity input) {
                 return (input == null) ? null : input.getConfig(key);
@@ -61,7 +67,10 @@ public class EntityFunctions {
         return new GetEntityConfigFunction();
     }
     
-    public static Function<Entity, String> displayName() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Entity, String> displayNameOld() {
+        // TODO PERSISTENCE WORKAROUND
         class GetEntityDisplayName implements Function<Entity, String> {
             @Override public String apply(Entity input) {
                 return (input == null) ? null : input.getDisplayName();
@@ -70,7 +79,10 @@ public class EntityFunctions {
         return new GetEntityDisplayName();
     }
     
-    public static Function<Identifiable, String> id() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Identifiable, String> idOld() {
+        // TODO PERSISTENCE WORKAROUND
         class GetIdFunction implements Function<Identifiable, String> {
             @Override public String apply(Identifiable input) {
                 return (input == null) ? null : input.getId();
@@ -79,10 +91,10 @@ public class EntityFunctions {
         return new GetIdFunction();
     }
 
-    /** returns a function which sets the given sensors on the entity passed in,
-     * with {@link Entities#UNCHANGED} and {@link Entities#REMOVE} doing those actions. */
-    public static Function<Entity,Void> settingSensorsConstant(final Map<AttributeSensor<?>,Object> values) {
-        checkNotNull(values, "values");
+    /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Entity,Void> settingSensorsConstantOld(final Map<AttributeSensor<?>,Object> values) {
+        // TODO PERSISTENCE WORKAROUND
         class SettingSensorsConstantFunction implements Function<Entity, Void> {
             @SuppressWarnings({ "unchecked", "rawtypes" })
             @Override public Void apply(Entity input) {
@@ -104,14 +116,10 @@ public class EntityFunctions {
         return new SettingSensorsConstantFunction();
     }
 
-    /** as {@link #settingSensorsConstant(Map)} but as a {@link Runnable} */
-    public static Runnable settingSensorsConstant(final Entity entity, final Map<AttributeSensor<?>,Object> values) {
-        checkNotNull(entity, "entity");
-        checkNotNull(values, "values");
-        return Functionals.runnable(Suppliers.compose(settingSensorsConstant(values), Suppliers.ofInstance(entity)));
-    }
-
-    public static <K,V> Function<Entity, Void> updatingSensorMapEntry(final AttributeSensor<Map<K,V>> mapSensor, final K key, final Supplier<? extends V> valueSupplier) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <K,V> Function<Entity, Void> updatingSensorMapEntryOld(final AttributeSensor<Map<K,V>> mapSensor, final K key, final Supplier<? extends V> valueSupplier) {
+        // TODO PERSISTENCE WORKAROUND
         class UpdatingSensorMapEntryFunction implements Function<Entity, Void> {
             @Override public Void apply(Entity input) {
                 ServiceStateLogic.updateMapSensorEntry((EntityLocal)input, mapSensor, key, valueSupplier.get());
@@ -120,11 +128,11 @@ public class EntityFunctions {
         }
         return new UpdatingSensorMapEntryFunction();
     }
-    public static <K,V> Runnable updatingSensorMapEntry(final Entity entity, final AttributeSensor<Map<K,V>> mapSensor, final K key, final Supplier<? extends V> valueSupplier) {
-        return Functionals.runnable(Suppliers.compose(updatingSensorMapEntry(mapSensor, key, valueSupplier), Suppliers.ofInstance(entity)));
-    }
 
-    public static Supplier<Collection<Application>> applications(final ManagementContext mgmt) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of non-static inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Supplier<Collection<Application>> applicationsOld(final ManagementContext mgmt) {
+        // TODO PERSISTENCE WORKAROUND
         class AppsSupplier implements Supplier<Collection<Application>> {
             @Override
             public Collection<Application> get() {
@@ -134,6 +142,136 @@ public class EntityFunctions {
         return new AppsSupplier();
     }
     
+    public static <T> Function<Entity, T> attribute(AttributeSensor<T> attribute) {
+        return new GetEntityAttributeFunction<T>(checkNotNull(attribute, "attribute"));
+    }
+
+    protected static class GetEntityAttributeFunction<T> implements Function<Entity, T> {
+        private final AttributeSensor<T> attribute;
+        protected GetEntityAttributeFunction(AttributeSensor<T> attribute) {
+            this.attribute = attribute;
+        }
+        @Override public T apply(Entity input) {
+            return (input == null) ? null : input.getAttribute(attribute);
+        }
+    };
+
+    public static <T> Function<Entity, T> config(ConfigKey<T> key) {
+        return new GetEntityConfigFunction<T>(checkNotNull(key, "key"));
+    }
+
+    protected static class GetEntityConfigFunction<T> implements Function<Entity, T> {
+        private final ConfigKey<T> key;
+
+        protected GetEntityConfigFunction(ConfigKey<T> key) {
+            this.key = key;
+        }
+
+        @Override public T apply(Entity input) {
+            return (input == null) ? null : input.getConfig(key);
+        }
+    };
+
+    public static Function<Entity, String> displayName() {
+        return GetEntityDisplayName.INSTANCE;
+    }
+
+    protected static class GetEntityDisplayName implements Function<Entity, String> {
+        public static final GetEntityDisplayName INSTANCE = new GetEntityDisplayName();
+        @Override public String apply(Entity input) {
+            return (input == null) ? null : input.getDisplayName();
+        }
+    };
+
+    public static Function<Identifiable, String> id() {
+        return GetIdFunction.INSTANCE;
+    }
+    
+    protected static class GetIdFunction implements Function<Identifiable, String> {
+        public static final GetIdFunction INSTANCE = new GetIdFunction();
+        @Override public String apply(Identifiable input) {
+            return (input == null) ? null : input.getId();
+        }
+    };
+
+
+    /** returns a function which sets the given sensors on the entity passed in,
+     * with {@link Entities#UNCHANGED} and {@link Entities#REMOVE} doing those actions. */
+    public static Function<Entity,Void> settingSensorsConstant(final Map<AttributeSensor<?>,Object> values) {
+        return new SettingSensorsConstantFunction(checkNotNull(values, "values"));
+    }
+
+    protected static class SettingSensorsConstantFunction implements Function<Entity, Void> {
+        private final Map<AttributeSensor<?>, Object> values;
+
+        protected SettingSensorsConstantFunction(Map<AttributeSensor<?>, Object> values) {
+            this.values = values;
+        }
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        @Override public Void apply(Entity input) {
+            for (Map.Entry<AttributeSensor<?>,Object> entry : values.entrySet()) {
+                AttributeSensor sensor = (AttributeSensor)entry.getKey();
+                Object value = entry.getValue();
+                if (value==Entities.UNCHANGED) {
+                    // nothing
+                } else if (value==Entities.REMOVE) {
+                    ((EntityInternal)input).sensors().remove(sensor);
+                } else {
+                    value = TypeCoercions.coerce(value, sensor.getTypeToken());
+                    ((EntityInternal)input).sensors().set(sensor, value);
+                }
+            }
+            return null;
+        }
+    }
+
+    /** as {@link #settingSensorsConstant(Map)} but as a {@link Runnable} */
+    public static Runnable settingSensorsConstant(final Entity entity, final Map<AttributeSensor<?>,Object> values) {
+        checkNotNull(entity, "entity");
+        checkNotNull(values, "values");
+        return Functionals.runnable(Suppliers.compose(settingSensorsConstant(values), Suppliers.ofInstance(entity)));
+    }
+
+    public static <K,V> Function<Entity, Void> updatingSensorMapEntry(final AttributeSensor<Map<K,V>> mapSensor, final K key, final Supplier<? extends V> valueSupplier) {
+        return new UpdatingSensorMapEntryFunction<K,V>(mapSensor, key, valueSupplier);
+    }
+    
+    protected static class UpdatingSensorMapEntryFunction<K, V> implements Function<Entity, Void> {
+        private final AttributeSensor<Map<K, V>> mapSensor;
+        private final K key;
+        private final Supplier<? extends V> valueSupplier;
+
+        public UpdatingSensorMapEntryFunction(AttributeSensor<Map<K, V>> mapSensor, K key, Supplier<? extends V> valueSupplier) {
+            this.mapSensor = mapSensor;
+            this.key = key;
+            this.valueSupplier = valueSupplier;
+        }
+        @Override public Void apply(Entity input) {
+            ServiceStateLogic.updateMapSensorEntry((EntityLocal)input, mapSensor, key, valueSupplier.get());
+            return null;
+        }
+    }
+
+    public static <K,V> Runnable updatingSensorMapEntry(final Entity entity, final AttributeSensor<Map<K,V>> mapSensor, final K key, final Supplier<? extends V> valueSupplier) {
+        return Functionals.runnable(Suppliers.compose(updatingSensorMapEntry(mapSensor, key, valueSupplier), Suppliers.ofInstance(entity)));
+    }
+
+    public static Supplier<Collection<Application>> applications(ManagementContext mgmt) {
+        return new AppsSupplier(checkNotNull(mgmt, "mgmt"));
+    }
+    
+    protected static class AppsSupplier implements Supplier<Collection<Application>> {
+        private final ManagementContext mgmt;
+
+        public AppsSupplier(ManagementContext mgmt) {
+            this.mgmt = mgmt;
+        }
+        @Override
+        public Collection<Application> get() {
+            return mgmt.getApplications();
+        }
+    }
+
     public static Function<Entity, Location> locationMatching(Predicate<? super Location> filter) {
         return new LocationMatching(filter);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/core/src/main/java/org/apache/brooklyn/feed/http/HttpValueFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/feed/http/HttpValueFunctions.java b/core/src/main/java/org/apache/brooklyn/feed/http/HttpValueFunctions.java
index 75dab74..0fec133 100644
--- a/core/src/main/java/org/apache/brooklyn/feed/http/HttpValueFunctions.java
+++ b/core/src/main/java/org/apache/brooklyn/feed/http/HttpValueFunctions.java
@@ -39,6 +39,7 @@ public class HttpValueFunctions {
     
     /** @deprecated since 0.7.0; only here for deserialization of persisted state */
     private static Function<HttpToolResponse, Integer> responseCodeLegacy() {
+        // TODO PERSISTENCE WORKAROUND kept anonymous function in case referenced in persisted state
         return new Function<HttpToolResponse, Integer>() {
             @Override public Integer apply(HttpToolResponse input) {
                 return input.getResponseCode();
@@ -70,6 +71,7 @@ public class HttpValueFunctions {
     
     /** @deprecated since 0.7.0; only here for deserialization of persisted state */
     private static Function<HttpToolResponse, String> stringContentsFunctionLegacy() {
+        // TODO PERSISTENCE WORKAROUND kept anonymous function in case referenced in persisted state
         return new Function<HttpToolResponse, String>() {
             @Override public String apply(HttpToolResponse input) {
                 return input.getContentAsString();
@@ -105,6 +107,7 @@ public class HttpValueFunctions {
 
     /** @deprecated since 0.7.0; only here for deserialization of persisted state */
     private static Function<HttpToolResponse, Long> latencyLegacy() {
+        // TODO PERSISTENCE WORKAROUND kept anonymous function in case referenced in persisted state
         return new Function<HttpToolResponse, Long>() {
             public Long apply(HttpToolResponse input) {
                 return input.getLatencyFullContent();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/core/src/main/java/org/apache/brooklyn/feed/http/JsonFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/feed/http/JsonFunctions.java b/core/src/main/java/org/apache/brooklyn/feed/http/JsonFunctions.java
index a3e04cd..beff29f 100644
--- a/core/src/main/java/org/apache/brooklyn/feed/http/JsonFunctions.java
+++ b/core/src/main/java/org/apache/brooklyn/feed/http/JsonFunctions.java
@@ -34,14 +34,20 @@ import org.apache.brooklyn.util.guava.MaybeFunctions;
 import com.google.common.base.Function;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Lists;
-import com.google.gson.*;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
 import com.jayway.jsonpath.JsonPath;
 
 public class JsonFunctions {
 
     private JsonFunctions() {} // instead use static utility methods
     
-    public static Function<String, JsonElement> asJson() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<String, JsonElement> asJsonOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<String, JsonElement>() {
             @Override public JsonElement apply(String input) {
                 return new JsonParser().parse(input);
@@ -49,7 +55,10 @@ public class JsonFunctions {
         };
     }
 
-    public static <T> Function<JsonElement, List<T>> forEach(final Function<JsonElement, T> func) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <T> Function<JsonElement, List<T>> forEachOld(final Function<JsonElement, T> func) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<JsonElement, List<T>>() {
             @Override public List<T> apply(JsonElement input) {
                 JsonArray array = (JsonArray) input;
@@ -62,7 +71,129 @@ public class JsonFunctions {
         };
     }
 
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<JsonElement, JsonElement> walkOld(final Iterable<String> elements) {
+        // TODO PERSISTENCE WORKAROUND
+        return new Function<JsonElement, JsonElement>() {
+            @Override public JsonElement apply(JsonElement input) {
+                JsonElement curr = input;
+                for (String element : elements) {
+                    JsonObject jo = curr.getAsJsonObject();
+                    curr = jo.get(element);
+                    if (curr==null)
+                        throw new NoSuchElementException("No element '"+element+" in JSON, when walking "+elements);
+                }
+                return curr;
+            }
+        };
+    }
+
+    /** as {@link #walk(Iterable))} but if any element is not found it simply returns null */
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<JsonElement, JsonElement> walkNOld(final Iterable<String> elements) {
+        // TODO PERSISTENCE WORKAROUND
+        return new Function<JsonElement, JsonElement>() {
+            @Override public JsonElement apply(JsonElement input) {
+                JsonElement curr = input;
+                for (String element : elements) {
+                    if (curr==null) return null;
+                    JsonObject jo = curr.getAsJsonObject();
+                    curr = jo.get(element);
+                }
+                return curr;
+            }
+        };
+    }
+
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Maybe<JsonElement>, Maybe<JsonElement>> walkMOld(final Iterable<String> elements) {
+        // TODO PERSISTENCE WORKAROUND
+        return new Function<Maybe<JsonElement>, Maybe<JsonElement>>() {
+            @Override public Maybe<JsonElement> apply(Maybe<JsonElement> input) {
+                Maybe<JsonElement> curr = input;
+                for (String element : elements) {
+                    if (curr.isAbsent()) return curr;
+                    JsonObject jo = curr.get().getAsJsonObject();
+                    JsonElement currO = jo.get(element);
+                    if (currO==null) return Maybe.absent("No element '"+element+" in JSON, when walking "+elements);
+                    curr = Maybe.of(currO);
+                }
+                return curr;
+            }
+        };
+    }
+
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <T> Function<JsonElement,T> getPathOld(final String path) {
+        // TODO PERSISTENCE WORKAROUND
+        return new Function<JsonElement, T>() {
+            @SuppressWarnings("unchecked")
+            @Override public T apply(JsonElement input) {
+                String jsonString = input.toString();
+                Object rawElement = JsonPath.read(jsonString, path);
+                return (T) rawElement;
+            }
+        };
+    }
+
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <T> Function<JsonElement, T> castOld(final Class<T> expected) {
+        // TODO PERSISTENCE WORKAROUND
+        return new Function<JsonElement, T>() {
+            @Override public T apply(JsonElement input) {
+                return doCast(input, expected);
+            }
+        };
+    }
     
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <T> Function<Maybe<JsonElement>, T> castMOld(final Class<T> expected, final T defaultValue) {
+        // TODO PERSISTENCE WORKAROUND
+        return new Function<Maybe<JsonElement>, T>() {
+            @Override
+            public T apply(Maybe<JsonElement> input) {
+                if (input.isAbsent()) return defaultValue;
+                return cast(expected).apply(input.get());
+            }
+        };
+    }
+
+    public static Function<String, JsonElement> asJson() {
+        return new AsJson();
+    }
+
+    protected static class AsJson implements Function<String, JsonElement> {
+        @Override public JsonElement apply(String input) {
+            return new JsonParser().parse(input);
+        }
+    }
+
+    public static <T> Function<JsonElement, List<T>> forEach(final Function<JsonElement, T> func) {
+        return new ForEach<T>(func);
+    }
+
+    protected static class ForEach<T> implements Function<JsonElement, List<T>> {
+        private final Function<JsonElement, T> func;
+        public ForEach(Function<JsonElement, T> func) {
+            this.func = func;
+        }
+
+        @Override public List<T> apply(JsonElement input) {
+            JsonArray array = (JsonArray) input;
+            List<T> result = Lists.newArrayList();
+            for (int i = 0; i < array.size(); i++) {
+                result.add(func.apply(array.get(i)));
+            }
+            return result;
+        }
+    }
+
     /** as {@link #walkM(Iterable)} taking a single string consisting of a dot separated path */
     public static Function<JsonElement, JsonElement> walk(String elementOrDotSeparatedElements) {
         return walk( Splitter.on('.').split(elementOrDotSeparatedElements) );
@@ -78,21 +209,25 @@ public class JsonFunctions {
     public static Function<JsonElement, JsonElement> walk(final Iterable<String> elements) {
         // could do this instead, pointing at Maybe for this, and for walkN, but it's slightly less efficient
 //      return Functionals.chain(MaybeFunctions.<JsonElement>wrap(), walkM(elements), MaybeFunctions.<JsonElement>get());
+        return new Walk(elements);
+    }
 
-        return new Function<JsonElement, JsonElement>() {
-            @Override public JsonElement apply(JsonElement input) {
-                JsonElement curr = input;
-                for (String element : elements) {
-                    JsonObject jo = curr.getAsJsonObject();
-                    curr = jo.get(element);
-                    if (curr==null)
-                        throw new NoSuchElementException("No element '"+element+" in JSON, when walking "+elements);
-                }
-                return curr;
+    protected static class Walk implements Function<JsonElement, JsonElement> {
+        private final Iterable<String> elements;
+        public Walk(Iterable<String> elements) {
+            this.elements = elements;
+        }
+        @Override public JsonElement apply(JsonElement input) {
+            JsonElement curr = input;
+            for (String element : elements) {
+                JsonObject jo = curr.getAsJsonObject();
+                curr = jo.get(element);
+                if (curr==null)
+                    throw new NoSuchElementException("No element '"+element+" in JSON, when walking "+elements);
             }
-        };
+            return curr;
+        }
     }
-
     
     /** as {@link #walk(String)} but if any element is not found it simply returns null */
     public static Function<JsonElement, JsonElement> walkN(@Nullable String elements) {
@@ -106,17 +241,24 @@ public class JsonFunctions {
 
     /** as {@link #walk(Iterable))} but if any element is not found it simply returns null */
     public static Function<JsonElement, JsonElement> walkN(final Iterable<String> elements) {
-        return new Function<JsonElement, JsonElement>() {
-            @Override public JsonElement apply(JsonElement input) {
-                JsonElement curr = input;
-                for (String element : elements) {
-                    if (curr==null) return null;
-                    JsonObject jo = curr.getAsJsonObject();
-                    curr = jo.get(element);
-                }
-                return curr;
+        return new WalkN(elements);
+    }
+
+    protected static class WalkN implements Function<JsonElement, JsonElement> {
+        private final Iterable<String> elements;
+
+        public WalkN(Iterable<String> elements) {
+            this.elements = elements;
+        }
+        @Override public JsonElement apply(JsonElement input) {
+            JsonElement curr = input;
+            for (String element : elements) {
+                if (curr==null) return null;
+                JsonObject jo = curr.getAsJsonObject();
+                curr = jo.get(element);
             }
-        };
+            return curr;
+        }
     }
 
     /** as {@link #walk(String))} and {@link #walk(Iterable)} */
@@ -133,89 +275,116 @@ public class JsonFunctions {
      * simply preserving a {@link Maybe#absent()} object if additional walks are requested upon it
      * (cf jquery) */
     public static Function<Maybe<JsonElement>, Maybe<JsonElement>> walkM(final Iterable<String> elements) {
-        return new Function<Maybe<JsonElement>, Maybe<JsonElement>>() {
-            @Override public Maybe<JsonElement> apply(Maybe<JsonElement> input) {
-                Maybe<JsonElement> curr = input;
-                for (String element : elements) {
-                    if (curr.isAbsent()) return curr;
-                    JsonObject jo = curr.get().getAsJsonObject();
-                    JsonElement currO = jo.get(element);
-                    if (currO==null) return Maybe.absent("No element '"+element+" in JSON, when walking "+elements);
-                    curr = Maybe.of(currO);
-                }
-                return curr;
-            }
-        };
+        return new WalkM(elements);
     }
 
+    protected static class WalkM implements Function<Maybe<JsonElement>, Maybe<JsonElement>> {
+        private final Iterable<String> elements;
+
+        public WalkM(Iterable<String> elements) {
+            this.elements = elements;
+        }
+
+        @Override public Maybe<JsonElement> apply(Maybe<JsonElement> input) {
+            Maybe<JsonElement> curr = input;
+            for (String element : elements) {
+                if (curr.isAbsent()) return curr;
+                JsonObject jo = curr.get().getAsJsonObject();
+                JsonElement currO = jo.get(element);
+                if (currO==null) return Maybe.absent("No element '"+element+" in JSON, when walking "+elements);
+                curr = Maybe.of(currO);
+            }
+            return curr;
+        }
+    }
+    
     /**
      * returns an element from a single json primitive value given a full path {@link com.jayway.jsonpath.JsonPath}
      */
     public static <T> Function<JsonElement,T> getPath(final String path) {
-        return new Function<JsonElement, T>() {
-            @SuppressWarnings("unchecked")
-            @Override public T apply(JsonElement input) {
-                String jsonString = input.toString();
-                Object rawElement = JsonPath.read(jsonString, path);
-                return (T) rawElement;
-            }
-        };
+        return new GetPath<T>(path);
     }
 
-    @SuppressWarnings("unchecked")
+    protected static class GetPath<T> implements Function<JsonElement, T> {
+        private final String path;
+
+        public GetPath(String path) {
+            this.path = path;
+        }
+        @SuppressWarnings("unchecked")
+        @Override public T apply(JsonElement input) {
+            String jsonString = input.toString();
+            Object rawElement = JsonPath.read(jsonString, path);
+            return (T) rawElement;
+        }
+    };
+
     public static <T> Function<JsonElement, T> cast(final Class<T> expected) {
-        return new Function<JsonElement, T>() {
-            @Override public T apply(JsonElement input) {
-                if (input == null) {
-                    return (T) null;
-                } else if (input.isJsonNull()) {
-                    return (T) null;
-                } else if (expected == boolean.class || expected == Boolean.class) {
-                    return (T) (Boolean) input.getAsBoolean();
-                } else if (expected == char.class || expected == Character.class) {
-                    return (T) (Character) input.getAsCharacter();
-                } else if (expected == byte.class || expected == Byte.class) {
-                    return (T) (Byte) input.getAsByte();
-                } else if (expected == short.class || expected == Short.class) {
-                    return (T) (Short) input.getAsShort();
-                } else if (expected == int.class || expected == Integer.class) {
-                    return (T) (Integer) input.getAsInt();
-                } else if (expected == long.class || expected == Long.class) {
-                    return (T) (Long) input.getAsLong();
-                } else if (expected == float.class || expected == Float.class) {
-                    return (T) (Float) input.getAsFloat();
-                } else if (expected == double.class || expected == Double.class) {
-                    return (T) (Double) input.getAsDouble();
-                } else if (expected == BigDecimal.class) {
-                    return (T) input.getAsBigDecimal();
-                } else if (expected == BigInteger.class) {
-                    return (T) input.getAsBigInteger();
-                } else if (Number.class.isAssignableFrom(expected)) {
-                    // TODO Will result in a class-cast if it's an unexpected sub-type of Number not handled above
-                    return (T) input.getAsNumber();
-                } else if (expected == String.class) {
-                    return (T) input.getAsString();
-                } else if (expected.isArray()) {
-                    JsonArray array = input.getAsJsonArray();
-                    Class<?> componentType = expected.getComponentType();
-                    if (JsonElement.class.isAssignableFrom(componentType)) {
-                        JsonElement[] result = new JsonElement[array.size()];
-                        for (int i = 0; i < array.size(); i++) {
-                            result[i] = array.get(i);
-                        }
-                        return (T) result;
-                    } else {
-                        Object[] result = (Object[]) Array.newInstance(componentType, array.size());
-                        for (int i = 0; i < array.size(); i++) {
-                            result[i] = cast(componentType).apply(array.get(i));
-                        }
-                        return (T) result;
-                    }
-                } else {
-                    throw new IllegalArgumentException("Cannot cast json element to type "+expected);
+        return new Cast<T>(expected);
+    }
+    
+    protected static class Cast<T> implements Function<JsonElement, T> {
+        private final Class<T> expected;
+
+        public Cast(Class<T> expected) {
+            this.expected = expected;
+        }
+
+        @Override public T apply(JsonElement input) {
+            return doCast(input, expected);
+        }
+    };
+
+    @SuppressWarnings("unchecked")
+    protected static <T> T doCast(JsonElement input, Class<T> expected) {
+        if (input == null) {
+            return (T) null;
+        } else if (input.isJsonNull()) {
+            return (T) null;
+        } else if (expected == boolean.class || expected == Boolean.class) {
+            return (T) (Boolean) input.getAsBoolean();
+        } else if (expected == char.class || expected == Character.class) {
+            return (T) (Character) input.getAsCharacter();
+        } else if (expected == byte.class || expected == Byte.class) {
+            return (T) (Byte) input.getAsByte();
+        } else if (expected == short.class || expected == Short.class) {
+            return (T) (Short) input.getAsShort();
+        } else if (expected == int.class || expected == Integer.class) {
+            return (T) (Integer) input.getAsInt();
+        } else if (expected == long.class || expected == Long.class) {
+            return (T) (Long) input.getAsLong();
+        } else if (expected == float.class || expected == Float.class) {
+            return (T) (Float) input.getAsFloat();
+        } else if (expected == double.class || expected == Double.class) {
+            return (T) (Double) input.getAsDouble();
+        } else if (expected == BigDecimal.class) {
+            return (T) input.getAsBigDecimal();
+        } else if (expected == BigInteger.class) {
+            return (T) input.getAsBigInteger();
+        } else if (Number.class.isAssignableFrom(expected)) {
+            // TODO Will result in a class-cast if it's an unexpected sub-type of Number not handled above
+            return (T) input.getAsNumber();
+        } else if (expected == String.class) {
+            return (T) input.getAsString();
+        } else if (expected.isArray()) {
+            JsonArray array = input.getAsJsonArray();
+            Class<?> componentType = expected.getComponentType();
+            if (JsonElement.class.isAssignableFrom(componentType)) {
+                JsonElement[] result = new JsonElement[array.size()];
+                for (int i = 0; i < array.size(); i++) {
+                    result[i] = array.get(i);
                 }
+                return (T) result;
+            } else {
+                Object[] result = (Object[]) Array.newInstance(componentType, array.size());
+                for (int i = 0; i < array.size(); i++) {
+                    result[i] = cast(componentType).apply(array.get(i));
+                }
+                return (T) result;
             }
-        };
+        } else {
+            throw new IllegalArgumentException("Cannot cast json element to type "+expected);
+        }
     }
     
     public static <T> Function<Maybe<JsonElement>, T> castM(final Class<T> expected) {
@@ -223,13 +392,21 @@ public class JsonFunctions {
     }
     
     public static <T> Function<Maybe<JsonElement>, T> castM(final Class<T> expected, final T defaultValue) {
-        return new Function<Maybe<JsonElement>, T>() {
-            @Override
-            public T apply(Maybe<JsonElement> input) {
-                if (input.isAbsent()) return defaultValue;
-                return cast(expected).apply(input.get());
-            }
-        };
+        return new CastM<T>(expected, defaultValue);
     }
 
+    protected static class CastM<T> implements Function<Maybe<JsonElement>, T> {
+        private final Class<T> expected;
+        private final T defaultValue;
+
+        public CastM(Class<T> expected, T defaultValue) {
+            this.expected = expected;
+            this.defaultValue = defaultValue;
+        }
+        @Override
+        public T apply(Maybe<JsonElement> input) {
+            if (input.isAbsent()) return defaultValue;
+            return cast(expected).apply(input.get());
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/core/src/main/java/org/apache/brooklyn/feed/ssh/SshValueFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/feed/ssh/SshValueFunctions.java b/core/src/main/java/org/apache/brooklyn/feed/ssh/SshValueFunctions.java
index 370c3ce..20a95c2 100644
--- a/core/src/main/java/org/apache/brooklyn/feed/ssh/SshValueFunctions.java
+++ b/core/src/main/java/org/apache/brooklyn/feed/ssh/SshValueFunctions.java
@@ -20,13 +20,18 @@ package org.apache.brooklyn.feed.ssh;
 
 import javax.annotation.Nullable;
 
+import org.apache.brooklyn.util.guava.Functionals;
+
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
 import com.google.common.base.Predicates;
 
 public class SshValueFunctions {
 
-    public static Function<SshPollValue, Integer> exitStatus() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<SshPollValue, Integer> exitStatusOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<SshPollValue, Integer>() {
             @Override public Integer apply(SshPollValue input) {
                 return input.getExitStatus();
@@ -34,7 +39,10 @@ public class SshValueFunctions {
         };
     }
 
-    public static Function<SshPollValue, String> stdout() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<SshPollValue, String> stdoutOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<SshPollValue, String>() {
             @Override public String apply(SshPollValue input) {
                 return input.getStdout();
@@ -42,7 +50,10 @@ public class SshValueFunctions {
         };
     }
     
-    public static Function<SshPollValue, String> stderr() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<SshPollValue, String> stderrOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<SshPollValue, String>() {
             @Override public String apply(SshPollValue input) {
                 return input.getStderr();
@@ -50,12 +61,10 @@ public class SshValueFunctions {
         };
     }
     
-    public static Function<SshPollValue, Boolean> exitStatusEquals(final int expected) {
-        return chain(SshValueFunctions.exitStatus(), Functions.forPredicate(Predicates.equalTo(expected)));
-    }
-
-    // TODO Do we want these chain methods? Does guava have them already? Duplicated in HttpValueFunctions.
-    public static <A,B,C> Function<A,C> chain(final Function<A,? extends B> f1, final Function<B,C> f2) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <A,B,C> Function<A,C> chainOld(final Function<A,? extends B> f1, final Function<B,C> f2) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<A,C>() {
             @Override public C apply(@Nullable A input) {
                 return f2.apply(f1.apply(input));
@@ -63,11 +72,62 @@ public class SshValueFunctions {
         };
     }
     
-    public static <A,B,C,D> Function<A,D> chain(final Function<A,? extends B> f1, final Function<B,? extends C> f2, final Function<C,D> f3) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <A,B,C,D> Function<A,D> chainOld(final Function<A,? extends B> f1, final Function<B,? extends C> f2, final Function<C,D> f3) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<A,D>() {
             @Override public D apply(@Nullable A input) {
                 return f3.apply(f2.apply(f1.apply(input)));
             }
         };
     }
+
+    public static Function<SshPollValue, Integer> exitStatus() {
+        return new ExitStatus();
+    }
+
+    protected static class ExitStatus implements Function<SshPollValue, Integer> {
+        @Override public Integer apply(SshPollValue input) {
+            return input.getExitStatus();
+        }
+    }
+
+    public static Function<SshPollValue, String> stdout() {
+        return new Stdout();
+    }
+
+    protected static class Stdout implements Function<SshPollValue, String> {
+        @Override public String apply(SshPollValue input) {
+            return input.getStdout();
+        }
+    }
+
+    public static Function<SshPollValue, String> stderr() {
+        return new Stderr();
+    }
+
+    protected static class Stderr implements Function<SshPollValue, String> {
+        @Override public String apply(SshPollValue input) {
+            return input.getStderr();
+        }
+    }
+
+    public static Function<SshPollValue, Boolean> exitStatusEquals(final int expected) {
+        return chain(SshValueFunctions.exitStatus(), Functions.forPredicate(Predicates.equalTo(expected)));
+    }
+
+    /**
+     * @deprecated since 0.9.0; use {@link Functionals#chain(Function, Function)}
+     */
+    public static <A,B,C> Function<A,C> chain(final Function<A,? extends B> f1, final Function<B,C> f2) {
+        return Functionals.chain(f1, f2);
+    }
+
+    /**
+     * @deprecated since 0.9.0; use {@link Functionals#chain(Function, Function, Function)}
+     */
+    public static <A,B,C,D> Function<A,D> chain(final Function<A,? extends B> f1, final Function<B,? extends C> f2, final Function<C,D> f3) {
+        return Functionals.chain(f1, f2, f3);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/core/src/test/java/org/apache/brooklyn/feed/http/JsonFunctionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/feed/http/JsonFunctionsTest.java b/core/src/test/java/org/apache/brooklyn/feed/http/JsonFunctionsTest.java
index 928035e..ba654a4 100644
--- a/core/src/test/java/org/apache/brooklyn/feed/http/JsonFunctionsTest.java
+++ b/core/src/test/java/org/apache/brooklyn/feed/http/JsonFunctionsTest.java
@@ -20,10 +20,9 @@ package org.apache.brooklyn.feed.http;
 
 import java.util.NoSuchElementException;
 
-import org.apache.brooklyn.feed.http.JsonFunctions;
 import org.apache.brooklyn.util.collections.Jsonya;
-import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.Jsonya.Navigator;
+import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.guava.Functionals;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.testng.Assert;
@@ -78,12 +77,18 @@ public class JsonFunctionsTest {
         Assert.assertTrue(m.isAbsent());
     }
 
-    @Test(expectedExceptions=Exception.class)
-    public void testWalkMWrong2() {
-        Maybe<JsonElement> m = JsonFunctions.walkM("europe", "spain", "barcelona").apply( Maybe.of( europeMap()) );
+    @Test(expectedExceptions=IllegalStateException.class)
+    public void testCastMWhenAbsent() {
+//        Maybe<JsonElement> m = JsonFunctions.walkM("europe", "spain", "barcelona").apply( Maybe.of( europeMap()) );
+        Maybe<JsonElement> m = Maybe.absent();
         JsonFunctions.castM(String.class).apply(m);
     }
 
+    @Test(expectedExceptions=UnsupportedOperationException.class)
+    public void testCastMWrong() {
+        Maybe<JsonElement> m = JsonFunctions.walkM("europe", "france").apply( Maybe.of( europeMap()) );
+        JsonFunctions.castM(String.class).apply(m);
+    }
     
     @Test
     public void testWalkN() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/core/src/test/java/org/apache/brooklyn/feed/ssh/SshValueFunctionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/feed/ssh/SshValueFunctionsTest.java b/core/src/test/java/org/apache/brooklyn/feed/ssh/SshValueFunctionsTest.java
new file mode 100644
index 0000000..51e818f
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/feed/ssh/SshValueFunctionsTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.apache.brooklyn.feed.ssh;
+
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+public class SshValueFunctionsTest {
+
+    private SshPollValue val = new SshPollValue(null, 123, "mystdout", "mystderr");
+    
+    @Test
+    public void testExitCode() throws Exception {
+        assertEquals(SshValueFunctions.exitStatus().apply(val), (Integer)123);
+    }
+    
+    @Test
+    public void testStdout() throws Exception {
+        assertEquals(SshValueFunctions.stdout().apply(val), "mystdout");
+    }
+    
+    @Test
+    public void testStderr() throws Exception {
+        assertEquals(SshValueFunctions.stderr().apply(val), "mystderr");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/software/base/src/main/java/org/apache/brooklyn/feed/jmx/JmxValueFunctions.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/feed/jmx/JmxValueFunctions.java b/software/base/src/main/java/org/apache/brooklyn/feed/jmx/JmxValueFunctions.java
index 5741099..a0549b2 100644
--- a/software/base/src/main/java/org/apache/brooklyn/feed/jmx/JmxValueFunctions.java
+++ b/software/base/src/main/java/org/apache/brooklyn/feed/jmx/JmxValueFunctions.java
@@ -24,40 +24,81 @@ import java.util.Map;
 import javax.management.openmbean.CompositeData;
 import javax.management.openmbean.TabularData;
 
+import org.apache.brooklyn.api.location.Location;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
+import com.google.common.base.Predicate;
 import com.google.common.collect.Maps;
 
 public class JmxValueFunctions {
 
     private static final Logger log = LoggerFactory.getLogger(JmxValueFunctions.class);
-    
-    /**
-     * @return a closure that converts a TabularDataSupport to a map.
-     */
-    public static Function<TabularData, Map> tabularDataToMap() {
+
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<TabularData, Map> tabularDataToMapOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<TabularData, Map>() {
             @Override public Map apply(TabularData input) {
                 return tabularDataToMap(input);
             }};
     }
 
-    public static Function<TabularData, Map> tabularDataToMapOfMaps() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<TabularData, Map> tabularDataToMapOfMapsOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<TabularData, Map>() {
             @Override public Map apply(TabularData input) {
                 return tabularDataToMapOfMaps(input);
             }};
     }
 
-    public static Function<CompositeData,Map> compositeDataToMap() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<CompositeData,Map> compositeDataToMapOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<CompositeData, Map>() {
             @Override public Map apply(CompositeData input) {
                 return compositeDataToMap(input);
             }};
     }
     
+    /**
+     * @return a closure that converts a TabularDataSupport to a map.
+     */
+    public static Function<TabularData, Map> tabularDataToMap() {
+        return new TabularDataToMap();
+    }
+
+    protected static class TabularDataToMap implements Function<TabularData, Map> {
+        @Override public Map apply(TabularData input) {
+            return tabularDataToMap(input);
+        }
+    }
+
+    public static Function<TabularData, Map> tabularDataToMapOfMaps() {
+        return new TabularDataToMapOfMaps();
+    }
+    
+    protected static class TabularDataToMapOfMaps implements Function<TabularData, Map> {
+        @Override public Map apply(TabularData input) {
+            return tabularDataToMapOfMaps(input);
+        }
+    }
+
+    public static Function<CompositeData,Map> compositeDataToMap() {
+        return new CompositeDataToMap();
+    }
+
+    protected static class CompositeDataToMap implements Function<CompositeData, Map> {
+        @Override public Map apply(CompositeData input) {
+            return compositeDataToMap(input);
+        }
+    }
+
     public static Map tabularDataToMap(TabularData table) {
         Map<String, Object> result = Maps.newLinkedHashMap();
         for (Object entry : table.values()) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/software/base/src/test/java/org/apache/brooklyn/feed/jmx/JmxValueFunctionsTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/feed/jmx/JmxValueFunctionsTest.java b/software/base/src/test/java/org/apache/brooklyn/feed/jmx/JmxValueFunctionsTest.java
new file mode 100644
index 0000000..83f4899
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/feed/jmx/JmxValueFunctionsTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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 org.apache.brooklyn.feed.jmx;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class JmxValueFunctionsTest {
+
+    @Test
+    public void testCompositeDataToMap() throws Exception {
+        CompositeType compositeType = new CompositeType(
+                "MyCompositeType", 
+                "my composite descr", 
+                new String[] {"key1", "key2"}, 
+                new String[] {"key1 descr", "key2 descr"}, 
+                new OpenType[] {SimpleType.STRING, SimpleType.STRING});
+        Map<String, ?> items = ImmutableMap.of(
+                "key1", "val1",
+                "key2", "val2");
+        CompositeData data = new CompositeDataSupport(compositeType, items);
+        
+        Map<String,?> result = JmxValueFunctions.compositeDataToMap(data);
+        assertEquals(result, items);
+        
+        Map<?,?> result2 = JmxValueFunctions.compositeDataToMap().apply(data);
+        assertEquals(result2, items);
+    }
+    
+    @Test
+    public void testTabularDataToMapOfMaps() throws Exception {
+        CompositeType rowType = new CompositeType(
+                "MyRowType", 
+                "my row descr", 
+                new String[] {"key1", "key2"}, 
+                new String[] {"key1 descr", "key2 descr"}, 
+                new OpenType[] {SimpleType.STRING, SimpleType.STRING});
+        TabularType tabularType = new TabularType(
+                "MyTabularType", 
+                "my table descr", 
+                rowType,
+                new String[] {"key1"});
+        Map<String, ?> row1 = ImmutableMap.of(
+                "key1", "row1.val1",
+                "key2", "row1.val2");
+        Map<String, ?> row2 = ImmutableMap.of(
+                "key1", "row2.val1",
+                "key2", "row2.val2");
+        TabularDataSupport table = new TabularDataSupport(tabularType);
+        table.put(new CompositeDataSupport(rowType, row1));
+        table.put(new CompositeDataSupport(rowType, row2));
+        
+        Map<List<?>, Map<String, Object>> result = JmxValueFunctions.tabularDataToMapOfMaps(table);
+        assertEquals(result, ImmutableMap.of(ImmutableList.of("row1.val1"), row1, ImmutableList.of("row2.val1"), row2));
+        
+        Map<?,?> result2 = JmxValueFunctions.tabularDataToMapOfMaps().apply(table);
+        assertEquals(result2, ImmutableMap.of(ImmutableList.of("row1.val1"), row1, ImmutableList.of("row2.val1"), row2));
+    }
+    
+    @Test
+    public void testTabularDataToMap() throws Exception {
+        CompositeType rowType = new CompositeType(
+                "MyRowType", 
+                "my row descr", 
+                new String[] {"key1", "key2"}, 
+                new String[] {"key1 descr", "key2 descr"}, 
+                new OpenType[] {SimpleType.STRING, SimpleType.STRING});
+        TabularType tabularType = new TabularType(
+                "MyTabularType", 
+                "my table descr", 
+                rowType,
+                new String[] {"key1"});
+        Map<String, ?> row1 = ImmutableMap.of(
+                "key1", "row1.val1",
+                "key2", "row1.val2");
+        Map<String, ?> row2 = ImmutableMap.of(
+                "key1", "row2.val1",
+                "key2", "row2.val2");
+        TabularDataSupport table = new TabularDataSupport(tabularType);
+        table.put(new CompositeDataSupport(rowType, row1));
+        table.put(new CompositeDataSupport(rowType, row2));
+        
+        Map<?,?> result = JmxValueFunctions.tabularDataToMap(table);
+        assertEquals(result, row2);
+        
+        Map<?,?> result2 = JmxValueFunctions.tabularDataToMap().apply(table);
+        assertEquals(result2, row2);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/utils/common/src/main/java/org/apache/brooklyn/util/guava/MaybeFunctions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/guava/MaybeFunctions.java b/utils/common/src/main/java/org/apache/brooklyn/util/guava/MaybeFunctions.java
index 905135c..70769dd 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/guava/MaybeFunctions.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/guava/MaybeFunctions.java
@@ -22,7 +22,10 @@ import com.google.common.base.Function;
 
 public class MaybeFunctions {
 
-    public static <T> Function<T, Maybe<T>> wrap() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <T> Function<T, Maybe<T>> wrapOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<T, Maybe<T>>() {
             @Override
             public Maybe<T> apply(T input) {
@@ -31,7 +34,10 @@ public class MaybeFunctions {
         };
     }
 
-    public static <T> Function<Maybe<T>, T> get() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <T> Function<Maybe<T>, T> getOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Maybe<T>, T>() {
             @Override
             public T apply(Maybe<T> input) {
@@ -40,7 +46,10 @@ public class MaybeFunctions {
         };
     }
 
-    public static <T> Function<Maybe<T>, T> or(final T value) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <T> Function<Maybe<T>, T> orOld(final T value) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Maybe<T>, T>() {
             @Override
             public T apply(Maybe<T> input) {
@@ -49,4 +58,41 @@ public class MaybeFunctions {
         };
     }
 
+    public static <T> Function<T, Maybe<T>> wrap() {
+        return new WrapMaybe<T>();
+    }
+
+    protected static class WrapMaybe<T> implements Function<T, Maybe<T>> {
+        @Override
+        public Maybe<T> apply(T input) {
+            return Maybe.fromNullable(input);
+        }
+    };
+
+    public static <T> Function<Maybe<T>, T> get() {
+        return new GetMaybe<T>();
+    }
+    
+    protected static class GetMaybe<T> implements Function<Maybe<T>, T> {
+        @Override
+        public T apply(Maybe<T> input) {
+            return input.get();
+        }
+    }
+
+    public static <T> Function<Maybe<T>, T> or(final T value) {
+        return new OrMaybe<T>(value);
+    }
+    
+    protected static class OrMaybe<T> implements Function<Maybe<T>, T> {
+        private final T value;
+
+        public OrMaybe(T value) {
+            this.value = value;
+        }
+        @Override
+        public T apply(Maybe<T> input) {
+            return input.or(value);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/utils/common/src/main/java/org/apache/brooklyn/util/math/MathFunctions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/math/MathFunctions.java b/utils/common/src/main/java/org/apache/brooklyn/util/math/MathFunctions.java
index 92dbe36..865aca3 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/math/MathFunctions.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/math/MathFunctions.java
@@ -20,13 +20,17 @@ package org.apache.brooklyn.util.math;
 
 import javax.annotation.Nullable;
 
+import org.apache.brooklyn.util.guava.Functionals;
 import org.apache.brooklyn.util.text.Strings;
 
 import com.google.common.base.Function;
 
 public class MathFunctions {
 
-    public static Function<Number, Integer> plus(final int addend) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Number, Integer> plusOld(final int addend) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Number, Integer>() {
             public Integer apply(@Nullable Number input) {
                 if (input==null) return null;
@@ -35,7 +39,10 @@ public class MathFunctions {
         };
     }
 
-    public static Function<Number, Long> plus(final long addend) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Number, Long> plusOld(final long addend) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Number, Long>() {
             public Long apply(@Nullable Number input) {
                 if (input==null) return null;
@@ -44,7 +51,10 @@ public class MathFunctions {
         };
     }
 
-    public static Function<Number, Double> plus(final double addend) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Number, Double> plusOld(final double addend) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Number, Double>() {
             public Double apply(@Nullable Number input) {
                 if (input==null) return null;
@@ -53,7 +63,10 @@ public class MathFunctions {
         };
     }
 
-    public static Function<Number, Integer> times(final int multiplicand) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Number, Integer> timesOld(final int multiplicand) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Number, Integer>() {
             public Integer apply(@Nullable Number input) {
                 if (input==null) return null;
@@ -62,7 +75,10 @@ public class MathFunctions {
         };
     }
 
-    public static Function<Number, Long> times(final long multiplicand) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Number, Long> timesOld(final long multiplicand) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Number, Long>() {
             public Long apply(@Nullable Number input) {
                 if (input==null) return null;
@@ -71,7 +87,10 @@ public class MathFunctions {
         };
     }
 
-    public static Function<Number, Double> times(final double multiplicand) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Number, Double> timesOld(final double multiplicand) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Number, Double>() {
             public Double apply(@Nullable Number input) {
                 if (input==null) return null;
@@ -80,7 +99,10 @@ public class MathFunctions {
         };
     }
 
-    public static Function<Number, Double> divide(final double divisor) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Number, Double> divideOld(final double divisor) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Number, Double>() {
             public Double apply(@Nullable Number input) {
                 if (input==null) return null;
@@ -89,7 +111,10 @@ public class MathFunctions {
         };
     }
 
-    public static <T> Function<T, Double> divide(final Function<T, ? extends Number> input, final double divisor) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static <T> Function<T, Double> divideOld(final Function<T, ? extends Number> input, final double divisor) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<T, Double>() {
             public Double apply(@Nullable T input2) {
                 if (input==null) return null;
@@ -101,7 +126,10 @@ public class MathFunctions {
     }
 
     /** returns a string of up to maxLen length (longer in extreme cases) also capped at significantDigits significantDigits */
-    public static Function<Number, String> readableString(final int significantDigits, final int maxLen) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Number, String> readableStringOld(final int significantDigits, final int maxLen) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Number, String>() {
             public String apply(@Nullable Number input) {
                 if (input==null) return null;
@@ -111,7 +139,10 @@ public class MathFunctions {
     }
 
     /** returns a string where the input number is expressed as percent, with given number of significant digits */
-    public static Function<Number, String> percent(final int significantDigits) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Number, String> percentOld(final int significantDigits) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Number, String>() {
             public String apply(@Nullable Number input) {
                 if (input==null) return null;
@@ -120,4 +151,157 @@ public class MathFunctions {
         };
     }
 
+    public static Function<Number, Integer> plus(int addend) {
+        return new PlusInt(addend);
+    }
+
+    protected static class PlusInt implements Function<Number, Integer> {
+        private final int addend;
+
+        public PlusInt(int addend) {
+            this.addend = addend;
+        }
+        public Integer apply(@Nullable Number input) {
+            if (input==null) return null;
+            return input.intValue() + addend;
+        }
+    }
+
+    public static Function<Number, Long> plus(long addend) {
+        return new PlusLong(addend);
+    }
+
+    protected static class PlusLong implements Function<Number, Long> {
+        private final long addend;
+
+        public PlusLong(long addend) {
+            this.addend = addend;
+        }
+        public Long apply(@Nullable Number input) {
+            if (input==null) return null;
+            return input.longValue() + addend;
+        }
+    }
+
+    public static Function<Number, Double> plus(final double addend) {
+        return new PlusDouble(addend);
+    }
+
+    protected static class PlusDouble implements Function<Number, Double> {
+        private final double addend;
+
+        public PlusDouble(double addend) {
+            this.addend = addend;
+        }
+        public Double apply(@Nullable Number input) {
+            if (input==null) return null;
+            return input.doubleValue() + addend;
+        }
+    }
+
+    public static Function<Number, Integer> times(final int multiplicand) {
+        return new TimesInt(multiplicand);
+    }
+
+    protected static class TimesInt implements Function<Number, Integer> {
+        private final int multiplicand;
+
+        public TimesInt(int multiplicand) {
+            this.multiplicand = multiplicand;
+        }
+        public Integer apply(@Nullable Number input) {
+            if (input==null) return null;
+            return input.intValue() * multiplicand;
+        }
+    }
+
+    public static Function<Number, Long> times(long multiplicand) {
+        return new TimesLong(multiplicand);
+    }
+
+    protected static class TimesLong implements Function<Number, Long> {
+        private final long multiplicand;
+
+        public TimesLong(long multiplicand) {
+            this.multiplicand = multiplicand;
+        }
+        public Long apply(@Nullable Number input) {
+            if (input==null) return null;
+            return input.longValue() * multiplicand;
+        }
+    }
+
+    public static Function<Number, Double> times(final double multiplicand) {
+        return new TimesDouble(multiplicand);
+    }
+
+    protected static class TimesDouble implements Function<Number, Double> {
+        private final double multiplicand;
+
+        public TimesDouble(double multiplicand) {
+            this.multiplicand = multiplicand;
+        }
+        public Double apply(@Nullable Number input) {
+            if (input==null) return null;
+            return input.doubleValue() * multiplicand;
+        }
+    }
+
+    public static Function<Number, Double> divide(double divisor) {
+        return new DivideDouble(divisor);
+    }
+
+    protected static class DivideDouble implements Function<Number, Double> {
+        private final double divisor;
+
+        public DivideDouble(double divisor) {
+            this.divisor = divisor;
+        }
+        public Double apply(@Nullable Number input) {
+            if (input==null) return null;
+            return input.doubleValue() / divisor;
+        }
+    }
+
+    /**
+     * @deprecated since 0.9.0; use {@link Functionals#chain(Function, Function)} and {@link MathFunctions#divide(double)}
+     */
+    public static <T> Function<T, Double> divide(final Function<T, ? extends Number> preprocessor, final double divisor) {
+        return Functionals.chain(preprocessor, divide(divisor));
+    }
+
+    /** returns a string of up to maxLen length (longer in extreme cases) also capped at significantDigits significantDigits */
+    public static Function<Number, String> readableString(int significantDigits, int maxLen) {
+        return new ReadableString(significantDigits, maxLen);
+    }
+
+    protected static class ReadableString implements Function<Number, String> {
+        private final int significantDigits;
+        private final int maxLen;
+        public ReadableString(int significantDigits, int maxLen) {
+            this.significantDigits = significantDigits;
+            this.maxLen = maxLen;
+        }
+        public String apply(@Nullable Number input) {
+            if (input==null) return null;
+            return Strings.makeRealString(input.doubleValue(), maxLen, significantDigits, 0);
+        }
+    };
+
+    /** returns a string where the input number is expressed as percent, with given number of significant digits */
+    public static Function<Number, String> percent(int significantDigits) {
+        return new Percent(significantDigits);
+    }
+
+    private static class Percent implements Function<Number, String> {
+        final int significantDigits;
+        public Percent(int significantDigits) {
+            this.significantDigits = significantDigits;
+        }
+
+        public String apply(@Nullable Number input) {
+            if (input==null) return null;
+            return readableString(significantDigits, significantDigits+3).apply(input.doubleValue() * 100d)+"%";
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f22b3eb4/utils/common/src/main/java/org/apache/brooklyn/util/text/StringFunctions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/text/StringFunctions.java b/utils/common/src/main/java/org/apache/brooklyn/util/text/StringFunctions.java
index 8ac249d..dd9a980 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/text/StringFunctions.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/text/StringFunctions.java
@@ -18,7 +18,7 @@
  */
 package org.apache.brooklyn.util.text;
 
-import java.util.List;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 import javax.annotation.Nullable;
 
@@ -31,7 +31,10 @@ import com.google.common.collect.Iterables;
 
 public class StringFunctions {
 
-    public static Function<String,String> append(final String suffix) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<String,String> appendOld(final String suffix) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<String, String>() {
             @Override
             @Nullable
@@ -42,7 +45,10 @@ public class StringFunctions {
         };
     }
 
-    public static Function<String,String> prepend(final String prefix) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<String,String> prependOld(final String prefix) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<String, String>() {
             @Override
             @Nullable
@@ -53,8 +59,10 @@ public class StringFunctions {
         };
     }
 
-    /** given e.g. "hello %s" returns a function which will insert a string into that pattern */
-    public static Function<Object, String> formatter(final String pattern) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Object, String> formatterOld(final String pattern) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Object, String>() {
             public String apply(@Nullable Object input) {
                 return String.format(pattern, input);
@@ -62,8 +70,10 @@ public class StringFunctions {
         };
     }
 
-    /** given e.g. "hello %s %s" returns a function which will insert an array of two strings into that pattern */
-    public static Function<Object[], String> formatterForArray(final String pattern) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Object[], String> formatterForArrayOld(final String pattern) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Object[], String>() {
             public String apply(@Nullable Object[] input) {
                 return String.format(pattern, input);
@@ -71,18 +81,10 @@ public class StringFunctions {
         };
     }
     
-    /** given e.g. "hello %s %s" returns a function which will insert an Iterable of two strings into that pattern */
-     public static Function<Iterable<?>, String> formatterForIterable(final String pattern) {
-        return new Function<Iterable<?>, String>() {
-            public String apply(@Nullable Iterable<?> input) {
-                Object[] arr = (input == null) ? null : Iterables.toArray(input, Object.class);
-                return String.format(pattern, arr);
-            }
-        };
-    }
-
-    /** joins the given objects in a collection as a toString with the given separator */
-    public static Function<Iterable<?>, String> joiner(final String separator) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Iterable<?>, String> joinerOld(final String separator) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Iterable<?>, String>() {
             public String apply(@Nullable Iterable<?> input) {
                 return Strings.join(input, separator);
@@ -90,8 +92,10 @@ public class StringFunctions {
         };
     }
 
-    /** joins the given objects as a toString with the given separator, but expecting an array of objects, not a collection */
-    public static Function<Object[], String> joinerForArray(final String separator) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<Object[], String> joinerForArrayOld(final String separator) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<Object[], String>() {
             public String apply(@Nullable Object[] input) {
                 if (input == null) return Strings.EMPTY;
@@ -100,13 +104,10 @@ public class StringFunctions {
         };
     }
 
-    /** provided here as a convenience; prefer {@link Functions#toStringFunction()} */
-    public static Function<Object,String> toStringFunction() {
-        return Functions.toStringFunction();
-    }
-
-    /** returns function which gives length of input, with -1 for nulls */
-    public static Function<String,Integer> length() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<String,Integer> lengthOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<String,Integer>() {
             @Override
             public Integer apply(@Nullable String input) {
@@ -116,10 +117,10 @@ public class StringFunctions {
         };
     }
 
-    /** Surrounds an input string with the given prefix and suffix */
-    public static Function<String,String> surround(final String prefix, final String suffix) {
-        Preconditions.checkNotNull(prefix);
-        Preconditions.checkNotNull(suffix);
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<String,String> surroundOld(final String prefix, final String suffix) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<String,String>() {
             @Override
             public String apply(@Nullable String input) {
@@ -129,7 +130,10 @@ public class StringFunctions {
         };
     }
 
-    public static Function<String, String> trim() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<String, String> trimOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<String, String>() {
             @Override
             public String apply(@Nullable String input) {
@@ -140,7 +144,10 @@ public class StringFunctions {
         };
     }
 
-    public static Function<String, String> toLowerCase() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<String, String> toLowerCaseOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<String, String>() {
             @Override
             public String apply(String input) {
@@ -149,7 +156,10 @@ public class StringFunctions {
         };
     }
 
-    public static Function<String, String> toUpperCase() {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<String, String> toUpperCaseOld() {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<String, String>() {
             @Override
             public String apply(String input) {
@@ -158,7 +168,10 @@ public class StringFunctions {
         };
     }
 
-    public static Function<String, String> convertCase(final CaseFormat src, final CaseFormat target) {
+    /** @deprecated since 0.9.0 kept only to allow conversion of anonymous inner classes */
+    @SuppressWarnings("unused") @Deprecated 
+    private static Function<String, String> convertCaseOld(final CaseFormat src, final CaseFormat target) {
+        // TODO PERSISTENCE WORKAROUND
         return new Function<String, String>() {
             @Override
             public String apply(String input) {
@@ -166,4 +179,220 @@ public class StringFunctions {
             }
         };
     }
+
+    public static Function<String,String> append(final String suffix) {
+        return new AppendFunction(checkNotNull(suffix, "suffix"));
+    }
+
+    private static class AppendFunction implements Function<String, String> {
+        private final String suffix;
+
+        public AppendFunction(String suffix) {
+            this.suffix = suffix;
+        }
+        @Override
+        @Nullable
+        public String apply(@Nullable String input) {
+            if (input==null) return null;
+            return input + suffix;
+        }
+    };
+
+    public static Function<String,String> prepend(final String prefix) {
+        return new PrependFunction(checkNotNull(prefix, "prefix"));
+    }
+    
+    protected static class PrependFunction implements Function<String, String> {
+        private final String prefix;
+
+        public PrependFunction(String prefix) {
+            this.prefix = prefix;
+        }
+
+        @Override
+        @Nullable
+        public String apply(@Nullable String input) {
+            if (input==null) return null;
+            return prefix + input;
+        }
+    }
+
+    /** given e.g. "hello %s" returns a function which will insert a string into that pattern */
+    public static Function<Object, String> formatter(final String pattern) {
+        return new FormatterFunction(pattern);
+    }
+
+    protected static class FormatterFunction implements Function<Object, String> {
+        private final String pattern;
+        
+        FormatterFunction(String pattern) {
+            this.pattern = pattern;
+        }
+        public String apply(@Nullable Object input) {
+            return String.format(pattern, input);
+        }
+    };
+
+    /** given e.g. "hello %s %s" returns a function which will insert an array of two strings into that pattern */
+    public static Function<Object[], String> formatterForArray(final String pattern) {
+        return new FormatterForArrayFunction(checkNotNull(pattern, "pattern"));
+    }
+    
+    protected static class FormatterForArrayFunction implements Function<Object[], String> {
+        private final String pattern;
+        
+        public FormatterForArrayFunction(String pattern) {
+            this.pattern = pattern;
+        }
+        public String apply(@Nullable Object[] input) {
+            return String.format(pattern, input);
+        }
+    }
+    
+    /** 
+     * Given e.g. "hello %s %s" returns a function which will insert an Iterable of two strings into that pattern
+     * 
+     * @since 0.9.0
+     */
+    public static Function<Iterable<?>, String> formatterForIterable(final String pattern) {
+        return new FormatterForIterableFunction(pattern);
+    }
+
+    protected static class FormatterForIterableFunction implements Function<Iterable<?>, String> {
+        final String pattern;
+
+        public FormatterForIterableFunction(String pattern) {
+            this.pattern = pattern;
+        }
+
+        public String apply(@Nullable Iterable<?> input) {
+            Object[] arr = (input == null) ? null : Iterables.toArray(input, Object.class);
+            return String.format(pattern, arr);
+        }
+    }
+
+    /** joins the given objects in a collection as a toString with the given separator */
+    public static Function<Iterable<?>, String> joiner(final String separator) {
+        return new JoinerFunction(separator);
+    }
+
+    private static class JoinerFunction implements Function<Iterable<?>, String> {
+        private final String separator;
+
+        public JoinerFunction(String separator) {
+            this.separator = separator;
+        }
+        public String apply(@Nullable Iterable<?> input) {
+            return Strings.join(input, separator);
+        }
+    }
+    
+    /** joins the given objects as a toString with the given separator, but expecting an array of objects, not a collection */
+    public static Function<Object[], String> joinerForArray(final String separator) {
+        return new JoinerForArrayFunction(checkNotNull(separator, "separator"));
+    }
+
+    private static class JoinerForArrayFunction implements Function<Object[], String> {
+        private final String separator;
+
+        protected JoinerForArrayFunction(String separator) {
+            this.separator = separator;
+        }
+        public String apply(@Nullable Object[] input) {
+            if (input == null) return Strings.EMPTY;
+            return Strings.join(input, separator);
+        }
+    }
+
+    /** provided here as a convenience; prefer {@link Functions#toStringFunction()} */
+    public static Function<Object,String> toStringFunction() {
+        return Functions.toStringFunction();
+    }
+
+    /** returns function which gives length of input, with -1 for nulls */
+    public static Function<String,Integer> length() {
+        return new LengthFunction();
+    }
+
+    protected static class LengthFunction implements Function<String,Integer> {
+        @Override
+        public Integer apply(@Nullable String input) {
+            if (input == null) return -1;
+            return input.length();
+        }
+    }
+
+    /** Surrounds an input string with the given prefix and suffix */
+    public static Function<String,String> surround(final String prefix, final String suffix) {
+        Preconditions.checkNotNull(prefix);
+        Preconditions.checkNotNull(suffix);
+        return new SurroundFunction(prefix, suffix);
+    }
+    
+    protected static class SurroundFunction implements Function<String,String> {
+        private final String prefix;
+        private final String suffix;
+        public SurroundFunction(String prefix, String suffix) {
+            this.prefix = prefix;
+            this.suffix = suffix;
+        }
+        @Override
+        public String apply(@Nullable String input) {
+            if (input == null) return null;
+            return prefix+input+suffix;
+        }
+    }
+
+    public static Function<String, String> trim() {
+        return new TrimFunction();
+    }
+    
+    protected static class TrimFunction implements Function<String, String> {
+        @Override
+        public String apply(@Nullable String input) {
+            if (input == null) return null;
+            if (Strings.isBlank(input)) return Strings.EMPTY;
+            return CharMatcher.BREAKING_WHITESPACE.trimFrom(input);
+        }
+    }
+
+    public static Function<String, String> toLowerCase() {
+        return new LowerCaseFunction();
+    }
+    
+    protected static class LowerCaseFunction implements Function<String, String> {
+        @Override
+        public String apply(String input) {
+            return input.toLowerCase();
+        }
+    }
+
+    public static Function<String, String> toUpperCase() {
+        return new UpperCaseFunction();
+    }
+    
+    protected static class UpperCaseFunction implements Function<String, String> {
+        @Override
+        public String apply(String input) {
+            return input.toUpperCase();
+        }
+    }
+
+    public static Function<String, String> convertCase(final CaseFormat src, final CaseFormat target) {
+        return new ConvertCaseFunction(checkNotNull(src, "src"), checkNotNull(target, "target"));
+    }
+    
+    protected static class ConvertCaseFunction implements Function<String, String> {
+        private final CaseFormat src;
+        private final CaseFormat target;
+
+        public ConvertCaseFunction(CaseFormat src, CaseFormat target) {
+            this.src = src;
+            this.target = target;
+        }
+        @Override
+        public String apply(String input) {
+            return src.to(target, input);
+        }
+    }
 }