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(); + } + +}
