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:24 UTC
[15/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/io/FileUtil.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/io/FileUtil.java b/utils/common/src/main/java/org/apache/brooklyn/util/io/FileUtil.java
new file mode 100644
index 0000000..cdc65a5
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/io/FileUtil.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.io;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.io.FileUtil;
+import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.stream.StreamGobbler;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.commons.io.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+
+public class FileUtil {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FileUtil.class);
+
+ private static boolean loggedSetFilePermissionsWarning = false;
+
+ // When we move to java 7, we can use Files.setPosixFilePermissions
+ public static void setFilePermissionsTo700(File file) throws IOException {
+ file.createNewFile();
+ boolean setRead = file.setReadable(false, false) & file.setReadable(true, true);
+ boolean setWrite = file.setWritable(false, false) & file.setWritable(true, true);
+ boolean setExec = file.setExecutable(false, false) & file.setExecutable(true, true);
+
+ if (setRead && setWrite && setExec) {
+ if (LOG.isTraceEnabled()) LOG.trace("Set permissions to 700 for file {}", file.getAbsolutePath());
+ } else {
+ if (loggedSetFilePermissionsWarning) {
+ if (LOG.isTraceEnabled()) LOG.trace("Failed to set permissions to 700 for file {}: setRead={}, setWrite={}, setExecutable={}",
+ new Object[] {file.getAbsolutePath(), setRead, setWrite, setExec});
+ } else {
+ if (Os.isMicrosoftWindows()) {
+ if (LOG.isDebugEnabled()) LOG.debug("Failed to set permissions to 700 for file {}; expected behaviour on Windows; setRead={}, setWrite={}, setExecutable={}; subsequent failures (on any file) will be logged at trace",
+ new Object[] {file.getAbsolutePath(), setRead, setWrite, setExec});
+ } else {
+ LOG.warn("Failed to set permissions to 700 for file {}: setRead={}, setWrite={}, setExecutable={}; subsequent failures (on any file) will be logged at trace",
+ new Object[] {file.getAbsolutePath(), setRead, setWrite, setExec});
+ }
+ loggedSetFilePermissionsWarning = true;
+ }
+ }
+ }
+
+ // When we move to java 7, we can use Files.setPosixFilePermissions
+ public static void setFilePermissionsTo600(File file) throws IOException {
+ file.createNewFile();
+ file.setExecutable(false, false);
+ file.setReadable(false, false);
+ file.setWritable(false, false);
+ file.setReadable(true, true);
+ file.setWritable(true, true);
+
+ boolean setRead = file.setReadable(false, false) & file.setReadable(true, true);
+ boolean setWrite = file.setWritable(false, false) & file.setWritable(true, true);
+ boolean setExec = file.setExecutable(false, false);
+
+ if (setRead && setWrite && setExec) {
+ if (LOG.isTraceEnabled()) LOG.trace("Set permissions to 600 for file {}", file.getAbsolutePath());
+ } else {
+ if (loggedSetFilePermissionsWarning) {
+ if (LOG.isTraceEnabled()) LOG.trace("Failed to set permissions to 600 for file {}: setRead={}, setWrite={}, setExecutable={}",
+ new Object[] {file.getAbsolutePath(), setRead, setWrite, setExec});
+ } else {
+ if (Os.isMicrosoftWindows()) {
+ if (LOG.isDebugEnabled()) LOG.debug("Failed to set permissions to 600 for file {}; expected behaviour on Windows; setRead={}, setWrite={}, setExecutable={}; subsequent failures (on any file) will be logged at trace",
+ new Object[] {file.getAbsolutePath(), setRead, setWrite, setExec});
+ } else {
+ LOG.warn("Failed to set permissions to 600 for file {}: setRead={}, setWrite={}, setExecutable={}; subsequent failures (on any file) will be logged at trace",
+ new Object[] {file.getAbsolutePath(), setRead, setWrite, setExec});
+ }
+ loggedSetFilePermissionsWarning = true;
+ }
+ }
+ }
+
+ public static void moveDir(File srcDir, File destDir) throws IOException, InterruptedException {
+ if (!Os.isMicrosoftWindows()) {
+ String cmd = "mv '"+srcDir.getAbsolutePath()+"' '"+destDir.getAbsolutePath()+"'";
+ Process proc = Runtime.getRuntime().exec(cmd);
+ proc.waitFor();
+ if (proc.exitValue() == 0) return;
+ }
+
+ FileUtils.moveDirectory(srcDir, destDir);
+ }
+
+ public static void copyDir(File srcDir, File destDir) throws IOException, InterruptedException {
+ if (!Os.isMicrosoftWindows()) {
+ String cmd = "cp -R '"+srcDir.getAbsolutePath()+"' '"+destDir.getAbsolutePath()+"'";
+ Process proc = Runtime.getRuntime().exec(cmd);
+ proc.waitFor();
+ if (proc.exitValue() == 0) return;
+ }
+
+ FileUtils.copyDirectory(srcDir, destDir);
+ }
+
+ /**
+ * This utility will be deleted when we move to Java 7
+ *
+ * @return The file permission (in a form like "-rwxr--r--"), or null if the permissions could not be determined.
+ */
+ @Beta
+ public static Maybe<String> getFilePermissions(File file) {
+ if (!file.exists()) return Maybe.absent("File "+file+" does not exist");
+
+ if (Os.isMicrosoftWindows()) {
+ return Maybe.absent("Cannot determine permissions on windows");
+ } else {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayOutputStream err = new ByteArrayOutputStream();
+ int exitcode = exec(ImmutableList.of("ls", "-ld", file.getAbsolutePath()), out, err);
+ if (exitcode != 0) {
+ if (LOG.isDebugEnabled()) LOG.debug("Could not determine permissions of file "+file+"; exit code "+exitcode+"; stderr "+new String(err.toByteArray()));
+ return Maybe.absent("Could not determine permission of file "+file+"; exit code "+exitcode);
+ }
+ String stdout = new String(out.toByteArray());
+ return (stdout.trim().isEmpty() ? Maybe.<String>absent("empty output") : Maybe.of(stdout.split("\\s")[0]));
+ }
+ }
+
+ // guava's Files.copy(InputStreamSupplier, File) is deprecated, and will be deleted in guava 18.0
+ @Beta
+ public static void copyTo(InputStream in, File dest) {
+ FileOutputStream out = null;
+ try {
+ out = new FileOutputStream(dest);
+ Streams.copy(in, out);
+ } catch (FileNotFoundException e) {
+ throw Exceptions.propagate(e);
+ } finally {
+ Streams.closeQuietly(out);
+ }
+ }
+
+ private static int exec(List<String> cmds, OutputStream out, OutputStream err) {
+ StreamGobbler errgobbler = null;
+ StreamGobbler outgobbler = null;
+
+ ProcessBuilder pb = new ProcessBuilder(cmds);
+
+ try {
+ Process p = pb.start();
+
+ if (out != null) {
+ InputStream outstream = p.getInputStream();
+ outgobbler = new StreamGobbler(outstream, out, (Logger) null);
+ outgobbler.start();
+ }
+ if (err != null) {
+ InputStream errstream = p.getErrorStream();
+ errgobbler = new StreamGobbler(errstream, err, (Logger) null);
+ errgobbler.start();
+ }
+
+ int result = p.waitFor();
+
+ if (outgobbler != null) outgobbler.blockUntilFinished();
+ if (errgobbler != null) errgobbler.blockUntilFinished();
+
+ return result;
+ } catch (Exception e) {
+ throw Exceptions.propagate(e);
+ } finally {
+ Streams.closeQuietly(outgobbler);
+ Streams.closeQuietly(errgobbler);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/AggregateClassLoader.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/AggregateClassLoader.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/AggregateClassLoader.java
new file mode 100644
index 0000000..cea9028
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/AggregateClassLoader.java
@@ -0,0 +1,173 @@
+/*
+ * 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.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.google.common.collect.Sets;
+
+/** looks for classes and resources in the classloaders added here
+ * <p>
+ * similar to XStream's CompositeClassLoader, but also supporting resources,
+ * exposing more info, a few conveniences, and a nice toString */
+public class AggregateClassLoader extends ClassLoader {
+
+ // thread safe -- and all access in this class is also synchronized,
+ // so that reset is guaranteed not to interfere with an add(0, cl)
+ private final CopyOnWriteArrayList<ClassLoader> classLoaders = new CopyOnWriteArrayList<ClassLoader>();
+
+ private AggregateClassLoader() {
+ //Don't pass load requests to the app classloader,
+ //always relay to the classLoaders list.
+ super(null);
+ }
+
+ /** creates default instance, with classloaders of Object and AggregateClassLoader */
+ public static AggregateClassLoader newInstanceWithDefaultLoaders() {
+ AggregateClassLoader cl = new AggregateClassLoader();
+ cl.addFirst(AggregateClassLoader.class.getClassLoader()); // whichever classloader loaded this jar.
+ cl.addFirst(Object.class.getClassLoader()); // bootstrap loader.
+ return cl;
+ }
+ /** creates default instance, with no classloaders (assuming this instance will itself be nested,
+ * or defaults will be added by caller) */
+ public static AggregateClassLoader newInstanceWithNoLoaders() {
+ return new AggregateClassLoader();
+ }
+
+ /** Add a loader to the first position in the search path. */
+ public void addFirst(ClassLoader classLoader) {
+ if (classLoader != null) {
+ synchronized (classLoaders) {
+ classLoaders.add(0, classLoader);
+ }
+ }
+ }
+ /** Add a loader to the last position in the search path. */
+ public void addLast(ClassLoader classLoader) {
+ if (classLoader != null) {
+ synchronized (classLoaders) {
+ classLoaders.add(classLoader);
+ }
+ }
+ }
+ /** Add a loader to the specific position in the search path.
+ * (It is callers responsibility to ensure that position is valid.) */
+ public void add(int index, ClassLoader classLoader) {
+ if (classLoader != null) {
+ synchronized (classLoaders) {
+ classLoaders.add(index, classLoader);
+ }
+ }
+ }
+
+ /** Resets the classloader shown here to be the given set */
+ public void reset(Collection<? extends ClassLoader> newClassLoaders) {
+ synchronized (classLoaders) {
+ // synchronize:
+ // * to prevent concurrent invocations
+ // * so add(0, cl) doesn't interfere
+ // * and for good measure we add before removing so that iterator always contains everything
+ // although since iterator access is synchronized that shouldn't be necessary
+ int count = classLoaders.size();
+ classLoaders.addAll(newClassLoaders);
+ for (int i=0; i<count; i++) {
+ classLoaders.remove(0);
+ }
+ }
+ }
+
+ /** True if nothing is in the list here */
+ public boolean isEmpty() {
+ return classLoaders.isEmpty();
+ }
+
+ /** Returns the _live_ (and modifiable) list of classloaders; dangerous and discouraged.
+ * @deprecated since 0.7.0 */
+ @Deprecated
+ public List<ClassLoader> getList() {
+ return classLoaders;
+ }
+
+ public Iterator<ClassLoader> iterator() {
+ synchronized (classLoaders) {
+ // CopyOnWriteList iterator is immutable view of snapshot
+ return classLoaders.iterator();
+ }
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ for (ClassLoader classLoader: classLoaders) {
+ try {
+ return classLoader.loadClass(name);
+ } catch (ClassNotFoundException notFound) {
+ /* ignore (nice if there were a better way than throwing... */
+ }
+ }
+ // last resort. see comment in XStream CompositeClassLoader
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ if (contextClassLoader != null)
+ return contextClassLoader.loadClass(name);
+ throw new ClassNotFoundException(name);
+ }
+
+ @Override
+ public String toString() {
+ return "AggregateClassLoader"+classLoaders;
+ }
+
+ @Override
+ public URL getResource(String name) {
+ URL result = null;
+ Iterator<ClassLoader> cli = iterator();
+ while (cli.hasNext()) {
+ ClassLoader classLoader=cli.next();
+ result = classLoader.getResource(name);
+ if (result!=null) return result;
+ }
+ // last resort. see comment in XStream CompositeClassLoader
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ if (contextClassLoader != null)
+ return contextClassLoader.getResource(name);
+ return null;
+ }
+
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ Set<URL> resources = Sets.newLinkedHashSet();
+ Iterator<ClassLoader> cli = iterator();
+ while (cli.hasNext()) {
+ ClassLoader classLoader=cli.next();
+ resources.addAll(Collections.list(classLoader.getResources(name)));
+ }
+ return Collections.enumeration(resources);
+ }
+
+ // TODO lesser used items, such as getPackage, findLibrary
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/AtomicReferences.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/AtomicReferences.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/AtomicReferences.java
new file mode 100644
index 0000000..8042afd
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/AtomicReferences.java
@@ -0,0 +1,48 @@
+/*
+ * 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.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+
+public class AtomicReferences {
+
+ /** sets the atomic reference to the given value, and returns whether there is any change */
+ public static boolean setIfDifferent(AtomicBoolean ref, boolean value) {
+ return ref.getAndSet(value) != value;
+ }
+
+ /** sets the atomic reference to the given value, and returns whether there is any change */
+ public static <T> boolean setIfDifferent(AtomicReference<T> ref, T value) {
+ return !Objects.equal(ref.getAndSet(value), value);
+ }
+
+ /** returns the given atomic as a Supplier */
+ public static <T> Supplier<T> supplier(final AtomicReference<T> ref) {
+ Preconditions.checkNotNull(ref);
+ return new Supplier<T>() {
+ @Override public T get() { return ref.get(); }
+ @Override public String toString() { return "AtomicRefSupplier"; }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Boxing.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Boxing.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Boxing.java
new file mode 100644
index 0000000..79a1830
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Boxing.java
@@ -0,0 +1,102 @@
+/*
+ * 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.lang.reflect.Array;
+
+import org.apache.brooklyn.util.guava.Maybe;
+
+import com.google.common.collect.ImmutableBiMap;
+
+public class Boxing {
+
+ public static boolean unboxSafely(Boolean ref, boolean valueIfNull) {
+ if (ref==null) return valueIfNull;
+ return ref.booleanValue();
+ }
+
+ public static final ImmutableBiMap<Class<?>, Class<?>> PRIMITIVE_TO_BOXED =
+ ImmutableBiMap.<Class<?>, Class<?>>builder()
+ .put(Integer.TYPE, Integer.class)
+ .put(Long.TYPE, Long.class)
+ .put(Double.TYPE, Double.class)
+ .put(Float.TYPE, Float.class)
+ .put(Boolean.TYPE, Boolean.class)
+ .put(Character.TYPE, Character.class)
+ .put(Byte.TYPE, Byte.class)
+ .put(Short.TYPE, Short.class)
+ .put(Void.TYPE, Void.class)
+ .build();
+
+ /** Returns the unboxed type for the given primitive type name, if available;
+ * e.g. {@link Integer#TYPE} for <code>int</code> (distinct from <code>Integer.class</code>),
+ * or null if not a primitive.
+ * */
+ public static Maybe<Class<?>> getPrimitiveType(String typeName) {
+ if (typeName!=null) {
+ for (Class<?> t: PRIMITIVE_TO_BOXED.keySet()) {
+ if (typeName.equals(t.getName())) return Maybe.<Class<?>>of(t);
+ }
+ }
+ return Maybe.absent("Not a primitive: "+typeName);
+ }
+
+ public static Class<?> boxedType(Class<?> type) {
+ if (PRIMITIVE_TO_BOXED.containsKey(type))
+ return PRIMITIVE_TO_BOXED.get(type);
+ return type;
+ }
+
+ /** sets the given element in an array to the indicated value;
+ * if the type is a primitive type, the appropriate primitive method is used
+ * <p>
+ * this is needed because arrays do not deal with autoboxing */
+ public static void setInArray(Object target, int index, Object value, Class<?> type) {
+ if (PRIMITIVE_TO_BOXED.containsKey(type)) {
+ if (type.equals(Integer.TYPE))
+ Array.setInt(target, index, (Integer)value);
+ else if (type.equals(Long.TYPE))
+ Array.setLong(target, index, (Long)value);
+ else if (type.equals(Double.TYPE))
+ Array.setDouble(target, index, (Double)value);
+ else if (type.equals(Float.TYPE))
+ Array.setFloat(target, index, (Float)value);
+ else if (type.equals(Boolean.TYPE))
+ Array.setBoolean(target, index, (Boolean)value);
+ else if (type.equals(Character.TYPE))
+ Array.setChar(target, index, (Character)value);
+ else if (type.equals(Byte.TYPE))
+ Array.setByte(target, index, (Byte)value);
+ else if (type.equals(Short.TYPE))
+ Array.setShort(target, index, (Short)value);
+
+ else if (type.equals(Void.TYPE))
+ Array.set(target, index, (Void)value);
+
+ else
+ // should not happen!
+ throw new IllegalStateException("Unsupported primitive: "+type);
+
+ return;
+ }
+
+ Array.set(target, index, value);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Enums.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Enums.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Enums.java
new file mode 100644
index 0000000..d3bfd83
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Enums.java
@@ -0,0 +1,170 @@
+/*
+ * 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.util.Arrays;
+import java.util.Set;
+
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.text.StringFunctions;
+import org.apache.brooklyn.util.text.Strings;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
+public class Enums {
+
+ /** returns a function which given an enum, returns its <code>name()</code> function
+ * @deprecated since 0.7.0 use {@link #nameFunction()} to avoid inner class */
+ @Deprecated
+ public static Function<Enum<?>,String> enumValueNameFunction() {
+ return new Function<Enum<?>,String>() {
+ @Override
+ public String apply(Enum<?> input) {
+ return input.name();
+ }
+ };
+ }
+
+ private static final class EnumToNameFunction implements Function<Enum<?>, String> {
+ @Override
+ public String apply(Enum<?> input) {
+ return input.name();
+ }
+ }
+
+ /** returns a function which given an enum, returns its <code>name()</code> function */
+ public static Function<Enum<?>,String> nameFunction() {
+ return new EnumToNameFunction();
+ }
+
+ private static final class EnumFromStringFunction<T extends Enum<?>> implements Function<String,T> {
+ private final Class<T> type;
+ public EnumFromStringFunction(Class<T> type) { this.type = type; }
+ @Override
+ public T apply(String input) {
+ return valueOfIgnoreCase(type, input).orNull();
+ }
+ }
+
+ /** returns a function which given a string, produces an enum of the given type or null */
+ public static <T extends Enum<?>> Function<String,T> fromStringFunction(Class<T> type) {
+ return new EnumFromStringFunction<T>(type);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Enum<?>> T[] values(Class<T> type) {
+ try {
+ return (T[]) type.getMethod("values").invoke(null);
+ } catch (Exception e) {
+ throw Exceptions.propagate(e);
+ }
+ }
+
+ /** as {@link #checkAllEnumeratedIgnoreCase(String, Enum[], String...)} using the same default strategy
+ * that {@link #valueOfIgnoreCase(Class, String)} applies */
+ public static void checkAllEnumeratedIgnoreCase(Class<? extends Enum<?>> type, String ...explicitValues) {
+ checkAllEnumeratedIgnoreCase(JavaClassNames.simpleClassName(type), values(type), explicitValues);
+ }
+ /** checks that all accepted enum values are represented by the given set of explicit values */
+ public static void checkAllEnumeratedIgnoreCase(String contextMessage, Enum<?>[] enumValues, String ...explicitValues) {
+ MutableSet<String> explicitValuesSet = MutableSet.copyOf(Iterables.transform(Arrays.asList(explicitValues), StringFunctions.toLowerCase()));
+
+ Set<Enum<?>> missingEnums = MutableSet.of();
+ for (Enum<?> e: enumValues) {
+ if (explicitValuesSet.remove(e.name().toLowerCase())) continue;
+ if (explicitValuesSet.remove(e.toString().toLowerCase())) continue;
+
+ if (explicitValuesSet.remove(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, e.name()).toLowerCase())) continue;
+ if (explicitValuesSet.remove(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, e.toString()).toLowerCase())) continue;
+
+ if (explicitValuesSet.remove(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, e.toString()).toLowerCase())) continue;
+ if (explicitValuesSet.remove(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, e.name()).toLowerCase())) continue;
+
+ missingEnums.add(e);
+ }
+
+ if (!missingEnums.isEmpty() || !explicitValuesSet.isEmpty()) {
+ throw new IllegalStateException("Not all options for "+contextMessage+" are enumerated; "
+ + "leftover enums = "+missingEnums+"; "
+ + "leftover values = "+explicitValuesSet);
+ }
+ }
+
+ /** as {@link #valueOfIgnoreCase(String, Enum[], String)} for all values of the given enum and using the enum type as the message */
+ public static <T extends Enum<?>> Maybe<T> valueOfIgnoreCase(Class<T> type, String givenValue) {
+ return valueOfIgnoreCase(JavaClassNames.simpleClassName(type), values(type), givenValue);
+ }
+
+ /** attempts to match the givenValue against the given enum values, first looking for exact matches (against name and toString),
+ * then matching ignoring case,
+ * then matching with {@link CaseFormat#UPPER_UNDERSCORE} converted to {@link CaseFormat#LOWER_CAMEL},
+ * then matching with {@link CaseFormat#LOWER_CAMEL} converted to {@link CaseFormat#UPPER_UNDERSCORE}
+ * (including case insensitive matches for the final two)
+ **/
+ public static <T extends Enum<?>> Maybe<T> valueOfIgnoreCase(String contextMessage, T[] enumValues, String givenValue) {
+ if (givenValue==null)
+ return Maybe.absent(new IllegalStateException("Value for "+contextMessage+" must not be null"));
+ if (Strings.isBlank(givenValue))
+ return Maybe.absent(new IllegalStateException("Value for "+contextMessage+" must not be blank"));
+
+ for (T v: enumValues)
+ if (v.name().equals(givenValue))
+ return Maybe.of(v);
+ for (T v: enumValues)
+ if (v.toString().equals(givenValue))
+ return Maybe.of(v);
+ for (T v: enumValues)
+ if (v.name().equalsIgnoreCase(givenValue)) return
+ Maybe.of(v);
+ for (T v: enumValues)
+ if (v.toString().equalsIgnoreCase(givenValue))
+ return Maybe.of(v);
+ for (T v: enumValues)
+ if (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, v.name()).equals(givenValue))
+ return Maybe.of(v);
+ for (T v: enumValues)
+ if (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, v.toString()).equals(givenValue))
+ return Maybe.of(v);
+ for (T v: enumValues)
+ if (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, v.name()).equalsIgnoreCase(givenValue))
+ return Maybe.of(v);
+ for (T v: enumValues)
+ if (CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, v.toString()).equalsIgnoreCase(givenValue))
+ return Maybe.of(v);
+ for (T v: enumValues)
+ if (CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, v.toString()).equalsIgnoreCase(givenValue))
+ return Maybe.of(v);
+ for (T v: enumValues)
+ if (CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, v.name()).equals(givenValue))
+ return Maybe.of(v);
+ for (T v: enumValues)
+ if (CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, v.toString()).equals(givenValue))
+ return Maybe.of(v);
+ for (T v: enumValues)
+ if (CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, v.name()).equalsIgnoreCase(givenValue))
+ return Maybe.of(v);
+
+ return Maybe.absent(new IllegalStateException("Invalid value "+givenValue+" for "+contextMessage));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Equals.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Equals.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Equals.java
new file mode 100644
index 0000000..cf17197
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Equals.java
@@ -0,0 +1,93 @@
+/*
+ * 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.lang.reflect.Field;
+
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+
+
+public class Equals {
+
+ private static final Logger log = LoggerFactory.getLogger(Equals.class);
+
+ /** Tests whether the objects given are either all null or all equal to the first argument */
+ public static boolean objects(Object o1, Object o2, Object... oo) {
+ if (!Objects.equal(o1, o2)) return false;
+ for (Object o: oo)
+ if (!Objects.equal(o1, o)) return false;
+ return true;
+ }
+
+ /** Tests whether the two objects given are either all null or all approximately equal
+ * (tolerance of 0.001 for floating point, but subject to change) */
+ // relatively high tolerance mainly due to enrichers such as Tomcat windowed average, in hot standby;
+ // could make smaller
+ @Beta
+ public static boolean approximately(Object o1, Object o2) {
+ if (o1 instanceof Number) {
+ if (o2 instanceof Number) {
+ return Math.abs( ((Number)o2).doubleValue()-((Number)o1).doubleValue() ) < 0.001;
+ }
+ }
+ return Objects.equal(o1, o2);
+ }
+
+ /** As {@link #approximately(Object, Object)} but testing all the arguments given. */
+ @Beta
+ public static boolean approximately(Object o1, Object o2, Object o3, Object... oo) {
+ if (!approximately(o1, o2)) return false;
+ if (!approximately(o1, o3)) return false;
+ for (Object o: oo)
+ if (!approximately(o1, o)) return false;
+ return true;
+ }
+
+ /** Useful for debugging EqualsBuilder.reflectionEquals */
+ public static void dumpReflectiveEquals(Object o1, Object o2) {
+ log.info("Comparing: "+o1+" "+o2);
+ Class<?> clazz = o1.getClass();
+ while (!(clazz.equals(Object.class))) {
+ log.info(" fields in: "+clazz);
+ for (Field f: clazz.getDeclaredFields()) {
+ f.setAccessible(true);
+ try {
+ log.info( " "+(Objects.equal(f.get(o1), f.get(o2)) ? "==" : "!=" ) +
+ " "+ f.getName()+ " "+ f.get(o1) +" "+ f.get(o2) +
+ " ("+ classOf(f.get(o1)) +" "+ classOf(f.get(o2)+")") );
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ log.info( " <error> "+e);
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+ }
+
+ private static String classOf(Object o) {
+ if (o==null) return null;
+ return o.getClass().toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/JavaClassNames.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/JavaClassNames.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/JavaClassNames.java
new file mode 100644
index 0000000..fb8e4be
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/JavaClassNames.java
@@ -0,0 +1,162 @@
+/*
+ * 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.net.Urls;
+import org.apache.brooklyn.util.text.Strings;
+
+import com.google.common.base.Preconditions;
+import com.google.common.reflect.TypeToken;
+
+public class JavaClassNames {
+
+ private static final StackTraceSimplifier STACK_TRACE_SIMPLIFIER_EXCLUDING_UTIL_JAVALANG =
+ StackTraceSimplifier.newInstance(StackTraceSimplifier.class.getPackage().getName()+".");
+
+ /** returns the Class of anything which isn't a class; if input is class it is pass-through */
+ public static Class<?> type(Object x) {
+ if (x==null) return null;
+ if (x instanceof Class) return (Class<?>)x;
+ if (x instanceof TypeToken) return ((TypeToken<?>)x).getRawType();
+ return x.getClass();
+ }
+
+ /** like type, but removes any array modifiers */
+ public static Class<?> componentType(Object x) {
+ Class<?> c = type(x);
+ if (c==null) return null;
+ while (c.isArray()) {
+ c = c.getComponentType();
+ }
+ return c;
+ }
+
+ /** returns a simplified name of the class, just the simple name if it seems useful, else the full name */
+ public static String simpleClassName(Class<?> t) {
+ int arrayCount = 0;
+ while (t.isArray()) {
+ arrayCount++;
+ t = t.getComponentType();
+ }
+ Class<?> ct = componentType(t);
+
+ String result = ct.getSimpleName();
+ if (Strings.isBlank(result) || result.length()<=4) {
+ if (ct.isPrimitive()) {
+ // TODO unbox
+ } else {
+ result = ct.getName();
+ }
+ }
+ return result+Strings.repeat("[]", arrayCount);
+ }
+
+ /** as {@link #simpleClassName(Class)} but taking the type of the object if it is not already a class
+ * or a type-token; callers should usually do the getClass themselves, unless they aren't sure whether
+ * it is already a Class-type object */
+ public static String simpleClassName(Object x) {
+ if (x==null) return null;
+ return simpleClassName(type(x));
+ }
+
+ /** as {@link #simpleClassName(Class)} but taking a string rep'n of the class name,
+ * and doing best effort to simplify it (without instantiating) */
+ public static String simplifyClassName(String className) {
+ if (className==null) return null;
+ int lastDot = className.lastIndexOf('.');
+ if (lastDot < className.length()-5)
+ return className.substring(lastDot+1);
+ return className;
+ }
+
+ /** as {@link #simpleClassName(Object)} but making the result clean for use on filesystems and as java identifiers */
+ public static String cleanSimpleClassName(Object x) {
+ return Strings.makeValidFilename(simpleClassName(x));
+ }
+
+ /** as {@link #simpleClassName(Object)} but making the result clean for use on filesystems and as java identifiers */
+ public static String cleanSimpleClassName(Class<?> x) {
+ return Strings.makeValidFilename(simpleClassName(x));
+ }
+
+ public static String packageName(Object x) {
+ return componentType(x).getPackage().getName();
+ }
+
+ /** returns e.g. "/com/acme/" for an object in package com.acme */
+ public static String packagePath(Object x) {
+ return Urls.mergePaths("/", componentType(x).getPackage().getName().replace('.', '/'), "/");
+ }
+
+ /** returns path relative to the package of x, unless path is absolute.
+ * useful to mimic Class.getResource(path) behaviour, cf Class.resolveName where the first argument below is the class. */
+ public static String resolveName(Object context, String path) {
+ Preconditions.checkNotNull(path, "path must not be null");
+ if (path.startsWith("/") || Urls.isUrlWithProtocol(path)) return path;
+ Preconditions.checkNotNull(context, "context must not be null when path is relative");
+ return packagePath(context)+path;
+ }
+
+ /** returns a "classpath:" URL given a context object and a file to be found in that directory or a sub-directory
+ * (ignoring the context object if the given path is absolute, i.e. starting with "/" or "protocol:")
+ * e.g. "classpath://com/acme/foo.txt" given a context object com.acme.SomeClass and "foo.txt" */
+ public static String resolveClasspathUrl(Object context, String path) {
+ if (Urls.isUrlWithProtocol(path)) return path;
+ // additional / comes from resolve name
+ return "classpath:/"+resolveName(context, path);
+ }
+
+ /** returns a cleaned stack trace; caller is usually at the top */
+ public static StackTraceElement[] currentStackTraceCleaned() {
+ return STACK_TRACE_SIMPLIFIER_EXCLUDING_UTIL_JAVALANG.clean(
+ Thread.currentThread().getStackTrace());
+ }
+
+ /** returns top of cleaned stack trace; usually the caller's location */
+ public static StackTraceElement currentStackElement() {
+ return STACK_TRACE_SIMPLIFIER_EXCLUDING_UTIL_JAVALANG.nthUseful(0,
+ Thread.currentThread().getStackTrace());
+ }
+
+ /** returns element in cleaned stack trace; usually the caller's location is at the top,
+ * and caller of that is up one, etc */
+ public static StackTraceElement callerStackElement(int depth) {
+ return STACK_TRACE_SIMPLIFIER_EXCLUDING_UTIL_JAVALANG.nthUseful(depth,
+ Thread.currentThread().getStackTrace());
+ }
+
+ /** returns nice class name and method for the given element */
+ public static String niceClassAndMethod(StackTraceElement st) {
+ return simplifyClassName(st.getClassName())+"."+st.getMethodName();
+ }
+
+ /** returns nice class name and method for the caller, going up the stack (filtered to remove invocation etc),
+ * with 0 typically being the context where this method is called, 1 being its caller, etc */
+ public static String callerNiceClassAndMethod(int depth) {
+ return niceClassAndMethod(callerStackElement(depth));
+ }
+
+ /** convenience for {@link #callerNiceClassAndMethod(int)} with depth 0
+ * <p>
+ * useful for tests and other debug-facing log messages! */
+ public static String niceClassAndMethod() {
+ return callerNiceClassAndMethod(0);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/LoadedClassLoader.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/LoadedClassLoader.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/LoadedClassLoader.java
new file mode 100644
index 0000000..dba726f
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/LoadedClassLoader.java
@@ -0,0 +1,44 @@
+/*
+ * 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.util.LinkedHashMap;
+import java.util.Map;
+
+/** a classloader which allows you to register classes and resources which this loader will return when needed,
+ * (essentially a registry rather than a classloader, but useful if you need to make new classes available in
+ * an old context) */
+public class LoadedClassLoader extends ClassLoader {
+
+ Map<String, Class<?>> loadedClasses = new LinkedHashMap<String, Class<?>>();
+
+ protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ Class<?> result = loadedClasses.get(name);
+ if (result==null) throw new ClassNotFoundException(""+name+" not known here");
+ if (resolve) resolveClass(result);
+ return result;
+ }
+
+ public void addClass(Class<?> clazz) {
+ loadedClasses.put(clazz.getName(), clazz);
+ }
+
+ // TODO could also add resources
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/util/javalang/MemoryUsageTracker.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/MemoryUsageTracker.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/MemoryUsageTracker.java
new file mode 100644
index 0000000..e3897cc
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/MemoryUsageTracker.java
@@ -0,0 +1,72 @@
+/*
+ * 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.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalListener;
+import com.google.common.cache.RemovalNotification;
+
+/**
+ * Tracks the amount of memory consumed by the given objects in use.
+ * <p>
+ * {@link WeakReference}s are used internally, so that shortly after a {@link #track(Object, long)}ed object is GC'd,
+ * the {@link #getBytesUsed()} value decrements appropriately.
+ */
+public class MemoryUsageTracker {
+
+ /**
+ * Shared instance for use for tracking memory used by {@link SoftReference}.
+ * <p>
+ * Callers should only use this field to {@link #track(Object, long)} objects which have (or will soon have)
+ * given up their strong references, so that only soft or weak references remain.
+ * Provided size estimates are accurate, {@link #getBytesUsed()} will report
+ * the amount of used memory which is reclaimable by collecting soft references.
+ * <p>
+ * This is particularly handy for tracking {@link SoftReference}s, because otherwise you can quickly get to a state
+ * where {@link Runtime#freeMemory()} looks very low.
+ **/
+ public static final MemoryUsageTracker SOFT_REFERENCES = new MemoryUsageTracker();
+
+ AtomicLong bytesUsed = new AtomicLong(0);
+
+ Cache<Object, Long> memoryTrackedReferences = CacheBuilder.newBuilder()
+ .weakKeys()
+ .removalListener(new RemovalListener<Object,Long>() {
+ @Override
+ public void onRemoval(RemovalNotification<Object, Long> notification) {
+ bytesUsed.addAndGet(-notification.getValue());
+ }
+ }).build();
+
+ public void track(Object instance, long bytesUsedByInstance) {
+ bytesUsed.addAndGet(bytesUsedByInstance);
+ memoryTrackedReferences.put(instance, bytesUsedByInstance);
+ }
+
+ public long getBytesUsed() {
+ memoryTrackedReferences.cleanUp();
+ return bytesUsed.get();
+ }
+
+}