Repository: qpid-interop-test Updated Branches: refs/heads/master 87e8eae63 -> 36c7b3be8
http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/f4b87a1e/src/python/qpid-interop-test/types/simple_type_tests.py ---------------------------------------------------------------------- diff --git a/src/python/qpid-interop-test/types/simple_type_tests.py b/src/python/qpid-interop-test/types/simple_type_tests.py new file mode 100755 index 0000000..78551eb --- /dev/null +++ b/src/python/qpid-interop-test/types/simple_type_tests.py @@ -0,0 +1,478 @@ +#!/usr/bin/env python + +""" +Module to test AMQP primitive types across different APIs +""" + +# +# 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. +# + +import argparse +import unittest + +from itertools import product +from json import dumps, loads +from os import getenv, path +from subprocess import check_output, CalledProcessError +from sys import exit +from time import mktime, time +from uuid import UUID, uuid4 + +from proton import int32, symbol, timestamp, ulong +from test_type_map import TestTypeMap +import broker_properties + + +# TODO - propose a sensible default when installation details are worked out +QPID_INTEROP_TEST_HOME = getenv('QPID_INTEROP_TEST_HOME') + + +class AmqpPrimitiveTypes(TestTypeMap): + """ + Class which contains all the described AMQP primitive types and the test values to be used in testing. + """ + + TYPE_MAP = { + 'null': ['None'], + 'boolean': ['True', + 'False'], + 'ubyte': ['0x0', + '0x7f', + '0x80', + '0xff'], + 'ushort': ['0x0', + '0x7fff', + '0x8000', + '0xffff'], + 'uint': ['0x0', + '0x7fffffff', + '0x80000000', + '0xffffffff'], + 'ulong': ['0x0', + '0x1', + '0xff', + '0x100', + '0x7fffffffffffffff', + '0x8000000000000000', + '0xffffffffffffffff'], + 'byte': ['-0x80', + '-0x1', + '0x0', + '0x7f'], + 'short': ['-0x8000', + '-0x1', + '0x0', + '0x7fff'], + 'int': ['-0x80000000', + '-0x1', + '0x0', + '0x7fffffff'], + 'long': ['-0x8000000000000000', + '-0x81', + '-0x80', + '-0x1', + '0x0', + '0x7f', + '0x80', + '0x7fffffffffffffff'], + # float and double: Because of difficulty with rounding of floating point numbers, we use the binary + # representation instead which should be exact when comparing sent and received values. + 'float': ['0x00000000', # 0.0 + '0x80000000', # -0.0 + '0x40490fdb', # pi (3.14159265359) positive decimal + '0xc02df854', # -e (-2.71828182846) negative decimal + '0x00000001', # Smallest positive denormalized number + '0x80000001', # Smallest negative denormalized number + '0x007fffff', # Largest positive denormalized number + '0x807fffff', # Largest negative denormalized number + '0x00800000', # Smallest positive normalized number + '0x80800000', # Smallest negative normalized number + '0x7f7fffff', # Largest positive normalized number + '0xff7fffff', # Largest negative normalized number + #'0x7f800000', # +Infinity # PROTON-1149 - fails on RHEL7 + #'0xff800000', # -Infinity # PROTON-1149 - fails on RHEL7 + '0x7fc00000', # +NaN + '0xffc00000'], # -NaN + 'double': ['0x0000000000000000', # 0.0 + '0x8000000000000000', # -0.0 + '0x400921fb54442eea', # pi (3.14159265359) positive decimal + '0xc005bf0a8b145fcf', # -e (-2.71828182846) negative decimal + '0x0000000000000001', # Smallest positive denormalized number + '0x8000000000000001', # Smallest negative denormalized number + '0x000fffffffffffff', # Largest positive denormalized number + '0x800fffffffffffff', # Largest negative denormalized number + '0x0010000000000000', # Smallest positive normalized number + '0x8010000000000000', # Smallest negative normalized number + '0x7fefffffffffffff', # Largest positive normalized number + '0xffefffffffffffff', # Largest negative normalized number + '0x7ff0000000000000', # +Infinity + '0xfff0000000000000', # -Infinity + '0x7ff8000000000000', # +NaN + '0xfff8000000000000'], # -NaN + # decimal32, decimal64, decimal128: + # Until more formal support for decimal32, decimal64 and decimal128 are included in Python, we use + # a hex format for basic tests, and treat the data as a binary blob. + 'decimal32': ['0x00000000', + '0x40490fdb', + '0xc02df854', + '0xff7fffff'], + 'decimal64': ['0x0000000000000000', + '0x400921fb54442eea', + '0xc005bf0a8b145fcf', + '0xffefffffffffffff'], + 'decimal128': ['0x00000000000000000000000000000000', + '0xff0102030405060708090a0b0c0d0e0f'], + 'char': [u'a', + u'Z', + u'0x1', + u'0x7f', + u'0x16b5', # Rune 'G' + u'0x10ffff'], + # timestamp: Must be in milliseconds since the Unix epoch + 'timestamp': ['0x0', + '0x%x' % int(mktime((2000, 1, 1, 0, 0, 0, 5, 1, 0))*1000), + '0x%x' % int(time()*1000) + ], + 'uuid': [str(UUID(int=0x0)), + str(UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')), + str(uuid4())], + 'binary': [bytes(), + bytes(12345), + b'Hello, world!', + b'\\x01\\x02\\x03\\x04\\x05abcde\\x80\\x81\\xfe\\xff', + b'The quick brown fox jumped over the lazy dog 0123456789.' * 100 + ], + # strings must be unicode to comply with AMQP spec + 'string': [u'', + u'Hello, world!', + u'"Hello, world!"', + u"Charlie's peach", + u'The quick brown fox jumped over the lazy dog 0123456789.' * 100 + ], + 'symbol': ['', + 'myDomain.123', + 'domain.0123456789.' * 100], + 'list': [[], + ['ubyte:1', 'int:-2', 'float:3.14'], + ['string:a', 'string:b', 'string:c'], + ['ulong:12345', 'timestamp:%d' % (time()*1000), 'short:-2500', 'uuid:%s' % uuid4(), 'symbol:a.b.c', 'none:', 'decimal64:0x400921fb54442eea'], + [[], 'none', ['ubyte:1', 'ubyte:2', 'ubyte:3'], 'boolean:True', 'boolean:False', {'string:hello': 'long:1234', 'string:goodbye': 'boolean:True'}], + [[], [[], [[], [], []], []], []], + ['short:0', 'short:1', 'short:2', 'short:3', 'short:4', 'short:5', 'short:6', 'short:7', 'short:8', 'short:9'] * 100 + ], + 'map': [{}, + {'string:one': 'ubyte:1', + 'string:two': 'ushort:2'}, + {'none:': 'string:None', + 'string:None': 'none:', + 'string:One': 'long:-1234567890', + 'short:2': 'int:2', + 'boolean:True': 'string:True', + 'string:False': 'boolean:False', + #['string:AAA', 'ushort:5951']: 'string:list value', + #{'byte:-55': 'ubyte:200', + # 'boolean:True': 'string:Hello, world!'}: 'symbol:map.value', + #'string:list': [], + 'string:map': {'char:A': 'int:1', + 'char:B': 'int:2'}}, + ], + # TODO: Support all AMQP types in array (including keys) +# 'array': [[], +# [1, 2, 3], +# ['Hello', 'world'], +# [[1, 2, 3], +# ['a', 'b', 'c'], +# [2.3, 3.4, 4,5], +# [True, False, True, True]] +# ] + } + + BROKER_SKIP = {'null': {'ActiveMQ': 'Null type not sent in Proton Python binding: PROTON-1091', + 'qpid-cpp': 'Null type not sent in Proton Python binding: PROTON-1091',}, + 'decimal32': {'ActiveMQ': 'decimal32 and decimal64 are sent byte reversed: PROTON-1160', + 'qpid-cpp': 'decimal32 not supported on qpid-cpp broker: QPIDIT-5, QPID-6328',}, + 'decimal64': {'ActiveMQ': 'decimal32 and decimal64 are sent byte reversed: PROTON-1160', + 'qpid-cpp': 'decimal64 not supported on qpid-cpp broker: QPIDIT-6, QPID-6328',}, + 'decimal128': {'qpid-cpp': 'decimal128 not supported on qpid-cpp broker: QPIDIT-3, QPID-6328',}, + 'char': {'qpid-cpp': 'char not supported on qpid-cpp broker: QPIDIT-4, QPID-6328',}, + } +# BROKER_SKIP = {} + + +class AmqpTypeTestCase(unittest.TestCase): + """ + Abstract base class for AMQP Type test cases + """ + + def run_test(self, broker_addr, amqp_type, test_value_list, send_shim, receive_shim): + """ + Run this test by invoking the shim send method to send the test values, followed by the shim receive method + to receive the values. Finally, compare the sent values with the received values. + """ + if len(test_value_list) > 0: + # TODO: When Artemis can support it (in the next release), revert the queue name back to 'qpid-interop...' + # Currently, Artemis only supports auto-create queues for JMS, and the queue name must be prefixed by 'jms.queue.' + #queue_name = 'qpid-interop.simple_type_tests.%s.%s.%s' % (amqp_type, send_shim.NAME, receive_shim.NAME) + queue_name = 'jms.queue.qpid-interop.simple_type_tests.%s.%s.%s' % (amqp_type, send_shim.NAME, receive_shim.NAME) + send_error_text = send_shim.send(broker_addr, queue_name, amqp_type, dumps(test_value_list)) + if len(send_error_text) > 0: + self.fail('Send shim \'%s\':\n%s' % (send_shim.NAME, send_error_text)) + receive_text = receive_shim.receive(broker_addr, queue_name, amqp_type, len(test_value_list)) + if isinstance(receive_text, list): + self.assertEqual(receive_text, test_value_list, msg='\n sent:%s\nreceived:%s' % \ + (test_value_list, receive_text)) + else: + self.fail(receive_text) + else: + self.fail('Type %s has no test values' % amqp_type) + + +def create_testcase_class(broker_name, types, broker_addr, amqp_type, shim_product): + """ + Class factory function which creates new subclasses to AmqpTypeTestCase. + """ + + def __repr__(self): + """Print the class name""" + return self.__class__.__name__ + + def add_test_method(cls, send_shim, receive_shim): + """Function which creates a new test method in class cls""" + + @unittest.skipIf(types.skip_test(amqp_type, broker_name), + types.skip_test_message(amqp_type, broker_name)) + def inner_test_method(self): + self.run_test(self.broker_addr, self.amqp_type, self.test_value_list, send_shim, receive_shim) + + inner_test_method.__name__ = 'test_%s_%s->%s' % (amqp_type, send_shim.NAME, receive_shim.NAME) + setattr(cls, inner_test_method.__name__, inner_test_method) + + class_name = amqp_type.title() + 'TestCase' + class_dict = {'__name__': class_name, + '__repr__': __repr__, + '__doc__': 'Test case for AMQP 1.0 simple type \'%s\'' % amqp_type, + 'amqp_type': amqp_type, + 'broker_addr': broker_addr, + 'test_value_list': types.get_test_values(amqp_type)} + new_class = type(class_name, (AmqpTypeTestCase,), class_dict) + for send_shim, receive_shim in shim_product: + add_test_method(new_class, send_shim, receive_shim) + return new_class + + +class Shim(object): + """ + Abstract shim class, parent of all shims. + """ + NAME = None + SEND = None + RECEIVE = None + USE_SHELL = False + + def send(self, broker_addr, queue_name, amqp_type, json_test_values_str): + """ + Send the values of type amqp_type in test_value_list to queue queue_name. Return output (if any) from stdout. + """ + arg_list = [] + arg_list.extend(self.SEND) + arg_list.extend([broker_addr, queue_name, amqp_type]) + arg_list.append(json_test_values_str) + + try: + #print '\n>>>', arg_list # DEBUG - useful to see command-line sent to shim + return check_output(arg_list, shell=self.USE_SHELL) + except CalledProcessError as exc: + return str(exc) + '\n\nOutput:\n' + exc.output + except Exception as exc: + return str(exc) + + + def receive(self, broker_addr, queue_name, amqp_type, num_test_values): + """ + Receive num_test_values messages containing type amqp_type from queue queue_name. If the first line returned + from stdout is the AMQP type, then the rest is assumed to be the returned test value list. Otherwise error + output is assumed. + """ + output = '' + try: + arg_list = [] + arg_list.extend(self.RECEIVE) + arg_list.extend([broker_addr, queue_name, amqp_type, str(num_test_values)]) + #print '\n>>>', arg_list # DEBUG - useful to see command-line sent to shim + output = check_output(arg_list) + #print '<<<', output # DEBUG - useful to see text received from shim + str_tvl = output.split('\n')[0:-1] # remove trailing \n + if len(str_tvl) == 1: + return output + if len(str_tvl) == 2: + return loads(str_tvl[1]) + else: + return loads("".join(str_tvl[1:])) + except CalledProcessError as exc: + return str(exc) + '\n\n' + exc.output + except Exception as exc: + return str(exc) + + +class ProtonPythonShim(Shim): + """ + Shim for qpid-proton Python client + """ + NAME = 'ProtonPython' + SHIM_LOC = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'qpid-proton-python', 'src') + SEND = [path.join(SHIM_LOC, 'TypesSenderShim.py')] + RECEIVE = [path.join(SHIM_LOC, 'TypesReceiverShim.py')] + + +class ProtonCppShim(Shim): + """ + Shim for qpid-proton C++ client + """ + NAME = 'ProtonCpp' + SHIM_LOC = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'qpid-proton-cpp', 'build', 'src') + SEND = [path.join(SHIM_LOC, 'AmqpSender')] + RECEIVE = [path.join(SHIM_LOC, 'AmqpReceiver')] + + +class QpidJmsShim(Shim): + """ + Shim for qpid-jms JMS client + """ + NAME = 'QpidJms' + + # Installed qpid versions + QPID_JMS_VER = '0.4.0-SNAPSHOT' + QPID_PROTON_J_VER = '0.10-SNAPSHOT' + + # Classpath components + QPID_INTEROP_TEST_SHIM_JAR = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'qpid-jms', 'target', 'qpid-jms-shim.jar') + MAVEN_REPO_PATH = path.join(getenv('HOME'), '.m2', 'repository') + JMS_API_JAR = path.join(MAVEN_REPO_PATH, 'org', 'apache', 'geronimo', 'specs', 'geronimo-jms_1.1_spec', '1.1.1', + 'geronimo-jms_1.1_spec-1.1.1.jar') + JMS_IMPL_JAR = path.join(MAVEN_REPO_PATH, 'org', 'apache', 'qpid', 'qpid-jms-client', QPID_JMS_VER, + 'qpid-jms-client-' + QPID_JMS_VER + '.jar') + LOGGER_API_JAR = path.join(MAVEN_REPO_PATH, 'org', 'slf4j', 'slf4j-api', '1.5.6', 'slf4j-api-1.5.6.jar') + LOGGER_IMPL_JAR = path.join(QPID_INTEROP_TEST_HOME, 'jars', 'slf4j-nop-1.5.6.jar') + PROTON_J_JAR = path.join(MAVEN_REPO_PATH, 'org', 'apache', 'qpid', 'proton-j', QPID_PROTON_J_VER, + 'proton-j-' + QPID_PROTON_J_VER + '.jar') + NETTY_JAR = path.join(MAVEN_REPO_PATH, 'io', 'netty', 'netty-all', '4.0.17.Final', 'netty-all-4.0.17.Final.jar') + + CLASSPATH = ':'.join([QPID_INTEROP_TEST_SHIM_JAR, + JMS_API_JAR, + JMS_IMPL_JAR, + LOGGER_API_JAR, + LOGGER_IMPL_JAR, + PROTON_J_JAR, + NETTY_JAR]) + JAVA_HOME = getenv('JAVA_HOME', '/usr/bin') # Default only works in Linux + JAVA_EXEC = path.join(JAVA_HOME, 'java') + SEND = [JAVA_EXEC, '-cp', CLASSPATH, 'org.apache.qpid.interop_test.shim.AmqpSender'] + RECEIVE = [JAVA_EXEC, '-cp', CLASSPATH, 'org.apache.qpid.interop_test.shim.AmqpReceiver'] + + +# SHIM_MAP contains an instance of each client language shim that is to be tested as a part of this test. For +# every shim in this list, a test is dynamically constructed which tests it against itself as well as every +# other shim in the list. +# +# As new shims are added, add them into this map to have them included in the test cases. +#SHIM_MAP = {ProtonPythonShim.NAME: ProtonPythonShim()} +#SHIM_MAP = {ProtonPythonShim.NAME: ProtonCppShim()} +SHIM_MAP = {ProtonCppShim.NAME: ProtonCppShim(), + ProtonPythonShim.NAME: ProtonPythonShim() + } + + +class TestOptions(object): + """ + Class controlling command-line arguments used to control the test. + """ + def __init__(self): + parser = argparse.ArgumentParser(description='Qpid-interop AMQP client interoparability test suite ' + 'for AMQP simple types') + parser.add_argument('--broker', action='store', default='localhost:5672', metavar='BROKER:PORT', + help='Broker against which to run test suite.') +# test_group = parser.add_mutually_exclusive_group() +# test_group.add_argument('--include-test', action='append', metavar='TEST-NAME', +# help='Name of test to include') +# test_group.add_argument('--exclude-test', action='append', metavar='TEST-NAME', +# help='Name of test to exclude') +# type_group = test_group.add_mutually_exclusive_group() +# type_group.add_argument('--include-type', action='append', metavar='AMQP-TYPE', +# help='Name of AMQP type to include. Supported types:\n%s' % +# sorted(AmqpPrimitiveTypes.TYPE_MAP.keys())) + parser.add_argument('--exclude-type', action='append', metavar='AMQP-TYPE', + help='Name of AMQP type to exclude. Supported types:\n%s' % + sorted(AmqpPrimitiveTypes.TYPE_MAP.keys())) +# shim_group = test_group.add_mutually_exclusive_group() +# shim_group.add_argument('--include-shim', action='append', metavar='SHIM-NAME', +# help='Name of shim to include. Supported shims:\n%s' % SHIM_NAMES) + parser.add_argument('--exclude-shim', action='append', metavar='SHIM-NAME', + help='Name of shim to exclude. Supported shims:\n%s' % sorted(SHIM_MAP.keys())) + self.args = parser.parse_args() + + +#--- Main program start --- + +if __name__ == '__main__': + + ARGS = TestOptions().args + #print 'ARGS:', ARGS # debug + + # Connect to broker to find broker type + CONNECTION_PROPS = broker_properties.getBrokerProperties(ARGS.broker) + if CONNECTION_PROPS is None: + print 'WARNING: Unable to get connection properties - unknown broker' + BROKER = 'unknown' + else: + print 'Test Broker: %s v.%s on %s' % (CONNECTION_PROPS[symbol(u'product')], + CONNECTION_PROPS[symbol(u'version')], + CONNECTION_PROPS[symbol(u'platform')]) + print + BROKER = CONNECTION_PROPS[symbol(u'product')] + + TYPES = AmqpPrimitiveTypes() + + # TEST_CASE_CLASSES is a list that collects all the test classes that are constructed. One class is constructed + # per AMQP type used as the key in map AmqpPrimitiveTypes.TYPE_MAP. + TEST_CASE_CLASSES = [] + + # TEST_SUITE is the final suite of tests that will be run and which contains all the dynamically created + # type classes, each of which contains a test for the combinations of client shims + TEST_SUITE = unittest.TestSuite() + + # Remove shims excluded from the command-line + if ARGS.exclude_shim is not None: + for shim in ARGS.exclude_shim: + SHIM_MAP.pop(shim) + # Create test classes dynamically + for at in sorted(TYPES.get_type_list()): + if ARGS.exclude_type is None or at not in ARGS.exclude_type: + test_case_class = create_testcase_class(BROKER, + TYPES, + ARGS.broker, + at, + product(SHIM_MAP.values(), repeat=2)) + TEST_CASE_CLASSES.append(test_case_class) + TEST_SUITE.addTest(unittest.makeSuite(test_case_class)) + + # Finally, run all the dynamically created tests + res = unittest.TextTestRunner(verbosity=2).run(TEST_SUITE) + if not res.wasSuccessful(): + exit(1) # Errors or failures present + http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/f4b87a1e/utils/.gitignore ---------------------------------------------------------------------- diff --git a/utils/.gitignore b/utils/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/utils/.gitignore @@ -0,0 +1 @@ +/target/ http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/f4b87a1e/utils/pom.xml ---------------------------------------------------------------------- diff --git a/utils/pom.xml b/utils/pom.xml new file mode 100644 index 0000000..72ea9da --- /dev/null +++ b/utils/pom.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <name>qpid-interop-test-utils</name> + <url>https://qpid.apache.org</url> + + <groupId>org.apache.qpid</groupId> + <artifactId>qpid-interop-test-utils</artifactId> + <packaging>jar</packaging> + <version>0.1.0-SNAPSHOT</version> + + <parent> + <groupId>org.apache.qpid</groupId> + <artifactId>qpid-interop-test-parent</artifactId> + <version>0.1.0-SNAPSHOT</version> + </parent> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.7</source> + <target>1.7</target> + <optimize>true</optimize> + <debug>true</debug> + <showDeprecation>true</showDeprecation> + <showWarnings>true</showWarnings> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + </dependencies> +</project> http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/f4b87a1e/utils/src/main/java/org/apache/qpid/qpid_interop_test/obj_util/BytesToJavaObj.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/org/apache/qpid/qpid_interop_test/obj_util/BytesToJavaObj.java b/utils/src/main/java/org/apache/qpid/qpid_interop_test/obj_util/BytesToJavaObj.java new file mode 100644 index 0000000..8c461ce --- /dev/null +++ b/utils/src/main/java/org/apache/qpid/qpid_interop_test/obj_util/BytesToJavaObj.java @@ -0,0 +1,83 @@ +/** + * 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.qpid.interop_test.obj_util; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.Serializable; + +public class BytesToJavaObj { + String hexObjStr = null; + Serializable obj = null; + + public BytesToJavaObj(String hexObjStr) { + this.hexObjStr = hexObjStr; + } + + public String run() { + byte[] bytes = hexStrToByteArray(this.hexObjStr); + this.obj = byteArrayToObject(bytes); + if (this.obj != null) { + return this.obj.getClass().getName() + ":" + this.obj.toString(); + } + return "<null>"; + } + + protected Serializable byteArrayToObject(byte[] bytes) { + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ObjectInput in = null; + try { + in = new ObjectInputStream(bis); + return (Serializable) in.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(System.out); + } catch (IOException e) { + e.printStackTrace(System.out); + } finally { + try { + bis.close(); + } catch (IOException e) {} // ignore + try { + in.close(); + } catch (IOException e) {} // ignore + } + return null; + } + + protected byte[] hexStrToByteArray(String hexStr) { + int len = hexStr.length(); + byte[] data = new byte[len / 2]; + for(int i=0; i<len; i+=2) { + data[i/2] = (byte)((Character.digit(hexStr.charAt(i), 16) << 4) + Character.digit(hexStr.charAt(i+1), 16)); + } + return data; + } + + // ========= main ========== + + public static void main(String[] args) { + if (args.length != 1) { + System.out.println("BytesToJavaObj: Incorrect argument count"); + System.out.println("BytesToJavaObj: Expected argument: \"<java_serialized_obj_str_hex>\""); + System.exit(1); + } + BytesToJavaObj btjo = new BytesToJavaObj(args[0]); + System.out.println(btjo.run()); + } +} http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/f4b87a1e/utils/src/main/java/org/apache/qpid/qpid_interop_test/obj_util/JavaObjToBytes.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/org/apache/qpid/qpid_interop_test/obj_util/JavaObjToBytes.java b/utils/src/main/java/org/apache/qpid/qpid_interop_test/obj_util/JavaObjToBytes.java new file mode 100644 index 0000000..2bfbde0 --- /dev/null +++ b/utils/src/main/java/org/apache/qpid/qpid_interop_test/obj_util/JavaObjToBytes.java @@ -0,0 +1,129 @@ +/** + * 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.qpid.interop_test.obj_util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +//import java.util.Arrays; + +public class JavaObjToBytes { + String javaClassName = null; + String ctorArgStr = null; + Serializable obj = null; + + public JavaObjToBytes(String javaClassName, String ctorArgStr) { + this.javaClassName = javaClassName; + this.ctorArgStr = ctorArgStr; + } + + public byte[] run() { + createJavaObject(); + return serializeJavaOjbect(); + } + + protected void createJavaObject() { + try { + Class<?> c = Class.forName(this.javaClassName); + if (this.javaClassName.compareTo("java.lang.Character") == 0) { + Constructor ctor = c.getConstructor(char.class); + if (this.ctorArgStr.length() == 1) { + // Use first character of string + obj = (Serializable)ctor.newInstance(this.ctorArgStr.charAt(0)); + } else if (this.ctorArgStr.length() == 4 || this.ctorArgStr.length() == 6) { + // Format '\xNN' or '\xNNNN' + obj = (Serializable)ctor.newInstance((char)Integer.parseInt(this.ctorArgStr.substring(2), 16)); + } else { + throw new Exception("JavaObjToBytes.createJavaObject() Malformed char string: \"" + this.ctorArgStr + "\""); + } + } else { + // Use string constructor + Constructor ctor = c.getConstructor(String.class); + obj = (Serializable)ctor.newInstance(this.ctorArgStr); + } + } + catch (ClassNotFoundException e) { + e.printStackTrace(System.out); + } + catch (NoSuchMethodException e) { + e.printStackTrace(System.out); + } + catch (InstantiationException e) { + e.printStackTrace(System.out); + } + catch (IllegalAccessException e) { + e.printStackTrace(System.out); + } + catch (InvocationTargetException e) { + e.printStackTrace(System.out); + } + catch (Exception e) { + e.printStackTrace(System.out); + } + } + + protected byte[] serializeJavaOjbect() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(this.obj); + return bos.toByteArray(); + } catch (IOException e) { + e.printStackTrace(System.out); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException e) {} // ignore + try { + bos.close(); + } catch (IOException e) {} // ignore + } + return null; + } + + // ========= main ========== + + public static void main(String[] args) { + if (args.length != 1) { + System.out.println("JavaObjToBytes: Incorrect argument count"); + System.out.println("JavaObjToBytes: Expected argument: \"<java_class_name>:<ctor_arg_str>\""); + System.exit(1); + } + int colonIndex = args[0].indexOf(":"); + if (colonIndex < 0) { + System.out.println("Error: Incorect argument format: " + args[0]); + System.exit(-1); + } + String javaClassName = args[0].substring(0, colonIndex); + String ctorArgStr = args[0].substring(colonIndex+1); + JavaObjToBytes jotb = new JavaObjToBytes(javaClassName, ctorArgStr); + byte[] bytes = jotb.run(); + System.out.println(args[0]); + for (byte b: bytes) { + System.out.print(String.format("%02x", b)); + } + System.out.println(); + } +} + --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
