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/08/18 17:03:23 UTC
[14/64] [abbrv] incubator-brooklyn git commit: BROOKLYN-162 - apply
org.apache package prefix to utils-common
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+")";
+ }
+ }
+
+
+}