mirror of https://github.com/apache/activemq.git
added support for the latest wireformat and clean stack trace support
git-svn-id: https://svn.apache.org/repos/asf/incubator/activemq/trunk@380771 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
fbb26ba7d7
commit
baabdde9f2
|
@ -19,15 +19,34 @@ using System.Collections;
|
|||
using OpenWire.Client.Commands;
|
||||
using OpenWire.Client.Core;
|
||||
|
||||
namespace OpenWire.Client {
|
||||
namespace OpenWire.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown when the broker returns an error
|
||||
/// </summary>
|
||||
public class BrokerException : OpenWireException {
|
||||
public BrokerException(BrokerError cause) : base("The operation failed: Type: "
|
||||
+ cause.ExceptionClass
|
||||
+ " stack: "
|
||||
+ cause.StackTrace) {
|
||||
public class BrokerException : OpenWireException
|
||||
{
|
||||
|
||||
private BrokerError brokerError;
|
||||
|
||||
public BrokerException(BrokerError brokerError) : base(
|
||||
brokerError.ExceptionClass + " : " + brokerError.Message)
|
||||
{
|
||||
this.brokerError = brokerError;
|
||||
}
|
||||
|
||||
public BrokerError BrokerError {
|
||||
get {
|
||||
return brokerError;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string StackTrace
|
||||
{
|
||||
get {
|
||||
return brokerError.StackTrace;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
/*
|
||||
* Copyright 2006 The Apache Software Foundation or its licensors, as
|
||||
* applicable.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
* Copyright 2006 The Apache Software Foundation or its licensors, as
|
||||
* applicable.
|
||||
*
|
||||
* Licensed 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;
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
/*
|
||||
* Copyright 2006 The Apache Software Foundation or its licensors, as
|
||||
* applicable.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
* Copyright 2006 The Apache Software Foundation or its licensors, as
|
||||
* applicable.
|
||||
*
|
||||
* Licensed 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;
|
||||
|
@ -36,32 +36,27 @@ namespace OpenWire.Client.Commands
|
|||
public const byte ID_TransactionId = 0;
|
||||
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
public override int GetHashCode() {
|
||||
int answer = 0;
|
||||
return answer;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override bool Equals(object that)
|
||||
{
|
||||
if (that is TransactionId)
|
||||
{
|
||||
public override bool Equals(object that) {
|
||||
if (that is TransactionId) {
|
||||
return Equals((TransactionId) that);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool Equals(TransactionId that)
|
||||
{
|
||||
public virtual bool Equals(TransactionId that) {
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
public override string ToString() {
|
||||
return GetType().Name + "["
|
||||
+ " ]";
|
||||
|
||||
|
@ -69,9 +64,12 @@ namespace OpenWire.Client.Commands
|
|||
|
||||
|
||||
|
||||
public override byte GetDataStructureType()
|
||||
{
|
||||
public override byte GetDataStructureType() {
|
||||
return ID_TransactionId;
|
||||
}
|
||||
|
||||
|
||||
// Properties
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,13 +37,19 @@ namespace OpenWire.Client.Commands
|
|||
|
||||
byte[] magic;
|
||||
int version;
|
||||
int options;
|
||||
bool cacheEnabled;
|
||||
bool compressionEnabled;
|
||||
bool stackTraceEnabled;
|
||||
bool tcpNoDelayEnabled;
|
||||
|
||||
public override string ToString() {
|
||||
return GetType().Name + "["
|
||||
+ " Magic=" + Magic
|
||||
+ " Version=" + Version
|
||||
+ " Options=" + Options
|
||||
+ " CacheEnabled=" + CacheEnabled
|
||||
+ " CompressionEnabled=" + CompressionEnabled
|
||||
+ " StackTraceEnabled=" + StackTraceEnabled
|
||||
+ " TcpNoDelayEnabled=" + TcpNoDelayEnabled
|
||||
+ " ]";
|
||||
|
||||
}
|
||||
|
@ -69,10 +75,28 @@ namespace OpenWire.Client.Commands
|
|||
set { this.version = value; }
|
||||
}
|
||||
|
||||
public int Options
|
||||
public bool CacheEnabled
|
||||
{
|
||||
get { return options; }
|
||||
set { this.options = value; }
|
||||
get { return cacheEnabled; }
|
||||
set { this.cacheEnabled = value; }
|
||||
}
|
||||
|
||||
public bool CompressionEnabled
|
||||
{
|
||||
get { return compressionEnabled; }
|
||||
set { this.compressionEnabled = value; }
|
||||
}
|
||||
|
||||
public bool StackTraceEnabled
|
||||
{
|
||||
get { return stackTraceEnabled; }
|
||||
set { this.stackTraceEnabled = value; }
|
||||
}
|
||||
|
||||
public bool TcpNoDelayEnabled
|
||||
{
|
||||
get { return tcpNoDelayEnabled; }
|
||||
set { this.tcpNoDelayEnabled = value; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,11 +10,8 @@ namespace OpenWire.Client
|
|||
/// </summary>
|
||||
public class Connection : IConnection
|
||||
{
|
||||
static private char[] MAGIC = new char[] { 'A', 'c', 't', 'i', 'v', 'e', 'M', 'Q' };
|
||||
|
||||
private ITransport transport;
|
||||
private ConnectionInfo info;
|
||||
private WireFormatInfo wireFormatInfo = new WireFormatInfo();
|
||||
private BrokerInfo brokerInfo; // from broker
|
||||
private WireFormatInfo brokerWireFormatInfo; // from broker
|
||||
private IList sessions = new ArrayList();
|
||||
|
@ -165,11 +162,6 @@ namespace OpenWire.Client
|
|||
}
|
||||
if (!connected)
|
||||
{
|
||||
// lets configure the wire format
|
||||
wireFormatInfo.Magic = CreateMagicBytes();
|
||||
wireFormatInfo.Version = 1;
|
||||
transport.Oneway(wireFormatInfo);
|
||||
|
||||
// now lets send the connection and see if we get an ack/nak
|
||||
SyncRequest(info);
|
||||
connected = true;
|
||||
|
@ -231,18 +223,5 @@ namespace OpenWire.Client
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method CreateMagicBytes
|
||||
/// </summary>
|
||||
/// <returns>A byte[]</retutns>
|
||||
private byte[] CreateMagicBytes()
|
||||
{
|
||||
byte[] answer = new byte[MAGIC.Length];
|
||||
for (int i = 0; i < answer.Length; i++)
|
||||
{
|
||||
answer[i] = (byte) MAGIC[i];
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,30 +15,76 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
using System;
|
||||
using System.Text;
|
||||
using OpenWire.Client.Core;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenWire.Client.Core
|
||||
{
|
||||
public struct StackTraceElement
|
||||
{
|
||||
public string ClassName;
|
||||
public string FileName;
|
||||
public string MethodName;
|
||||
public int LineNumber;
|
||||
}
|
||||
|
||||
|
||||
namespace OpenWire.Client.Core {
|
||||
/// <summary>
|
||||
/// Represents an exception on the broker
|
||||
/// </summary>
|
||||
public class BrokerError : AbstractCommand {
|
||||
public class BrokerError : AbstractCommand
|
||||
{
|
||||
private string message;
|
||||
private string exceptionClass;
|
||||
private string stackTrace;
|
||||
private StackTraceElement[] stackTraceElements = {};
|
||||
private BrokerError cause;
|
||||
|
||||
public string Message {
|
||||
public string Message
|
||||
{
|
||||
get { return message; }
|
||||
set { message = value; }
|
||||
}
|
||||
|
||||
public string ExceptionClass {
|
||||
public string ExceptionClass
|
||||
{
|
||||
get { return exceptionClass; }
|
||||
set { exceptionClass = value; }
|
||||
}
|
||||
|
||||
public string StackTrace {
|
||||
get { return stackTrace; }
|
||||
set { stackTrace = value; }
|
||||
public StackTraceElement[] StackTraceElements
|
||||
{
|
||||
get { return stackTraceElements; }
|
||||
set { stackTraceElements = value; }
|
||||
}
|
||||
|
||||
public BrokerError Cause
|
||||
{
|
||||
get { return cause; }
|
||||
set { cause = value; }
|
||||
}
|
||||
|
||||
public String StackTrace {
|
||||
get {
|
||||
StringWriter writer = new StringWriter();
|
||||
PrintStackTrace(writer);
|
||||
return writer.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public void PrintStackTrace(TextWriter writer)
|
||||
{
|
||||
writer.WriteLine(exceptionClass + ": " + message);
|
||||
for (int i = 0; i < stackTraceElements.Length; i++)
|
||||
{
|
||||
StackTraceElement element = stackTraceElements[i];
|
||||
writer.WriteLine(" at " + element.ClassName + "." + element.MethodName + "(" + element.FileName + ":" + element.LineNumber + ")");
|
||||
}
|
||||
if (cause != null)
|
||||
{
|
||||
writer.WriteLine("Nested Exception:");
|
||||
cause.PrintStackTrace(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -525,12 +525,26 @@ namespace OpenWire.Client.Core
|
|||
{
|
||||
if (bs.ReadBoolean())
|
||||
{
|
||||
String clazz = ReadString(dataIn, bs);
|
||||
String message = ReadString(dataIn, bs);
|
||||
|
||||
BrokerError answer = new BrokerError();
|
||||
answer.ExceptionClass = clazz;
|
||||
answer.Message = message;
|
||||
|
||||
answer.ExceptionClass = ReadString(dataIn, bs);
|
||||
answer.Message = ReadString(dataIn, bs);
|
||||
if (wireFormat.StackTraceEnabled)
|
||||
{
|
||||
short length = ReadShort(dataIn);
|
||||
StackTraceElement[] stackTrace = new StackTraceElement[length];
|
||||
for (int i = 0; i < stackTrace.Length; i++)
|
||||
{
|
||||
StackTraceElement element = new StackTraceElement();
|
||||
element.ClassName = ReadString(dataIn, bs);
|
||||
element.MethodName = ReadString(dataIn, bs);
|
||||
element.FileName = ReadString(dataIn, bs);
|
||||
element.LineNumber = ReadInt(dataIn);
|
||||
stackTrace[i] = element;
|
||||
}
|
||||
answer.StackTraceElements = stackTrace;
|
||||
answer.Cause = UnmarshalBrokerError(wireFormat, dataIn, bs);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
else
|
||||
|
@ -552,6 +566,21 @@ namespace OpenWire.Client.Core
|
|||
bs.WriteBoolean(true);
|
||||
rc += WriteString(o.ExceptionClass, bs);
|
||||
rc += WriteString(o.Message, bs);
|
||||
if (wireFormat.StackTraceEnabled)
|
||||
{
|
||||
rc += 2;
|
||||
StackTraceElement[] stackTrace = o.StackTraceElements;
|
||||
for (int i = 0; i < stackTrace.Length; i++)
|
||||
{
|
||||
StackTraceElement element = stackTrace[i];
|
||||
rc += WriteString(element.ClassName, bs);
|
||||
rc += WriteString(element.MethodName, bs);
|
||||
rc += WriteString(element.FileName, bs);
|
||||
rc += 4;
|
||||
}
|
||||
rc += MarshalBrokerError(wireFormat, o.Cause, bs);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
@ -566,6 +595,21 @@ namespace OpenWire.Client.Core
|
|||
{
|
||||
WriteString(o.ExceptionClass, dataOut, bs);
|
||||
WriteString(o.Message, dataOut, bs);
|
||||
if (wireFormat.StackTraceEnabled)
|
||||
{
|
||||
StackTraceElement[] stackTrace = o.StackTraceElements;
|
||||
WriteShort((short) stackTrace.Length, dataOut);
|
||||
|
||||
for (int i = 0; i < stackTrace.Length; i++)
|
||||
{
|
||||
StackTraceElement element = stackTrace[i];
|
||||
WriteString(element.ClassName, dataOut, bs);
|
||||
WriteString(element.MethodName, bs);
|
||||
WriteString(element.FileName, bs);
|
||||
WriteInt(element.LineNumber, dataOut);
|
||||
}
|
||||
MarshalBrokerError(wireFormat, o.Cause, dataOut, bs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,17 +28,37 @@ namespace OpenWire.Client.Core
|
|||
/// </summary>
|
||||
public class OpenWireFormat
|
||||
{
|
||||
static private char[] MAGIC = new char[] { 'A', 'c', 't', 'i', 'v', 'e', 'M', 'Q' };
|
||||
|
||||
private DataStreamMarshaller[] dataMarshallers;
|
||||
private const byte NULL_TYPE = 0;
|
||||
|
||||
private WireFormatInfo wireFormatInfo = new WireFormatInfo();
|
||||
|
||||
public OpenWireFormat()
|
||||
{
|
||||
// lets configure the wire format
|
||||
wireFormatInfo.Magic = CreateMagicBytes();
|
||||
wireFormatInfo.Version = 1;
|
||||
wireFormatInfo.StackTraceEnabled = true;
|
||||
wireFormatInfo.TcpNoDelayEnabled = true;
|
||||
|
||||
dataMarshallers = new DataStreamMarshaller[256];
|
||||
MarshallerFactory factory = new MarshallerFactory();
|
||||
factory.configure(this);
|
||||
}
|
||||
|
||||
public WireFormatInfo WireFormatInfo {
|
||||
get {
|
||||
return wireFormatInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public bool StackTraceEnabled {
|
||||
get {
|
||||
return wireFormatInfo.StackTraceEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
public void addMarshaller(DataStreamMarshaller marshaller)
|
||||
{
|
||||
byte type = marshaller.GetDataStructureType();
|
||||
|
@ -182,5 +202,19 @@ namespace OpenWire.Client.Core
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method CreateMagicBytes
|
||||
/// </summary>
|
||||
/// <returns>A byte[]</retutns>
|
||||
private byte[] CreateMagicBytes()
|
||||
{
|
||||
byte[] answer = new byte[MAGIC.Length];
|
||||
for (int i = 0; i < answer.Length; i++)
|
||||
{
|
||||
answer[i] = (byte) MAGIC[i];
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,14 +70,13 @@ namespace OpenWire.Client.Core
|
|||
NetworkStream networkStream = new NetworkStream(socket);
|
||||
socketWriter = new BinaryWriter(networkStream);
|
||||
socketReader = new BinaryReader(networkStream);
|
||||
/*
|
||||
socketWriter = new BinaryWriter(new NetworkStream(socket));
|
||||
socketReader = new BinaryReader(new NetworkStream(socket));
|
||||
*/
|
||||
|
||||
// now lets create the background read thread
|
||||
readThread = new Thread(new ThreadStart(ReadLoop));
|
||||
readThread.Start();
|
||||
|
||||
// lets send the wireformat we're using
|
||||
Oneway(wireformat.WireFormatInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,8 +100,16 @@ namespace OpenWire.Client.Core
|
|||
|
||||
public Response Request(Command command)
|
||||
{
|
||||
FutureResponse response = AsyncRequest(command);
|
||||
return response.Response;
|
||||
FutureResponse future = AsyncRequest(command);
|
||||
Response response = future.Response;
|
||||
if (response is ExceptionResponse)
|
||||
{
|
||||
ExceptionResponse er = (ExceptionResponse) response;
|
||||
BrokerError brokerError = er.Exception;
|
||||
throw new BrokerException(brokerError);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -149,21 +156,14 @@ namespace OpenWire.Client.Core
|
|||
if (response is ExceptionResponse)
|
||||
{
|
||||
ExceptionResponse er = (ExceptionResponse) response;
|
||||
Exception e = new BrokerException(er.Exception);
|
||||
BrokerError brokerError = er.Exception;
|
||||
if (this.Exception != null)
|
||||
{
|
||||
this.Exception(this, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
this.Exception(this, new BrokerException(brokerError));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
future.Response = response;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("ERROR: Unknown response ID: " + response.CommandId + " for response: " + response);
|
||||
|
|
|
@ -58,7 +58,10 @@ namespace OpenWire.Client.IO
|
|||
WireFormatInfo info = (WireFormatInfo)o;
|
||||
info.Magic = ReadBytes(dataIn, 8);
|
||||
info.Version = DataStreamMarshaller.ReadInt(dataIn);
|
||||
info.Options = DataStreamMarshaller.ReadInt(dataIn);
|
||||
info.CacheEnabled = bs.ReadBoolean();
|
||||
info.CompressionEnabled = bs.ReadBoolean();
|
||||
info.StackTraceEnabled = bs.ReadBoolean();
|
||||
info.TcpNoDelayEnabled = bs.ReadBoolean();
|
||||
|
||||
}
|
||||
|
||||
|
@ -70,8 +73,12 @@ namespace OpenWire.Client.IO
|
|||
WireFormatInfo info = (WireFormatInfo)o;
|
||||
|
||||
int rc = base.Marshal1(wireFormat, info, bs);
|
||||
bs.WriteBoolean(info.CacheEnabled);
|
||||
bs.WriteBoolean(info.CompressionEnabled);
|
||||
bs.WriteBoolean(info.StackTraceEnabled);
|
||||
bs.WriteBoolean(info.TcpNoDelayEnabled);
|
||||
|
||||
return rc + 10;
|
||||
return rc + 9;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -83,7 +90,10 @@ namespace OpenWire.Client.IO
|
|||
WireFormatInfo info = (WireFormatInfo)o;
|
||||
dataOut.Write(info.Magic, 0, 8);
|
||||
DataStreamMarshaller.WriteInt(info.Version, dataOut);
|
||||
DataStreamMarshaller.WriteInt(info.Options, dataOut);
|
||||
bs.ReadBoolean();
|
||||
bs.ReadBoolean();
|
||||
bs.ReadBoolean();
|
||||
bs.ReadBoolean();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
|
||||
namespace OpenWire.Client
|
||||
{
|
||||
[TestFixture]
|
||||
public class BadConsumeTest : TestSupport
|
||||
{
|
||||
[Test]
|
||||
public void TestBadConsumeOperationToTestExceptions()
|
||||
{
|
||||
IConnectionFactory factory = new ConnectionFactory("localhost", 61616);
|
||||
using (IConnection connection = factory.CreateConnection())
|
||||
{
|
||||
ISession session = connection.CreateSession();
|
||||
|
||||
try
|
||||
{
|
||||
IMessageConsumer consumer = session.CreateConsumer(null);
|
||||
Assert.Fail("Should have thrown an exception!");
|
||||
}
|
||||
catch (BrokerException e)
|
||||
{
|
||||
Console.WriteLine("Caught expected exception: " + e);
|
||||
Console.WriteLine("Stack: " + e.StackTrace);
|
||||
Console.WriteLine("BrokerStrack: " + e.BrokerError.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,6 @@ namespace OpenWire.Client
|
|||
public abstract class TestSupport
|
||||
{
|
||||
|
||||
[ Test ]
|
||||
public virtual void SendAndSyncReceive()
|
||||
{
|
||||
IConnectionFactory factory = new ConnectionFactory("localhost", 61616);
|
||||
|
@ -81,8 +80,12 @@ namespace OpenWire.Client
|
|||
return destination;
|
||||
}
|
||||
|
||||
protected abstract IMessage CreateMessage(ISession session);
|
||||
protected virtual IMessage CreateMessage(ISession session) {
|
||||
return session.CreateMessage();
|
||||
}
|
||||
|
||||
protected abstract void AssertValidMessage(IMessage message);
|
||||
protected virtual void AssertValidMessage(IMessage message) {
|
||||
Assert.IsNotNull(message, "Null Message!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue