content: /*
   * 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.activemq.openwire;
  
  import java.beans.BeanInfo;
  import java.beans.Introspector;
  import java.beans.PropertyDescriptor;
  import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.DataInputStream;
  import java.io.DataOutputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.InputStream;
  import java.lang.reflect.Array;
  import java.lang.reflect.Method;
  import java.net.URI;
  import java.net.URL;
  import java.util.Enumeration;
  import java.util.HashSet;
  import java.util.Set;
  
  import org.apache.activemq.command.ActiveMQDestination;
  import org.apache.activemq.command.ActiveMQQueue;
  import org.apache.activemq.command.ActiveMQTextMessage;
  import org.apache.activemq.command.BrokerId;
  import org.apache.activemq.command.BrokerInfo;
  import org.apache.activemq.command.ConnectionId;
  import org.apache.activemq.command.ConsumerId;
  import org.apache.activemq.command.DataStructure;
  import org.apache.activemq.command.LocalTransactionId;
  import org.apache.activemq.command.Message;
  import org.apache.activemq.command.MessageAck;
  import org.apache.activemq.command.MessageId;
  import org.apache.activemq.command.NetworkBridgeFilter;
  import org.apache.activemq.command.ProducerId;
  import org.apache.activemq.command.SessionId;
  import org.apache.activemq.command.TransactionId;
  import org.apache.activemq.filter.BooleanExpression;
  import org.apache.activemq.openwire.v1.ActiveMQTextMessageTest;
  import org.apache.activemq.openwire.v1.BrokerInfoTest;
  import org.apache.activemq.openwire.v1.MessageAckTest;
  import org.apache.activemq.test.TestSupport;
  import org.apache.activemq.util.ByteSequence;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public abstract class DataFileGeneratorTestSupport extends TestSupport {
  
     protected static final Object[] EMPTY_ARGUMENTS = {};
     private static final Logger LOG = LoggerFactory.getLogger(DataFileGeneratorTestSupport.class);
  
     private static final Throwable SINGLETON_EXCEPTION = new Exception("shared exception");
     private static final File MODULE_BASE_DIR;
     private static final File CONTROL_DIR;
  
     static {
        File basedir = null;
        try {
           URL resource = DataFileGeneratorTestSupport.class.getResource("DataFileGeneratorTestSupport.class");
           URI baseURI = new URI(resource.toString()).resolve("../../../../..");
           basedir = new File(baseURI).getCanonicalFile();
        } catch (Exception e) {
           throw new RuntimeException(e);
        }
        MODULE_BASE_DIR = basedir;
        CONTROL_DIR = new File(MODULE_BASE_DIR, "src/test/resources/openwire-control");
     }
  
     private int counter;
     private OpenWireFormat openWireformat;
  
     public void xtestControlFileIsValid() throws Exception {
        generateControlFile();
        assertControlFileIsEqual();
     }
  
     public void testGenerateAndReParsingIsTheSame() throws Exception {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        DataOutputStream ds = new DataOutputStream(buffer);
        Object expected = createObject();
        LOG.info("Created: " + expected);
        openWireformat.marshal(expected, ds);
        ds.close();
  
        // now lets try parse it back again
        ByteArrayInputStream in = new ByteArrayInputStream(buffer.toByteArray());
        DataInputStream dis = new DataInputStream(in);
        Object actual = openWireformat.unmarshal(dis);
        assertBeansEqual("", new HashSet<>(), expected, actual);
  
        LOG.info("Parsed: " + actual);
     }
  
     protected void assertBeansEqual(String message,
                                     Set<Object> comparedObjects,
                                     Object expected,
                                     Object actual) throws Exception {
        assertNotNull("Actual object should be equal to: " + expected + " but was null", actual);
        if (comparedObjects.contains(expected)) {
           return;
        }
        comparedObjects.add(expected);
        Class<? extends Object> type = expected.getClass();
        assertEquals("Should be of same type", type, actual.getClass());
        BeanInfo beanInfo = Introspector.getBeanInfo(type);
        PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
        for (int i = 0; i < descriptors.length; i++) {
           PropertyDescriptor descriptor = descriptors[i];
           Method method = descriptor.getReadMethod();
           if (method != null) {
              String name = descriptor.getName();
              Object expectedValue = null;
              Object actualValue = null;
              try {
                 expectedValue = method.invoke(expected, EMPTY_ARGUMENTS);
                 actualValue = method.invoke(actual, EMPTY_ARGUMENTS);
              } catch (Exception e) {
                 LOG.info("Failed to access property: " + name);
              }
              assertPropertyValuesEqual(message + name, comparedObjects, expectedValue, actualValue);
           }
        }
     }
  
     protected void assertPropertyValuesEqual(String name,
                                              Set<Object> comparedObjects,
                                              Object expectedValue,
                                              Object actualValue) throws Exception {
        String message = "Property " + name + " not equal";
        if (expectedValue == null) {
           assertNull("Property " + name + " should be null", actualValue);
        } else if (expectedValue instanceof Object[]) {
           assertArrayEqual(message, comparedObjects, (Object[]) expectedValue, (Object[]) actualValue);
        } else if (expectedValue.getClass().isArray()) {
           assertPrimitiveArrayEqual(message, comparedObjects, expectedValue, actualValue);
        } else {
           if (expectedValue instanceof Exception) {
              assertExceptionsEqual(message, (Exception) expectedValue, actualValue);
           } else if (expectedValue instanceof ByteSequence) {
              assertByteSequencesEqual(message, (ByteSequence) expectedValue, actualValue);
           } else if (expectedValue instanceof DataStructure) {
              assertBeansEqual(message + name, comparedObjects, expectedValue, actualValue);
           } else if (expectedValue instanceof Enumeration) {
              assertEnumerationEqual(message + name, comparedObjects, (Enumeration<?>) expectedValue, (Enumeration<?>) actualValue);
           } else {
              assertEquals(message, expectedValue, actualValue);
           }
  
        }
     }
  
     protected void assertArrayEqual(String message,
                                     Set<Object> comparedObjects,
                                     Object[] expected,
                                     Object[] actual) throws Exception {
        assertEquals(message + ". Array length", expected.length, actual.length);
        for (int i = 0; i < expected.length; i++) {
           assertPropertyValuesEqual(message + ". element: " + i, comparedObjects, expected[i], actual[i]);
        }
     }
  
     protected void assertEnumerationEqual(String message,
                                           Set<Object> comparedObjects,
                                           Enumeration<?> expected,
                                           Enumeration<?> actual) throws Exception {
        while (expected.hasMoreElements()) {
           Object expectedElem = expected.nextElement();
           Object actualElem = actual.nextElement();
           assertPropertyValuesEqual(message + ". element: " + expectedElem, comparedObjects, expectedElem, actualElem);
        }
     }
  
     protected void assertPrimitiveArrayEqual(String message,
                                              Set<Object> comparedObjects,
                                              Object expected,
                                              Object actual) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, Exception {
        int length = Array.getLength(expected);
        assertEquals(message + ". Array length", length, Array.getLength(actual));
        for (int i = 0; i < length; i++) {
           assertPropertyValuesEqual(message + ". element: " + i, comparedObjects, Array.get(expected, i), Array.get(actual, i));
        }
     }
  
     protected void assertByteSequencesEqual(String message, ByteSequence expected, Object actualValue) {
        assertTrue(message + ". Actual value should be a ByteSequence but was: " + actualValue, actualValue instanceof ByteSequence);
        ByteSequence actual = (ByteSequence) actualValue;
        int length = expected.getLength();
        assertEquals(message + ". Length", length, actual.getLength());
        int offset = expected.getOffset();
        assertEquals(message + ". Offset", offset, actual.getOffset());
        byte[] data = expected.getData();
        byte[] actualData = actual.getData();
        for (int i = 0; i < length; i++) {
           assertEquals(message + ". Offset " + i, data[offset + i], actualData[offset + i]);
        }
     }
  
     protected void assertExceptionsEqual(String message, Exception expected, Object actualValue) {
        assertTrue(message + ". Actual value should be an exception but was: " + actualValue, actualValue instanceof Exception);
        Exception actual = (Exception) actualValue;
        assertEquals(message, expected.getMessage(), actual.getMessage());
     }
  
     @Override
     protected void setUp() throws Exception {
        super.setUp();
        openWireformat = createOpenWireFormat();
     }
  
     public void generateControlFile() throws Exception {
        CONTROL_DIR.mkdirs();
        File dataFile = new File(CONTROL_DIR, getClass().getName() + ".bin");
  
        FileOutputStream os = new FileOutputStream(dataFile);
        DataOutputStream ds = new DataOutputStream(os);
        openWireformat.marshal(createObject(), ds);
        ds.close();
     }
  
     public InputStream generateInputStream() throws Exception {
  
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        DataOutputStream ds = new DataOutputStream(os);
        openWireformat.marshal(createObject(), ds);
        ds.close();
  
        return new ByteArrayInputStream(os.toByteArray());
     }
  
     public void assertControlFileIsEqual() throws Exception {
        File dataFile = new File(CONTROL_DIR, getClass().getName() + ".bin");
        FileInputStream is1 = new FileInputStream(dataFile);
        int pos = 0;
        try {
           InputStream is2 = generateInputStream();
           int a = is1.read();
           int b = is2.read();
           pos++;
           assertEquals("Data does not match control file: " + dataFile + " at byte position " + pos, a, b);
           while (a >= 0 && b >= 0) {
              a = is1.read();
              b = is2.read();
              pos++;
              assertEquals("Data does not match control file: " + dataFile + " at byte position " + pos, a, b);
           }
           is2.close();
        } finally {
           is1.close();
        }
     }
  
     protected abstract Object createObject() throws Exception;
  
     protected void populateObject(Object info) throws Exception {
        // empty method to allow derived classes to call super
        // to simplify generated code
     }
  
     protected OpenWireFormat createOpenWireFormat() {
        OpenWireFormat wf = new OpenWireFormat();
        wf.setCacheEnabled(true);
        wf.setStackTraceEnabled(false);
        wf.setVersion(OpenWireFormat.DEFAULT_WIRE_VERSION);
        return wf;
     }
  
     protected BrokerId createBrokerId(String text) {
        return new BrokerId(text);
     }
  
     protected TransactionId createTransactionId(String string) {
        return new LocalTransactionId(createConnectionId(string), ++counter);
     }
  
     protected ConnectionId createConnectionId(String string) {
        return new ConnectionId(string);
     }
  
     protected SessionId createSessionId(String string) {
        return new SessionId(createConnectionId(string), ++counter);
     }
  
     protected ProducerId createProducerId(String string) {
        return new ProducerId(createSessionId(string), ++counter);
     }
  
     protected ConsumerId createConsumerId(String string) {
        return new ConsumerId(createSessionId(string), ++counter);
     }
  
     protected MessageId createMessageId(String string) {
        return new MessageId(createProducerId(string), ++counter);
     }
  
     protected ActiveMQDestination createActiveMQDestination(String string) {
        return new ActiveMQQueue(string);
     }
  
     protected Message createMessage(String string) throws Exception {
        ActiveMQTextMessage message = (ActiveMQTextMessage) ActiveMQTextMessageTest.SINGLETON.createObject();
        message.setText(string);
        return message;
     }
  
     protected BrokerInfo createBrokerInfo(String string) throws Exception {
        return (BrokerInfo) BrokerInfoTest.SINGLETON.createObject();
     }
  
     protected MessageAck createMessageAck(String string) throws Exception {
        return (MessageAck) MessageAckTest.SINGLETON.createObject();
     }
  
     protected DataStructure createDataStructure(String string) throws Exception {
        return createBrokerInfo(string);
     }
  
     protected Throwable createThrowable(String string) {
        // we have issues with stack frames not being equal so share the same
        // exception each time
        return SINGLETON_EXCEPTION;
     }
  
     protected BooleanExpression createBooleanExpression(String string) {
        return new NetworkBridgeFilter(null, new BrokerId(string), 10, 10);
     }
  
  }
  