http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
new file mode 100644
index 0000000..decc1f0
--- /dev/null
+++ 
b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
@@ -0,0 +1,788 @@
+/*
+ * 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.util.javalang;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+/**
+ * Reflection utilities ("borrowed" from cloudsoft monterey).
+ * 
+ * @author aled
+ */
+public class Reflections {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(Reflections.class);
+
+    public static class ReflectionNotFoundException extends RuntimeException {
+        private static final long serialVersionUID = 9032835250796708037L;
+        public ReflectionNotFoundException(String message, Throwable cause) {
+            super(message, cause);
+        }
+        public ReflectionNotFoundException(String message) {
+            super(message);
+        }
+    }
+
+    public static class ReflectionAccessException extends RuntimeException {
+        private static final long serialVersionUID = 6569605861192432009L;
+
+        public ReflectionAccessException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    private final ClassLoader classLoader;
+    
+    public Reflections(ClassLoader classLoader) {
+        this.classLoader = checkNotNull(classLoader);
+    }
+
+    public Object loadInstance(String classname, Object...argValues) throws 
ReflectionNotFoundException, ReflectionAccessException {
+        Class<?> clazz = loadClass(classname);
+        Optional<?> v = null;
+        try {
+            v = invokeConstructorWithArgs(clazz, argValues);
+            if (v.isPresent()) return v.get();
+        } catch (Exception e) {
+            throw new IllegalStateException("Error invoking constructor for 
"+clazz+Arrays.toString(argValues) + ": " + Exceptions.collapseText(e));
+        }
+        throw new IllegalStateException("No suitable constructor for 
"+clazz+Arrays.toString(argValues));
+    }
+    public Object loadInstance(String classname, Class<?>[] argTypes, Object[] 
argValues) throws ReflectionNotFoundException, ReflectionAccessException {
+        Class<?> clazz = loadClass(classname);
+        Constructor<?> constructor = loadConstructor(clazz, argTypes);
+        return loadInstance(constructor, argValues);
+    }
+
+    public Object loadInstance(String classname) throws 
ReflectionNotFoundException, ReflectionAccessException {
+        Class<?> clazz = loadClass(classname);
+        try {
+            return clazz.newInstance();
+        } catch (InstantiationException e) {
+            throw new ReflectionAccessException("Failed to create instance of 
class '" + classname + "' using class loader " + classLoader + ": " + 
Exceptions.collapseText(e), e);
+        } catch (IllegalAccessException e) {
+            throw new ReflectionAccessException("Failed to create instance of 
class '" + classname + "' using class loader " + classLoader + ": " + 
Exceptions.collapseText(e), e);
+        }
+    }
+
+    /** instantiates the given class from its binary name */
+    public Class<?> loadClass(String classname) throws 
ReflectionNotFoundException {
+        try {
+            return classLoader.loadClass(classname);
+        } catch (ClassNotFoundException e) {
+            throw new ReflectionNotFoundException("Failed to load class '" + 
classname + "' using class loader " + classLoader + ": " + 
Exceptions.collapseText(e), e);
+        } catch (NoClassDefFoundError e) {
+            throw new ReflectionNotFoundException("Failed to load class '" + 
classname + "' using class loader " + classLoader + ": " + 
Exceptions.collapseText(e), e);
+        } catch (UnsupportedClassVersionError e) {
+            throw new ReflectionNotFoundException("Failed to load class '" + 
classname + "' using class loader " + classLoader + ": " + 
Exceptions.collapseText(e), e);
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    public <T> Class<? extends T> loadClass(String classname, Class<T> 
superType) throws ReflectionNotFoundException {
+        return (Class<? extends T>) loadClass(classname);
+    }
+
+    /** given a nested part, e.g. Inner$VeryInner, this will recurse through 
clazz.Inner, looking for VeryInner,
+     * then looking in each supertype (interface) of clazz for Inner.VeryInner;
+     * <p>
+     * so it will find Clazz.Inner.VeryInner wherever in the hierarchy it is 
defined
+     * <p>
+     * (as opposed to ClassLoader which requires Inner.VeryInner to be 
_declared_ in clazz, not in any supertype
+     * <p>
+     * returns null if not found
+     */
+    public static Class<?> loadInnerClassPossiblyInheritted(Class<?> clazz, 
String nestedPart) throws ReflectionNotFoundException {
+        Set<String> visited = new HashSet<String>();
+        Class<?> result = loadInnerClassPossiblyInheritted(visited, clazz, 
nestedPart);
+        if (result!=null) return result;
+        throw new ReflectionNotFoundException("Inner class " + nestedPart + " 
could not be found in " + clazz + " or any of its super-types");
+    }
+    
+    /** as 2-arg, but maintains set of  visited elements, and returns null if 
not found */
+    private static Class<?> loadInnerClassPossiblyInheritted(Set<String> 
visited, Class<?> clazz, String nestedPart) throws ReflectionNotFoundException {
+        if (clazz==null) return null;
+        if (nestedPart==null || nestedPart.length()==0) return clazz;
+
+        int i1 = nestedPart.indexOf('$');
+        int i2 = nestedPart.indexOf('.');
+        int idx = (i2 > -1 && (i2 < i1 || i1==-1) ? i2 : i1);
+        String thisClassToFind = nestedPart;
+        String nextClassesToFind = "";
+        if (idx>=0) {
+            thisClassToFind = nestedPart.substring(0, idx);
+            nextClassesToFind = nestedPart.substring(idx+1);
+        }
+
+        if (!visited.add(clazz.getCanonicalName()+"!"+nestedPart)) {
+            //already visited
+            return null;
+        }
+
+        Class<?>[] members = clazz.getClasses();
+        for (int i = 0; i < members.length; i++) {
+            if (members[i].getSimpleName().equals(thisClassToFind)) {
+                Class<?> clazzI = loadInnerClassPossiblyInheritted(visited, 
members[i], nextClassesToFind);
+                if (clazzI!=null) return clazzI;
+            }
+        }
+
+        //look in supertype first (not sure if necessary)
+        Class<?> result = loadInnerClassPossiblyInheritted(visited, 
clazz.getSuperclass(), nestedPart);
+        if (result!=null) return result;
+
+        for (Class<?> iface : clazz.getInterfaces()) {
+            result = loadInnerClassPossiblyInheritted(visited, iface, 
nestedPart);
+            if (result!=null) return result;
+        }
+        return null;
+    }
+
+    /** does not look through ancestors of outer class */
+    public Class<?> loadInnerClassNotInheritted(String outerClassname, String 
innerClassname) throws ReflectionNotFoundException {
+        return loadClass(outerClassname + "$" + innerClassname);
+    }
+
+    /** does not look through ancestors of outer class
+     * <p>
+     * uses the classloader set in this class, not in the clazz supplied */
+    public Class<?> loadInnerClassNotInheritted(Class<?> outerClazz, String 
innerClassname) throws ReflectionNotFoundException {
+        return loadClass(outerClazz.getName() + "$" + innerClassname);
+    }
+
+    public Constructor<?> loadConstructor(Class<?> clazz, Class<?>[] argTypes) 
throws ReflectionAccessException {
+        try {
+            return clazz.getConstructor(argTypes);
+        } catch (SecurityException e) {
+            throw new ReflectionAccessException("Failed to load constructor of 
class '" + clazz + " with argument types " + Arrays.asList(argTypes) + ": " + 
Exceptions.collapseText(e), e);
+        } catch (NoSuchMethodException e) {
+            throw new ReflectionAccessException("Failed to load constructor of 
class '" + clazz + " with argument types " + Arrays.asList(argTypes) + ": " + 
Exceptions.collapseText(e), e);
+        }
+    }
+
+    /** Invokes a suitable constructor, supporting varargs and primitives */
+    public static <T> Optional<T> invokeConstructorWithArgs(ClassLoader 
classLoader, String className, Object...argsArray) {
+        Reflections reflections = new Reflections(classLoader);
+        @SuppressWarnings("unchecked")
+        Class<T> clazz = (Class<T>) reflections.loadClass(className);
+        return invokeConstructorWithArgs(reflections, clazz, argsArray, false);
+    }
+
+    /** Invokes a suitable constructor, supporting varargs and primitives */
+    public static <T> Optional<T> invokeConstructorWithArgs(ClassLoader 
classLoader, Class<T> clazz, Object[] argsArray, boolean setAccessible) {
+        Reflections reflections = new Reflections(classLoader);
+        return invokeConstructorWithArgs(reflections, clazz, argsArray, 
setAccessible);
+    }
+
+    /** Invokes a suitable constructor, supporting varargs and primitives */
+    public static <T> Optional<T> invokeConstructorWithArgs(Class<T> clazz, 
Object...argsArray) {
+        return invokeConstructorWithArgs(clazz, argsArray, false);
+    }
+
+    /** Invokes a suitable constructor, supporting varargs and primitives */
+    public static <T> Optional<T> invokeConstructorWithArgs(Class<T> clazz, 
Object[] argsArray, boolean setAccessible) {
+        Reflections reflections = new Reflections(clazz.getClassLoader());
+        return invokeConstructorWithArgs(reflections, clazz, argsArray, 
setAccessible);
+    }
+
+    /** Invokes a suitable constructor, supporting varargs and primitives, 
additionally supporting setAccessible */
+    @SuppressWarnings("unchecked")
+    public static <T> Optional<T> invokeConstructorWithArgs(Reflections 
reflections, Class<T> clazz, Object[] argsArray, boolean setAccessible) {
+        for (Constructor<?> constructor : clazz.getConstructors()) {
+            Class<?>[] parameterTypes = constructor.getParameterTypes();
+            if (constructor.isVarArgs()) {
+                if (typesMatchUpTo(argsArray, parameterTypes, 
parameterTypes.length-1)) {
+                    Class<?> varargType = 
parameterTypes[parameterTypes.length-1].getComponentType();
+                    boolean varargsMatch = true;
+                    for (int i=parameterTypes.length-1; i<argsArray.length; 
i++) {
+                        if 
(!Boxing.boxedType(varargType).isInstance(argsArray[i]) ||
+                                                (varargType.isPrimitive() && 
argsArray[i]==null)) {
+                            varargsMatch = false;
+                            break;
+                        }
+                    }
+                    if (varargsMatch) {
+                        Object varargs = Array.newInstance(varargType, 
argsArray.length+1 - parameterTypes.length);
+                        for (int i=parameterTypes.length-1; 
i<argsArray.length; i++) {
+                            Boxing.setInArray(varargs, 
i+1-parameterTypes.length, argsArray[i], varargType);
+                        }
+                        Object[] newArgsArray = new 
Object[parameterTypes.length];
+                        System.arraycopy(argsArray, 0, newArgsArray, 0, 
parameterTypes.length-1);
+                        newArgsArray[parameterTypes.length-1] = varargs;
+                        if (setAccessible) constructor.setAccessible(true);
+                        return (Optional<T>) 
Optional.of(reflections.loadInstance(constructor, newArgsArray));
+                    }
+                }
+            }
+            if (typesMatch(argsArray, parameterTypes)) {
+                if (setAccessible) constructor.setAccessible(true);
+                return (Optional<T>) 
Optional.of(reflections.loadInstance(constructor, argsArray));
+            }
+        }
+        return Optional.absent();
+    }
+    
+    
+    /** returns a single constructor in a given class, or throws an exception 
*/
+    public Constructor<?> loadSingleConstructor(Class<?> clazz) {
+        Constructor<?>[] constructors = clazz.getConstructors();
+        if (constructors.length == 1) {
+            return constructors[0];
+        }
+        throw new IllegalArgumentException("Class " + clazz + " has more than 
one constructor");
+    }
+
+    public <T> T loadInstance(Constructor<T> constructor, Object...argValues) 
throws IllegalArgumentException, ReflectionAccessException {
+        try {
+            try {
+                return constructor.newInstance(argValues);
+            } catch (IllegalArgumentException e) {
+                try {
+                    LOG.warn("Failure passing provided arguments 
("+getIllegalArgumentsErrorMessage(constructor, argValues)+"; "+e+"); 
attempting to reconstitute");
+                    argValues = (Object[]) updateFromNewClassLoader(argValues);
+                    return constructor.newInstance(argValues);
+                } catch (Throwable e2) {
+                    LOG.warn("Reconstitution attempt failed (will rethrow 
original excaption): "+e2, e2);
+                    throw e;
+                }
+            }
+        } catch (IllegalArgumentException e) {
+            throw new 
IllegalArgumentException(getIllegalArgumentsErrorMessage(constructor, 
argValues)+": " + Exceptions.collapseText(e), e);
+        } catch (InstantiationException e) {
+            throw new ReflectionAccessException("Failed to create instance of 
" + constructor.getDeclaringClass() + ": " + Exceptions.collapseText(e), e);
+        } catch (IllegalAccessException e) {
+            throw new ReflectionAccessException("Failed to create instance of 
" + constructor.getDeclaringClass() + ": " + Exceptions.collapseText(e), e);
+        } catch (InvocationTargetException e) {
+            throw new ReflectionAccessException("Failed to create instance of 
" + constructor.getDeclaringClass() + ": " + Exceptions.collapseText(e), e);
+        }
+    }
+
+    public Method loadMethod(Class<?> clazz, String methodName, Class<?>[] 
argTypes) throws ReflectionNotFoundException, ReflectionAccessException {
+        try {
+            return clazz.getMethod(methodName, argTypes);
+        } catch (NoClassDefFoundError e) {
+            throw new ReflectionNotFoundException("Failed to invoke method " + 
methodName + " on class " + clazz + " with argument types " + 
Arrays.asList(argTypes) + ", using class loader " + clazz.getClassLoader() + ": 
" + Exceptions.collapseText(e), e);
+        } catch (NoSuchMethodException e) {
+            throw new ReflectionNotFoundException("Failed to invoke method " + 
methodName + " on class " + clazz + " with argument types " + 
Arrays.asList(argTypes) + ": " + Exceptions.collapseText(e), e);
+        } catch (SecurityException e) {
+            throw new ReflectionAccessException("Failed to invoke method " + 
methodName + " on class " + clazz + " with argument types " + 
Arrays.asList(argTypes) + ": " + Exceptions.collapseText(e), e);
+        }
+    }
+
+    /** returns the first method matching the given name */
+    public Method loadMethod(Class<?> clazz, String methodName) throws 
ReflectionNotFoundException, ReflectionAccessException {
+        try {
+            Method[] allmethods = clazz.getMethods();
+            for (int i = 0; i < allmethods.length; i++) {
+                if (allmethods[i].getName().equals(methodName)) {
+                    return allmethods[i];
+                }
+            }
+            throw new ReflectionNotFoundException("Cannot find method " + 
methodName + " on class " + clazz);
+
+        } catch (SecurityException e) {
+            throw new ReflectionAccessException("Failed to invoke method '" + 
methodName + " on class " + clazz + ": " + Exceptions.collapseText(e), e);
+        }
+    }
+
+    /**
+     * 
+     * @throws ReflectionAccessException If invocation failed due to illegal 
access or the invoked method failed
+     * @throws IllegalArgumentException  If the arguments were invalid
+     */
+    public Object invokeMethod(Method method, Object obj, Object... argValues) 
throws ReflectionAccessException {
+        try {
+            return method.invoke(obj, argValues);
+        } catch (IllegalArgumentException e) {
+            throw new 
IllegalArgumentException(getIllegalArgumentsErrorMessage(method, argValues), e);
+        } catch (IllegalAccessException e) {
+            throw new ReflectionAccessException("Failed to invoke method '" + 
method.toGenericString() + " on class " + method.getDeclaringClass() + " with 
argument values " + Arrays.asList(argValues) + ": " + 
Exceptions.collapseText(e), e);
+        } catch (InvocationTargetException e) {
+            throw new ReflectionAccessException("Failed to invoke method '" + 
method.toGenericString() + " on class " + method.getDeclaringClass() + " with 
argument values " + Arrays.asList(argValues) + ": " + 
Exceptions.collapseText(e), e);
+        }
+    }
+
+    public Object invokeStaticMethod(Method method, Object... argValues) 
throws IllegalArgumentException, ReflectionAccessException {
+        try {
+            return method.invoke(null, argValues);
+        } catch (IllegalArgumentException e) {
+            throw new 
IllegalArgumentException(getIllegalArgumentsErrorMessage(method, argValues), e);
+        } catch (IllegalAccessException e) {
+            throw new ReflectionAccessException("Failed to invoke method '" + 
method.toGenericString() + " on class " + method.getDeclaringClass() + " with 
argument values " + Arrays.asList(argValues) + ": " + 
Exceptions.collapseText(e), e);
+        } catch (InvocationTargetException e) {
+            throw new ReflectionAccessException("Failed to invoke method '" + 
method.toGenericString() + " on class " + method.getDeclaringClass() + " with 
argument values " + Arrays.asList(argValues) + ": " + 
Exceptions.collapseText(e), e);
+        }
+    }
+
+    public Object loadStaticField(Class<?> clazz, String fieldname) throws 
ReflectionAccessException {
+        return loadStaticFields(clazz, new String[] {fieldname}, null)[0];
+    }
+
+    public Object[] loadStaticFields(Class<?> clazz, String[] fieldnamesArray, 
Object[] defaults) throws ReflectionAccessException {
+        Object[] result = new Object[fieldnamesArray.length];
+        if (defaults!=null) {
+            for (int i = 0; i < defaults.length; i++) {
+                result[i] = defaults[i];
+            }
+        }
+
+        List<String> fieldnames = Arrays.asList(fieldnamesArray);
+        Field[] classFields = clazz.getDeclaredFields();
+
+        for (int i = 0; i < classFields.length; i++) {
+            Field field = classFields[i];
+            int index = fieldnames.indexOf(field.getName());
+            if (index >= 0) {
+                try {
+                    result[index] = field.get(null);
+                } catch (IllegalArgumentException e) {
+                    throw new ReflectionAccessException("Failed to load field 
'" + field.getName() + " from class " + clazz + ": " + 
Exceptions.collapseText(e), e);
+                } catch (IllegalAccessException e) {
+                    throw new ReflectionAccessException("Failed to load field 
'" + field.getName() + " from class " + clazz + ": " + 
Exceptions.collapseText(e), e);
+                }
+            }
+        }
+        return result;
+    }
+
+    private static String getIllegalArgumentsErrorMessage(Method method, 
Object[] argValues) {
+        return method.toGenericString() + " not applicable for the parameters 
of type " + argumentTypesToString(argValues);
+    }
+
+    private static String getIllegalArgumentsErrorMessage(Constructor<?> 
constructor, Object[] argValues) {
+        return constructor.toGenericString() + " not applicable for the 
parameters of type " + argumentTypesToString(argValues);
+    }
+
+    private static String argumentTypesToString(Object[] argValues) {
+        StringBuffer msg = new StringBuffer("(");
+        for (int i = 0; i < argValues.length; i++) {
+            if (i != 0) msg.append(", ");
+            msg.append(argValues[i] != null ? 
argValues[i].getClass().getName() : "null");
+        }
+        msg.append(")");
+        return msg.toString();
+    }
+
+    /** copies all fields from the source to target; very little compile-time 
safety checking, so use with care
+     * @throws IllegalAccessException
+     * @throws IllegalArgumentException */
+    public static <T> void copyFields(T source, T target) throws 
IllegalArgumentException, IllegalAccessException {
+        Class<? extends Object> clazz = source.getClass();
+        while (clazz!=null) {
+            Field[] fields = clazz.getDeclaredFields();
+            for (Field f : fields) {
+                f.setAccessible(true);
+                Object vs = f.get(source);
+                Object vt = f.get(target);
+                if ((vs==null && vt!=null) || (vs!=null && !vs.equals(vt))) {
+                    f.set(target, vs);
+                }
+            }
+            clazz = clazz.getSuperclass();
+        }
+    }
+
+    /**
+     * Loads class given its canonical name format (e.g. com.acme.Foo.Inner),
+     * using iterative strategy (trying com.acme.Foo$Inner, then 
com.acme$Foo$Inner, etc).
+     * @throws ReflectionNotFoundException 
+     */
+    public Class<?> loadClassFromCanonicalName(String canonicalName) throws 
ClassNotFoundException, ReflectionNotFoundException {
+        ClassNotFoundException err = null;
+        String name = canonicalName;
+        do {
+            try {
+                return classLoader.loadClass(name);
+            } catch (ClassNotFoundException e) {
+                if (err == null) err = e;
+                int lastIndexOf = name.lastIndexOf(".");
+                if (lastIndexOf >= 0) {
+                    name = name.substring(0, lastIndexOf) + "$" + 
name.substring(lastIndexOf+1);
+                }
+            }
+        } while (name.contains("."));
+        throw err;
+    }
+
+    /** finds the resource in the classloader, if it exists; inserts or 
replaces leading slash as necessary
+     * (i believe it should _not_ have one, but there is some inconsistency)
+     * 
+     * Will return null if no resource is found.
+     */
+    @Nullable
+    public URL getResource(String r) {
+        URL u = null;
+        u = classLoader.getResource(r);
+        if (u!=null) return u;
+        
+        if (r.startsWith("/")) r = r.substring(1);
+        else r = "/"+r;
+        return classLoader.getResource(r);
+    }
+
+    /**
+     * Serialize the given object, then reload using the current class loader;
+     * this removes linkages to instances with classes loaded by an older 
class loader.
+     * <p>
+     * (like a poor man's clone)
+     * <p>
+     * aka "reconstitute(Object)"
+     */
+    public final Object updateFromNewClassLoader(Object data) throws 
IOException, ClassNotFoundException {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        new ObjectOutputStream(bytes).writeObject(data);
+        Object reconstituted = new ObjectInputStream(new 
ByteArrayInputStream(bytes.toByteArray())).readObject();
+        if (LOG.isDebugEnabled()) LOG.debug("Reconstituted data: " + 
reconstituted + ", class loader: " + classLoader);
+        return reconstituted;
+    }
+
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public static <T> Class<? super T> findSuperType(T impl, String typeName) {
+        Set<Class<?>> toinspect = new LinkedHashSet<Class<?>>();
+        Set<Class<?>> inspected = new HashSet<Class<?>>();
+        toinspect.add(impl.getClass());
+        
+        while (toinspect.size() > 0) {
+            Class<?> clazz = toinspect.iterator().next(); // get and remove 
the first element
+            if (clazz.getName().equals(typeName)) {
+                return (Class<? super T>) clazz;
+            }
+            inspected.add(clazz);
+            List<Class<?>> toAdd = Arrays.asList(clazz.getInterfaces());
+            toinspect.addAll( toAdd );
+            if (clazz.getSuperclass() != null) 
toinspect.add(clazz.getSuperclass());
+            toinspect.removeAll(inspected);
+        }
+        
+        return null;
+    }
+    
+    /** whereas Class.getInterfaces() only returns interfaces directly 
implemented by a class,
+     * this walks the inheritance hierarchy to include interfaces implemented 
by superclass/ancestors;
+     * (note it does not include superinterfaces)
+     */
+    public static Set<Class<?>> getInterfacesIncludingClassAncestors(Class<?> 
clazz) {
+        Set<Class<?>> result = new LinkedHashSet<Class<?>>();
+        while (clazz!=null) {
+            for (Class<?> iface: clazz.getInterfaces())
+                result.add(iface);
+            clazz = clazz.getSuperclass();
+        }
+        return result;
+    }
+
+    public static Method findMethod(Class<?> clazz, String name, Class<?>... 
parameterTypes) throws NoSuchMethodException {
+        if (clazz == null || name == null) {
+            throw new NullPointerException("Must not be null: clazz="+clazz+"; 
name="+name);
+        }
+        Class<?> clazzToInspect = clazz;
+        NoSuchMethodException toThrowIfFails = null;
+        
+        while (clazzToInspect != null) {
+            try {
+                return clazzToInspect.getDeclaredMethod(name, parameterTypes);
+            } catch (NoSuchMethodException e) {
+                if (toThrowIfFails == null) toThrowIfFails = e;
+                clazzToInspect = clazzToInspect.getSuperclass();
+            }
+        }
+        throw toThrowIfFails;
+    }
+    
+    public static Field findField(Class<?> clazz, String name) throws 
NoSuchFieldException {
+        if (clazz == null || name == null) {
+            throw new NullPointerException("Must not be null: clazz="+clazz+"; 
name="+name);
+        }
+        Class<?> clazzToInspect = clazz;
+        NoSuchFieldException toThrowIfFails = null;
+        
+        while (clazzToInspect != null) {
+            try {
+                return clazzToInspect.getDeclaredField(name);
+            } catch (NoSuchFieldException e) {
+                if (toThrowIfFails == null) toThrowIfFails = e;
+                clazzToInspect = clazzToInspect.getSuperclass();
+            }
+        }
+        throw toThrowIfFails;
+    }
+    
+    public static List<Field> findPublicFieldsOrderedBySuper(Class<?> clazz) {
+        checkNotNull(clazz, "clazz");
+        MutableList.Builder<Field> result = MutableList.<Field>builder();
+        Stack<Class<?>> tovisit = new Stack<Class<?>>();
+        Set<Class<?>> visited = Sets.newLinkedHashSet();
+        tovisit.push(clazz);
+        
+        while (!tovisit.isEmpty()) {
+            Class<?> nextclazz = tovisit.pop();
+            if (!visited.add(nextclazz)) {
+                continue; // already visited
+            }
+            if (nextclazz.getSuperclass() != null) 
tovisit.add(nextclazz.getSuperclass());
+            tovisit.addAll(Arrays.asList(nextclazz.getInterfaces()));
+            
+            
result.addAll(Iterables.filter(Arrays.asList(nextclazz.getDeclaredFields()), 
new Predicate<Field>() {
+                @Override public boolean apply(Field input) {
+                    return Modifier.isPublic(input.getModifiers());
+                }}));
+            
+        }
+        
+        List<Field> resultList = result.build();
+        Collections.sort(resultList, new Comparator<Field>() {
+            @Override public int compare(Field f1, Field f2) {
+                Field fsubbest = inferSubbestField(f1, f2);
+                return (fsubbest == null) ? 0 : (fsubbest == f1 ? 1 : -1);
+            }});
+        
+        return resultList;
+    }
+    
+    // TODO I've seen strange behaviour where class.getMethods() does not 
include methods from interfaces.
+    // Also the ordering guarantees here are useful...
+    public static List<Method> findPublicMethodsOrderedBySuper(Class<?> clazz) 
{
+        checkNotNull(clazz, "clazz");
+        MutableList.Builder<Method> result = MutableList.<Method>builder();
+        Stack<Class<?>> tovisit = new Stack<Class<?>>();
+        Set<Class<?>> visited = Sets.newLinkedHashSet();
+        tovisit.push(clazz);
+        
+        while (!tovisit.isEmpty()) {
+            Class<?> nextclazz = tovisit.pop();
+            if (!visited.add(nextclazz)) {
+                continue; // already visited
+            }
+            if (nextclazz.getSuperclass() != null) 
tovisit.add(nextclazz.getSuperclass());
+            tovisit.addAll(Arrays.asList(nextclazz.getInterfaces()));
+            
+            
result.addAll(Iterables.filter(Arrays.asList(nextclazz.getDeclaredMethods()), 
new Predicate<Method>() {
+                @Override public boolean apply(Method input) {
+                    return Modifier.isPublic(input.getModifiers());
+                }}));
+            
+        }
+        
+        List<Method> resultList = result.build();
+        Collections.sort(resultList, new Comparator<Method>() {
+            @Override public int compare(Method m1, Method m2) {
+                Method msubbest = inferSubbestMethod(m1, m2);
+                return (msubbest == null) ? 0 : (msubbest == m1 ? 1 : -1);
+            }});
+        
+        return resultList;
+    }
+    
+    /**
+     * Gets the field that is in the sub-class; or null if one field does not 
come from a sub-class of the other field's class
+     */
+    public static Field inferSubbestField(Field f1, Field f2) {
+        Class<?> c1 = f1.getDeclaringClass();
+        Class<?> c2 = f2.getDeclaringClass();
+        boolean isSuper1 = c1.isAssignableFrom(c2);
+        boolean isSuper2 = c2.isAssignableFrom(c1);
+        return (isSuper1) ? (isSuper2 ? null : f2) : (isSuper2 ? f1 : null);
+    }
+    
+    /**
+     * Gets the method that is in the sub-class; or null if one method does 
not come from a sub-class of the other method's class
+     */
+    public static Method inferSubbestMethod(Method m1, Method m2) {
+        Class<?> c1 = m1.getDeclaringClass();
+        Class<?> c2 = m2.getDeclaringClass();
+        boolean isSuper1 = c1.isAssignableFrom(c2);
+        boolean isSuper2 = c2.isAssignableFrom(c1);
+        return (isSuper1) ? (isSuper2 ? null : m2) : (isSuper2 ? m1 : null);
+    }
+    
+    /**
+     * Gets the class that is in the sub-class; or null if neither is a 
sub-class of the other.
+     */
+    public static Class<?> inferSubbest(Class<?> c1, Class<?> c2) {
+        boolean isSuper1 = c1.isAssignableFrom(c2);
+        boolean isSuper2 = c2.isAssignableFrom(c1);
+        return (isSuper1) ? (isSuper2 ? null : c2) : (isSuper2 ? c1 : null);
+    }
+    
+    /** convenience for casting the given candidate to the given type (without 
any coercion, and allowing candidate to be null) */
+    @SuppressWarnings("unchecked")
+    public static <T> T cast(Object candidate, Class<? extends T> type) {
+        if (candidate==null) return null;
+        if (!type.isAssignableFrom(candidate.getClass()))
+            throw new IllegalArgumentException("Requires a "+type+", but had a 
"+candidate.getClass()+" ("+candidate+")");
+        return (T)candidate;
+    }
+
+    /** invokes the given method on the given clazz or instance, doing 
reasonably good matching on args etc 
+     * @throws InvocationTargetException 
+     * @throws IllegalAccessException 
+     * @throws IllegalArgumentException */
+    public static Optional<Object> invokeMethodWithArgs(Object 
clazzOrInstance, String method, List<Object> args) throws 
IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+        return invokeMethodWithArgs(clazzOrInstance, method, args, false);
+    }
+    public static Optional<Object> invokeMethodWithArgs(Object 
clazzOrInstance, String method, List<Object> args, boolean setAccessible) 
throws IllegalArgumentException, IllegalAccessException, 
InvocationTargetException {
+        Preconditions.checkNotNull(clazzOrInstance, "clazz or instance");
+        Preconditions.checkNotNull(method, "method");
+        Preconditions.checkNotNull(args, "args to "+method);
+        
+        Class<?> clazz;
+        Object instance;
+        if (clazzOrInstance instanceof Class) {
+            clazz = (Class<?>)clazzOrInstance;
+            instance = null;
+        } else {
+            clazz = clazzOrInstance.getClass();
+            instance = clazzOrInstance;
+        }
+        
+        Object[] argsArray = args.toArray();
+
+        for (Method m: clazz.getMethods()) {
+            if (method.equals(m.getName())) {
+                Class<?>[] parameterTypes = m.getParameterTypes();
+                if (m.isVarArgs()) {
+                    if (typesMatchUpTo(argsArray, parameterTypes, 
parameterTypes.length-1)) {
+                        Class<?> varargType = 
parameterTypes[parameterTypes.length-1].getComponentType();
+                        boolean varargsMatch = true;
+                        for (int i=parameterTypes.length-1; 
i<argsArray.length; i++) {
+                            if 
(!Boxing.boxedType(varargType).isInstance(argsArray[i]) ||
+                                    (varargType.isPrimitive() && 
argsArray[i]==null)) {
+                                varargsMatch = false;
+                                break;
+                            }
+                        }
+                        if (varargsMatch) {
+                            Object varargs = Array.newInstance(varargType, 
argsArray.length+1 - parameterTypes.length);
+                            for (int i=parameterTypes.length-1; 
i<argsArray.length; i++) {
+                                Boxing.setInArray(varargs, 
i+1-parameterTypes.length, argsArray[i], varargType);
+                            }
+                            Object[] newArgsArray = new 
Object[parameterTypes.length];
+                            System.arraycopy(argsArray, 0, newArgsArray, 0, 
parameterTypes.length-1);
+                            newArgsArray[parameterTypes.length-1] = varargs;
+                            if (setAccessible) m.setAccessible(true);
+                            return Optional.of(m.invoke(instance, 
newArgsArray));
+                        }
+                    }
+                }
+                if (typesMatch(argsArray, parameterTypes)) {
+                    if (setAccessible) m.setAccessible(true);
+                    return Optional.of(m.invoke(instance, argsArray));
+                }
+            }
+        }
+        
+        return Optional.absent();
+    }
+
+    /** true iff all args match the corresponding types */
+    public static boolean typesMatch(Object[] argsArray, Class<?>[] 
parameterTypes) {
+        if (argsArray.length != parameterTypes.length)
+            return false;
+        return typesMatchUpTo(argsArray, parameterTypes, argsArray.length);
+    }
+    
+    /** true iff the initial N args match the corresponding types */
+    public static boolean typesMatchUpTo(Object[] argsArray, Class<?>[] 
parameterTypes, int lengthRequired) {
+        if (argsArray.length < lengthRequired || parameterTypes.length < 
lengthRequired)
+            return false;
+        for (int i=0; i<lengthRequired; i++) {
+            if (argsArray[i]==null) continue;
+            if (Boxing.boxedType(parameterTypes[i]).isInstance(argsArray[i])) 
continue;
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Gets all the interfaces implemented by the given type, including its 
parent classes.
+     *
+     * @param type the class to look up
+     * @return an immutable list of the interface classes
+     */
+    public static List<Class<?>> getAllInterfaces(@Nullable Class<?> type) {
+        Set<Class<?>> found = Sets.newLinkedHashSet();
+        findAllInterfaces(type, found);
+        return ImmutableList.copyOf(found);
+    }
+
+    /** Recurse through the class hierarchies of the type and its interfaces. 
*/
+    private static void findAllInterfaces(@Nullable Class<?> type, 
Set<Class<?>> found) {
+        if (type == null) return;
+        for (Class<?> i : type.getInterfaces()) {
+            if (found.add(i)) { // not seen before
+                findAllInterfaces(i, found);
+            }
+        }
+        findAllInterfaces(type.getSuperclass(), found);
+    }
+
+    public static boolean hasNoArgConstructor(Class<?> clazz) {
+        try {
+            clazz.getConstructor(new Class[0]);
+            return true;
+        } catch (NoSuchMethodException e) {
+            return false;
+        }
+    }
+
+    public static boolean hasNoNonObjectFields(Class<? extends Object> clazz) {
+        if (Object.class.equals(clazz)) return true;
+        if (clazz.getDeclaredFields().length>0) return false;
+        return hasNoNonObjectFields(clazz.getSuperclass());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Serializers.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Serializers.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Serializers.java
new file mode 100644
index 0000000..d084674
--- /dev/null
+++ 
b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Serializers.java
@@ -0,0 +1,121 @@
+/*
+ * 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.util.javalang;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OutputStream;
+
+import org.apache.brooklyn.util.stream.Streams;
+
+public class Serializers {
+
+    public interface ObjectReplacer {
+        public static final ObjectReplacer NOOP = new ObjectReplacer() {
+            @Override public Object replace(Object toserialize) {
+                return toserialize;
+            }
+            @Override public Object resolve(Object todeserialize) {
+                return todeserialize;
+            }
+        };
+        
+        public Object replace(Object toserialize);
+        public Object resolve(Object todeserialize);
+    }
+
+    public static <T> T reconstitute(T object) throws IOException, 
ClassNotFoundException {
+        return reconstitute(object, ObjectReplacer.NOOP);
+    }
+    
+    public static <T> T reconstitute(T object, ObjectReplacer replacer) throws 
IOException, ClassNotFoundException {
+        if (object == null) return null;
+        return reconstitute(object, object.getClass().getClassLoader(), 
replacer);
+    }
+    
+    public static <T> T reconstitute(T object, ClassLoader classLoader) throws 
IOException, ClassNotFoundException {
+        return reconstitute(object, classLoader, ObjectReplacer.NOOP);
+    }
+    
+    @SuppressWarnings("unchecked")
+    public static <T> T reconstitute(T object, ClassLoader classLoader, final 
ObjectReplacer replacer) throws IOException, ClassNotFoundException {
+        if (object == null) return null;
+        
+        class ReconstitutingObjectOutputStream extends ObjectOutputStream {
+            public ReconstitutingObjectOutputStream(OutputStream outputStream) 
throws IOException {
+                super(outputStream);
+                enableReplaceObject(true);
+            }
+            @Override
+            protected Object replaceObject(Object obj) throws IOException {
+                return replacer.replace(obj);
+            }
+        };
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ReconstitutingObjectOutputStream(baos);
+        oos.writeObject(object);
+        oos.close();
+        
+        class ReconstitutingObjectInputStream extends 
ClassLoaderObjectInputStream {
+            public ReconstitutingObjectInputStream(InputStream inputStream, 
ClassLoader classLoader) throws IOException {
+                super(inputStream, classLoader);
+                super.enableResolveObject(true);
+            }
+            @Override protected Object resolveObject(Object obj) throws 
IOException {
+                return replacer.resolve(obj);
+            }
+        };
+        ByteArrayInputStream bais = new 
ByteArrayInputStream(baos.toByteArray());
+        ReconstitutingObjectInputStream ois = new 
ReconstitutingObjectInputStream(bais, classLoader);
+        try {
+            return (T) ois.readObject();
+        } finally {
+            Streams.closeQuietly(ois);
+        }
+    }
+    
+    /**
+     * Follows pattern in 
org.apache.commons.io.input.ClassLoaderObjectInputStream
+     */
+    public static class ClassLoaderObjectInputStream extends ObjectInputStream 
{
+
+        private final ClassLoader classLoader;
+        
+        public ClassLoaderObjectInputStream(InputStream inputStream, 
ClassLoader classLoader) throws IOException {
+            super(inputStream);
+            this.classLoader = classLoader;
+        }
+
+        @Override
+        protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) 
throws IOException, ClassNotFoundException {
+            Class<?> clazz = Class.forName(objectStreamClass.getName(), false, 
classLoader);
+
+            if (clazz != null) {
+                return clazz;
+            } else {
+                return super.resolveClass(objectStreamClass);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/StackTraceSimplifier.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/StackTraceSimplifier.java
 
b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/StackTraceSimplifier.java
new file mode 100644
index 0000000..cdfbe1e
--- /dev/null
+++ 
b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/StackTraceSimplifier.java
@@ -0,0 +1,202 @@
+/*
+ * 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.util.javalang;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Utility class for cleaning up stacktraces.
+ */
+public class StackTraceSimplifier {
+
+    private static final Logger log = 
LoggerFactory.getLogger(StackTraceSimplifier.class);
+    
+    /** comma-separated prefixes (not regexes) */
+    public static final String DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME = 
"brooklyn.util.javalang.StackTraceSimplifier.blacklist";
+    
+    /** @deprecated since 0.6.0 use {@link 
#DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME} */ @Deprecated
+    public static final String LEGACY_DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME = 
"groovy.sanitized.stacktraces";
+    
+    private static final Collection<String> DEFAULT_BLACKLIST;
+    
+    static {
+        ImmutableSet.Builder<String> blacklist = ImmutableSet.builder();
+        blacklist.addAll(Arrays.asList(
+                System.getProperty(DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME,
+                        "java.," +
+                        "javax.," +
+                        "sun.," +
+                        "groovy.," +
+                        "org.codehaus.groovy.," +
+                        "gjdk.groovy.,"
+                    ).split("(\\s|,)+")));
+        
+        String legacyDefaults = 
System.getProperty(LEGACY_DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME);
+        if (Strings.isNonBlank(legacyDefaults)) {
+            log.warn("Detected ude of legacy system property 
"+LEGACY_DEFAULT_BLACKLIST_SYSTEM_PROPERTY_NAME);
+            blacklist.addAll(Arrays.asList(legacyDefaults.split("(\\s|,)+")));
+        }
+        
+        DEFAULT_BLACKLIST = blacklist.build();
+    }
+    
+    private static final StackTraceSimplifier DEFAULT_INSTACE = newInstance();
+    
+    private final Collection<String> blacklist;
+    
+    protected StackTraceSimplifier() {
+        this(true);
+    }
+
+    protected StackTraceSimplifier(boolean includeDefaultBlacklist, String 
...packages) {
+        ImmutableSet.Builder<String> blacklistB = ImmutableSet.builder();
+        if (includeDefaultBlacklist)
+            blacklistB.addAll(DEFAULT_BLACKLIST);
+        blacklistB.add(packages);
+        blacklist = blacklistB.build();
+    }
+
+    public static StackTraceSimplifier newInstance() {
+        return new StackTraceSimplifier();
+    }
+
+    public static StackTraceSimplifier newInstance(String 
...additionalBlacklistPackagePrefixes) {
+        return new StackTraceSimplifier(true, 
additionalBlacklistPackagePrefixes);
+    }
+
+    public static StackTraceSimplifier newInstanceExcludingOnly(String 
...blacklistPackagePrefixes) {
+        return new StackTraceSimplifier(false, blacklistPackagePrefixes);
+    }
+
+    /** @return whether the given element is useful, that is, not in the 
blacklist */
+    public boolean isUseful(StackTraceElement el) {
+        for (String s: blacklist){
+            if (el.getClassName().startsWith(s)) return false;;
+            // gets underscores in some contexts ?
+            if (el.getClassName().replace('_', '.').startsWith(s)) return 
false;
+        }
+
+        return true;
+    }
+
+    /** @return new list containing just the {@link 
#isUseful(StackTraceElement)} stack trace elements */
+    public List<StackTraceElement> clean(Iterable<StackTraceElement> st) {
+        List<StackTraceElement> result = new LinkedList<StackTraceElement>();
+        for (StackTraceElement element: st){
+            if (isUseful(element)){
+                result.add(element);
+            }
+        }
+
+        return result;
+    }
+
+    /** @return new array containing just the {@link 
#isUseful(StackTraceElement)} stack trace elements */
+    public StackTraceElement[] clean(StackTraceElement[] st) {
+        List<StackTraceElement> result = clean(Arrays.asList(st));
+        return result.toArray(new StackTraceElement[result.size()]);
+    }
+
+    /** @return first {@link #isUseful(StackTraceElement)} stack trace 
elements, or null */
+    public StackTraceElement firstUseful(StackTraceElement[] st) {
+        return nthUseful(0, st);
+    }
+
+    /** @return (n+1)th {@link #isUseful(StackTraceElement)} stack trace 
elements (ie 0 is {@link #firstUseful(StackTraceElement[])}), or null */
+    public StackTraceElement nthUseful(int n, StackTraceElement[] st) {
+        for (StackTraceElement element: st){
+            if (isUseful(element)) {
+                if (n==0) 
+                    return element;
+                n--;
+            }
+        }        
+        return null;
+    }
+
+    /** {@link #clean(StackTraceElement[])} the given throwable instance, 
returning the same instance for convenience */
+    public <T extends Throwable> T cleaned(T t) {
+        t.setStackTrace(clean(t.getStackTrace()));
+        return t;
+    }
+
+    // ---- statics
+    
+    /** static convenience for {@link #isUseful(StackTraceElement)} */
+    public static boolean isStackTraceElementUseful(StackTraceElement el) {
+        return DEFAULT_INSTACE.isUseful(el);
+    }
+
+    /** static convenience for {@link #clean(Iterable)} */
+    public static List<StackTraceElement> 
cleanStackTrace(Iterable<StackTraceElement> st) {
+        return DEFAULT_INSTACE.clean(st);
+    }
+
+    /** static convenience for {@link #clean(StackTraceElement[])} */
+    public static StackTraceElement[] cleanStackTrace(StackTraceElement[] st) {
+        return DEFAULT_INSTACE.clean(st);
+    }
+
+    /** static convenience for {@link #cleaned(Throwable)} */
+    public static <T extends Throwable> T cleanedStackTrace(T t) {
+        return DEFAULT_INSTACE.cleaned(t);
+    }
+    
+    public static String toString(Throwable t) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        t.printStackTrace(pw);
+        return sw.getBuffer().toString();
+    }
+
+    /** returns the number of times the calling method occurs elsewhere in the 
stack trace;
+     * 0 if no recursion, 1 if it has cycled three times, etc. */
+    @Beta  // useful to track down things like 
https://github.com/apache/incubator-brooklyn/pull/489
+    public static int getRecursiveCallCount() {
+        StackTraceElement[] t = cleanStackTrace(new 
Throwable().getStackTrace());
+        Iterator<StackTraceElement> ti = Arrays.asList(t).iterator();
+        ti.next();
+        if (!ti.hasNext()) return 0;
+        // t0 is the caller
+        StackTraceElement t0 = ti.next();
+        String l0 = 
t0.getClassName()+"."+t0.getMethodName()+"("+t0.getFileName()+")";
+        int count = 0;
+        while (ti.hasNext()) {
+            StackTraceElement ta = ti.next();
+            String li = 
ta.getClassName()+"."+ta.getMethodName()+"("+ta.getFileName()+")";
+            // if we have something in a different method, then something back 
in the method 
+            // from which the recursive check came, then return true
+            if (li.equals(l0)) count++;
+        }
+        return count;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Threads.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Threads.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Threads.java
new file mode 100644
index 0000000..9b5eb79
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Threads.java
@@ -0,0 +1,61 @@
+/*
+ * 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.util.javalang;
+
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Threads {
+
+    private static final Logger log = LoggerFactory.getLogger(Threads.class);
+    
+    public static Thread addShutdownHook(final Runnable task) {
+        Thread t = new Thread("shutdownHookThread") {
+            public void run() {
+                try {
+                    task.run();
+                } catch (Exception e) {
+                    log.error("Failed to execute shutdownhook", e);
+                }
+            }
+        };
+        Runtime.getRuntime().addShutdownHook(t);
+        return t;
+    }
+    
+    public static boolean removeShutdownHook(Thread hook) {
+        try {
+            return Runtime.getRuntime().removeShutdownHook(hook);
+        } catch (IllegalStateException e) {
+            // probably shutdown in progress
+            String text = Exceptions.collapseText(e);
+            if (text.contains("Shutdown in progress")) {
+                if (log.isTraceEnabled()) {
+                    log.trace("Could not remove shutdown hook "+hook+": 
"+text);
+                }
+            } else {
+                log.warn("Could not remove shutdown hook "+hook+": "+text);
+                log.debug("Shutdown hook removal details: "+e, e);
+            }
+            return false;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/logging/LoggingSetup.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/logging/LoggingSetup.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/logging/LoggingSetup.java
new file mode 100644
index 0000000..35467bc
--- /dev/null
+++ 
b/utils/common/src/main/java/org/apache/brooklyn/util/logging/LoggingSetup.java
@@ -0,0 +1,39 @@
+/*
+ * 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.util.logging;
+
+import org.slf4j.bridge.SLF4JBridgeHandler;
+
+public class LoggingSetup {
+
+    /** bridge java.util.logging messages to slf4j
+     * <p>
+     * without this, we get ugly java.util.logging messages on the console and 
_not_ in the file;
+     * with this, the excludes rules (which route the common j.u.l categories 
to the file _only_) 
+     * will apply to j.u.l loggers 
+     * <p>
+     * typically this is invoked in a static block on a class (in tests and in 
BrooklynWebServer) 
+     * or could be done on app startup */
+    public static void installJavaUtilLoggingBridge() {
+        SLF4JBridgeHandler.removeHandlersForRootLogger();
+        SLF4JBridgeHandler.install();
+    }
+    
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/logging/SimpleOneLineLogFormatter.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/logging/SimpleOneLineLogFormatter.java
 
b/utils/common/src/main/java/org/apache/brooklyn/util/logging/SimpleOneLineLogFormatter.java
new file mode 100644
index 0000000..43faf4b
--- /dev/null
+++ 
b/utils/common/src/main/java/org/apache/brooklyn/util/logging/SimpleOneLineLogFormatter.java
@@ -0,0 +1,140 @@
+/*
+ * 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.util.logging;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.logging.Formatter;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import com.google.common.base.Throwables;
+
+//useful for java.util.logger, which by default puts every message on two 
lines (horrid)
+//however we don't typically use java.util.logger...
+public class SimpleOneLineLogFormatter extends Formatter {
+
+    public SimpleOneLineLogFormatter() {
+        this(true, false, false);
+    }
+
+    public SimpleOneLineLogFormatter(boolean showLevel, boolean showThread, 
boolean showCaller) {
+        this.showLevel = showLevel;
+        this.showThread = showThread;
+        this.showCaller = showCaller;
+    }
+
+
+    public final boolean showLevel;
+    public final boolean showThread;
+    public final boolean showCaller;
+
+    // use shared date and formatter to minimize memory/time overhead
+    protected final Date date = new Date();
+    protected DateFormat dateFormat = new SimpleDateFormat(getDateFormat());
+
+    public String getDateFormat() {
+        return "yyyy-MM-dd HH:mm:ss.SSSZ";
+    }
+
+    /** uses "YYYY-DD-MM hh:mm:ss.SSS  message" format */ 
+    public String format(LogRecord record) {
+        StringBuffer sb = new StringBuffer();
+        appendDate(record, sb);
+        appendLevel(record, sb);
+        sb.append("  ");
+        sb.append(formatMessage(record));
+        appendThreadAndCaller(record, sb);
+        appendDetailsWithNewLine(sb, record);
+        return sb.toString();
+    }
+
+    protected void appendLevel(LogRecord record, StringBuffer sb) {
+        if (showLevel) {
+            sb.append(" [").append(record.getLevel()).append("]");
+        }
+    }
+
+    protected void appendDate(LogRecord record, StringBuffer sb) {
+        synchronized (date) {
+            date.setTime(record.getMillis());
+            sb.append(dateFormat.format(date));
+        }
+    }
+
+    protected void appendThreadAndCaller(LogRecord record, StringBuffer sb) {
+        if (showThread || showCaller) {
+            sb.append(" [");
+            if (showThread)
+                sb.append(getThreadName(record));
+            if (showThread && showCaller) sb.append(", ");
+            if (showCaller) {
+                if (record.getSourceClassName() != null) {    
+                    sb.append(record.getSourceClassName());
+                } else {
+                    sb.append(record.getLoggerName());
+                }
+                if (record.getSourceMethodName() != null) {    
+                    sb.append(" ");
+                    sb.append(record.getSourceMethodName());
+                }
+            }
+            sb.append("]");
+        }
+    }
+
+    protected void appendDetailsWithNewLine(StringBuffer sb, LogRecord record) 
{
+        if (record.getThrown() != null) {
+            try {
+                sb.append('\n');
+                StringWriter sw = new StringWriter();
+                PrintWriter pw = new PrintWriter(sw);
+                record.getThrown().printStackTrace(pw);
+                pw.close();
+                sb.append(sw.toString());
+            } catch (Exception ex) {
+                //shouldn't happen with printwriter
+                throw Throwables.propagate(ex);
+            }
+        } else {
+            sb.append('\n');
+        }
+    }
+
+    protected String getThreadName(LogRecord record) {
+        //try to get the thread's name
+        //only possible if we are the thread (unless do something messy like 
cache or access private fields)
+        //fortunately we typically are the thread
+        LogRecord lr = new LogRecord(Level.INFO, "");
+        if (lr.getThreadID()==record.getThreadID())
+            return Thread.currentThread().getName() + " 
("+record.getThreadID()+")";
+        //otherwise just say the number
+        return "thread ("+record.getThreadID()+")";
+    }
+
+    public static class LogFormatterWithThreadAndCaller extends 
SimpleOneLineLogFormatter {
+        public LogFormatterWithThreadAndCaller() {
+            super(true, true, true);
+        }
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/math/BitList.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/math/BitList.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/math/BitList.java
new file mode 100644
index 0000000..4784f77
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/math/BitList.java
@@ -0,0 +1,271 @@
+/*
+ * 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.util.math;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+import com.google.common.collect.Lists;
+import com.google.common.primitives.Bytes;
+
+/** represents an immutable ordered collection of bits with a known length
+ * <p>
+ * when converting to and from bytes and larger numbers, this representation 
+ * uses the least-significant first convention both for bits and for bytes 
(little endian)
+ * <p> 
+ * (i.e. least significant byte is first, as is the least significant bit;
+ * ninth element in this list is the least significant bit in the second byte,
+ * so a list {0,0,0,0,0,0,0,0,1,0} represents 256)
+ **/
+public class BitList {
+
+    private final BitSet bits;
+    protected final int length;
+    
+    protected BitList(BitSet bits, int length) {
+        assert length >= bits.length();
+        this.bits = bits;
+        this.length = length;
+    }
+    
+    public static BitList newInstance(BitSet bits, int length) {
+        return new BitList(bits, length);
+    }
+    
+    public int length() {
+        return length;
+    }
+
+    public boolean get(int index) {
+        if (index<0 || index>=length)
+            throw new IndexOutOfBoundsException("Index "+index+" in "+this);
+        return bits.get(index);
+    }
+    
+    public static BitList newInstance(byte ...bytes) {
+        BitSet bits = new BitSet();
+        for (int i=0; i < bytes.length*8; i++)
+            if ((bytes[i/8] & (1 << (i%8))) > 0)
+                bits.set(i);
+        return newInstance(bits, bytes.length*8);
+    }
+
+    /** as {@link #newInstance(byte...)}, but accepting ints for convenience; 
+     * only the least significant 8 bits of the parameters are considered */
+    public static BitList newInstanceFromBytes(int ...bytes) {
+        BitSet bits = new BitSet();
+        for (int i=0; i < bytes.length*8; i++)
+            if ((bytes[i/8] & (1 << (i%8))) > 0)
+                bits.set(i);
+        return newInstance(bits, bytes.length*8);
+    }
+    
+    public static BitList newInstance(List<Boolean> l) {
+        BitSet bs = new BitSet();
+        for (int i=0; i<l.size(); i++)
+            bs.set(i, l.get(i));
+        return new BitList(bs, l.size());
+    }
+
+    public static BitList newInstance(boolean ...l) {
+        BitSet bs = new BitSet();
+        for (int i=0; i<l.length; i++)
+            bs.set(i, l[i]);
+        return new BitList(bs, l.length);
+    }
+    
+    public static BitList newInstance(BigInteger x) {
+        BitSet bs = new BitSet();
+        for (int i=0; i<x.bitLength(); i++)
+            if (x.testBit(i)) bs.set(i);
+        return new BitList(bs, x.bitLength());
+    }
+
+    /**
+     * returns the bits converted to bytes, with least significant bit first
+     * *and* first 8 bits in the first byte  
+     * <p> 
+     * NB this may be different to BitSet.valueOf available since java 7 (as 
late as that!)
+     * which reverses the order of the bytes */
+    public byte[] asBytes() {
+        byte[] bytes = new byte[(length+7)/8];
+        for (int i=0; i<bits.length(); i++)
+            if (bits.get(i))
+                bytes[i/8] |= 1 << (i%8);
+        return bytes;
+    }
+
+    public int[] asUnsignedBytes() {
+        int[] bytes = new int[(length+7)/8];
+        for (int i=0; i<bits.length(); i++)
+            if (bits.get(i))
+                bytes[i/8] |= 1 << (i%8);
+        return bytes;
+    }
+
+    /** nb: BitSet forgets the length */
+    public BitSet asBitSet() {
+        return (BitSet) bits.clone();
+    }
+
+    public List<Boolean> asList() {
+        List<Boolean> list = new ArrayList<Boolean>();
+        for (int i=0; i<length(); i++) {
+            list.add(get(i));
+        }
+        return list;
+    }
+    
+    /** represents the result of this bit list logically ORred with the other 
*/ 
+    public BitList orred(BitList other) {
+        BitSet result = asBitSet();
+        result.or(other.asBitSet());
+        return new BitList(result, Math.max(length, other.length));
+    }
+
+    /** represents the result of this bit list logically ANDed with the other 
*/ 
+    public BitList anded(BitList other) {
+        BitSet result = asBitSet();
+        result.and(other.asBitSet());
+        return new BitList(result, Math.max(length, other.length));
+    }
+
+    /** represents the result of this bit list logically XORred with the other 
*/ 
+    public BitList xorred(BitList other) {
+        BitSet result = asBitSet();
+        result.xor(other.asBitSet());
+        return new BitList(result, Math.max(length, other.length));
+    }
+
+    /** represents the result of this bit list logically notted */ 
+    public BitList notted() {
+        BitSet result = asBitSet();
+        result.flip(0, length);
+        return new BitList(result, length);
+    }
+
+    /** creates a new instance with the given length, either reducing the list 
or padding it with 0's 
+     * (at the end, in both cases) 
+     */
+    public BitList resized(int length) {
+        BitSet b2 = asBitSet();
+        if (b2.length()>length) 
+            b2.clear(length, b2.length());
+        return newInstance(b2, length);
+    }
+
+    public BitList reversed() {
+        BitSet b = new BitSet();
+        for (int from=bits.length()-1, to=length-bits.length(); from>=0; 
from--, to++) {
+            if (get(from)) b.set(to);
+        }
+        return new BitList(b, length);
+    }
+
+    public int commonPrefixLength(BitList other) {
+        int i=0;
+        while (i<length && i<other.length) {
+            if (get(i)!=other.get(i)) return i;
+            i++;
+        }
+        return i;
+    }
+
+    /** true iff the length is 0; see also isZero */
+    public boolean isEmpty() {
+        return length==0;
+    }
+
+    /** true iff all bits are 0 */
+    public boolean isZero() {
+        return bits.cardinality()==0;
+    }
+
+    public BigInteger asBigInteger() {
+        if (length==0) return BigInteger.ZERO;
+        return new BigInteger(Bytes.toArray(Lists.reverse(asByteList())));
+    }
+
+    public boolean[] asArray() {
+        boolean[] result = new boolean[length];
+        for (int i=0; i<length; i++)
+            result[i] = get(i);
+        return result;
+    }
+    
+    public List<Byte> asByteList() {
+        return Bytes.asList(asBytes());
+    }
+
+    /** returns value of this as a byte(ignoring any too-high bits) */
+    public byte byteValue() {
+        return asBigInteger().byteValue();
+    }
+
+    /** returns value of this as an integer (ignoring any too-high bits) */
+    public int intValue() {
+        return asBigInteger().intValue();
+    }
+    
+    /** returns value of this as a long (ignoring any too-high bits) */
+    public long longValue() {
+        return asBigInteger().longValue();
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((bits == null) ? 0 : bits.hashCode());
+        result = prime * result + length;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BitList other = (BitList) obj;
+        if (bits == null) {
+            if (other.bits != null)
+                return false;
+        } else if (!bits.equals(other.bits))
+            return false;
+        if (length != other.length)
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (int i=0; i<length; i++) {
+            if (i%8==0 && i>0) sb.append(":"); //for readability
+            sb.append(get(i) ? '1' : '0');
+        }
+        return sb.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/math/BitUtils.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/math/BitUtils.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/math/BitUtils.java
new file mode 100644
index 0000000..9585124
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/math/BitUtils.java
@@ -0,0 +1,70 @@
+/*
+ * 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.util.math;
+
+public class BitUtils {
+
+    /** reverses the bits in a byte, i.e.  128 = 0b1000000 = bit list 
{0,0,0,0,0,0,0,1},
+     * reversed yields 1 = 0b00000001 = bit list {1,0,0,0,0,0,0,0} */
+    public static byte reverseBitSignificance(byte b) {
+        int result = 0;
+        for (int i=0; i<8; i++) {
+            result <<= 1;
+            if ((b&1)==1) result++;
+            b >>= 1;
+        }
+        return (byte)result;
+    }
+    
+    /** as {@link #reverseBitSignificance(byte)} but accepting int for 
convenience */
+    public static byte reverseBitSignificanceInByte(int b) {
+        return reverseBitSignificance((byte)b);
+    }
+    
+    /** returns an array of bytes where the bits in each byte have been 
reversed;
+     * note however the order of the arguments is not reversed;
+     * useful e.g. in working with IP address CIDR's */
+    public static byte[] reverseBitSignificance(byte ...bytes) {
+        byte[] result = new byte[bytes.length];
+        for (int i=0; i<bytes.length; i++)
+            result[i] = reverseBitSignificance(bytes[i]);
+        return result;
+    }
+
+    /** as {@link #reverseBitSignificance(byte...)}, but taking ints for 
convenience (ignoring high bits) */
+    public static byte[] reverseBitSignificanceInBytes(int ...bytes) {
+        byte[] result = new byte[bytes.length];
+        for (int i=0; i<bytes.length; i++)
+            result[i] = reverseBitSignificance((byte)bytes[i]);
+        return result;
+    }
+    
+    /** why oh why are bytes signed! */
+    public static int unsigned(byte b) {
+        if (b<0) return b+256;
+        return b;
+    }
+
+    /** returns the value in 0..255 which is equivalent mod 256 */
+    public static int unsignedByte(int b) {
+        if (b<0) return (b%256)+256;
+        return (b%256);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/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
new file mode 100644
index 0000000..92dbe36
--- /dev/null
+++ 
b/utils/common/src/main/java/org/apache/brooklyn/util/math/MathFunctions.java
@@ -0,0 +1,123 @@
+/*
+ * 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.util.math;
+
+import javax.annotation.Nullable;
+
+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) {
+        return new Function<Number, Integer>() {
+            public Integer apply(@Nullable Number input) {
+                if (input==null) return null;
+                return input.intValue() + addend;
+            }
+        };
+    }
+
+    public static Function<Number, Long> plus(final long addend) {
+        return new Function<Number, Long>() {
+            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 Function<Number, Double>() {
+            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 Function<Number, Integer>() {
+            public Integer apply(@Nullable Number input) {
+                if (input==null) return null;
+                return input.intValue() * multiplicand;
+            }
+        };
+    }
+
+    public static Function<Number, Long> times(final long multiplicand) {
+        return new Function<Number, Long>() {
+            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 Function<Number, Double>() {
+            public Double apply(@Nullable Number input) {
+                if (input==null) return null;
+                return input.doubleValue() * multiplicand;
+            }
+        };
+    }
+
+    public static Function<Number, Double> divide(final double divisor) {
+        return new Function<Number, Double>() {
+            public Double apply(@Nullable Number input) {
+                if (input==null) return null;
+                return input.doubleValue() / divisor;
+            }
+        };
+    }
+
+    public static <T> Function<T, Double> divide(final Function<T, ? extends 
Number> input, final double divisor) {
+        return new Function<T, Double>() {
+            public Double apply(@Nullable T input2) {
+                if (input==null) return null;
+                Number n = input.apply(input2);
+                if (n==null) return null;
+                return n.doubleValue() / divisor;
+            }
+        };
+    }
+
+    /** 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) {
+        return new Function<Number, String>() {
+            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(final int 
significantDigits) {
+        return new Function<Number, String>() {
+            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/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/math/MathPredicates.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/math/MathPredicates.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/math/MathPredicates.java
new file mode 100644
index 0000000..e03c379
--- /dev/null
+++ 
b/utils/common/src/main/java/org/apache/brooklyn/util/math/MathPredicates.java
@@ -0,0 +1,106 @@
+/*
+ * 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.util.math;
+
+import javax.annotation.Nullable;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+
+public class MathPredicates {
+
+    /**
+     * Creates a predicate comparing a given number with {@code val}. 
+     * A number of {@code null} passed to the predicate will always return 
false.
+     */
+    public static <T extends Number> Predicate<T> greaterThan(final double 
val) {
+        return new Predicate<T>() {
+            public boolean apply(@Nullable T input) {
+                return (input == null) ? false : input.doubleValue() > val;
+            }
+        };
+    }
+
+    /**
+     * Creates a predicate comparing a given number with {@code val}. 
+     * A number of {@code null} passed to the predicate will always return 
false.
+     */
+    public static <T extends Number> Predicate<T> greaterThanOrEqual(final 
double val) {
+        return new Predicate<T>() {
+            public boolean apply(@Nullable T input) {
+                return (input == null) ? false : input.doubleValue() >= val;
+            }
+        };
+    }
+
+    /**
+     * Creates a predicate comparing a given number with {@code val}. 
+     * A number of {@code null} passed to the predicate will always return 
false.
+     */
+    public static <T extends Number> Predicate<T> lessThan(final double val) {
+        return new Predicate<T>() {
+            public boolean apply(@Nullable T input) {
+                return (input == null) ? false : input.doubleValue() < val;
+            }
+        };
+    }
+
+    /**
+     * Creates a predicate comparing a given number with {@code val}. 
+     * A number of {@code null} passed to the predicate will always return 
false.
+     */
+    public static <T extends Number> Predicate<T> lessThanOrEqual(final double 
val) {
+        return new Predicate<T>() {
+            public boolean apply(@Nullable T input) {
+                return (input == null) ? false : input.doubleValue() <= val;
+            }
+        };
+    }
+    
+    /**
+     * Creates a predicate comparing a given number with {@code val}. 
+     * A number of {@code null} passed to the predicate will always return 
false.
+     */
+    public static <T extends Number> Predicate<T> equalsApproximately(final 
Number val, final double delta) {
+        return new EqualsApproximately<T>(val, delta);
+    }
+    /** Convenience for {@link #equalsApproximately(double,double)} with a 
delta of 10^{-6}. */
+    public static <T extends Number> Predicate<T> equalsApproximately(final 
Number val) {
+        return equalsApproximately(val, 0.0000001);
+    }
+
+    private static final class EqualsApproximately<T extends Number> 
implements Predicate<T> {
+        private final double val;
+        private final double delta;
+        private EqualsApproximately(Number val, double delta) {
+            this.val = val.doubleValue();
+            Preconditions.checkArgument(delta>=0, "delta must be 
non-negative");
+            this.delta = delta;
+        }
+        public boolean apply(@Nullable T input) {
+            return (input == null) ? false : Math.abs(input.doubleValue() - 
val) <= delta;
+        }
+        @Override
+        public String toString() {
+            return "equals-approximately("+val+" +- "+delta+")";
+        }
+    }
+
+
+}

Reply via email to