package com.orbitz.monitoring.lib.decomposer; import static org.junit.Assert.*; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import com.orbitz.monitoring.api.monitor.AttributeHolder; import com.orbitz.monitoring.api.monitor.CompositeAttributeHolder; import com.orbitz.monitoring.api.monitor.EventMonitor; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.beanutils.DynaBean; import org.apache.commons.beanutils.MethodUtils; import org.junit.Before; import org.junit.Test; /** * Tests {@link AttributeDecomposer} * @author Doug Barth */ public class AttributeDecomposerTest { private AttributeDecomposer _decomposer; /** * Prepares for each test */ @Before public void setUp() { _decomposer = new AttributeDecomposer(); } /** * @see AttributeDecomposer#decompose(Object) */ @Test public void testDecomposeOC() { OC<OC<OC<String>>> deepObject = new OC<OC<OC<String>>>( new OC<OC<String>>(new OC<String>("foo"))); assertNotNull(deepObject.writeOnly); assertNotNull(deepObject.privateInnerClass); EventMonitor monitor = new EventMonitor("test"); monitor.set("deepObject", _decomposer.decompose(deepObject)); final DynaBean actual = (DynaBean)monitor.get("deepObject"); final DynaBean subActual = (DynaBean)actual.get("object"); final DynaBean subSubActual = (DynaBean)subActual.get("object"); String value = ((String)subSubActual.get("object")); assertEquals("foo", value); assertNull(actual.get("writeOnly")); assertEquals(null, ((DynaBean)actual.get("privateInnerClass")).get("value")); } /** * @see AttributeDecomposer#decompose(Object) */ @Test public void testDecomposeClass() { EventMonitor monitor = new EventMonitor("test"); monitor.set("klass", _decomposer.decompose(Object.class)); assertEquals("java.lang.Object", monitor.get("klass")); } /** * @see AttributeDecomposer#decompose(Object) */ @Test public void testDecomposeException() { Exception foo = new Exception("bar"); EventMonitor monitor = new EventMonitor("test"); monitor.set("foo", _decomposer.decompose(foo)); final DynaBean actualException = (DynaBean)monitor.get("foo"); assertEquals("bar", actualException.get("message")); } /** * @see AttributeDecomposer#decompose(Object) */ @Test public void testDecomposeArray() { String[][] stringArray = new String[][] {new String[] {"foo"}, new String[] {"bar"}}; Serializable decomposed = _decomposer.decompose(stringArray); EventMonitor monitor = new EventMonitor("test"); monitor.set("stringArray", decomposed); final List<List<String>> actual = monitor.getAsList("stringArray"); assertEquals("foo", actual.get(0).get(0)); assertEquals("bar", actual.get(1).get(0)); } /** * @see AttributeDecomposer#decompose(Object) * @throws Exception in case of failure */ @SuppressWarnings("unchecked") @Test public void testDecomposeCircularGraph() throws Exception { Node a = new Node("a"); Node b = new Node("b"); Node c = new Node("c"); b.addNext(c); c.addNext(b); a.addNext(b); EventMonitor monitor = new EventMonitor("test"); monitor.set("a", _decomposer.decompose(a)); final DynaBean actualA = (DynaBean)monitor.get("a"); final DynaBean actualB = ((Set<DynaBean>)actualA.get("next")).iterator().next(); final DynaBean actualC = ((Set<DynaBean>)actualB.get("next")).iterator().next(); assertEquals("a", actualA.get("value")); assertEquals("b", actualB.get("value")); assertEquals("c", actualC.get("value")); } /** * @see AttributeDecomposer#decompose(Object) */ @Test public void testDecomposeListOfList() { List<List<String>> topList = new ArrayList<List<String>>(); List<String> innerList = new ArrayList<String>(); innerList.add("bar"); topList.add(innerList); Serializable decomposed = _decomposer.decompose(topList); EventMonitor monitor = new EventMonitor("test"); monitor.set("topList", decomposed); final List<List<String>> actual = monitor.getAsList("topList"); assertEquals("bar", actual.get(0).get(0)); } /** * @see AttributeDecomposer#decompose(Object) */ @Test public void testDecomposeMapsOfMaps() { Map<String, Map<String, String>> topMap = new HashMap<String, Map<String, String>>(); Map<String, String> innerMap = new HashMap<String, String>(); innerMap.put("foo", "bar"); topMap.put("innerMap", innerMap); AttributeHolder holder = new AttributeHolder(topMap); Serializable decomposed = _decomposer.decompose(holder); EventMonitor monitor = new EventMonitor("test"); monitor.set("holder", decomposed); final AttributeHolder actual = (AttributeHolder)monitor.get("holder"); @SuppressWarnings("unchecked") Map<String, Map<String, String>> actualTopMap = (Map<String, Map<String, String>>)actual .getValue(); assertEquals(topMap, actualTopMap); } /** * @see AttributeDecomposer#decompose(Object) */ @SuppressWarnings("unchecked") @Test public void testDecomposePrimitives() { List<Node> list = new ArrayList<Node>(); list.add(new Node("foo")); CompositeAttributeHolder holder = new CompositeAttributeHolder(list); AttributeHolder decomposedHolder = (AttributeHolder)_decomposer.decompose(holder); assertEquals(list.get(0).value, ((List<DynaBean>)decomposedHolder.getValue()).get(0).get("value")); } /** * @see AttributeDecomposer#decompose(Object) */ @Test public void testDecomposePrimitives2() { AttributeHolder holder = new AttributeHolder("someString"); AttributeHolder decomposedHolder = (AttributeHolder)_decomposer.decompose(holder); assertEquals(holder.getValue(), decomposedHolder.getValue()); } /** * @see AttributeDecomposer#decompose(Object) */ @Test public void testDecomposeSet() { Set<String> set = Sets.newHashSet("foo"); Serializable decomposed = _decomposer.decompose(set); EventMonitor monitor = new EventMonitor("test"); monitor.set("set", decomposed); assertTrue(Sets.difference(set, monitor.getAsSet("set")).size() == 0); } /** * @see AttributeDecomposer#decompose(Object) */ @SuppressWarnings("unchecked") @Test public void testEqualsVsIdentity() { Node a1 = new Node("a"); Node a2 = new Node("a"); a1.addNext(a2); assertEquals(a1, a2); assertNotSame(a1, a2); EventMonitor monitor = new EventMonitor("test"); monitor.set("node", _decomposer.decompose(a1)); final DynaBean node = (DynaBean)monitor.get("node"); assertEquals("a", node.get("value")); assertEquals("a", ((Set<DynaBean>)node.get("next")).iterator().next().get("value")); } /** * @see AttributeDecomposer#decompose(Object) */ @Test public void testStringBufferSupport() { StringBuffer buffer = new StringBuffer("abc"); String decomposed = (String)_decomposer.decompose(buffer); assertEquals(buffer.toString(), decomposed); } /** * This class is public so that {@link MethodUtils}, which is used by ReflectiveDecomposer, can * see it */ public static class Node implements Serializable { private static final long serialVersionUID = 1L; private Set<Node> next = new HashSet<Node>(); private final String value; /** * Creates a node with the specified value * @param value the value of the node */ public Node(final String value) { this.value = value; } /** * Adds a node to the collection of next nodes for this node * @param next the node to add */ public void addNext(final Node next) { this.next.add(next); } /** * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(final Object o) { if (o instanceof Node) { Node that = (Node)o; return Objects.equal(this.value, that.value); } return false; } /** * Gets the collection of next nodes * @return the next nodes */ public Set<Node> getNext() { return this.next; } /** * Gets the value of this node * @return the value */ public String getValue() { return this.value; } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return Objects.hashCode(this.value); } /** * Sets the collection of next nodes * @param set the next nodes to set */ public void setNext(final Set<Node> set) { Preconditions.checkNotNull(set); this.next = set; } } /** * An object container<br> * This class is public so that {@link MethodUtils}, which is used by ReflectiveDecomposer, can * see it * @param <T> the type of {@link OC#getObject()} */ public static class OC<T> { private final InnerClass privateInnerClass; private final T object; private final OC<T> self; private Object writeOnly; /** * Creates a new object container * @param object */ public OC(final T object) { this.object = object; this.self = this; this.privateInnerClass = new InnerClass(); this.writeOnly = new Object(); } /** * Gets a value with a class that is private * @return the private class value */ public InnerClass getPrivateInnerClass() { return this.privateInnerClass; } /** * Gets the value of this container * @return the value */ public T getObject() { return this.object; } /** * Gets a reference to this container, to ensure that cyclic references are ignored * @return a reference to this */ public OC<T> getSelf() { return this.self; } /** * Sets a value that cannot be gotten * @param o the value to set */ public void setWriteOnly(final Object o) { this.writeOnly = o; } private static class InnerClass { private String value; public InnerClass() { this.value = "hello"; } @SuppressWarnings("unused") public String getValue() { return this.value; } @SuppressWarnings("unused") public void setValue(final String value) { this.value = value; } } } }