diff options
author | Ted Ross <tross@apache.org> | 2009-06-11 15:54:37 +0000 |
---|---|---|
committer | Ted Ross <tross@apache.org> | 2009-06-11 15:54:37 +0000 |
commit | 641f048cb8b86be0304441a6227759d7ad420ff3 (patch) | |
tree | e450e074085d41a476a3c2d6daafcfe99183a60f | |
parent | 91f06a93d86eff2a597347dc2f15b2431eb0feae (diff) | |
download | qpid-python-641f048cb8b86be0304441a6227759d7ad420ff3.tar.gz |
QPID-1786 - Committed qmf patches from Bryan Kearney
Additionally updated existing qmf and Qman to be compatible.
The magic number for qmf messages has been incremented.
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@783818 13f79535-47bb-0310-9956-ffa450edef68
54 files changed, 4461 insertions, 259 deletions
diff --git a/qpid/cpp/bindings/qmf/ruby/Makefile.am b/qpid/cpp/bindings/qmf/ruby/Makefile.am index 8e10b418dc..4b5f42bae0 100644 --- a/qpid/cpp/bindings/qmf/ruby/Makefile.am +++ b/qpid/cpp/bindings/qmf/ruby/Makefile.am @@ -13,7 +13,7 @@ rubylibdir = $(RUBY_LIB) dist_rubylib_DATA = qmf.rb $(generated_file_list): $(srcdir)/ruby.i $(srcdir)/../qmfengine.i - swig -ruby -c++ -Wall -I/usr/include $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -o qmfengine.cpp $(srcdir)/ruby.i + swig -ruby -c++ -Wall -I/usr/include $(INCLUDES) $(QPID_CXXFLAGS) -o qmfengine.cpp $(srcdir)/ruby.i AM_CPPFLAGS = $(QPID_CXXFLAGS) $(INCLUDES) -I$(srcdir) -I$(RUBY_INC) -I$(RUBY_INC_ARCH) AM_CXXFLAGS = $(QPID_CXXFLAGS) diff --git a/qpid/cpp/examples/qmf-agent/Makefile b/qpid/cpp/examples/qmf-agent/Makefile index 4c5daa6888..7e460bcd16 100644 --- a/qpid/cpp/examples/qmf-agent/Makefile +++ b/qpid/cpp/examples/qmf-agent/Makefile @@ -27,7 +27,7 @@ CC = gcc LIB_DIR = $(QPID_DIR)/cpp/src/.libs CC_INCLUDES = -I$(SRC_DIR) -I$(QPID_DIR)/cpp/src -I$(QPID_DIR)/cpp/src/gen -I$(GEN_DIR) CC_FLAGS = -g -O3 -LD_FLAGS = -lqmfagent -L$(LIB_DIR) +LD_FLAGS = -lqmfagent -lqmfcommon -L$(LIB_DIR) SPEC_DIR = $(QPID_DIR)/specs MGEN_DIR = $(QPID_DIR)/cpp/managementgen MGEN = $(MGEN_DIR)/qmf-gen diff --git a/qpid/cpp/managementgen/qmfgen/management-types.xml b/qpid/cpp/managementgen/qmfgen/management-types.xml index 626880afb3..e235920447 100644 --- a/qpid/cpp/managementgen/qmfgen/management-types.xml +++ b/qpid/cpp/managementgen/qmfgen/management-types.xml @@ -36,7 +36,7 @@ <type name="float" base="FLOAT" cpp="float" encode="@.putFloat(#)" decode="# = @.getFloat()" accessor="direct" init="0."/> <type name="double" base="DOUBLE" cpp="double" encode="@.putDouble(#)" decode="# = @.getDouble()" accessor="direct" init="0."/> <type name="uuid" base="UUID" cpp="::qpid::framing::Uuid" encode="#.encode(@)" decode="#.decode(@)" accessor="direct" init="::qpid::framing::Uuid()" byRef="y"/> -<type name="map" base="FTABLE" cpp="::qpid::framing::FieldTable" encode="#.encode(@)" decode="#.decode(@)" accessor="direct" init="::qpid::framing::FieldTable()" byRef="y"/> +<type name="map" base="FTABLE" cpp="::qpid::framing::FieldTable" encode="#.qmfEncode(@)" decode="#.decode(@)" accessor="direct" init="::qpid::framing::FieldTable()" byRef="y"/> <type name="hilo8" base="U8" cpp="uint8_t" encode="@.putOctet(#)" decode="# = @.getOctet()" style="wm" accessor="counter" init="0"/> <type name="hilo16" base="U16" cpp="uint16_t" encode="@.putShort(#)" decode="# = @.getShort()" style="wm" accessor="counter" init="0"/> diff --git a/qpid/cpp/managementgen/qmfgen/templates/Class.cpp b/qpid/cpp/managementgen/qmfgen/templates/Class.cpp index 973d92586a..52ffce0eb4 100644 --- a/qpid/cpp/managementgen/qmfgen/templates/Class.cpp +++ b/qpid/cpp/managementgen/qmfgen/templates/Class.cpp @@ -98,6 +98,7 @@ void /*MGEN:Class.NameCap*/::writeSchema (Buffer& buf) buf.putShortString (packageName); // Package Name buf.putShortString (className); // Class Name buf.putBin128 (md5Sum); // Schema Hash + buf.putOctet (0); // No Superclass buf.putShort (/*MGEN:Class.ConfigCount*/); // Config Element Count buf.putShort (/*MGEN:Class.InstCount*/); // Inst Element Count buf.putShort (/*MGEN:Class.MethodCount*/); // Method Count diff --git a/qpid/cpp/managementgen/qmfgen/templates/Event.cpp b/qpid/cpp/managementgen/qmfgen/templates/Event.cpp index 2ffec8bcdf..a4fc28990d 100644 --- a/qpid/cpp/managementgen/qmfgen/templates/Event.cpp +++ b/qpid/cpp/managementgen/qmfgen/templates/Event.cpp @@ -65,6 +65,7 @@ void Event/*MGEN:Event.NameCap*/::writeSchema (Buffer& buf) buf.putShortString (packageName); // Package Name buf.putShortString (eventName); // Event Name buf.putBin128 (md5Sum); // Schema Hash + buf.putOctet (0); // No Superclass buf.putShort (/*MGEN:Event.ArgCount*/); // Argument Count // Arguments diff --git a/qpid/cpp/src/qmf/Agent.cpp b/qpid/cpp/src/qmf/Agent.cpp index 1071e445c8..84f37c5bb4 100644 --- a/qpid/cpp/src/qmf/Agent.cpp +++ b/qpid/cpp/src/qmf/Agent.cpp @@ -484,7 +484,7 @@ void AgentImpl::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq) { buf.putOctet('A'); buf.putOctet('M'); - buf.putOctet('2'); + buf.putOctet('3'); buf.putOctet(opcode); buf.putLong (seq); } @@ -501,7 +501,7 @@ bool AgentImpl::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) *opcode = buf.getOctet(); *seq = buf.getLong(); - return h1 == 'A' && h2 == 'M' && h3 == '2'; + return h1 == 'A' && h2 == 'M' && h3 == '3'; } AgentEventImpl::Ptr AgentImpl::eventDeclareQueue(const string& name) diff --git a/qpid/cpp/src/qmf/SchemaImpl.cpp b/qpid/cpp/src/qmf/SchemaImpl.cpp index 57d6148cac..716c4db4f0 100644 --- a/qpid/cpp/src/qmf/SchemaImpl.cpp +++ b/qpid/cpp/src/qmf/SchemaImpl.cpp @@ -272,6 +272,7 @@ void SchemaObjectClassImpl::encode(Buffer& buffer) const buffer.putShortString(package); buffer.putShortString(name); hash.encode(buffer); + buffer.putOctet(0); // No parent class buffer.putShort((uint16_t) properties.size()); buffer.putShort((uint16_t) statistics.size()); buffer.putShort((uint16_t) methods.size()); diff --git a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp index 6c6fbdfe3c..c5e2682cba 100644 --- a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp +++ b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp @@ -565,7 +565,7 @@ void ManagementAgentImpl::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq { buf.putOctet('A'); buf.putOctet('M'); - buf.putOctet('2'); + buf.putOctet('3'); buf.putOctet(opcode); buf.putLong (seq); } @@ -582,7 +582,7 @@ bool ManagementAgentImpl::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *se *opcode = buf.getOctet(); *seq = buf.getLong(); - return h1 == 'A' && h2 == 'M' && h3 == '2'; + return h1 == 'A' && h2 == 'M' && h3 == '3'; } ManagementAgentImpl::PackageMap::iterator ManagementAgentImpl::findOrAddPackage(const string& name) diff --git a/qpid/cpp/src/qpid/console/Broker.cpp b/qpid/cpp/src/qpid/console/Broker.cpp index 4f90afd39a..68279a4f8e 100644 --- a/qpid/cpp/src/qpid/console/Broker.cpp +++ b/qpid/cpp/src/qpid/console/Broker.cpp @@ -72,7 +72,7 @@ void Broker::encodeHeader(Buffer& buf, uint8_t opcode, uint32_t seq) const { buf.putOctet('A'); buf.putOctet('M'); - buf.putOctet('2'); + buf.putOctet('3'); buf.putOctet(opcode); buf.putLong (seq); } @@ -89,7 +89,7 @@ bool Broker::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) const *opcode = buf.getOctet(); *seq = buf.getLong(); - return h1 == 'A' && h2 == 'M' && h3 == '2'; + return h1 == 'A' && h2 == 'M' && h3 == '3'; } void Broker::received(qpid::client::Message& msg) diff --git a/qpid/cpp/src/qpid/console/Schema.cpp b/qpid/cpp/src/qpid/console/Schema.cpp index 9b4312d11f..a248b2d09a 100644 --- a/qpid/cpp/src/qpid/console/Schema.cpp +++ b/qpid/cpp/src/qpid/console/Schema.cpp @@ -119,9 +119,18 @@ SchemaClass::SchemaClass(const uint8_t _kind, const ClassKey& _key, framing::Buf kind(_kind), key(_key) { if (kind == KIND_TABLE) { - uint16_t propCount = buffer.getShort(); - uint16_t statCount = buffer.getShort(); - uint16_t methodCount = buffer.getShort(); + uint8_t hasSupertype = buffer.getOctet(); + uint16_t propCount = buffer.getShort(); + uint16_t statCount = buffer.getShort(); + uint16_t methodCount = buffer.getShort(); + + if (hasSupertype) { + string unused; + buffer.getShortString(unused); + buffer.getShortString(unused); + buffer.getLongLong(); + buffer.getLongLong(); + } for (uint16_t idx = 0; idx < propCount; idx++) properties.push_back(new SchemaProperty(buffer)); diff --git a/qpid/cpp/src/qpid/console/Value.cpp b/qpid/cpp/src/qpid/console/Value.cpp index 1a7f690baf..c557699f33 100644 --- a/qpid/cpp/src/qpid/console/Value.cpp +++ b/qpid/cpp/src/qpid/console/Value.cpp @@ -114,7 +114,7 @@ string MapValue::str() const MapValue::MapValue(framing::Buffer& buffer) { - value.decode(buffer); + value.qmfDecode(buffer); } diff --git a/qpid/cpp/src/qpid/console/Value.h b/qpid/cpp/src/qpid/console/Value.h index 5a0915c69b..fbbe0b6a58 100644 --- a/qpid/cpp/src/qpid/console/Value.h +++ b/qpid/cpp/src/qpid/console/Value.h @@ -98,6 +98,8 @@ namespace console { std::string str() const; bool isUint() const { return true; } uint32_t asUint() const { return value; } + bool isUint64() const { return true; } + uint64_t asUint64() const { return (uint64_t) value; } private: uint32_t value; }; @@ -108,6 +110,8 @@ namespace console { std::string str() const; bool isInt() const { return true; } int32_t asInt() const { return value; } + bool isInt64() const { return true; } + int64_t asInt64() const { return (int64_t) value; } private: int32_t value; }; @@ -160,6 +164,8 @@ namespace console { std::string str() const; bool isFloat() const { return true; } float asFloat() const { return value; } + bool isDouble() const { return true; } + double asDouble() const { return (double) value; } private: float value; }; diff --git a/qpid/cpp/src/qpid/framing/FieldTable.cpp b/qpid/cpp/src/qpid/framing/FieldTable.cpp index 559aa8b013..3e0921fe8d 100644 --- a/qpid/cpp/src/qpid/framing/FieldTable.cpp +++ b/qpid/cpp/src/qpid/framing/FieldTable.cpp @@ -53,6 +53,23 @@ uint32_t FieldTable::encodedSize() const { return len; } +uint32_t FieldTable::qmfEncodedSize() const { + uint32_t len(4/*size field*/ + 4/*count field*/); + for(ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { + // shortstr_len_byte + key size + typecode + len += 1 + (i->first).size() + 1; + ValuePtr value(i->second); + if (value->convertsTo<int>()) { + len += 4; + } else if (value->convertsTo<uint64_t>()) { + len += 8; + } else if (value->convertsTo<string>()) { + len += 2 + value->get<string>().size(); + } + } + return len; +} + int FieldTable::count() const { return values.size(); } @@ -191,7 +208,7 @@ bool FieldTable::getDouble(const std::string& name, double& value) const { // return getValue<uint64_t>(name); //} -void FieldTable::encode(Buffer& buffer) const{ +void FieldTable::encode(Buffer& buffer) const { buffer.putLong(encodedSize() - 4); buffer.putLong(values.size()); for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { @@ -220,6 +237,122 @@ void FieldTable::decode(Buffer& buffer){ } } +#define QMF_TYPE_U8 1 +#define QMF_TYPE_U16 2 +#define QMF_TYPE_U32 3 +#define QMF_TYPE_U64 4 +#define QMF_TYPE_SSTR 6 +#define QMF_TYPE_LSTR 7 +#define QMF_TYPE_ABSTIME 8 +#define QMF_TYPE_DELTATIME 9 +#define QMF_TYPE_REF 10 +#define QMF_TYPE_BOOL 11 +#define QMF_TYPE_FLOAT 12 +#define QMF_TYPE_DOUBLE 13 +#define QMF_TYPE_UUID 14 +#define QMF_TYPE_S8 16 +#define QMF_TYPE_S16 17 +#define QMF_TYPE_S32 18 +#define QMF_TYPE_S64 19 +#define QMF_TYPE_OBJECT 20 +#define QMF_TYPE_MAP 15 +#define QMF_TYPE_LIST 21 +#define QMF_TYPE_ARRAY 22 + +void FieldTable::qmfEncode(Buffer& buffer) const { + buffer.putLong(qmfEncodedSize() - 4); + buffer.putLong(values.size()); + for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { + ValuePtr value(i->second); + buffer.putShortString(i->first); + if (value->convertsTo<int>()) { + buffer.putOctet(QMF_TYPE_S32); + buffer.putLong(value->get<int>()); + } else if (value->convertsTo<uint64_t>()) { + buffer.putOctet(QMF_TYPE_U64); + buffer.putLongLong(value->get<uint64_t>()); + } else if (value->convertsTo<string>()) { + buffer.putOctet(QMF_TYPE_LSTR); + buffer.putMediumString(value->get<string>()); + } + } +} + +void FieldTable::qmfDecode(Buffer& buffer) { + clear(); + uint32_t len = buffer.getLong(); + if (len) { + uint32_t available = buffer.available(); + if (available < len) + throw IllegalArgumentException(QPID_MSG("Not enough data for field table.")); + uint32_t count = buffer.getLong(); + uint32_t leftover = available - len; + while(buffer.available() > leftover && count--) { + std::string name; + std::string sstr; + std::string lstr; + buffer.getShortString(name); + uint8_t typecode = buffer.getOctet(); + switch (typecode) { + case QMF_TYPE_U8: + values[name] = ValuePtr(new IntegerValue(buffer.getOctet())); + break; + case QMF_TYPE_U16: + values[name] = ValuePtr(new IntegerValue(buffer.getShort())); + break; + case QMF_TYPE_U32: + values[name] = ValuePtr(new IntegerValue(buffer.getLong())); + break; + case QMF_TYPE_U64: + values[name] = ValuePtr(new Unsigned64Value(buffer.getLongLong())); + break; + case QMF_TYPE_SSTR: + buffer.getShortString(sstr); + values[name] = ValuePtr(new Str16Value(sstr)); + break; + case QMF_TYPE_LSTR: + buffer.getMediumString(lstr); + values[name] = ValuePtr(new Str16Value(lstr)); + break; + case QMF_TYPE_ABSTIME: + values[name] = ValuePtr(new Unsigned64Value(buffer.getLongLong())); + break; + case QMF_TYPE_DELTATIME: + values[name] = ValuePtr(new Unsigned64Value(buffer.getLongLong())); + break; + case QMF_TYPE_BOOL: + values[name] = ValuePtr(new IntegerValue(buffer.getOctet())); + break; + case QMF_TYPE_FLOAT: + values[name] = ValuePtr(new FloatValue(buffer.getFloat())); + break; + case QMF_TYPE_DOUBLE: + values[name] = ValuePtr(new DoubleValue(buffer.getDouble())); + break; + case QMF_TYPE_S8: + values[name] = ValuePtr(new IntegerValue(buffer.getOctet())); + break; + case QMF_TYPE_S16: + values[name] = ValuePtr(new IntegerValue(buffer.getShort())); + break; + case QMF_TYPE_S32: + values[name] = ValuePtr(new IntegerValue(buffer.getLong())); + break; + case QMF_TYPE_S64: + values[name] = ValuePtr(new Unsigned64Value(buffer.getLongLong())); + break; + case QMF_TYPE_REF: + case QMF_TYPE_UUID: + case QMF_TYPE_OBJECT: + case QMF_TYPE_MAP: + case QMF_TYPE_LIST: + case QMF_TYPE_ARRAY: + break; + } + } + } +} + bool FieldTable::operator==(const FieldTable& x) const { if (values.size() != x.values.size()) return false; diff --git a/qpid/cpp/src/qpid/framing/FieldTable.h b/qpid/cpp/src/qpid/framing/FieldTable.h index a07568559f..dfadfebdcb 100644 --- a/qpid/cpp/src/qpid/framing/FieldTable.h +++ b/qpid/cpp/src/qpid/framing/FieldTable.h @@ -60,6 +60,10 @@ class FieldTable QPID_COMMON_EXTERN void encode(Buffer& buffer) const; QPID_COMMON_EXTERN void decode(Buffer& buffer); + QPID_COMMON_EXTERN uint32_t qmfEncodedSize() const; + QPID_COMMON_EXTERN void qmfEncode(Buffer& buffer) const; + QPID_COMMON_EXTERN void qmfDecode(Buffer& buffer); + QPID_COMMON_EXTERN int count() const; QPID_COMMON_EXTERN void set(const std::string& name, const ValuePtr& value); QPID_COMMON_EXTERN ValuePtr get(const std::string& name) const; diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.cpp b/qpid/cpp/src/qpid/management/ManagementAgent.cpp index 8fcc5264e4..d0ad80902b 100644 --- a/qpid/cpp/src/qpid/management/ManagementAgent.cpp +++ b/qpid/cpp/src/qpid/management/ManagementAgent.cpp @@ -246,7 +246,7 @@ void ManagementAgent::encodeHeader (Buffer& buf, uint8_t opcode, uint32_t seq) { buf.putOctet ('A'); buf.putOctet ('M'); - buf.putOctet ('2'); + buf.putOctet ('3'); buf.putOctet (opcode); buf.putLong (seq); } @@ -260,7 +260,7 @@ bool ManagementAgent::checkHeader (Buffer& buf, uint8_t *opcode, uint32_t *seq) *opcode = buf.getOctet(); *seq = buf.getLong(); - return h1 == 'A' && h2 == 'M' && h3 == '2'; + return h1 == 'A' && h2 == 'M' && h3 == '3'; } void ManagementAgent::sendBuffer(Buffer& buf, @@ -1097,10 +1097,18 @@ size_t ManagementAgent::validateTableSchema(Buffer& inBuffer) inBuffer.getShortString(text); inBuffer.getBin128(hash); + uint8_t superType = inBuffer.getOctet(); + uint16_t propCount = inBuffer.getShort(); uint16_t statCount = inBuffer.getShort(); uint16_t methCount = inBuffer.getShort(); + if (superType == 1) { + inBuffer.getShortString(text); + inBuffer.getShortString(text); + inBuffer.getBin128(hash); + } + for (uint16_t idx = 0; idx < propCount + statCount; idx++) { FieldTable ft; ft.decode(inBuffer); @@ -1142,9 +1150,16 @@ size_t ManagementAgent::validateEventSchema(Buffer& inBuffer) inBuffer.getShortString(text); inBuffer.getShortString(text); inBuffer.getBin128(hash); + + uint8_t superType = inBuffer.getOctet(); uint16_t argCount = inBuffer.getShort(); + if (superType == 1) { + inBuffer.getShortString(text); + inBuffer.getShortString(text); + inBuffer.getBin128(hash); + } for (uint16_t idx = 0; idx < argCount; idx++) { FieldTable ft; ft.decode(inBuffer); diff --git a/qpid/java/agent/build.xml b/qpid/java/agent/build.xml new file mode 100644 index 0000000000..7d83f009e9 --- /dev/null +++ b/qpid/java/agent/build.xml @@ -0,0 +1,41 @@ +<!-- + - + - 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 name="QMF Agent" default="build"> + + <property name="module.depends" value="common client jms"/> + <property name="module.main" value="org.apache.qpid.agent.Agent"/> + + <import file="../module.xml"/> + + <target name="copy-bin-release" description="copy dependencies into module release"> + <copy todir="${module.release}/bin" failonerror="true"> + <fileset dir="${module.bin}"/> + </copy> + <copy todir="${module.release}/bin" failonerror="true" flatten="true"> + <fileset dir="${basedir}/../common/bin"/> + </copy> + <chmod dir="${module.release}/bin" perm="ugo+rx" includes="**/*"/> + + </target> + + <target name="release-bin" depends="release-bin-tasks"/> + +</project> diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/Agent.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/Agent.java new file mode 100644 index 0000000000..fa80694017 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/Agent.java @@ -0,0 +1,650 @@ +/* + * + * 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.agent; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.agent.binding.BindingContext; +import org.apache.qpid.agent.binding.BindingUtils; +import org.apache.qpid.agent.binding.ClassBinding; +import org.apache.qpid.agent.binding.BindingException; +import org.apache.qpid.agent.binding.MethodBinding; +import org.apache.qpid.agent.binding.ParameterBinding; +import org.apache.qpid.agent.binding.PropertyBinding; +import org.apache.qpid.agent.binding.TypeBinding; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.transport.codec.BBDecoder; +import org.apache.qpid.transport.codec.BBEncoder; +import org.apache.qpid.transport.codec.Decoder; + +/** + * Agent + * + */ +public class Agent implements MessageListener +{ + // The following are settings to configure the Agent + private AMQConnection connection; + private boolean sessionTransacted = false; + private int acknowledgeMode = Session.AUTO_ACKNOWLEDGE; + private String label; + private UUID systemId; + // this list holds the objects until the agent is started + private List managedObjects = new ArrayList(); + private List registeredClasses = new ArrayList(); + // The following instance variables are not + // able to be set by the end user. + private Session session; + private MessageProducer prod; + private MessageConsumer cons; + private Queue reply; + private BindingContext bctx = new BindingContext(); + private Map<Long, ManagedObject> objects = new Hashtable<Long, ManagedObject>(); + private long bbank; + private long abank; + private static Log log = LogFactory.getLog(Agent.class); + private volatile boolean inside = false; + + public Agent() + { + systemId = UUID.randomUUID(); + log.debug(String.format("Agent with uid %s created", systemId + .toString())); + } + + public Agent(String label, UUID systemId) + { + this.systemId = systemId; + this.label = label; + log.debug(String.format("Agent with name %s and uid %s created", label, + systemId.toString())); + } + + public void register(ManagedObject managedObject) + { + Class managedClass = managedObject.getObjectClass(); + long id = managedObject.getId(); + ClassBinding cb = bctx.register(managedClass); + managedObject.setManagedClassName(cb.getName()); + managedObject.setManagedPackageName(cb.getPackage()); + log.debug(String.format( + "Added managed object id '%d' for package '%s' class '%s'", id, + managedObject.getManagedPackageName(), managedObject + .getManagedClassName())); + objects.put(id, managedObject); + managedObjects.add(managedObject); + } + + public void registerClass(Class cls) + { + bctx.register(cls); + if (!registeredClasses.contains(cls)) + { + registeredClasses.add(cls); + } + } + + public void start() + { + log.debug(String.format("Agent with uid %s and name %s starting", + systemId.toString(), label)); + for (Object clsName : registeredClasses.toArray()) + { + try + { + Class cls = Class.forName(clsName.toString()); + this.registerClass(cls); + } catch (Exception e) + { + log.error("Could not register class " + clsName); + } + } + for (Object obj : managedObjects.toArray()) + { + this.register((ManagedObject) obj); + } + try + { + session = connection.createSession(sessionTransacted, + acknowledgeMode); + reply = session.createQueue("direct://amq.direct//" + label); + cons = session.createConsumer(reply); + cons.setMessageListener(this); + prod = session.createProducer(null); + } catch (JMSException e) + { + throw new AgentException(e); + } + attachRequest(label, systemId); + try + { + connection.start(); + } catch (JMSException e) + { + throw new AgentException(e); + } + } + + public void raiseEvent(Object value, EventSeverity sev) + { + log.debug(String.format("Sending event of class %s with Severity %s", + value.getClass(), sev.ordinal())); + BBEncoder enc = this.init('e'); + ClassBinding cb = bctx.getClassBinding(value.getClass()); + String pkg = cb.getPackage(); + String cls = cb.getName(); + enc.writeStr8(pkg); + enc.writeStr8(cls); + enc.writeBin128(cb.getSchemaHash()); + long now = System.currentTimeMillis() * 1000000; + enc.writeInt64(now); + enc.writeUint8((short) sev.ordinal()); + for (PropertyBinding p : cb.getProperties()) + { + p.getType().encode(enc, BindingUtils.get(p, value)); + } + send( + String.format("console.event.%d.%d.%s.%s", bbank, abank, pkg, + cls), enc); + } + + public void onMessage(Message message) + { + if (inside) + { + new Throwable().printStackTrace(); + } + inside = true; + Decoder dec = readBody(message); + Destination replyTo; + try + { + replyTo = message.getJMSReplyTo(); + } catch (JMSException e) + { + throw new AgentException(e); + } + byte[] magic = dec.readBytes(3); + if (magic[0] != 'A' || magic[1] != 'M' || magic[2] != '2') + { + throw new AgentException("bad magic: " + new String(magic)); + } + short op = dec.readUint8(); + long seq = dec.readUint32(); + log.debug("Message recieved: " + (char) op); + switch (op) + { + case 'a': + this.handleAgentAttach(seq, replyTo, dec); + break; + case 'G': + this.handleGetQuery(seq, replyTo, dec); + break; + case 'M': + this.handleMethodRequest(seq, replyTo, dec); + break; + case 'S': + this.handleSchemaRequest(seq, replyTo, dec); + break; + case 'x': + // TODO + break; + default: + throw new IllegalArgumentException("opcode: " + ((char) op)); + } + inside = false; + } + + protected ClassBinding getClassBinding(ManagedObject mobj) + { + return bctx.getClassBinding(mobj.getObjectClass()); + } + + private byte[] ensure(int capacity, byte[] body, int size) + { + if (capacity > body.length) + { + byte[] copy = new byte[capacity]; + System.arraycopy(body, 0, copy, 0, size); + body = copy; + } + return body; + } + + private Decoder readBody(Message message) + { + BytesMessage msg = (BytesMessage) message; + BBDecoder dec = new BBDecoder(); + byte[] buf = new byte[1024]; + byte[] body = new byte[1024]; + int size = 0; + int n; + try + { + while ((n = msg.readBytes(buf)) > 0) + { + body = ensure(size + n, body, size); + System.arraycopy(buf, 0, body, size, n); + size += n; + } + } catch (JMSException e) + { + throw new AgentException(e); + } + dec.init(ByteBuffer.wrap(body, 0, size)); + return dec; + } + + protected void handleAgentAttach(long seq, Destination replyTo, Decoder dec) + { + log.debug("Agent Attach Message"); + bbank = dec.readUint32(); + abank = dec.readUint32(); + try + { + MessageConsumer mc = session + .createConsumer(session + .createQueue(String + .format( + "management://qpid.management//%s?routingkey='agent.%d.%d'", + label, bbank, abank))); + mc.setMessageListener(this); + } catch (JMSException e) + { + throw new AgentException(e); + } + for (String packageName : bctx.getPackages()) + { + packageIndication(packageName); + } + for (ClassBinding cb : bctx.getAllBindings()) + { + classIndication(cb); + } + for (ManagedObject mo : objects.values()) + { + content('i', seq, null, mo); + } + } + + protected void handleMethodRequest(long seq, Destination replyTo, + Decoder dec) + { + dec.readUint64(); // first part of object-id + long id = dec.readUint64(); + ManagedObject mo = objects.get(id); + if (mo == null) + { + methodResponse(seq, replyTo, 1, String.format( + "no such object: 0x%x", id)); + } else + { + dec.readStr8(); // pkg + dec.readStr8(); // cls + dec.readBin128(); // hash + String mname = dec.readStr8(); + ClassBinding cls = getClassBinding(mo); + MethodBinding method = cls.getMethod(mname); + if (method == null) + { + methodResponse(seq, replyTo, 2, String.format( + "no such method: %s", mname)); + } else + { + log.trace("Handle method: " + method.getName()); + List<ParameterBinding> params = method.getInParameters(); + Object[] args = new Object[params.size()]; + int idx = 0; + for (ParameterBinding p : params) + { + TypeBinding typeBinding = p.getType(); + log + .trace(String + .format( + "Decoding parameter with type %s ref package %s ref class %s ", + typeBinding.getCode(), typeBinding + .getRefPackage(), + typeBinding.getRefClass())); + args[idx++] = typeBinding.decode(dec); + log.trace("Done"); + } + try + { + Object[] result = mo.invoke(method, args); + methodResponse(seq, replyTo, 0, null, method, result); + } catch (BindingException ex) + { + log + .error(String + .format( + "An exception occured invoking method %s. Stack trace sent to console.", + method.getName())); + StringWriter str = new StringWriter(); + PrintWriter writer = new PrintWriter(str); + ex.printStackTrace(writer); + writer.flush(); + methodResponse(seq, replyTo, 7, str.toString()); + } + log.trace("Done with method: " + method.getName()); + } + } + } + + protected void handleGetQuery(long seq, Destination replyTo, Decoder dec) + { + Map<String, Object> data = dec.readMap(); + if (data.containsKey("_objectid")) + { + long objId = (Long) data.get("_objectid"); + log.debug("Get Request message for object id " + objId); + ManagedObject mo = objects.get(objId); + if (mo == null) + { + methodResponse(seq, replyTo, 1, String.format( + "no such object: 0x%x", objId)); + } else + { + content('g', seq, replyTo, mo); + } + } else if (data.containsKey("_class")) + { + String className = (String) data.get("_class"); + String packageName = (String) data.get("_package"); + log.debug(String.format( + "Get Request message for package '%s' class '%s'", + packageName, className)); + for (ManagedObject mo : objects.values()) + { + if (mo.getManagedClassName().equals(className)) + { + if ((packageName == null) || packageName.equals("") + || packageName.equals(mo.getManagedPackageName())) + { + content('g', seq, replyTo, mo); + } + } + } + } else + { + for (ManagedObject mo : objects.values()) + { + content('g', seq, replyTo, mo); + } + } + complete(seq, replyTo); + } + + protected void handleSchemaRequest(long seq, Destination replyTo, + Decoder dec) + { + String pkg = dec.readStr8(); + String cls = dec.readStr8(); + log.debug(String.format( + "SchemaRequest message for package '%s' class '%s'", pkg, cls)); + ClassBinding cb = bctx.getClassBinding(pkg, cls); + if (cb == null) + { + throw new AgentException("no such class: " + pkg + ", " + cls); + } + schemaResponse(seq, cb); + } + + protected BBEncoder init(char opcode) + { + return init(opcode, 0); + } + + protected BBEncoder init(char opcode, long sequence) + { + BBEncoder enc = new BBEncoder(1024); + enc.init(); + enc.writeUint8((short) 'A'); + enc.writeUint8((short) 'M'); + enc.writeUint8((short) '2'); + enc.writeUint8((short) opcode); + enc.writeUint32(sequence); + return enc; + } + + protected void send(BBEncoder enc) + { + send("broker", enc); + } + + protected void send(Destination dest, BBEncoder enc) + { + try + { + byte[] buf = new byte[1024]; + byte[] body = new byte[1024]; + BytesMessage msg = session.createBytesMessage(); + ByteBuffer slice = enc.segment(); + while (slice.hasRemaining()) + { + int n = Math.min(buf.length, slice.remaining()); + slice.get(buf, 0, n); + msg.writeBytes(buf, 0, n); + } + msg.setJMSReplyTo(reply); + // ???: I assume this is thread safe. + prod.send(dest, msg); + } catch (JMSException e) + { + throw new AgentException(e); + } + } + + protected void send(String routingKey, BBEncoder enc) + { + try + { + send(session + .createQueue("management://qpid.management//?routingkey='" + + routingKey + "'"), enc); + } catch (JMSException e) + { + throw new AgentException(e); + } + } + + private void attachRequest(String label, UUID systemId) + { + BBEncoder enc = init('A'); + enc.writeStr8(label); + enc.writeUuid(systemId); + enc.writeUint32(0); + enc.writeUint32(0); + send(enc); + } + + private void packageIndication(String pkg) + { + BBEncoder enc = init('p'); + enc.writeStr8(pkg); + send(enc); + } + + private void classIndication(ClassBinding cb) + { + BBEncoder enc = init('q'); + enc.writeUint8(cb.getKind()); + enc.writeStr8(cb.getPackage()); + enc.writeStr8(cb.getName()); + enc.writeBin128(cb.getSchemaHash()); // schema hash? + send(enc); + } + + private void schemaResponse(long seq, ClassBinding cb) + { + BBEncoder enc = init('s', seq); + cb.encode(enc); + send(enc); + } + + private void content(char c, long seq, Destination dest, ManagedObject mo) + { + BBEncoder enc = init(c, seq); + ClassBinding cb = getClassBinding(mo); + String pkg = cb.getPackage(); + String cls = cb.getName(); + enc.writeStr8(pkg); + enc.writeStr8(cls); + enc.writeBin128(cb.getSchemaHash()); + long now = System.currentTimeMillis() * 1000000; + enc.writeUint64(now); + enc.writeUint64(now); + enc.writeUint64(0); + enc.writeUint64(0x0000FFFFFFFFFFFFL & ((bbank << 28) | abank)); + enc.writeUint64(mo.getId()); + for (PropertyBinding p : cb.getProperties()) + { + p.getType().encode(enc, mo.get(p)); + } + if (dest == null) + { + send(String.format("console.obj.%d.%d.%s.%s", bbank, abank, pkg, + cls), enc); + } else + { + send(dest, enc); + } + } + + private void complete(long seq, Destination dest) + { + BBEncoder enc = init('z', seq); + enc.writeUint32(0); + enc.writeStr8(""); + send(dest, enc); + } + + private void methodResponse(long seq, Destination dest, int status, + String text) + { + methodResponse(seq, dest, status, text, null, null); + } + + private void methodResponse(long seq, Destination dest, int status, + String text, MethodBinding method, Object[] result) + { + BBEncoder enc = init('m', seq); + enc.writeUint32(status); + enc.writeStr16(text == null ? "" : text); + if (method != null) + { + int idx = 0; + for (ParameterBinding p : method.getOutParameters()) + { + p.getType().encode(enc, result[idx++]); + } + } + send(dest, enc); + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public AMQConnection getConnection() + { + return connection; + } + + public void setConnection(AMQConnection connection) + { + this.connection = connection; + } + + public boolean isSessionTransacted() + { + return sessionTransacted; + } + + public void setSessionTransacted(boolean sessionTransacted) + { + this.sessionTransacted = sessionTransacted; + } + + public void setManagedObjects(List objectList) + { + this.managedObjects = objectList; + } + + public List getManagedObjects() + { + return managedObjects; + } + + public void setRegisteredClasses(List objectList) + { + this.registeredClasses = objectList; + } + + public List getRegisteredClasses() + { + return this.registeredClasses; + } + + public static void main(String[] args) throws Exception + { + String broker = args[0]; + String name = args[1]; + String url = String.format( + "amqp://guest:guest@/?brokerlist='tcp://%s'", broker); + AMQConnection conn = new AMQConnection(url); + Agent agent = new Agent(name, UUID.randomUUID()); + agent.setConnection(conn); + for (int i = 2; i < args.length; i++) + { + Class<?> cls = Class.forName(args[i]); + agent.register(new ManagedPOJO(cls.newInstance())); + } + agent.start(); + while (true) + { + Thread.sleep(1000); + } + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/AgentException.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/AgentException.java new file mode 100644 index 0000000000..ed189d2bc0 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/AgentException.java @@ -0,0 +1,43 @@ +/* + * + * 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.agent; + +/** + * AgentException + * + */ +public class AgentException extends RuntimeException +{ + public AgentException(String msg) + { + super(msg); + } + + public AgentException(Throwable t) + { + super(t); + } + + public AgentException(String msg, Throwable t) + { + super(msg, t); + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/EventSeverity.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/EventSeverity.java new file mode 100644 index 0000000000..a3528d9804 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/EventSeverity.java @@ -0,0 +1,6 @@ +package org.apache.qpid.agent; + +public enum EventSeverity +{ + EMERGENCY, ALERT, CRIT, ERROR, WARN, NOTICE, INFO, DEBUG +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedEJB.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedEJB.java new file mode 100644 index 0000000000..c27ff7acc6 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedEJB.java @@ -0,0 +1,104 @@ +/* + * + * 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.agent; + +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import org.apache.qpid.agent.binding.BindingUtils; +import org.apache.qpid.agent.binding.MethodBinding; +import org.apache.qpid.agent.binding.PropertyBinding; + +public class ManagedEJB extends ManagedObjectBase +{ + protected String className; + protected String jndiLocation; + + protected Object getEJB() + { + try + { + InitialContext ctx = new InitialContext(); + return ctx.lookup(jndiLocation); + } catch (NamingException e) + { + throw new AgentException("Error looking up EJB at " + jndiLocation, + e); + } + } + + @Override + public Object get(PropertyBinding property) + { + return BindingUtils.get(property, this.getEJB()); + } + + @Override + public long getId() + { + return System.identityHashCode(this); + } + + @Override + public Class getObjectClass() + { + try + { + return Class.forName(className); + } catch (ClassNotFoundException e) + { + throw new AgentException(String.format( + "No class named %s was found", className), e); + } + } + + @Override + public Object[] invoke(MethodBinding method, Object... args) + { + return BindingUtils.invoke(method, this.getEJB(), args); + } + + @Override + public void set(PropertyBinding property, Object value) + { + BindingUtils.set(property, value, this.getEJB()); + } + + public String getClassName() + { + return className; + } + + public void setClassName(String className) + { + this.className = className; + } + + public String getJndiLocation() + { + return jndiLocation; + } + + public void setJndiLocation(String jndiLocation) + { + this.jndiLocation = jndiLocation; + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedObject.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedObject.java new file mode 100644 index 0000000000..679ee81d76 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedObject.java @@ -0,0 +1,49 @@ +/* + * + * 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.agent; + +import org.apache.qpid.agent.binding.MethodBinding; +import org.apache.qpid.agent.binding.PropertyBinding; + +public interface ManagedObject +{ + public abstract long getId(); + + public abstract Class getObjectClass(); + + public abstract Object get(PropertyBinding property); + + public abstract void set(PropertyBinding property, Object value); + + public abstract Object[] invoke(MethodBinding method, Object... args); + + public abstract String getName(); + + public abstract void setName(String name); + + public String getManagedClassName(); + + public String getManagedPackageName(); + + public void setManagedClassName(String aName); + + public void setManagedPackageName(String aName); +}
\ No newline at end of file diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedObjectBase.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedObjectBase.java new file mode 100644 index 0000000000..51789ae11f --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedObjectBase.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.qpid.agent; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.agent.binding.MethodBinding; +import org.apache.qpid.agent.binding.PropertyBinding; + +public abstract class ManagedObjectBase implements ManagedObject +{ + private static Log log = LogFactory.getLog(ManagedObjectBase.class); + protected String name; + protected String managedClassName; + protected String managedPackageName; + + public abstract long getId(); + + public abstract Object get(PropertyBinding property); + + public abstract void set(PropertyBinding property, Object value); + + public abstract Object[] invoke(MethodBinding method, Object... args); + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getManagedClassName() + { + return managedClassName; + } + + public void setManagedClassName(String managedClassName) + { + this.managedClassName = managedClassName; + } + + public String getManagedPackageName() + { + return managedPackageName; + } + + public void setManagedPackageName(String managedPackageName) + { + this.managedPackageName = managedPackageName; + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedPOJO.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedPOJO.java new file mode 100644 index 0000000000..9a707fe09e --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/ManagedPOJO.java @@ -0,0 +1,87 @@ +/* + * + * 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.agent; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.agent.binding.BindingUtils; +import org.apache.qpid.agent.binding.MethodBinding; +import org.apache.qpid.agent.binding.PropertyBinding; + +public class ManagedPOJO extends ManagedObjectBase implements ManagedObject +{ + private Log log = LogFactory.getLog(ManagedPOJO.class); + private Object managed; + + public ManagedPOJO() + { + super(); + } + + public ManagedPOJO(Object managed) + { + super(); + this.setManaged(managed); + } + + @Override + public long getId() + { + if (managed == null) + { + throw new AgentException("The managed object is null"); + } + return System.identityHashCode(managed); + } + + public Class getObjectClass() + { + return managed.getClass(); + } + + public Object getManaged() + { + return managed; + } + + public void setManaged(Object managed) + { + this.managed = managed; + } + + @Override + public Object get(PropertyBinding property) + { + return BindingUtils.get(property, managed); + } + + @Override + public Object[] invoke(MethodBinding method, Object... args) + { + return BindingUtils.invoke(method, managed, args); + } + + @Override + public void set(PropertyBinding property, Object value) + { + BindingUtils.set(property, value, managed); + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFEvent.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFEvent.java new file mode 100644 index 0000000000..06ef46c648 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFEvent.java @@ -0,0 +1,38 @@ +/* + * + * 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.agent.annotations; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target(TYPE) +@Retention(RUNTIME) +@Documented +public @interface QMFEvent +{ + String eventName(); + + String packageName(); +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFHide.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFHide.java new file mode 100644 index 0000000000..c173e701a5 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFHide.java @@ -0,0 +1,35 @@ +/* + * + * 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.agent.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RUNTIME) +@Documented +public @interface QMFHide +{ +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFObject.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFObject.java new file mode 100644 index 0000000000..c883051fda --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFObject.java @@ -0,0 +1,38 @@ +/* + * + * 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.agent.annotations; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target(TYPE) +@Retention(RUNTIME) +@Documented +public @interface QMFObject +{ + String className(); + + String packageName(); +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFProperty.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFProperty.java new file mode 100644 index 0000000000..82e1b3a1af --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFProperty.java @@ -0,0 +1,36 @@ +/* + * + * 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.agent.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RUNTIME) +@Documented +public @interface QMFProperty +{ + boolean optional() default false; +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFSeeAlso.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFSeeAlso.java new file mode 100644 index 0000000000..924e12a574 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFSeeAlso.java @@ -0,0 +1,36 @@ +/* + * + * 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.agent.annotations; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target(TYPE) +@Retention(RUNTIME) +@Documented +public @interface QMFSeeAlso +{ + Class[] value(); +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFType.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFType.java new file mode 100644 index 0000000000..2b5aaa1983 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/annotations/QMFType.java @@ -0,0 +1,38 @@ +/* + * + * 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.agent.annotations; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target(TYPE) +@Retention(RUNTIME) +@Documented +public @interface QMFType +{ + String className(); + + String packageName(); +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/BindingContext.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/BindingContext.java new file mode 100644 index 0000000000..480d8b526c --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/BindingContext.java @@ -0,0 +1,209 @@ +/* + * + * 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.agent.binding; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.agent.annotations.QMFEvent; +import org.apache.qpid.agent.annotations.QMFObject; +import org.apache.qpid.agent.annotations.QMFSeeAlso; +import org.apache.qpid.agent.annotations.QMFType; + +public class BindingContext +{ + private static Log log = LogFactory.getLog(BindingContext.class); + private Map<Key, ClassBinding> classes = new Hashtable<Key, ClassBinding>(); + private ArrayList<String> packages = new ArrayList<String>(); + + static class Key + { + String packageName = ""; + String className = ""; + boolean object = false; + + @Override + public int hashCode() + { + return (packageName + "." + className).hashCode(); + } + + @Override + public boolean equals(Object obj) + { + return ((obj.getClass() == Key.class) + && (((Key) obj).packageName.equals(packageName)) && (((Key) obj).className + .equals(className))); + } + } + + public BindingContext() + { + Key key = new Key(); + key.className = "Object"; + key.packageName = "org.apache.qmf"; + key.object = false; + ClassBinding cb = new ClassBinding("org.apache.qmf", "Object", + Object.class, false, this); + classes.put(key, cb); + packages.add("org.apache.qmf"); + } + + public ClassBinding getClassBinding(Class clazz) + { + return classes.get(getClassKey(clazz)); + } + + public ClassBinding getClassBinding(String packageName, String className) + { + Key key = new Key(); + key.packageName = packageName; + key.className = className; + return classes.get(key); + } + + public ClassBinding register(Class cls) + { + String name = cls.getName(); + ClassBinding cb = getClassBinding(cls); + if (cb == null) + { + Key key = getClassKey(cls); + // Create and store the internal representations + if (cls.isEnum()) + { + cb = new EnumBinding(key.packageName, key.className, cls, + key.object, this); + } else + { + cb = new ClassBinding(key.packageName, key.className, cls, + key.object, this); + } + log.debug(String.format( + "Added class binding '%s' in package %s for class %s'", + key.className, key.packageName, cls.getCanonicalName())); + classes.put(key, cb); + if (!packages.contains(key.packageName)) + { + packages.add(key.packageName); + } + // Parse the methods after adding the class to avoid recursion + cb.parse(); + // See if there are other classes which should be looked at + QMFSeeAlso seeAlso = (QMFSeeAlso) cls + .getAnnotation(QMFSeeAlso.class); + if (seeAlso != null) + { + for (Class seeAlsoCls : seeAlso.value()) + { + this.register(seeAlsoCls); + } + } + } + return cb; + } + + public TypeBinding getTypeBinding(Class cls) + { + // Look for a built in type + TypeBinding type = QMFTypeBinding.forClass(cls); + // Have we seen it before? + if (type == null) + { + type = this.getClassBinding(cls); + } + if ((type == null) && List.class.isAssignableFrom(cls)) + { + type = new ListBinding(this, cls); + } + if ((type == null) && Map.class.isAssignableFrom(cls)) + { + type = new MapBinding(this, cls); + } + // Add it, but since we have not seen it before do not expose methods + if (type == null) + { + type = this.register(cls); + } + return type; + } + + // FIXME: Need to store these keys off so we dont create alot of objects + protected Key getClassKey(Class cls) + { + Key key = new Key(); + QMFObject objAnnotation = (QMFObject) cls + .getAnnotation(QMFObject.class); + if (objAnnotation != null) + { + key.className = objAnnotation.className(); + key.packageName = objAnnotation.packageName(); + key.object = true; + } else + { + QMFType typeAnnotation = (QMFType) cls.getAnnotation(QMFType.class); + if (typeAnnotation != null) + { + key.className = typeAnnotation.className(); + key.packageName = typeAnnotation.packageName(); + } else + { + QMFEvent eventAnnotation = (QMFEvent) cls + .getAnnotation(QMFEvent.class); + if (eventAnnotation != null) + { + key.className = eventAnnotation.eventName(); + key.packageName = eventAnnotation.packageName(); + } else + { + // If this is Object, we return the fake + // object value + if (cls == Object.class) + { + key.className = "Object"; + key.packageName = "org.apache.qmf"; + } else + { + String name = cls.getName(); + int lastDot = name.lastIndexOf('.'); + key.className = name.substring(lastDot + 1); + key.packageName = name.substring(0, lastDot); + } + } + } + } + return key; + } + + public ArrayList<String> getPackages() + { + return packages; + } + + public Collection<ClassBinding> getAllBindings() + { + return classes.values(); + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/BindingException.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/BindingException.java new file mode 100644 index 0000000000..f8e436290c --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/BindingException.java @@ -0,0 +1,50 @@ +/* + * + * 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.agent.binding; + +/** + * ManagedException + * + */ +public class BindingException extends RuntimeException +{ + private static final long serialVersionUID = -7350845525748113340L; + + public BindingException(Throwable t) + { + super(t); + } + + public BindingException() + { + super(); + } + + public BindingException(String message, Throwable cause) + { + super(message, cause); + } + + public BindingException(String message) + { + super(message); + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/BindingUtils.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/BindingUtils.java new file mode 100644 index 0000000000..14f3fda0f1 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/BindingUtils.java @@ -0,0 +1,137 @@ +/* + * + * 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.agent.binding; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class BindingUtils +{ + private static Log log = LogFactory.getLog(BindingUtils.class); + + public static Object get(PropertyBinding property, Object managed) + { + String name = property.getName(); + return get(name, managed); + } + + public static void set(PropertyBinding property, Object value, + Object managed) + { + String name = property.getName(); + TypeBinding type = property.getType(); + try + { + Method meth = managed.getClass().getMethod(accessor("set", name), + type.getJavaClass()); + meth.invoke(managed, value); + } catch (NoSuchMethodException e) + { + throw new BindingException(e); + } catch (IllegalAccessException e) + { + throw new BindingException(e); + } catch (InvocationTargetException e) + { + throw new BindingException(e.getTargetException()); + } + } + + public static Object[] invoke(MethodBinding method, Object managed, + Object... args) + { + log.debug(String.format("Invoking %s on %s", method.getName(), managed + .getClass())); + List<ParameterBinding> in = method.getInParameters(); + List<ParameterBinding> out = method.getOutParameters(); + Class<?>[] classes = new Class<?>[in.size()]; + int idx = 0; + for (ParameterBinding p : in) + { + classes[idx++] = p.getType().getJavaClass(); + } + Object result; + try + { + Method meth = managed.getClass().getMethod(method.getName(), + classes); + result = meth.invoke(managed, args); + } catch (NoSuchMethodException e) + { + throw new BindingException(e); + } catch (IllegalAccessException e) + { + throw new BindingException(e); + } catch (InvocationTargetException e) + { + throw new BindingException(e.getTargetException()); + } + Object[] results = new Object[out.size()]; + // XXX: need better way to distinguish this case + if (out.size() == 1 && out.get(0).getName().equals("result")) + { + results[0] = result; + } else + { + for (int i = 0; i < results.length; i++) + { + results[i] = get(out.get(i).getName(), result); + } + } + return results; + } + + public static String accessor(String pfx, String property) + { + return pfx + Character.toUpperCase(property.charAt(0)) + + property.substring(1); + } + + public static Object get(String name, Object obj) + { + Object returnValue = null; + try + { + BeanInfo info = Introspector.getBeanInfo(obj.getClass()); + PropertyDescriptor[] pds = info.getPropertyDescriptors(); + for (PropertyDescriptor pd : pds) + { + if (pd.getName().equals(name)) + { + Method getMethod = pd.getReadMethod(); + returnValue = getMethod.invoke(obj); + break; + } + } + } catch (Exception e) + { + throw new BindingException(e); + } + return returnValue; + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/ClassBinding.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/ClassBinding.java new file mode 100644 index 0000000000..d1ad04adef --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/ClassBinding.java @@ -0,0 +1,599 @@ +/* + * + * 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.agent.binding; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.agent.annotations.QMFEvent; +import org.apache.qpid.agent.annotations.QMFObject; +import org.apache.qpid.agent.annotations.QMFProperty; +import org.apache.qpid.agent.annotations.QMFSeeAlso; +import org.apache.qpid.agent.annotations.QMFType; +import org.apache.qpid.agent.annotations.QMFHide; +import org.apache.qpid.transport.codec.Decoder; +import org.apache.qpid.transport.codec.Encoder; + +public class ClassBinding implements TypeBinding +{ + private static Log log = LogFactory.getLog(ClassBinding.class); + + private static enum MethodType + { + READ_ONLY, READ_WRITE, METHOD, IGNORE + } + + protected boolean exposeBehaviour = true; + protected String pkg; + protected BindingContext bctx; + protected String name; + protected ArrayList<PropertyBinding> properties = new ArrayList<PropertyBinding>(); + protected ArrayList<MethodBinding> methods = new ArrayList<MethodBinding>(); + protected Map<String, MethodBinding> methodsByName = new HashMap<String, MethodBinding>(); + protected Class javaClass; + protected short kind = 1; + protected byte hash[] = null; + protected ClassBinding superType = null; + + public ClassBinding(String pkg, String name, Class cls, + boolean exposeBehaviour, BindingContext bctx) + { + this.pkg = pkg; + this.name = name; + this.bctx = bctx; + this.javaClass = cls; + this.exposeBehaviour = exposeBehaviour; + } + + protected MethodType classify(Class<?> cls, Method m) + { + String name = m.getName(); + MethodType returnValue = MethodType.METHOD; + String propPrefixes[] = + { "get", "is" }; + for (String prefix : propPrefixes) + { + if (name.startsWith(prefix) && m.getParameterTypes().length == 0) + { + try + { + Class<?> type = m.getReturnType(); + Method setter = cls.getMethod("set" + + name.substring(prefix.length()), type); + returnValue = MethodType.READ_WRITE; + } catch (NoSuchMethodException e) + { + returnValue = MethodType.READ_ONLY; + } + break; + } + } + return returnValue; + } + + protected String property(Method m) + { + String name = m.getName(); + String propPrefixes[] = + { "get", "is" }; + for (String prefix : propPrefixes) + { + if (name.startsWith(prefix) && m.getParameterTypes().length == 0) + { + String sfx = name.substring(prefix.length()); + return Character.toLowerCase(sfx.charAt(0)) + sfx.substring(1); + } + } + // If we got here, it is n invalid property + throw new IllegalArgumentException("" + m); + } + + protected ArrayList<Method> getMethods(Class cls) + { + ArrayList returnValue = new ArrayList(); + ArrayList nameList = new ArrayList(); + if ((cls != null) && (!cls.equals(Object.class))) + { + for (Method m : cls.getDeclaredMethods()) + { + if (m.getAnnotation(QMFHide.class) == null) + // && (!Modifier.isAbstract(m.getModifiers()))) + { + returnValue.add(m); + nameList.add(m.getName()); + } + } + // Look at the superclass, if it is also a + // QMF object then stop. + Class superType = cls.getSuperclass(); + if (!this.hasQMFSupertype(cls)) + { + for (Method m : this.getMethods(cls.getSuperclass())) + { + if (!nameList.contains(m.getName())) + { + returnValue.add(m); + nameList.add(m.getName()); + } + } + } + } + return returnValue; + } + + protected boolean hasQMFSupertype(Class cls) + { + boolean returnValue = false; + Class superType = cls.getSuperclass(); + if (superType != null) + { + if ((superType.getAnnotation(QMFObject.class) != null) + || (superType.getAnnotation(QMFType.class) != null) + || (superType.getAnnotation(QMFSeeAlso.class) != null) + || (superType.getAnnotation(QMFEvent.class) != null)) + { + returnValue = true; + } + } + return returnValue; + } + + protected boolean isOptional(Method m, TypeBinding type) + { + boolean returnValue = false; + // Look for the annotaiton first + QMFProperty ann = m.getAnnotation(QMFProperty.class); + if (ann != null) + { + returnValue = ann.optional(); + } else + { + returnValue = type.optionalDefault(); + } + return returnValue; + } + + public ClassBinding parse() + { + log.debug(String.format( + "Parsing class binding '%s' for package '%s' from class %s", + name, pkg, javaClass.getName())); + for (Method m : this.getMethods(javaClass)) + { + String mname = m.getName(); + Class<?> type = m.getReturnType(); + switch (classify(javaClass, m)) + { + case READ_ONLY: + TypeBinding tb = bctx.getTypeBinding(type); + boolean optional = isOptional(m, tb); + properties.add(new PropertyBinding(property(m), tb, + PropertyBinding.READ_ONLY, optional)); + break; + case READ_WRITE: + TypeBinding tbnd = bctx.getTypeBinding(type); + boolean opt = isOptional(m, tbnd); + properties.add(new PropertyBinding(property(m), tbnd, + PropertyBinding.READ_WRITE, opt)); + break; + case METHOD: + // Only expose methods if told to + if (exposeBehaviour) + { + List<ParameterBinding> params = new ArrayList<ParameterBinding>(); + int arg = 0; + for (Class pcls : m.getParameterTypes()) + { + params.add(new ParameterBinding("arg" + arg++, bctx + .getTypeBinding(pcls), true, false)); + } + if (type != void.class) + { + params.add(new ParameterBinding("result", bctx + .getTypeBinding(type), false, true)); + } + methods.add(new MethodBinding(mname, params)); + } + break; + case IGNORE: + break; + } + } + for (MethodBinding m : methods) + { + methodsByName.put(m.getName(), m); + } + QMFEvent eventAnnotation = (QMFEvent) javaClass + .getAnnotation(QMFEvent.class); + if (eventAnnotation != null) + { + kind = 2; // Event Type + } + // if (this.hasQMFSupertype(javaClass)) { + if ((javaClass.getSuperclass() != Object.class) + && (javaClass.getSuperclass() != null)) + { + superType = bctx.register(javaClass.getSuperclass()); + } + return this; + } + + public String getPackage() + { + return pkg; + } + + public String getName() + { + return name; + } + + public List<PropertyBinding> getProperties() + { + return properties; + } + + public List<PropertyBinding> getAllProperties() + { + if (this.superType == null) + { + return properties; + } else + { + List<PropertyBinding> newList = new ArrayList<PropertyBinding>( + properties); + for (PropertyBinding p : superType.getAllProperties()) + { + if (!newList.contains(p)) + { + newList.add(p); + } + } + return newList; + } + } + + public List<MethodBinding> getMethods() + { + return methods; + } + + public MethodBinding getMethod(String name) + { + return methodsByName.get(name); + } + + // Use this format + // bytes value + // 0-3 package name + // 4-7 class name + // 8-11 property signature hash + // 12-15 method signature hash + // FIXME: Hash codes seem to mess things up + public byte[] getSchemaHash() + { + if (null == hash) + { + hash = new byte[16]; + StringBuilder blder = new StringBuilder(); + int packageHash = pkg.hashCode(); + int classHash = name.hashCode(); + int propertyHash = 0; + int methodHash = 0; + for (PropertyBinding p : properties) + { + blder.append(p.getName()).append(":").append( + p.getType().getCode()).append(":") + .append(p.getAccess()).append(":").append( + p.isOptional()); + } + propertyHash = blder.toString().hashCode(); + blder = new StringBuilder(); + for (MethodBinding m : methods) + { + blder.append(m.getName()); + for (ParameterBinding p : m.getParameters()) + { + String direction = p.isIn() ? "in" : "out"; + blder.append(":").append(p.getName()).append(":").append( + direction).append(":") + .append(p.getType().getCode()); + } + } + methodHash = blder.toString().hashCode(); + hash[0] = (byte) (packageHash >> 24); + hash[1] = (byte) (packageHash >> 16); + hash[2] = (byte) (packageHash >> 8); + hash[3] = (byte) (packageHash); + hash[4] = (byte) (classHash >> 24); + hash[5] = (byte) (classHash >> 16); + hash[6] = (byte) (classHash >> 8); + hash[7] = (byte) (classHash); + hash[8] = (byte) (propertyHash >> 24); + hash[9] = (byte) (propertyHash >> 16); + hash[10] = (byte) (propertyHash >> 8); + hash[11] = (byte) (propertyHash); + hash[12] = (byte) (methodHash >> 24); + hash[13] = (byte) (methodHash >> 16); + hash[14] = (byte) (methodHash >> 8); + hash[15] = (byte) (methodHash); + } + return hash; + } + + public void encode(Encoder enc) + { + log.debug(String.format("encoding %s %s with superclass %s", this + .getRefClass(), this.getRefPackage(), superType)); + enc.writeUint8(kind); // kind + enc.writeStr8(pkg); + enc.writeStr8(name); + enc.writeBin128(this.getSchemaHash()); // schema hash + // Send true (1) if we have a super-type + if (superType == null) + { + enc.writeUint8((short) 0); + } else + { + enc.writeUint8((short) 1); + } + enc.writeUint16(properties.size()); + // Events do not have the method size sent + if (kind == 1) + { + enc.writeUint16(0); + enc.writeUint16(methods.size()); + } + // Add the super type information if we have it + if (superType != null) + { + enc.writeStr8(superType.pkg); + enc.writeStr8(superType.name); + enc.writeBin128(superType.getSchemaHash()); // schema hash + } + for (PropertyBinding p : properties) + { + log.trace("encoding property " + p.getName()); + p.encode(enc); + } + for (MethodBinding m : methods) + { + m.encode(enc); + } + } + + // Type Binding functions + public short getCode() + { + return (short) 20; + } + + public Class<?> getJavaClass() + { + return javaClass; + } + + public Object decode(Decoder dec) + { + // FIXME This only works with POJOs + short typeCode = dec.readUint8(); + log.trace("Type code: " + typeCode); + if (typeCode == 20) + { + String packageName = dec.readStr8(); + String className = dec.readStr8(); + log + .debug(String + .format( + "Decoding an object for package %s class %s with bindings for %s %s", + packageName, className, this.pkg, this.name)); + byte schemaHash[] = dec.readBin128(); + // Check to see that this is me, and not a subclass + if (packageName.equals(this.pkg) && className.equals(this.name)) + { + return decodeWithNoHeaders(dec); + } else + { + ClassBinding mcls = bctx + .getClassBinding(packageName, className); + return mcls.decodeWithNoHeaders(dec); + } + } else + { + TypeBinding tb = QMFTypeBinding.getType(typeCode); + return tb.decode(dec); + } + } + + protected Object decodeWithNoHeaders(Decoder dec) + { + Object instance = null; + try + { + log.trace("Creating a new instance of " + this.javaClass.getName()); + instance = this.javaClass.newInstance(); + } catch (Exception e) + { + log.error("Could not instantiate object of class" + + this.javaClass.getName()); + throw new BindingException(e); + } + List<String> excludes = this.processPresenceMasks(dec); + for (PropertyBinding p : getAllProperties()) + { + if (!excludes.contains(p.getName())) + { + Object value = p.getType().decode(dec); + BindingUtils.set(p, value, instance); + } + } + return instance; + } + + protected List<String> processPresenceMasks(Decoder dec) + { + List<String> excludes = new ArrayList<String>(); + short bit = 0; + short mask = 0; + for (PropertyBinding prop : properties) + { + if (prop.isOptional()) + { + if (bit == 0) + { + mask = dec.readUint8(); + bit = 1; + } + if ((mask & bit) == 0) + { + log.trace("Going in exlude " + prop.getName()); + excludes.add(prop.getName()); + } + bit *= 2; + if (bit == 256) + { + bit = 0; + } + } + } + return excludes; + } + + public void encode(Encoder enc, Object value) + { + // if the object is null, assume this is the + // correct class + if (value == null || (value.getClass().equals(this.javaClass))) + { + String pkg = getPackage(); + String cls = getName(); + log.debug(String.format("Encoding class %s:%s", pkg, cls)); + enc.writeUint8(this.getCode()); + enc.writeStr8(pkg); + enc.writeStr8(cls); + enc.writeBin128(this.getSchemaHash()); + short bit = 0; + short mask = 0; + if (value != null) + { + // Encode the property presence masks first. + // if this is not an event + if (!isEvent()) + { + for (PropertyBinding p : getAllProperties()) + { + if (p.isOptional()) + { + Object pValue = BindingUtils.get(p, value); + if (bit == 0) + bit = 1; + if (pValue != null) + { + mask |= bit; + } + if (bit == 128) + { + enc.writeUint8(mask); + bit = 0; + mask = 0; + } else + { + bit = (short) (bit << 1); + } + } + } + if (bit != 0) + { + enc.writeUint8(mask); + } + } + // Now put the actual properties + for (PropertyBinding p : getAllProperties()) + { + Object pValue = BindingUtils.get(p, value); + if (!p.isOptional() || !(pValue == null)) + { + log.trace(String.format("Encoding property %s", p + .getName())); + p.getType().encode(enc, pValue); + } + } + } + log.debug(String.format("Done with %s:%s", pkg, cls)); + } else + { + TypeBinding tb = bctx.getTypeBinding(value.getClass()); + if (tb == null) + { + throw new BindingException(String.format( + "No class named %s defined for this context ", value + .getClass())); + } else + { + if (tb.isNative()) + { + enc.writeUint8(tb.getCode()); + } + tb.encode(enc, value); + } + } + } + + public boolean isNative() + { + return false; + } + + public boolean optionalDefault() + { + return true; + } + + public String getRefClass() + { + return this.name; + } + + public String getRefPackage() + { + return this.pkg; + } + + public short getKind() + { + return kind; + } + + public boolean isEvent() + { + return kind == 2; + } + + public void setKind(short kind) + { + this.kind = kind; + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/EnumBinding.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/EnumBinding.java new file mode 100644 index 0000000000..37d20341d9 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/EnumBinding.java @@ -0,0 +1,99 @@ +/* + * + * 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.agent.binding; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.transport.codec.Decoder; +import org.apache.qpid.transport.codec.Encoder; + +public class EnumBinding extends ClassBinding +{ + private static Log log = LogFactory.getLog(EnumBinding.class); + + public EnumBinding(String pkg, String name, Class cls, + boolean exposeBehaviour, BindingContext bctx) + { + super(pkg, name, cls, exposeBehaviour, bctx); + } + + @Override + public void encode(Encoder enc) + { + enc.writeUint8((short) 1); // kind + enc.writeStr8(pkg); + enc.writeStr8(name); + enc.writeBin128(new byte[16]); // schema hash + // FIXME Is there a way to send the valid types? + } + + @Override + public void encode(Encoder enc, Object value) + { + if (value != null) + { + enc.writeStr16(value.toString()); + } else + { + enc.writeStr16(""); + } + } + + @Override + public Object decode(Decoder dec) + { + // FIXME This only works with POJOs + Object instance = null; + try + { + String value = dec.readStr16(); + instance = Enum.valueOf((Class<Enum>) this.getJavaClass(), value); + } catch (Exception e) + { + log.error("Could not create an enum of type " + + this.javaClass.getName()); + throw new BindingException(e); + } + return instance; + } + + // Make this look like a String + @Override + public short getCode() + { + return (short) 7; + } + + @Override + public EnumBinding parse() + { + log.debug(String.format( + "Parsing enum binding '%s' for package '%s' from class %s", + name, pkg, javaClass.getName())); + return this; + } + + @Override + public boolean optionalDefault() + { + return false; + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/ListBinding.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/ListBinding.java new file mode 100644 index 0000000000..fcdd20a0c1 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/ListBinding.java @@ -0,0 +1,122 @@ +/* + * + * 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.agent.binding; + +import java.nio.ByteBuffer; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.transport.codec.BBDecoder; +import org.apache.qpid.transport.codec.BBEncoder; +import org.apache.qpid.transport.codec.Decoder; +import org.apache.qpid.transport.codec.Encoder; + +public class ListBinding implements TypeBinding +{ + private static Log log = LogFactory.getLog(ListBinding.class); + protected BindingContext bctx; + protected Class javaClass; + + public ListBinding(BindingContext bctx, Class javaClass) + { + this.bctx = bctx; + this.javaClass = javaClass; + } + + public void encode(Encoder enc, Object value) + { + List list = (List) value; + BBEncoder newEncoder = new BBEncoder(10); + newEncoder.writeUint32(list.size()); + for (Object obj : list) + { + TypeBinding type = bctx.getTypeBinding(obj.getClass()); + newEncoder.writeUint8(type.getCode()); + type.encode(newEncoder, obj); + } + enc.writeVbin32(newEncoder.buffer().array()); + } + + public Object decode(Decoder dec) + { + List list = null; + try + { + list = (List) javaClass.newInstance(); + } catch (Exception e) + { + throw new BindingException( + "Could not create a List implementation for " + + javaClass.getName(), e); + } + BBDecoder newDecoder = new BBDecoder(); + newDecoder.init(ByteBuffer.wrap(dec.readVbin32())); + long count = newDecoder.readUint32(); + while (count > 0) + { + short typeCode = newDecoder.readUint8(); + TypeBinding type = QMFTypeBinding.getType(typeCode); + if (type == null) + { + type = bctx.getTypeBinding(Object.class); + } + list.add(type.decode(newDecoder)); + count -= 1; + } + return list; + } + + // QMF List Type + public short getCode() + { + return (short) 21; + } + + @Override + public Class<?> getJavaClass() + { + return javaClass; + } + + @Override + public String getRefClass() + { + return null; + } + + @Override + public String getRefPackage() + { + return null; + } + + @Override + public boolean isNative() + { + return true; + } + + public boolean optionalDefault() + { + return false; + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/MapBinding.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/MapBinding.java new file mode 100644 index 0000000000..8b0df57e89 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/MapBinding.java @@ -0,0 +1,133 @@ +/* + * + * 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.agent.binding; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.transport.codec.BBDecoder; +import org.apache.qpid.transport.codec.BBEncoder; +import org.apache.qpid.transport.codec.Decoder; +import org.apache.qpid.transport.codec.Encoder; + +public class MapBinding implements TypeBinding +{ + private static Log log = LogFactory.getLog(MapBinding.class); + protected BindingContext bctx; + protected Class javaClass; + + public MapBinding(BindingContext bctx, Class javaClass) + { + this.bctx = bctx; + this.javaClass = javaClass; + } + + @SuppressWarnings("unchecked") + public void encode(Encoder enc, Object value) + { + Map map = (Map) value; + BBEncoder newEncoder = new BBEncoder(10); + newEncoder.writeUint32(map.size()); + for (Object key : map.keySet()) + { + String keyString = key.toString(); + Object mapValue = map.get(key); + TypeBinding binding = bctx.getTypeBinding(mapValue.getClass()); + newEncoder.writeStr8(keyString); + newEncoder.writeUint8(binding.getCode()); + binding.encode(newEncoder, mapValue); + } + enc.writeVbin32(newEncoder.buffer().array()); + } + + public Object decode(Decoder dec) + { + Map map = null; + try + { + if (javaClass.isInterface()) + { + map = new HashMap(); + } else + { + map = (Map) javaClass.newInstance(); + } + } catch (Exception e) + { + throw new BindingException( + "Could not create a Map implementation for " + + javaClass.getName(), e); + } + BBDecoder newDecoder = new BBDecoder(); + newDecoder.init(ByteBuffer.wrap(dec.readVbin32())); + long count = newDecoder.readUint32(); + while (count > 0) + { + String key = newDecoder.readStr8(); + short typeCode = newDecoder.readUint8(); + TypeBinding type = QMFTypeBinding.getType(typeCode); + if (type == null) + { + type = bctx.getTypeBinding(Object.class); + } + map.put(key, type.decode(newDecoder)); + count -= 1; + } + return map; + } + + // QMF List Type + public short getCode() + { + return (short) 15; + } + + @Override + public Class<?> getJavaClass() + { + return javaClass; + } + + @Override + public String getRefClass() + { + return null; + } + + @Override + public String getRefPackage() + { + return null; + } + + @Override + public boolean isNative() + { + return true; + } + + public boolean optionalDefault() + { + return false; + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/MethodBinding.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/MethodBinding.java new file mode 100644 index 0000000000..d6dec92b13 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/MethodBinding.java @@ -0,0 +1,88 @@ +/* + * + * 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.agent.binding; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.transport.codec.Encoder; + +public class MethodBinding +{ + private final String name; + private final List<ParameterBinding> parameters; + private final List<ParameterBinding> inParameters = new ArrayList<ParameterBinding>(); + private final List<ParameterBinding> outParameters = new ArrayList<ParameterBinding>(); + private Log log = LogFactory.getLog(MethodBinding.class); + + public MethodBinding(String name, List<ParameterBinding> parameters) + { + this.name = name; + this.parameters = parameters; + for (ParameterBinding p : parameters) + { + if (p.isIn()) + { + inParameters.add(p); + } + if (p.isOut()) + { + outParameters.add(p); + } + } + } + + public String getName() + { + return name; + } + + public List<ParameterBinding> getParameters() + { + return parameters; + } + + public List<ParameterBinding> getInParameters() + { + return inParameters; + } + + public List<ParameterBinding> getOutParameters() + { + return outParameters; + } + + void encode(Encoder enc) + { + Map map = new HashMap(); + map.put("name", name); + map.put("argCount", parameters.size()); + enc.writeMap(map); + for (ParameterBinding p : parameters) + { + p.encode(enc); + } + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/ParameterBinding.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/ParameterBinding.java new file mode 100644 index 0000000000..3c707dc9cb --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/ParameterBinding.java @@ -0,0 +1,118 @@ +/* + * + * 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.agent.binding; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.transport.codec.Encoder; + +public class ParameterBinding +{ + private final String name; + private final TypeBinding type; + private final boolean in; + private final boolean out; + + public ParameterBinding(String name, TypeBinding type, boolean in, + boolean out) + { + this.name = name; + this.type = type; + this.in = in; + this.out = out; + } + + public String getName() + { + return name; + } + + public TypeBinding getType() + { + return type; + } + + public boolean isIn() + { + return in; + } + + public boolean isOut() + { + return out; + } + + void encode(Encoder enc) + { + Map map = new HashMap(); + map.put("name", name); + map.put("type", type.getCode()); + map.put("dir", (in ? "I" : "") + (out ? "O" : "")); + if (!type.isNative()) + { + map.put("refClass", type.getRefClass()); + map.put("refPackage", type.getRefPackage()); + } + enc.writeMap(map); + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + (in ? 1231 : 1237); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + (out ? 1231 : 1237); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ParameterBinding other = (ParameterBinding) obj; + if (in != other.in) + return false; + if (name == null) + { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (out != other.out) + return false; + if (type == null) + { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + return true; + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/PropertyBinding.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/PropertyBinding.java new file mode 100644 index 0000000000..6c94dc5b17 --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/PropertyBinding.java @@ -0,0 +1,128 @@ +/* + * + * 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.agent.binding; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.transport.codec.Encoder; + +public class PropertyBinding +{ + private static Log log = LogFactory.getLog(PropertyBinding.class); + public final static int READ_CREATE = 1; + public final static int READ_WRITE = 2; + public final static int READ_ONLY = 3; + private String name; + private TypeBinding type; + private int accessType; + private boolean optional; + + public PropertyBinding(String name, TypeBinding type, int accessType, + boolean optional) + { + this.name = name; + this.type = type; + this.accessType = accessType; + this.optional = optional; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + accessType; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PropertyBinding other = (PropertyBinding) obj; + if (accessType != other.accessType) + return false; + if (name == null) + { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (type == null) + { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + return true; + } + + public String getName() + { + return name; + } + + public TypeBinding getType() + { + return type; + } + + public int getAccess() + { + return accessType; + } + + public boolean isIndex() + { + return false; + } + + public boolean isOptional() + { + return optional; + } + + void encode(Encoder enc) + { + Map map = new HashMap(); + map.put("name", name); + map.put("type", type.getCode()); + map.put("access", getAccess()); + map.put("index", isIndex() ? 1 : 0); + map.put("optional", isOptional() ? 1 : 0); + if (!type.isNative()) + { + map.put("refClass", type.getRefClass()); + map.put("refPackage", type.getRefPackage()); + } + enc.writeMap(map); + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/QMFTypeBinding.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/QMFTypeBinding.java new file mode 100644 index 0000000000..9d86f27c1a --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/QMFTypeBinding.java @@ -0,0 +1,462 @@ +/* + * + * 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.agent.binding; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.transport.codec.Decoder; +import org.apache.qpid.transport.codec.Encoder; + +public abstract class QMFTypeBinding implements TypeBinding +{ + private static final Map<Class<?>, QMFTypeBinding> TYPES = new HashMap<Class<?>, QMFTypeBinding>(); + private static final Map<Short, QMFTypeBinding> TYPES_BY_CODE = new HashMap<Short, QMFTypeBinding>(); + static + { + new QMFTypeBinding(null, (short) 1) + { + @Override + public Object decode(Decoder dec) + { + return Short.valueOf(dec.readUint8()); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeUint8(((Number) value).shortValue()); + } + }; + new QMFTypeBinding(null, (short) 2) + { + @Override + public Object decode(Decoder dec) + { + return Integer.valueOf(dec.readUint16()); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeUint16(((Number) value).intValue()); + } + }; + new QMFTypeBinding(null, (short) 3) + { + @Override + public Object decode(Decoder dec) + { + return Long.valueOf(dec.readUint32()); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeUint32(((Number) value).longValue()); + } + }; + new QMFTypeBinding(null, (short) 4) + { + @Override + public Object decode(Decoder dec) + { + return Long.valueOf(dec.readUint64()); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeUint64(((Number) value).longValue()); + } + }; + new QMFTypeBinding(null, (short) 6) // short string + { + @Override + public Object decode(Decoder dec) + { + return dec.readStr8(); + } + + @Override + public void encode(Encoder enc, Object value) + { + if (null == value) + value = ""; + enc.writeStr8((String) value); + } + }; + new QMFTypeBinding(String.class, (short) 7) // long string + { + @Override + public Object decode(Decoder dec) + { + return dec.readStr16(); + } + + @Override + public void encode(Encoder enc, Object value) + { + if (null == value) + value = ""; + enc.writeStr16((String) value); + } + }; + new QMFTypeBinding(Date.class, (short) 8) + { + @Override + public Object decode(Decoder dec) + { + return new Date(dec.readDatetime()); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeDatetime(((Date) value).getTime()); + } + }; + new QMFTypeBinding(Boolean.class, (short) 11) + { + @Override + public Object decode(Decoder dec) + { + return Boolean.valueOf(dec.readUint8() != 0); + } + + @Override + public void encode(Encoder enc, Object value) + { + if (((Boolean) value).booleanValue()) + { + enc.writeUint8((short) 1); + } else + { + enc.writeUint8((short) 0); + } + } + + @Override + public short[] alternateTypes() + { + short[] types = + { 5 }; + return types; + } + }; + new QMFTypeBinding(boolean.class, (short) 11) + { + @Override + public Object decode(Decoder dec) + { + return dec.readUint8() != 0; + } + + @Override + public void encode(Encoder enc, Object value) + { + if (((Boolean) value).booleanValue()) + { + enc.writeUint8((short) 1); + } else + { + enc.writeUint8((short) 0); + } + } + }; + new QMFTypeBinding(Float.class, (short) 12) + { + @Override + public Object decode(Decoder dec) + { + return Float.valueOf(dec.readFloat()); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeFloat(((Number) value).floatValue()); + } + }; + new QMFTypeBinding(float.class, (short) 12) + { + @Override + public Object decode(Decoder dec) + { + return dec.readFloat(); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeFloat(((Number) value).floatValue()); + } + }; + new QMFTypeBinding(Double.class, (short) 13) + { + @Override + public Object decode(Decoder dec) + { + return Double.valueOf(dec.readDouble()); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeDouble(((Number) value).doubleValue()); + } + }; + new QMFTypeBinding(double.class, (short) 13) + { + @Override + public Object decode(Decoder dec) + { + return dec.readDouble(); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeDouble(((Number) value).doubleValue()); + } + }; + new QMFTypeBinding(UUID.class, (short) 14) + { + @Override + public Object decode(Decoder dec) + { + return dec.readUuid(); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeUuid((UUID) value); + } + }; + new QMFTypeBinding(byte.class, (short) 16) + { + @Override + public Object decode(Decoder dec) + { + return dec.readInt8(); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeInt8(((Number) value).byteValue()); + } + }; + new QMFTypeBinding(Short.class, (short) 17) + { + @Override + public Object decode(Decoder dec) + { + return Short.valueOf(dec.readInt16()); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeInt16(((Number) value).shortValue()); + } + }; + new QMFTypeBinding(short.class, (short) 17) + { + @Override + public Object decode(Decoder dec) + { + return dec.readInt16(); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeInt16(((Number) value).shortValue()); + } + }; + new QMFTypeBinding(Integer.class, (short) 18) + { + @Override + public Object decode(Decoder dec) + { + return Integer.valueOf(dec.readInt32()); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeInt32(((Number) value).intValue()); + } + }; + new QMFTypeBinding(int.class, (short) 18) + { + @Override + public Object decode(Decoder dec) + { + return dec.readInt32(); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeInt32(((Number) value).intValue()); + } + }; + new QMFTypeBinding(Long.class, (short) 19) + { + @Override + public Object decode(Decoder dec) + { + return Long.valueOf(dec.readInt64()); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeInt64(((Number) value).longValue()); + } + }; + new QMFTypeBinding(long.class, (short) 19) + { + @Override + public Object decode(Decoder dec) + { + return dec.readInt64(); + } + + @Override + public void encode(Encoder enc, Object value) + { + enc.writeInt64(((Number) value).longValue()); + } + }; + } + + public static final QMFTypeBinding forClass(Class<?> cls) + { + QMFTypeBinding t = TYPES.get(cls); + return t; + } + + public static final boolean isBound(Class<?> cls) + { + return TYPES.containsKey(cls); + } + + public static QMFTypeBinding getType(short code) + { + return TYPES_BY_CODE.get(code); + } + + private final Class<?> cls; + private final short code; + + private QMFTypeBinding(Class<?> cls, short code) + { + this.cls = cls; + this.code = code; + if (cls != null) + { + TYPES.put(cls, this); + } + TYPES_BY_CODE.put(code, this); + for (short type : this.alternateTypes()) + { + TYPES_BY_CODE.put(type, this); + } + } + + public Class<?> getJavaClass() + { + return cls; + } + + public short getCode() + { + return code; + } + + public boolean isNative() + { + return true; + } + + public boolean optionalDefault() + { + return false; + } + + public String getRefClass() + { + return null; + } + + public String getRefPackage() + { + return null; + } + + public abstract Object decode(Decoder dec); + + public abstract void encode(Encoder enc, Object value); + + public short[] alternateTypes() + { + short[] types = + {}; + return types; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((cls == null) ? 0 : cls.hashCode()); + result = prime * result + code; + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + QMFTypeBinding other = (QMFTypeBinding) obj; + if (cls == null) + { + if (other.cls != null) + return false; + } else if (!cls.equals(other.cls)) + return false; + if (code != other.code) + return false; + return true; + } +} diff --git a/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/TypeBinding.java b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/TypeBinding.java new file mode 100644 index 0000000000..492e533fdc --- /dev/null +++ b/qpid/java/agent/src/main/java/org/apache/qpid/agent/binding/TypeBinding.java @@ -0,0 +1,43 @@ +/* + * + * 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.agent.binding; + +import org.apache.qpid.transport.codec.Decoder; +import org.apache.qpid.transport.codec.Encoder; + +public interface TypeBinding +{ + public Object decode(Decoder dec); + + public void encode(Encoder enc, Object value); + + public Class<?> getJavaClass(); + + public short getCode(); + + public boolean isNative(); + + public String getRefClass(); + + public String getRefPackage(); + + public boolean optionalDefault(); +} diff --git a/qpid/java/agent/src/test/java/org/apache/qpid/agent/Crumpet.java b/qpid/java/agent/src/test/java/org/apache/qpid/agent/Crumpet.java new file mode 100644 index 0000000000..67095c809b --- /dev/null +++ b/qpid/java/agent/src/test/java/org/apache/qpid/agent/Crumpet.java @@ -0,0 +1,70 @@ +/* + * + * 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.agent; + +import java.util.ArrayList; + +import org.apache.qpid.agent.annotations.QMFSeeAlso; +import org.apache.qpid.agent.annotations.QMFType; + +/** + * Crumpet + * + */ +@QMFType(className = "Crumpet", packageName = "org.apache.test") +@QMFSeeAlso( +{ Pikelet.class }) +public class Crumpet +{ + private String foo = "fooValue"; + private String bar = "barValue"; + private ArrayList<String> ingredients = new ArrayList<String>(); + + public String getFoo() + { + return foo; + } + + public void setFoo(String foo) + { + this.foo = foo; + } + + public String getBar() + { + return bar; + } + + public void setBar(String bar) + { + this.bar = bar; + } + + public ArrayList<String> getIngredients() + { + return ingredients; + } + + public void setIngredients(ArrayList<String> ingredients) + { + this.ingredients = ingredients; + } +} diff --git a/qpid/java/agent/src/test/java/org/apache/qpid/agent/Muppet.java b/qpid/java/agent/src/test/java/org/apache/qpid/agent/Muppet.java new file mode 100644 index 0000000000..f039ab9baa --- /dev/null +++ b/qpid/java/agent/src/test/java/org/apache/qpid/agent/Muppet.java @@ -0,0 +1,113 @@ +/* + * + * 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.agent; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.qpid.agent.annotations.QMFObject; + +@QMFObject(className = "Muppet", packageName = "org.apache.test") +public class Muppet extends Puppet +{ + private Log log = LogFactory.getLog(Muppet.class); + + public String getSomething() + { + return "something"; + } + + public void doSomething(String str) + { + log.debug(String.format("doSomething: %s", str)); + } + + public String returnSomething() + { + log.debug("returning something"); + return "asdf"; + } + + public Crumpet gimmieCrumpet(String asdf, int n, float f, Map foo) + { + log.debug(String + .format("mmm, crumpet: %s, %s, %s, %s", asdf, n, f, foo)); + Crumpet crumpet = new Crumpet(); + crumpet.getIngredients().add("Butter"); + crumpet.getIngredients().add("Salt"); + crumpet.getIngredients().add("Flour"); + return crumpet; + } + + public Crumpet gimmieCrumpet2() + { + Pikelet pik = new Pikelet(); + pik.getIngredients().add("Butter"); + pik.getIngredients().add("Salt"); + pik.getIngredients().add("Eggs"); + pik.getCrumpets().put("Crumpet1", + this.gimmieCrumpet("2121", 1, 1, null)); + return pik; + } + + public List gimmeLotsOfCrumpets() + { + log.debug("Asking for lots of Crumpets"); + ArrayList<Crumpet> returnValue = new ArrayList<Crumpet>(); + Crumpet crumpet = new Crumpet(); + crumpet.getIngredients().add("Chocolate"); + returnValue.add(crumpet); + crumpet = new Crumpet(); + crumpet.getIngredients().add("Pecans"); + returnValue.add(crumpet); + crumpet = new Pikelet(); + crumpet.getIngredients().add("Poached Eggs"); + returnValue.add(crumpet); + return returnValue; + } + + public int divideByZero() + { + return 1 / 0; + } + + public Crumpet takeCrumpet(Crumpet newCrumpet) + { + log.debug(String.format("I gots me a crumpet: foo: '%s' bar: '%s'", + newCrumpet.getFoo(), newCrumpet.getBar())); + log.debug("My crumpet's class is " + newCrumpet.getClass().getName()); + for (String ingredient : newCrumpet.getIngredients()) + { + log.debug("My crumpet is made of " + ingredient); + } + return newCrumpet; + } + + public Object takeSomething(Object obj) + { + log.debug(String.format("I gots me a something: '%s'", obj.getClass() + .getName())); + return obj; + } +} diff --git a/qpid/java/agent/src/test/java/org/apache/qpid/agent/Pikelet.java b/qpid/java/agent/src/test/java/org/apache/qpid/agent/Pikelet.java new file mode 100644 index 0000000000..f820fa6258 --- /dev/null +++ b/qpid/java/agent/src/test/java/org/apache/qpid/agent/Pikelet.java @@ -0,0 +1,52 @@ +/* + * + * 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.agent; + +import java.util.HashMap; + +import org.apache.qpid.agent.annotations.QMFType; + +@QMFType(className = "Pikelet", packageName = "org.apache.test") +public class Pikelet extends Crumpet +{ + protected String shape; + HashMap<String, Crumpet> crumpets = new HashMap<String, Crumpet>(); + + public String getShape() + { + return shape; + } + + public void setShape(String shape) + { + this.shape = shape; + } + + public HashMap<String, Crumpet> getCrumpets() + { + return crumpets; + } + + public void setCrumpets(HashMap<String, Crumpet> crumpets) + { + this.crumpets = crumpets; + } +} diff --git a/qpid/java/agent/src/test/java/org/apache/qpid/agent/Puppet.java b/qpid/java/agent/src/test/java/org/apache/qpid/agent/Puppet.java new file mode 100644 index 0000000000..bfd34840f8 --- /dev/null +++ b/qpid/java/agent/src/test/java/org/apache/qpid/agent/Puppet.java @@ -0,0 +1,29 @@ +/* + * + * 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.agent; + +public class Puppet +{ + public int countStrings() + { + return 4; + } +} diff --git a/qpid/java/build.deps b/qpid/java/build.deps index 6bb9bfa09e..275f3e3776 100644 --- a/qpid/java/build.deps +++ b/qpid/java/build.deps @@ -55,7 +55,7 @@ javassist=lib/javassist.jar jetty=lib/jetty-6.1.14.jar jetty-util=lib/jetty-util-6.1.14.jar jetty-bootstrap=lib/start.jar - +jms=lib/jms-1.1.jar jsp-api=lib/jsp-api-2.1.jar jsp-impl=lib/jsp-2.1.jar core-lib=lib/core-3.1.1.jar @@ -86,6 +86,8 @@ broker.libs=${common.libs} ${commons-cli} ${commons-logging} ${log4j} \ broker-plugins.libs=${common.libs} ${felix.libs} management-client.libs=${jsp.libs} ${log4j} ${slf4j-log4j} ${slf4j-api} ${commons-pool} ${geronimo-servlet} ${muse.libs} ${javassist} ${xalan} ${mina-core} ${mina-filter-ssl} +agent.libs=${jms} + junit-toolkit.libs=${log4j} ${junit} ${slf4j-api} test.libs=${slf4j-log4j} ${junit-toolkit.libs} systests.libs=${client.libs} ${test.libs} ${broker.libs} diff --git a/qpid/java/build.xml b/qpid/java/build.xml index 3b70707e61..530d220d54 100644 --- a/qpid/java/build.xml +++ b/qpid/java/build.xml @@ -22,7 +22,7 @@ <import file="common.xml"/> - <property name="modules.core" value="junit-toolkit common broker client tools"/> + <property name="modules.core" value="junit-toolkit common broker client tools agent"/> <property name="modules.examples" value="client/example"/> <property name="modules.tests" value="systests perftests integrationtests testkit"/> <property name="modules.management" value="management/common management/client management/eclipse-plugin"/> diff --git a/qpid/java/management/client/src/main/java/org/apache/qpid/management/Protocol.java b/qpid/java/management/client/src/main/java/org/apache/qpid/management/Protocol.java index c1b1ceb5b4..48ef10f60a 100644 --- a/qpid/java/management/client/src/main/java/org/apache/qpid/management/Protocol.java +++ b/qpid/java/management/client/src/main/java/org/apache/qpid/management/Protocol.java @@ -27,7 +27,7 @@ package org.apache.qpid.management; */ public interface Protocol { - String MAGIC_NUMBER = "AM2"; + String MAGIC_NUMBER = "AM3"; char SCHEMA_REQUEST_OPCODE = 'S'; char SCHEMA_RESPONSE_OPCODE = Character.toLowerCase(SCHEMA_REQUEST_OPCODE); diff --git a/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/SchemaResponseMessageHandler.java b/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/SchemaResponseMessageHandler.java index ee5efe2af6..e05bcee820 100644 --- a/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/SchemaResponseMessageHandler.java +++ b/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/SchemaResponseMessageHandler.java @@ -68,11 +68,18 @@ public class SchemaResponseMessageHandler extends BaseMessageHandler String className = decoder.readStr8(); Binary schemaHash = new Binary(decoder.readBin128()); - + + int hasSuperclass = decoder.readUint8(); int howManyProperties = decoder.readUint16(); int howManyStatistics = decoder.readUint16(); int howManyMethods = decoder.readUint16(); + if (hasSuperclass != 0) { + String parentPackageName = decoder.readStr8(); + String parentClassName = decoder.readStr8(); + Binary parentHash = new Binary(decoder.readBin128()); + } + _domainModel.addSchema( packageName, className, @@ -214,4 +221,4 @@ public class SchemaResponseMessageHandler extends BaseMessageHandler } return result; } - }
\ No newline at end of file + } diff --git a/qpid/python/commands/qpid-config b/qpid/python/commands/qpid-config index 838e9f340f..59145620cd 100755 --- a/qpid/python/commands/qpid-config +++ b/qpid/python/commands/qpid-config @@ -473,7 +473,7 @@ try: except KeyboardInterrupt: print except Exception,e: - print "Failed:", e.args + print "Failed: %s: %s" % (e.__class__.__name__, e) sys.exit(1) bm.Disconnect() diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py index de8df06adc..36553562f7 100644 --- a/qpid/python/qmf/console.py +++ b/qpid/python/qmf/console.py @@ -25,10 +25,13 @@ import qpid import struct import socket import re +from qpid.datatypes import UUID +from qpid.datatypes import timestamp +from qpid.datatypes import datetime from qpid.peer import Closed from qpid.session import SessionDetached -from qpid.connection import Connection, ConnectionFailed -from qpid.datatypes import Message, RangedSet +from qpid.connection import Connection, ConnectionFailed, Timeout +from qpid.datatypes import Message, RangedSet, UUID from qpid.util import connect, ssl, URL from qpid.codec010 import StringCodec as Codec from threading import Lock, Condition, Thread @@ -107,6 +110,289 @@ class BrokerURL(URL): def match(self, host, port): return socket.gethostbyname(self.host) == socket.gethostbyname(host) and self.port == port +class Object(object): + """ This class defines a 'proxy' object representing a real managed object on an agent. + Actions taken on this proxy are remotely affected on the real managed object. + """ + def __init__(self, session, broker, schema, codec, prop, stat, managed=True, kwargs={}): + self._session = session + self._broker = broker + self._schema = schema + self._managed = managed + if self._managed: + self._currentTime = codec.read_uint64() + self._createTime = codec.read_uint64() + self._deleteTime = codec.read_uint64() + self._objectId = ObjectId(codec) + else: + self._currentTime = None + self._createTime = None + self._deleteTime = None + self._objectId = None + self._properties = [] + self._statistics = [] + if codec: + if prop: + notPresent = self._parsePresenceMasks(codec, schema) + for property in schema.getProperties(): + if property.name in notPresent: + self._properties.append((property, None)) + else: + self._properties.append((property, self._session._decodeValue(codec, property.type, broker))) + if stat: + for statistic in schema.getStatistics(): + self._statistics.append((statistic, self._session._decodeValue(codec, statistic.type, broker))) + else: + for property in schema.getProperties(): + if property.optional: + self._properties.append((property, None)) + else: + self._properties.append((property, self._session._defaultValue(property, broker, kwargs))) + for statistic in schema.getStatistics(): + self._statistics.append((statistic, self._session._defaultValue(statistic, broker, kwargs))) + + def getBroker(self): + """ Return the broker from which this object was sent """ + return self._broker + + def getObjectId(self): + """ Return the object identifier for this object """ + return self._objectId + + def getClassKey(self): + """ Return the class-key that references the schema describing this object. """ + return self._schema.getKey() + + def getSchema(self): + """ Return the schema that describes this object. """ + return self._schema + + def getMethods(self): + """ Return a list of methods available for this object. """ + return self._schema.getMethods() + + def getTimestamps(self): + """ Return the current, creation, and deletion times for this object. """ + return self._currentTime, self._createTime, self._deleteTime + + def isDeleted(self): + """ Return True iff this object has been deleted. """ + return self._deleteTime != 0 + + def isManaged(self): + """ Return True iff this object is a proxy for a managed object on an agent. """ + return self._managed + + def getIndex(self): + """ Return a string describing this object's primary key. """ + result = u"" + for property, value in self._properties: + if property.index: + if result != u"": + result += u":" + try: + valstr = unicode(self._session._displayValue(value, property.type)) + except: + valstr = u"<undecodable>" + result += valstr + return result + + def getProperties(self): + """ Return a list of object properties """ + return self._properties + + def getStatistics(self): + """ Return a list of object statistics """ + return self._statistics + + def mergeUpdate(self, newer): + """ Replace properties and/or statistics with a newly received update """ + if not self.isManaged(): + raise Exception("Object is not managed") + if self._objectId != newer._objectId: + raise Exception("Objects with different object-ids") + if len(newer.getProperties()) > 0: + self._properties = newer.getProperties() + if len(newer.getStatistics()) > 0: + self._statistics = newer.getStatistics() + + def update(self): + """ Contact the agent and retrieve the lastest property and statistic values for this object. """ + if not self.isManaged(): + raise Exception("Object is not managed") + obj = self._session.getObjects(_objectId = self._objectId, _broker=self._broker) + if obj: + self.mergeUpdate(obj[0]) + else: + raise Exception("Underlying object no longer exists") + + def __repr__(self): + if self.isManaged(): + id = self.getObjectId().__repr__() + else: + id = "unmanaged" + key = self.getClassKey() + return key.getPackageName() + ":" + key.getClassName() +\ + "[" + id + "] " + self.getIndex().encode("utf8") + + def __getattr__(self, name): + for method in self._schema.getMethods(): + if name == method.name: + return lambda *args, **kwargs : self._invoke(name, args, kwargs) + for property, value in self._properties: + if name == property.name: + return value + if name == "_" + property.name + "_" and property.type == 10: # Dereference references + deref = self._session.getObjects(_objectId=value, _broker=self._broker) + if len(deref) != 1: + return None + else: + return deref[0] + for statistic, value in self._statistics: + if name == statistic.name: + return value + raise Exception("Type Object has no attribute '%s'" % name) + + def __setattr__(self, name, value): + if name[0] == '_': + super.__setattr__(self, name, value) + return + + for prop, unusedValue in self._properties: + if name == prop.name: + newprop = (prop, value) + newlist = [] + for old, val in self._properties: + if name == old.name: + newlist.append(newprop) + else: + newlist.append((old, val)) + self._properties = newlist + return + super.__setattr__(self, name, value) + + def _sendMethodRequest(self, name, args, kwargs, synchronous=False, timeWait=None): + for method in self._schema.getMethods(): + if name == method.name: + aIdx = 0 + sendCodec = Codec(self._broker.conn.spec) + seq = self._session.seqMgr._reserve((method, synchronous)) + self._broker._setHeader(sendCodec, 'M', seq) + self._objectId.encode(sendCodec) + self._schema.getKey().encode(sendCodec) + sendCodec.write_str8(name) + + count = 0 + for arg in method.arguments: + if arg.dir.find("I") != -1: + count += 1 + if count != len(args): + raise Exception("Incorrect number of arguments: expected %d, got %d" % (count, len(args))) + + for arg in method.arguments: + if arg.dir.find("I") != -1: + self._session._encodeValue(sendCodec, args[aIdx], arg.type) + aIdx += 1 + if timeWait: + ttl = timeWait * 1000 + else: + ttl = None + smsg = self._broker._message(sendCodec.encoded, "agent.%d.%d" % + (self._objectId.getBrokerBank(), self._objectId.getAgentBank()), + ttl=ttl) + if synchronous: + try: + self._broker.cv.acquire() + self._broker.syncInFlight = True + finally: + self._broker.cv.release() + self._broker._send(smsg) + return seq + return None + + def _invoke(self, name, args, kwargs): + if not self.isManaged(): + raise Exception("Object is not managed") + if "_timeout" in kwargs: + timeout = kwargs["_timeout"] + else: + timeout = self._broker.SYNC_TIME + + if "_async" in kwargs and kwargs["_async"]: + sync = False + if "_timeout" not in kwargs: + timeout = None + else: + sync = True + + seq = self._sendMethodRequest(name, args, kwargs, sync, timeout) + if seq: + if not sync: + return seq + try: + self._broker.cv.acquire() + starttime = time() + while self._broker.syncInFlight and self._broker.error == None: + self._broker.cv.wait(timeout) + if time() - starttime > timeout: + self._session.seqMgr._release(seq) + raise RuntimeError("Timed out waiting for method to respond") + finally: + self._broker.cv.release() + if self._broker.error != None: + errorText = self._broker.error + self._broker.error = None + raise Exception(errorText) + return self._broker.syncResult + raise Exception("Invalid Method (software defect) [%s]" % name) + + def _encodeUnmanaged(self, codec): + # emit presence masks for optional properties + mask = 0 + bit = 0 + for prop, value in self._properties: + if prop.optional: + if bit == 0: + bit = 1 + if value: + mask |= bit + bit = bit << 1 + if bit == 256: + bit = 0 + codec.write_uint8(mask) + mask = 0 + if bit != 0: + codec.write_uint8(mask) + + codec.write_uint8(20) + codec.write_str8(self._schema.getKey().getPackageName()) + codec.write_str8(self._schema.getKey().getClassName()) + codec.write_bin128(self._schema.getKey().getHash()) + + # encode properties + for prop, value in self._properties: + if value != None: + self._session._encodeValue(codec, value, prop.type) + + # encode statistics + for stat, value in self._statistics: + self._session._encodeValue(codec, value, stat.type) + + def _parsePresenceMasks(self, codec, schema): + excludeList = [] + bit = 0 + for property in schema.getProperties(): + if property.optional: + if bit == 0: + mask = codec.read_uint8() + bit = 1 + if (mask & bit) == 0: + excludeList.append(property.name) + bit *= 2 + if bit == 256: + bit = 0 + return excludeList + class Session: """ An instance of the Session class represents a console session running @@ -119,6 +405,18 @@ class Session: DEFAULT_GET_WAIT_TIME = 60 + ENCODINGS = { + str: 7, + timestamp: 8, + datetime: 8, + int: 9, + long: 9, + float: 13, + UUID: 14, + Object: 20, + list: 21 + } + def __init__(self, console=None, rcvObjects=True, rcvEvents=True, rcvHeartbeats=True, manageConnections=False, userBindings=False): """ @@ -265,6 +563,13 @@ class Session: agentList.append(a) return agentList + def makeObject(self, classKey, broker=None, **kwargs): + """ Create a new, unmanaged object of the schema indicated by classKey """ + schema = self.getSchema(classKey) + if schema == None: + raise Exception("Schema not found for classKey") + return Object(self, broker, schema, None, True, True, False, kwargs) + def getObjects(self, **kwargs): """ Get a list of objects from QMF agents. All arguments are passed by name(keyword). @@ -521,7 +826,7 @@ class Session: if code == 0: for arg in method.arguments: if arg.dir.find("O") != -1: - outArgs[arg.name] = self._decodeValue(codec, arg.type) + outArgs[arg.name] = self._decodeValue(codec, arg.type, broker) result = MethodResult(code, text, outArgs) if synchronous: try: @@ -559,7 +864,7 @@ class Session: def _handleSchemaResp(self, broker, codec, seq): kind = codec.read_uint8() classKey = ClassKey(codec) - _class = SchemaClass(kind, classKey, codec) + _class = SchemaClass(kind, classKey, codec, self) try: self.cv.acquire() self.packages[classKey.getPackageName()][classKey.getPackageKey()] = _class @@ -605,9 +910,10 @@ class Session: self.console.objectStats(broker, object) def _handleError(self, error): - self.error = error try: self.cv.acquire() + if len(self.syncSequenceList) > 0: + self.error = error self.syncSequenceList = [] self.cv.notify() finally: @@ -621,7 +927,7 @@ class Session: return False return True - def _decodeValue(self, codec, typecode): + def _decodeValue(self, codec, typecode, broker=None): """ Decode, from the codec, a value based on its typecode. """ if typecode == 1: data = codec.read_uint8() # U8 elif typecode == 2: data = codec.read_uint16() # U16 @@ -636,11 +942,59 @@ class Session: elif typecode == 12: data = codec.read_float() # FLOAT elif typecode == 13: data = codec.read_double() # DOUBLE elif typecode == 14: data = codec.read_uuid() # UUID - elif typecode == 15: data = codec.read_map() # FTABLE elif typecode == 16: data = codec.read_int8() # S8 elif typecode == 17: data = codec.read_int16() # S16 elif typecode == 18: data = codec.read_int32() # S32 elif typecode == 19: data = codec.read_int64() # S63 + elif typecode == 15: # FTABLE + data = {} + sc = Codec(codec.spec, codec.read_vbin32()) + if sc.encoded: + count = sc.read_uint32() + while count > 0: + k = sc.read_str8() + code = sc.read_uint8() + v = self._decodeValue(sc, code, broker) + data[k] = v + count -= 1 + elif typecode == 20: # OBJECT + # Peek at the type, and if it is still 20 pull it decode. If + # Not, call back into self. + inner_type_code = codec.read_uint8() + if inner_type_code == 20: + classKey = ClassKey(codec) + try: + self.cv.acquire() + pname = classKey.getPackageName() + if pname not in self.packages: + return None + pkey = classKey.getPackageKey() + if pkey not in self.packages[pname]: + return None + schema = self.packages[pname][pkey] + finally: + self.cv.release() + data = Object(self, broker, schema, codec, True, True, False) + else: + data = self._decodeValue(codec, inner_type_code, broker) + elif typecode == 21: # List + #taken from codec10.read_list + sc = Codec(codec.spec, codec.read_vbin32()) + count = sc.read_uint32() + data = [] + while count > 0: + type = sc.read_uint8() + data.append(self._decodeValue(sc,type,broker)) + count -= 1 + elif typecode == 22: #Array + #taken from codec10.read_array + sc = Codec(codec.spec, codec.read_vbin32()) + count = sc.read_uint32() + type = sc.read_uint8() + data = [] + while count > 0: + data.append(self._decodeValue(sc,type,broker)) + count -= 1 else: raise ValueError("Invalid type code: %d" % typecode) return data @@ -660,14 +1014,54 @@ class Session: elif typecode == 12: codec.write_float (float(value)) # FLOAT elif typecode == 13: codec.write_double (float(value)) # DOUBLE elif typecode == 14: codec.write_uuid (value.bytes) # UUID - elif typecode == 15: codec.write_map (value) # FTABLE elif typecode == 16: codec.write_int8 (int(value)) # S8 elif typecode == 17: codec.write_int16 (int(value)) # S16 elif typecode == 18: codec.write_int32 (int(value)) # S32 elif typecode == 19: codec.write_int64 (int(value)) # S64 + elif typecode == 20: value._encodeUnmanaged(codec) # OBJECT + elif typecode == 15: # FTABLE + sc = Codec(codec.spec) + if value is not None: + sc.write_uint32(len(value)) + for k, v in value.items(): + mtype = self.encoding(v) + sc.write_str8(k) + sc.write_uint8(mtype) + self._encodeValue(sc, v, mtype) + else: + sc.write_uint32(0) + codec.write_vbin32(sc.encoded) + elif typecode == 21: # List + sc = Codec(codec.spec) + self._encodeValue(sc, len(value), 3) + for o in value: + ltype=self.encoding(o) + self._encodeValue(sc,ltype,1) + self._encodeValue(sc, o, ltype) + codec.write_vbin32(sc.encoded) + elif typecode == 22: # Array + sc = Codec(codec.spec) + self._encodeValue(sc, len(value), 3) + if len(value) > 0: + ltype = self.encoding(value[0]) + self._encodeValue(sc,ltype,1) + for o in value: + self._encodeValue(sc, o, ltype) + codec.write_vbin32(sc.encoded) else: raise ValueError ("Invalid type code: %d" % typecode) + def encoding(self, value): + return self._encoding(value.__class__) + + def _encoding(self, klass): + if Session.ENCODINGS.has_key(klass): + return self.ENCODINGS[klass] + for base in klass.__bases__: + result = self._encoding(base, obj) + if result != None: + return result + def _displayValue(self, value, typecode): """ """ if typecode == 1: return unicode(value) @@ -690,8 +1084,64 @@ class Session: elif typecode == 17: return unicode(value) elif typecode == 18: return unicode(value) elif typecode == 19: return unicode(value) + elif typecode == 20: return unicode(value.__repr__()) + elif typecode == 21: return unicode(value.__repr__()) + elif typecode == 22: return unicode(value.__repr__()) + else: + raise ValueError ("Invalid type code: %d" % typecode) + + def _defaultValue(self, stype, broker=None, kwargs={}): + """ """ + typecode = stype.type + if typecode == 1: return 0 + elif typecode == 2: return 0 + elif typecode == 3: return 0 + elif typecode == 4: return 0 + elif typecode == 6: return "" + elif typecode == 7: return "" + elif typecode == 8: return 0 + elif typecode == 9: return 0 + elif typecode == 10: return ObjectId(None) + elif typecode == 11: return False + elif typecode == 12: return 0.0 + elif typecode == 13: return 0.0 + elif typecode == 14: return UUID([0 for i in range(16)]) + elif typecode == 15: return {} + elif typecode == 16: return 0 + elif typecode == 17: return 0 + elif typecode == 18: return 0 + elif typecode == 19: return 0 + elif typecode == 21: return [] + elif typecode == 22: return [] + elif typecode == 20: + try: + if "classKeys" in kwargs: + keyList = kwargs["classKeys"] + else: + keyList = None + classKey = self._bestClassKey(stype.refPackage, stype.refClass, keyList) + if classKey: + return self.makeObject(classKey, broker, kwargs) + except: + pass + return None else: raise ValueError ("Invalid type code: %d" % typecode) + + def _bestClassKey(self, pname, cname, preferredList): + """ """ + if pname == None or cname == None: + if len(preferredList) == 0: + return None + return preferredList[0] + for p in preferredList: + if p.getPackageName() == pname and p.getClassName() == cname: + return p + clist = self.getClasses(pname) + for c in clist: + if c.getClassName() == cname: + return c + return None def _sendMethodRequest(self, broker, schemaKey, objectId, name, argList): """ This function can be used to send a method request to an object given only the @@ -783,18 +1233,24 @@ class SchemaClass: CLASS_KIND_TABLE = 1 CLASS_KIND_EVENT = 2 - def __init__(self, kind, key, codec): + def __init__(self, kind, key, codec, session): self.kind = kind self.classKey = key self.properties = [] self.statistics = [] self.methods = [] self.arguments = [] + self.session = session + hasSupertype = codec.read_uint8() if self.kind == self.CLASS_KIND_TABLE: propCount = codec.read_uint16() statCount = codec.read_uint16() methodCount = codec.read_uint16() + if hasSupertype == 1: + self.superTypeKey = ClassKey(codec) + else: + self.superTypeKey = None ; for idx in range(propCount): self.properties.append(SchemaProperty(codec)) for idx in range(statCount): @@ -804,6 +1260,10 @@ class SchemaClass: elif self.kind == self.CLASS_KIND_EVENT: argCount = codec.read_uint16() + if (hasSupertype): + self.superTypeKey = ClassKey(codec) + else: + self.superTypeKey = None ; for idx in range(argCount): self.arguments.append(SchemaArgument(codec, methodArg=False)) @@ -823,19 +1283,32 @@ class SchemaClass: def getProperties(self): """ Return the list of properties for the class. """ - return self.properties + if (self.superTypeKey == None): + return self.properties + else: + return self.properties + self.session.getSchema(self.superTypeKey).getProperties() def getStatistics(self): """ Return the list of statistics for the class. """ - return self.statistics + if (self.superTypeKey == None): + return self.statistics + else: + return self.statistics + self.session.getSchema(self.superTypeKey).getStatistics() def getMethods(self): """ Return the list of methods for the class. """ - return self.methods + if (self.superTypeKey == None): + return self.methods + else: + return self.methods + self.session.getSchema(self.superTypeKey).getMethods() def getArguments(self): """ Return the list of events for the class. """ - return self.arguments + """ Return the list of methods for the class. """ + if (self.superTypeKey == None): + return self.arguments + else: + return self.arguments + self.session.getSchema(self.superTypeKey).getArguments() class SchemaProperty: """ """ @@ -846,18 +1319,22 @@ class SchemaProperty: self.access = str(map["access"]) self.index = map["index"] != 0 self.optional = map["optional"] != 0 - self.unit = None - self.min = None - self.max = None - self.maxlen = None - self.desc = None + self.refPackage = None + self.refClass = None + self.unit = None + self.min = None + self.max = None + self.maxlen = None + self.desc = None for key, value in map.items(): - if key == "unit" : self.unit = value - elif key == "min" : self.min = value - elif key == "max" : self.max = value - elif key == "maxlen" : self.maxlen = value - elif key == "desc" : self.desc = value + if key == "unit" : self.unit = value + elif key == "min" : self.min = value + elif key == "max" : self.max = value + elif key == "maxlen" : self.maxlen = value + elif key == "desc" : self.desc = value + elif key == "refPackage" : self.refPackage = value + elif key == "refClass" : self.refClass = value def __repr__(self): return self.name @@ -920,6 +1397,8 @@ class SchemaArgument: self.maxlen = None self.desc = None self.default = None + self.refPackage = None + self.refClass = None for key, value in map.items(): if key == "unit" : self.unit = value @@ -928,6 +1407,8 @@ class SchemaArgument: elif key == "maxlen" : self.maxlen = value elif key == "desc" : self.desc = value elif key == "default" : self.default = value + elif key == "refPackage" : self.refPackage = value + elif key == "refClass" : self.refClass = value class ObjectId: """ Object that represents QMF object identifiers """ @@ -987,209 +1468,6 @@ class ObjectId: def __eq__(self, other): return (self.first, self.second).__eq__(other) -class Object(object): - """ This class defines a 'proxy' object representing a real managed object on an agent. - Actions taken on this proxy are remotely affected on the real managed object. - """ - def __init__(self, session, broker, schema, codec, prop, stat): - self._session = session - self._broker = broker - self._schema = schema - self._currentTime = codec.read_uint64() - self._createTime = codec.read_uint64() - self._deleteTime = codec.read_uint64() - self._objectId = ObjectId(codec) - self._properties = [] - self._statistics = [] - if prop: - notPresent = self._parsePresenceMasks(codec, schema) - for property in schema.getProperties(): - if property.name in notPresent: - self._properties.append((property, None)) - else: - self._properties.append((property, self._session._decodeValue(codec, property.type))) - if stat: - for statistic in schema.getStatistics(): - self._statistics.append((statistic, self._session._decodeValue(codec, statistic.type))) - - def getBroker(self): - """ Return the broker from which this object was sent """ - return self._broker - - def getObjectId(self): - """ Return the object identifier for this object """ - return self._objectId - - def getClassKey(self): - """ Return the class-key that references the schema describing this object. """ - return self._schema.getKey() - - def getSchema(self): - """ Return the schema that describes this object. """ - return self._schema - - def getMethods(self): - """ Return a list of methods available for this object. """ - return self._schema.getMethods() - - def getTimestamps(self): - """ Return the current, creation, and deletion times for this object. """ - return self._currentTime, self._createTime, self._deleteTime - - def isDeleted(self): - """ Return True iff this object has been deleted. """ - return self._deleteTime != 0 - - def getIndex(self): - """ Return a string describing this object's primary key. """ - result = u"" - for property, value in self._properties: - if property.index: - if result != u"": - result += u":" - try: - valstr = unicode(self._session._displayValue(value, property.type)) - except: - valstr = u"<undecodable>" - result += valstr - return result - - def getProperties(self): - """ Return a list of object properties """ - return self._properties - - def getStatistics(self): - """ Return a list of object statistics """ - return self._statistics - - def mergeUpdate(self, newer): - """ Replace properties and/or statistics with a newly received update """ - if self._objectId != newer._objectId: - raise Exception("Objects with different object-ids") - if len(newer.getProperties()) > 0: - self._properties = newer.getProperties() - if len(newer.getStatistics()) > 0: - self._statistics = newer.getStatistics() - - def update(self): - """ Contact the agent and retrieve the lastest property and statistic values for this object. """ - obj = self._session.getObjects(_objectId = self._objectId, _broker=self._broker) - if obj: - self.mergeUpdate(obj[0]) - else: - raise Exception("Underlying object no longer exists") - - def __repr__(self): - key = self.getClassKey() - return key.getPackageName() + ":" + key.getClassName() +\ - "[" + self.getObjectId().__repr__() + "] " + self.getIndex().encode("utf8") - - def __getattr__(self, name): - for method in self._schema.getMethods(): - if name == method.name: - return lambda *args, **kwargs : self._invoke(name, args, kwargs) - for property, value in self._properties: - if name == property.name: - return value - if name == "_" + property.name + "_" and property.type == 10: # Dereference references - deref = self._session.getObjects(_objectId=value, _broker=self._broker) - if len(deref) != 1: - return None - else: - return deref[0] - for statistic, value in self._statistics: - if name == statistic.name: - return value - raise Exception("Type Object has no attribute '%s'" % name) - - def _sendMethodRequest(self, name, args, kwargs, synchronous=False, timeWait=None): - for method in self._schema.getMethods(): - if name == method.name: - aIdx = 0 - sendCodec = Codec(self._broker.conn.spec) - seq = self._session.seqMgr._reserve((method, synchronous)) - self._broker._setHeader(sendCodec, 'M', seq) - self._objectId.encode(sendCodec) - self._schema.getKey().encode(sendCodec) - sendCodec.write_str8(name) - - count = 0 - for arg in method.arguments: - if arg.dir.find("I") != -1: - count += 1 - if count != len(args): - raise Exception("Incorrect number of arguments: expected %d, got %d" % (count, len(args))) - - for arg in method.arguments: - if arg.dir.find("I") != -1: - self._session._encodeValue(sendCodec, args[aIdx], arg.type) - aIdx += 1 - if timeWait: - ttl = timeWait * 1000 - else: - ttl = None - smsg = self._broker._message(sendCodec.encoded, "agent.%d.%d" % - (self._objectId.getBrokerBank(), self._objectId.getAgentBank()), - ttl=ttl) - if synchronous: - try: - self._broker.cv.acquire() - self._broker.syncInFlight = True - finally: - self._broker.cv.release() - self._broker._send(smsg) - return seq - return None - - def _invoke(self, name, args, kwargs): - if "_timeout" in kwargs: - timeout = kwargs["_timeout"] - else: - timeout = self._broker.SYNC_TIME - - if "_async" in kwargs and kwargs["_async"]: - sync = False - if "_timeout" not in kwargs: - timeout = None - else: - sync = True - - seq = self._sendMethodRequest(name, args, kwargs, sync, timeout) - if seq: - if not sync: - return seq - try: - self._broker.cv.acquire() - starttime = time() - while self._broker.syncInFlight and self._broker.error == None: - self._broker.cv.wait(timeout) - if time() - starttime > timeout: - self._session.seqMgr._release(seq) - raise RuntimeError("Timed out waiting for method to respond") - finally: - self._broker.cv.release() - if self._broker.error != None: - errorText = self._broker.error - self._broker.error = None - raise Exception(errorText) - return self._broker.syncResult - raise Exception("Invalid Method (software defect) [%s]" % name) - - def _parsePresenceMasks(self, codec, schema): - excludeList = [] - bit = 0 - for property in schema.getProperties(): - if property.optional: - if bit == 0: - mask = codec.read_uint8() - bit = 1 - if (mask & bit) == 0: - excludeList.append(property.name) - bit *= 2 - if bit == 256: - bit = 0 - return excludeList - class MethodResult(object): """ """ def __init__(self, status, text, outArgs): @@ -1361,9 +1639,13 @@ class Broker: self.reqsOutstanding = 1 sock = connect(self.host, self.port) + sock.settimeout(5) if self.ssl: sock = ssl(sock) - self.conn = Connection(sock, username=self.authUser, password=self.authPass) + self.conn = Connection(sock, username=self.authUser, password=self.authPass, heartbeat=2) + def aborted(): + raise Timeout("read timed out") + self.conn.aborted = aborted self.conn.start() self.replyName = "reply-%s" % self.amqpSessionId self.amqpSession = self.conn.session(self.amqpSessionId) @@ -1424,7 +1706,7 @@ class Broker: """ Compose the header of a management message. """ codec.write_uint8(ord('A')) codec.write_uint8(ord('M')) - codec.write_uint8(ord('2')) + codec.write_uint8(ord('3')) codec.write_uint8(ord(opcode)) codec.write_uint32(seq) @@ -1438,7 +1720,7 @@ class Broker: if octet != 'M': return None, None octet = chr(codec.read_uint8()) - if octet != '2': + if octet != '3': return None, None opcode = chr(codec.read_uint8()) seq = codec.read_uint32() @@ -1453,6 +1735,7 @@ class Broker: dp.ttl = ttl mp = self.amqpSession.message_properties() mp.content_type = "x-application/qmf" + mp.user_id = self.authUser mp.reply_to = self.amqpSession.reply_to("amq.direct", self.replyName) return Message(dp, mp, body) @@ -1576,7 +1859,7 @@ class Event: self.schema = session.packages[pname][pkey] self.arguments = {} for arg in self.schema.arguments: - self.arguments[arg.name] = session._decodeValue(codec, arg.type) + self.arguments[arg.name] = session._decodeValue(codec, arg.type, broker) def __repr__(self): if self.schema == None: diff --git a/qpid/python/qpid/management.py b/qpid/python/qpid/management.py index 546e68ae8e..b97709d367 100644 --- a/qpid/python/qpid/management.py +++ b/qpid/python/qpid/management.py @@ -401,7 +401,7 @@ class managementClient: """ Compose the header of a management message. """ codec.write_uint8 (ord ('A')) codec.write_uint8 (ord ('M')) - codec.write_uint8 (ord ('2')) + codec.write_uint8 (ord ('3')) codec.write_uint8 (opcode) codec.write_uint32 (seq) @@ -415,7 +415,7 @@ class managementClient: if octet != 'M': return None octet = chr (codec.read_uint8 ()) - if octet != '2': + if octet != '3': return None opcode = chr (codec.read_uint8 ()) seq = codec.read_uint32 () @@ -672,9 +672,14 @@ class managementClient: packageName = codec.read_str8 () className = codec.read_str8 () hash = codec.read_bin128 () + hasSupertype = codec.read_uint8() configCount = codec.read_uint16 () instCount = codec.read_uint16 () methodCount = codec.read_uint16 () + if hasSupertype != 0: + supertypePackage = codec.read_str8() + supertypeClass = codec.read_str8() + supertypeHash = codec.read_bin128() if packageName not in self.packages: return diff --git a/qpid/python/qpid/managementdata.py b/qpid/python/qpid/managementdata.py index e1fd8d54eb..84eb9c3ff8 100644 --- a/qpid/python/qpid/managementdata.py +++ b/qpid/python/qpid/managementdata.py @@ -360,6 +360,12 @@ class ManagementData: return "int32" elif typecode == 19: return "int64" + elif typecode == 20: + return "object" + elif typecode == 21: + return "list" + elif typecode == 22: + return "array" else: raise ValueError ("Invalid type code: %d" % typecode) diff --git a/qpid/ruby/lib/qpid/qmf.rb b/qpid/ruby/lib/qpid/qmf.rb index b7309155c3..56037fff62 100644 --- a/qpid/ruby/lib/qpid/qmf.rb +++ b/qpid/ruby/lib/qpid/qmf.rb @@ -773,10 +773,16 @@ module Qpid::Qmf @methods = [] @arguments = [] + has_supertype = codec.read_uint8 if @kind == CLASS_KIND_TABLE prop_count = codec.read_uint16 stat_count = codec.read_uint16 method_count = codec.read_uint16 + if has_supertype == 1 + codec.read_str8 + codec.read_str8 + codec.read_bin128 + end prop_count.times { |idx| @properties << SchemaProperty.new(codec) } stat_count.times { |idx| @@ -1111,13 +1117,11 @@ module Qpid::Qmf def invoke(method, name, args) kwargs = args[args.size - 1] sync = true - timeout = nil + timeout = DEFAULT_METHOD_WAIT_TIME if kwargs.class == Hash if kwargs.include?(:timeout) timeout = kwargs[:timeout] - else - timeout = DEFAULT_METHOD_WAIT_TIME end if kwargs.include?(:async) @@ -1343,7 +1347,7 @@ module Qpid::Qmf def set_header(codec, opcode, seq=0) codec.write_uint8(?A) codec.write_uint8(?M) - codec.write_uint8(?2) + codec.write_uint8(?3) codec.write_uint8(opcode) codec.write_uint32(seq) end @@ -1508,7 +1512,7 @@ module Qpid::Qmf begin return [nil, nil] unless codec.read_uint8 == ?A return [nil, nil] unless codec.read_uint8 == ?M - return [nil, nil] unless codec.read_uint8 == ?2 + return [nil, nil] unless codec.read_uint8 == ?3 opcode = codec.read_uint8 seq = codec.read_uint32 return [opcode, seq] |