blob: d9c5948e576513bc209a2473808b82256b5b48e6 [file] [log] [blame]
/*
*
* 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.
*
*/
using System;
using System.Collections.Generic;
using System.IO ;
using System.Reflection ;
using System.Threading ;
using log4net ;
using org.apache.qpid.client ;
using org.apache.qpid.transport.util;
using org.apache.qpid.transport.codec ;
namespace org.apache.qpid.console
{
/**
* Main Interaction point for interaction with the bus. Can be used to locate objects
* on the bus, and invoke messages on them.
*/
public class Session
{
public static ILog log = LogManager.GetLogger(typeof(Session)) ;
public static int CONTEXT_SYNC = 1 ;
public static int CONTEXT_STARTUP = 2 ;
public static int CONTEXT_MULTIGET = 3 ;
public static int DEFAULT_GET_WAIT_TIME = 60000 ;
public bool RecieveObjects = true ;
public bool RecieveEvents = true ;
public bool RecieveHeartbeat = true ;
public bool UserBindings = false ;
public Console Console ;
protected Dictionary<string, Dictionary<string, SchemaClass>> Packages = new Dictionary<string, Dictionary<string, SchemaClass>>() ;
protected List<Broker> Brokers = new List<Broker>() ;
protected SequenceManager SequenceManager = new SequenceManager() ;
protected object LockObject = new Object();
protected List<long> SyncSequenceList = new List<long>() ;
protected List<QMFObject> GetResult ;
protected Object SyncResult ;
public Session()
{
}
public Session(Console console) {
Console = console ;
}
public void AddBroker(string url) {
BrokerURL brokerurl = GetBrokerURL(url) ;
Broker broker = new Broker(this, brokerurl) ;
Brokers.Add(broker) ;
Dictionary<string, object> args = new Dictionary<string, object>() ;
args.Add("_class", "agent") ;
args.Add("_broker", broker) ;
this.GetObjects(args) ;
}
public void RemoveBroker(Broker broker) {
if (Brokers.Contains(broker)) {
Brokers.Remove(broker) ;
}
broker.Shutdown() ;
}
public void Close() {
foreach (Broker broker in Brokers.ToArray()) {
this.RemoveBroker(broker) ;
}
}
protected BrokerURL GetBrokerURL(string url) {
return new BrokerURL(url) ;
}
public List<QMFObject> GetObjects(Dictionary<string, object> args) {
List<Broker> brokerList = null ;
List<Agent> agentList = new List<Agent>() ;
if (args.ContainsKey("_broker")) {
brokerList = new List<Broker>() ;
brokerList.Add((Broker)args["_broker"]) ;
} else {
brokerList = this.Brokers ;
}
foreach (Broker broker in brokerList) {
broker.WaitForStable() ;
}
if (args.ContainsKey("_agent")) {
Agent agent = (Agent)args["_agent"] ;
if (brokerList.Contains(agent.Broker)) {
agentList.Add(agent) ;
} else {
throw new Exception("Agent is not managed by this console or the supplied broker") ;
}
} else {
if (args.ContainsKey("_objectId")) {
ObjectID oid = (ObjectID) args["_objectId"] ;
foreach (Broker broker in Brokers) {
foreach (Agent agent in broker.Agents.Values) {
if ((agent.AgentBank == oid.AgentBank()) && (agent.BrokerBank == oid.BrokerBank())) {
agentList.Add(agent) ;
}
}
}
}
else {
foreach (Broker broker in brokerList) {
foreach (Agent agent in broker.Agents.Values) {
if (agent.Broker.IsConnected()) {
agentList.Add(agent) ;
}
}
}
}
}
GetResult = new List<QMFObject>() ;
if (agentList.Count > 0) {
//FIXME Add a bunch of other suff too
foreach (Agent agent in agentList) {
Dictionary<string, object> getParameters = new Dictionary<string, object>() ;
Broker broker = agent.Broker ;
long seq = -1 ;
lock(LockObject) {
seq = SequenceManager.Reserve(Session.CONTEXT_MULTIGET) ;
SyncSequenceList.Add(seq) ;
}
object packageName = null ;
object className = null ;
object key = null ;
object sClass = null ;
object oid = null ;
object hash = null ;
args.TryGetValue("_schema", out sClass) ;
args.TryGetValue("_key", out key) ;
args.TryGetValue("_class", out className) ;
args.TryGetValue("_package", out packageName) ;
args.TryGetValue("_objectID", out oid) ;
args.TryGetValue("_hash", out hash) ;
if ((className == null) && (oid == null) && (oid == null)) {
throw new Exception("No class supplied, use '_schema', '_key', '_class', or '_objectId' argument") ;
}
if (oid != null) {
getParameters.Add("_objectID", oid) ;
}
else {
if (sClass != null) {
key = key ?? ((SchemaClass)sClass).Key ;
}
if (key != null) {
ClassKey cKey = (ClassKey)key ;
className = className ?? cKey.ClassName ;
packageName = packageName ?? cKey.PackageName ;
hash = hash ?? cKey.Hash ;
}
if (packageName != null) {
getParameters.Add("_package", packageName) ;
}
if (className != null) {
getParameters.Add("_class", className) ;
}
if (hash != null) {
getParameters.Add("_hash", hash) ;
}
foreach (KeyValuePair<string, object> pair in args) {
if (!pair.Key.StartsWith("_")) {
getParameters.Add(pair.Key, pair.Value) ;
}
}
}
IEncoder enc = broker.CreateEncoder('G', seq) ;
enc.WriteMap(getParameters) ;
string routingKey = String.Format("agent.{0}.{1}", agent.BrokerBank, agent.AgentBank) ;
Message msg = broker.CreateMessage(enc, routingKey) ;
log.Debug("Get Object Keys: ") ;
foreach (string pKey in getParameters.Keys) {
log.Debug(String.Format("\tKey: '{0}' Value: '{1}'", pKey, getParameters[pKey])) ;
}
broker.Send(msg) ;
}
int waittime = DEFAULT_GET_WAIT_TIME ;
bool timeout = false ;
if (args.ContainsKey("_timeout")) {
waittime = (int) args["_timeout"] ;
}
DateTime start = DateTime.Now ;
lock (LockObject) {
// FIXME ERROR
while (SyncSequenceList.Count > 0){
Monitor.Wait(LockObject,waittime) ;
TimeSpan duration = DateTime.Now - start;
if (duration.TotalMilliseconds > waittime) {
foreach (long pendingSeq in SyncSequenceList) {
SequenceManager.Release(pendingSeq) ;
}
SyncSequenceList.Clear() ;
timeout = true ;
}
}
}
//FIXME Add the error logic
if ((GetResult.Count == 0) && timeout) {
throw new Exception ("Get Request timed out") ;
}
}
return GetResult ;
}
public List<string> GetPackages() {
this.WaitForStable() ;
List<string> returnValue = new List<string>() ;
foreach (String name in Packages.Keys) {
returnValue.Add(name) ;
}
return returnValue ;
}
public List<ClassKey> GetClasses(string packageName) {
List<ClassKey> returnValue = new List<ClassKey>() ;
this.WaitForStable() ;
if (Packages.ContainsKey(packageName)) {
foreach (SchemaClass sClass in Packages[packageName].Values) {
returnValue.Add(sClass.Key) ;
}
}
return returnValue ;
}
public SchemaClass GetSchema(ClassKey key) {
return GetSchema(key, true) ;
}
protected SchemaClass GetSchema(ClassKey key, bool waitForStable) {
if (waitForStable) {
this.WaitForStable() ;
}
SchemaClass returnValue = null ;
try {
returnValue = Packages[key.PackageName][key.GetKeyString()] ;
}
catch (KeyNotFoundException) {
// eat it
}
return returnValue ;
}
protected void WaitForStable() {
foreach (Broker broker in Brokers) {
broker.WaitForStable() ;
}
}
public Broker GetBroker(long BrokerBank) {
Broker returnValue = null ;
foreach (Broker broker in Brokers) {
if (broker.BrokerBank() == BrokerBank) {
returnValue = broker ;
break ;
}
}
return returnValue ;
}
public MethodResult InvokeMethod(QMFObject obj, string name, List<object> args, bool synchronous, int timeToLive) {
Broker aBroker = this.GetBroker(obj.BrokerBank()) ;
long seq = this.SendMethodRequest(obj, aBroker, name, args, synchronous, timeToLive) ;
if (seq != 0) {
if (!synchronous) {
return null ;
}
try {
aBroker.WaitForSync(timeToLive) ;
} catch (Exception e) {
SequenceManager.Release(seq) ;
throw e ;
}
// FIXME missing error logic in the broker
return (MethodResult) SyncResult ;
}
return null ;
}
protected long SendMethodRequest(QMFObject obj, Broker aBroker, string name, List<object> args, bool synchronous, int timeToLive) {
SchemaMethod method = obj.Schema.GetMethod(name) ;
if (args == null) {
args = new List<object>() ;
}
long seq = 0 ;
if (method != null) {
KeyValuePair<SchemaMethod, bool> pair = new KeyValuePair<SchemaMethod, bool>(method, synchronous) ;
seq = SequenceManager.Reserve(pair) ;
IEncoder enc = aBroker.CreateEncoder('M', seq) ;
obj.ObjectID.encode(enc) ;
obj.Schema.Key.encode(enc) ;
enc.WriteStr8(name) ;
if (args.Count < method.InputArgCount) {
throw new Exception(String.Format("Incorrect number of arguments: expected {0}, got{1}", method.InputArgCount, args.Count)) ;
}
int argIndex = 0 ;
foreach (SchemaArgument arg in method.Arguments) {
if (arg.IsInput()) {;
this.EncodeValue(enc, arg.Type, args[argIndex]) ;
argIndex += 1 ;
}
}
Message msg = aBroker.CreateMessage(enc,obj.RoutingKey(),timeToLive) ;
if (synchronous) {
aBroker.SetSyncInFlight(true) ;
}
aBroker.Send(msg) ;
}
return seq ;
}
public QMFObject MakeObject(ClassKey key) {
SchemaClass sClass = this.GetSchema(key) ;
if (sClass == null) {
throw new Exception("No schema found for class " + key.ToString()) ;
}
return this.CreateQMFObject(sClass, true, true, false) ;
}
public QMFObject MakeObject(String keyString) {
return this.MakeObject(new ClassKey(keyString)) ;
}
// Callback Methods
public void HandleNewAgent(Agent agent) {
if (Console != null) {
Console.NewAgent(agent) ;
}
}
public void HandleAgentRemoved(Agent agent) {
if (Console != null) {
Console.AgentRemoved(agent) ;
}
}
public void HandleBrokerConnect(Broker broker) {
if (Console != null) {
Console.BrokerConnected(broker) ;
}
}
public void HandleBrokerDisconnect(Broker broker) {
if (Console != null) {
Console.BrokerDisconnected(broker) ;
}
}
public void HandleBrokerResponse(Broker broker, IDecoder decoder, long sequence) {
if (Console != null) {
Console.BrokerInformation(broker) ;
}
long seq = SequenceManager.Reserve(CONTEXT_STARTUP) ;
IEncoder endocder = broker.CreateEncoder('P', seq) ;
broker.Send(endocder) ;
}
public void HandlePackageIndicator(Broker broker, IDecoder decoder, long sequence) {
string packageName = decoder.ReadStr8() ;
bool notify = false ;
if (!Packages.ContainsKey(packageName)) {
lock (LockObject) {
Packages[packageName] = new Dictionary<string, SchemaClass>() ;
notify = true ;
}
}
if (notify && Console != null) {
Console.NewPackage(packageName) ;
}
broker.IncrementOutstanding() ;
long seq = SequenceManager.Reserve(Session.CONTEXT_STARTUP) ;
IEncoder enc = broker.CreateEncoder('Q', seq) ;
enc.WriteStr8(packageName) ;
broker.Send(enc) ;
}
public void HandleCommandComplete(Broker broker, IDecoder decoder, long sequence) {
long code = decoder.ReadUint32() ;
string text = decoder.ReadStr8() ;
Object context = this.SequenceManager.Release(sequence) ;
if (context.Equals(CONTEXT_STARTUP)) {
broker.DecrementOutstanding() ;
} else {
if ((context.Equals(CONTEXT_SYNC)) & broker.GetSyncInFlight()) {
broker.SetSyncInFlight(false) ;
} else {
if (context.Equals(CONTEXT_MULTIGET) && SyncSequenceList.Contains(sequence)) {
lock(LockObject) {
SyncSequenceList.Remove(sequence) ;
if (SyncSequenceList.Count == 0) {
Monitor.PulseAll(LockObject) ;
}
}
}
}
}
}
public void HandleClassIndicator(Broker broker, IDecoder decoder, long sequence) {
short kind = decoder.ReadUint8() ;
ClassKey classKey = new ClassKey(decoder) ;
bool unknown = false ;
lock (LockObject) {
if (Packages.ContainsKey(classKey.PackageName)) {
if (!Packages[classKey.PackageName].ContainsKey(classKey.GetKeyString())) {
unknown = true ;
}
}
}
if (unknown) {
broker.IncrementOutstanding() ;
long seq = SequenceManager.Reserve(Session.CONTEXT_STARTUP) ;
IEncoder enc = broker.CreateEncoder('S', seq) ;
classKey.encode(enc) ;
broker.Send(enc) ;
}
}
public void HandleMethodResponse(Broker broker, IDecoder decoder, long sequence) {
long code = decoder.ReadUint32() ;
string text = decoder.ReadStr16() ;
Dictionary<string, object> outArgs = new Dictionary<string, object>() ;
object obj = SequenceManager.Release(sequence) ;
if (obj == null) {
return ;
}
KeyValuePair<SchemaMethod, bool> pair = (KeyValuePair<SchemaMethod, bool>) obj ;
if (code == 0) {
foreach (SchemaArgument arg in pair.Key.Arguments) {
if (arg.IsOutput()) {
outArgs.Add(arg.Name, this.DecodeValue(decoder, arg.Type)) ;
}
}
}
MethodResult result = new MethodResult(code, text, outArgs) ;
if (pair.Value) {
this.SyncResult = result;
broker.SetSyncInFlight(false) ;
}
if (Console != null) {
Console.MethodResponse(broker, sequence, result) ;
}
}
public void HandleHeartbeatIndicator(Broker broker, IDecoder decoder, long sequence, IMessage msg) {
if (Console != null) {
long brokerBank = 1 ;
long agentBank = 0 ;
try {
string routingKey = msg.DeliveryProperties.GetRoutingKey() ;
if (routingKey != null) {
agentBank = Agent.GetBrokerBank(routingKey) ;
brokerBank = Agent.GetBrokerBank(routingKey) ;
}
}
catch (Exception e) {
log.Warn("Internal QPID error", e) ;
}
string agentKey = Agent.AgentKey(agentBank, brokerBank) ;
long timestamp = decoder.ReadUint64() ;
if (broker.Agents.ContainsKey(agentKey)) {
Agent agent = broker.Agents[agentKey] ;
Console.HearbeatRecieved(agent, timestamp) ;
}
}
}
public void HandleEventIndicator(Broker broker, IDecoder decoder, long sequence) {
if (Console != null) {
QMFEvent newEvent = new QMFEvent(this, decoder) ;
Console.EventRecieved(broker, newEvent) ;
}
}
public void HandleSchemaResponse(Broker broker, IDecoder decoder, long sequence) {
short kind = decoder.ReadUint8() ;
ClassKey classKey = new ClassKey(decoder) ;
SchemaClass sClass = new SchemaClass(kind, classKey, decoder, this) ;
lock(LockObject) {
Dictionary<string, SchemaClass> classMappings = Packages[sClass.PackageName] ;
classMappings.Remove(sClass.ClassKeyString) ;
classMappings.Add(sClass.ClassKeyString, sClass) ;
}
SequenceManager.Release(sequence) ;
broker.DecrementOutstanding() ;
if (Console != null) {
this.Console.NewClass(kind, classKey) ;
}
}
public void HandleContentIndicator(Broker broker, IDecoder decoder, long sequence, bool hasProperties, bool hasStatistics) {
ClassKey key = new ClassKey(decoder) ;
SchemaClass sClass = null ;;
lock (LockObject) {
sClass = GetSchema(key, false) ;
}
if (sClass != null) {
QMFObject obj = this.CreateQMFObject(sClass, decoder, hasProperties, hasStatistics, true) ;
if (key.PackageName.Equals("org.apache.qpid.broker") && key.ClassName.Equals("agent") && hasProperties) {
broker.UpdateAgent(obj) ;
}
lock (LockObject) {
if (SyncSequenceList.Contains(sequence)) {
if (!obj.IsDeleted() && this.SelectMatch(obj)) {
GetResult.Add(obj) ;
}
}
}
if (Console != null) {
if (hasProperties) {
Console.ObjectProperties(broker, obj) ;
}
if (hasStatistics) {
Console.ObjectStatistics(broker, obj) ;
}
}
}
}
public bool SelectMatch(QMFObject obj) {
return true ;
}
public object DecodeValue(IDecoder dec, short type) {
switch (type) {
case 1: return dec.ReadUint8() ; // U8
case 2: return dec.ReadUint16() ; // U16
case 3: return dec.ReadUint32() ; // U32
case 4: return dec.ReadUint64() ; // U64
case 6: return dec.ReadStr8() ; // SSTR
case 7: return dec.ReadStr16() ; // LSTR
case 8: return dec.ReadDatetime() ; // ABSTIME
case 9: return dec.ReadUint32() ; // DELTATIME
case 10: return new ObjectID(dec) ; // ref
case 11: return dec.ReadUint8() != 0 ; // bool
case 12: return dec.ReadFloat() ; // float
case 13: return dec.ReadDouble() ; // double
case 14: return dec.ReadUuid() ; // UUID
case 15: return dec.ReadMap() ; // Ftable
case 16: return dec.ReadInt8() ; // int8
case 17: return dec.ReadInt16() ; // int16
case 18: return dec.ReadInt32() ; // int32
case 19: return dec.ReadInt64() ; // int64
case 20: // Object
// Peek into the inner type code, make sure
// it is actually an object
object returnValue = null ;
short innerTypeCode = dec.ReadUint8() ;
if (innerTypeCode != 20) {
returnValue = this.DecodeValue(dec, innerTypeCode) ;
}
else {
ClassKey classKey = new ClassKey(dec) ;
lock(LockObject) {
SchemaClass sClass = GetSchema(classKey) ;
if (sClass != null) {
returnValue = this.CreateQMFObject(sClass, dec, true, true, false) ;
}
}
}
return returnValue;
case 21: // List
{
MSDecoder lDec = new MSDecoder();
lDec.Init(new MemoryStream(dec.ReadVbin32()));
long count = lDec.ReadUint32();
List<object> newList = new List<object>();
while (count > 0)
{
short innerType = lDec.ReadUint8();
newList.Add(this.DecodeValue(lDec, innerType));
count -= 1;
}
return newList;
}
case 22: // Array
{
MSDecoder aDec = new MSDecoder();
aDec.Init(new MemoryStream(dec.ReadVbin32()));
long cnt = aDec.ReadUint32();
short innerType = aDec.ReadUint8();
List<object> aList = new List<object>();
while (cnt > 0)
{
aList.Add(this.DecodeValue(aDec, innerType));
cnt -= 1;
}
return aList;
}
default:
throw new Exception(String.Format("Invalid Type Code: {0}", type)) ;
}
}
public void EncodeValue(IEncoder enc, short type, object val) {
try {
switch ((int)type) {
case 1: enc.WriteUint8((short) val) ; break; // U8
case 2: enc.WriteUint16((int) val) ; break; // U16
case 3: enc.WriteUint32((long) val) ; break; // U32
case 4: enc.WriteUint64((long) val) ; break; // U64
case 6: enc.WriteStr8((string) val) ; break; // SSTR
case 7: enc.WriteStr16((string) val) ; break; // LSTR
case 8: enc.WriteDatetime((long) val); break; // ABSTIME
case 9: enc.WriteUint32((long) val); break; // DELTATIME
case 10: ((ObjectID)val).encode(enc) ; break; // ref
case 11:
if ((bool) val) {
enc.WriteUint8(1) ;
} else {
enc.WriteUint8(0) ;
}
break ;
case 12: enc.WriteFloat((float) val); break; // FLOAT
case 13: enc.WriteDouble((double) val); break; // DOUBLE
case 14: enc.WriteUuid((UUID) val) ; break ; // UUID
case 15: enc.WriteMap((Dictionary<string, object>) val) ; break ; // Ftable
case 16: enc.WriteInt8((short) val) ; break; // int8
case 17: enc.WriteInt16((int) val) ; break; // int16
case 18: enc.WriteInt32(long.Parse(""+ val)) ; break; // int32
case 19: enc.WriteInt64(long.Parse("" + val)) ; break; // int64
case 20: // Object
// Check that the object has a session, if not
// take ownership of it
QMFObject qObj = (QMFObject) val ;
if (qObj.Session == null) {
qObj.Session = this ;
}
qObj.Encode(enc) ;
break;
case 21: // List
List<object> items = (List<object>) val ;
MSEncoder lEnc = new MSEncoder(1) ;
lEnc.Init() ;
lEnc.WriteUint32(items.Count) ;
foreach (object obj in items) {
short innerType = Util.QMFType(obj) ;
lEnc.WriteUint8(innerType) ;
this.EncodeValue(lEnc,innerType,obj) ;
}
enc.WriteVbin32(lEnc.Segment().ToArray()) ;
break ;
case 22: // Array
List<object> aItems = (List<object>) val ;
MSEncoder aEnc = new MSEncoder(1) ;
aEnc.Init() ;
long aCount = aItems.Count ;
aEnc.WriteUint32(aCount) ;
if (aCount > 0) {
Object anObj = aItems[0] ;
short innerType = Util.QMFType(anObj) ;
aEnc.WriteUint8(innerType) ;
foreach (object obj in aItems) {
this.EncodeValue(aEnc,innerType,obj) ;
}
}
enc.WriteVbin32(aEnc.Segment().ToArray()) ;
break ;
default:
throw new Exception(String.Format("Invalid Type Code: {0}", type)) ;
}
}
catch (System.InvalidCastException e) {
string msg = String.Format("Class cast exception for typecode {0}, type {1} ", type, val.GetType()) ;
log.Error(msg) ;
throw new Exception(msg + type, e) ;
}
}
public List<string> BindingKeys() {
List<string> bindings = new List<string>() ;
bindings.Add("schema.#") ;
if (RecieveObjects & RecieveEvents & RecieveHeartbeat & !UserBindings) {
bindings.Add("console.#") ;
}
else {
if (RecieveObjects & !UserBindings) {
bindings.Add("console.obj.#") ;
}
else {
bindings.Add("console.obj.*.*.org.apache.qpid.broker.agent") ;
}
if (RecieveEvents) {
bindings.Add("console.event.#") ;
}
if (RecieveHeartbeat) {
bindings.Add("console.heartbeat.#") ;
}
}
return bindings ;
}
protected QMFObject CreateQMFObject(SchemaClass schema, bool hasProperties, bool hasStats , bool isManaged) {
Type realClass = typeof(QMFObject) ;
if (Console != null) {
realClass = Console.TypeMapping(schema.Key) ;
}
Type[] types = new Type[] {typeof(Session), typeof(SchemaClass), typeof(bool), typeof(bool),typeof(bool)} ;
object[] args = new object[] {this, schema, hasProperties, hasStats, isManaged} ;
ConstructorInfo ci = realClass.GetConstructor(types);
return (QMFObject) ci.Invoke(args) ;
}
protected QMFObject CreateQMFObject(SchemaClass schema, IDecoder dec, bool hasProperties, bool hasStats , bool isManaged) {
Type realClass = typeof(QMFObject) ;
if (Console != null) {
realClass = Console.TypeMapping(schema.Key) ;
}
Type[] types = new Type[] {typeof(Session), typeof(SchemaClass), typeof(IDecoder), typeof(bool), typeof(bool),typeof(bool)} ;
object[] args = new object[] {this, schema, dec, hasProperties, hasStats, isManaged} ;
ConstructorInfo ci = realClass.GetConstructor(types);
return (QMFObject) ci.Invoke(args) ;
}
}
}