You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@activemq.apache.org by StefanL <st...@yahoo.se> on 2007/09/04 08:06:02 UTC

Re: ActiveMQ.Net: Client does not include timestamp when calculating expiration

Hi Jim,

Thanks for your fix which I am about to use. Just a note on the type
initializer in DateUtils,
    static DateUtils() 
    { 
        epochDiff = (javaEpoch.ToFileTime() - windowsEpoch.ToFileTime())  /
TimeSpan.TicksPerMillisecond; 
    } 

This fails here in GMT+1 land when the 'windowsEpoch.ToFileTime()' is called
with ArgumentException.
If I instead do 'new DateTime(1601, 1, 1, 1, 0, 0, 0).ToFileTime()' this
gives 0/zero.
Note the 1 hour specified to the DateTime ctor.

Anyway, I would suggest that the row would be 
epochDiff = (javaEpoch.ToFileTimeUtc() - windowsEpoch.ToFileTimeUtc())  /
TimeSpan.TicksPerMillisecond;

Which runs without exceptions, hopefully it runs correct also..
Regards
 Stefan


semog wrote:
> 
> Hi Peter,
> 
> I have just spent the last week diagnosing and fixing this very problem in
> the .NET client.  It took me a long time to figure out exactly where the
> problem was.  There seems to be a disconnect between the concept of "time
> to live" and "expiration".  "Time to live" is a relative timespan, while
> "expiration" is an absolute date/time.  In addition, there is a bug in the
> DateUtils class when converting between .NET DateTime and Java dates. 
> Here is the fix that I have come up with.  Basically, I fixed the date
> utility conversion functions.  I then made the concept of "time to live"
> transparent to the client API.  ActiveMQ seems to require an absolute
> "expiration" time, so I hid the translation of that inside the
> ActiveMQMessage class.  Following are the gist of my changes.  I will show
> the key parts that you can add, but you may have to change some
> supplementary files to make the change complete, but those supplementary
> changes should be very obvious (i.e., adding a declaration, or changing
> the NMSExpiration to NMSTimeToLive).  Try these changes and see if they
> work for you:
> 
> Here is my new implementation of the DateUtils class.  This is a complete
> drop-in replacement.  The key fix here was the old implementation mixed
> calculating ticks with millisecends.  Ticks are defined as 100-nanosecond
> increments, a calculation error factor of 10,000:
> 
> 	internal class DateUtils
> 	{
> 		/// <summary>
> 		/// The start of the Windows epoch
> 		/// </summary>
> 		public static readonly DateTime windowsEpoch = new DateTime(1601, 1, 1,
> 0, 0, 0, 0);
> 		/// <summary>
> 		/// The start of the Java epoch
> 		/// </summary>
> 		public static readonly DateTime javaEpoch = new DateTime(1970, 1, 1, 0,
> 0, 0, 0);
> 		
> 		/// <summary>
> 		/// The difference between the Windows epoch and the Java epoch
> 		/// in milliseconds.
> 		/// </summary>
> 		public static readonly long epochDiff; /* = 1164447360000L; */
> 
> 		static DateUtils()
> 		{
> 			epochDiff = (javaEpoch.ToFileTime() - windowsEpoch.ToFileTime())
> 							/ TimeSpan.TicksPerMillisecond;
> 		}
> 
> 		public static long ToJavaTime(DateTime dateTime)
> 		{
> 			return (dateTime.ToFileTime() / TimeSpan.TicksPerMillisecond) -
> epochDiff;
> 		}
> 
> 		public static DateTime ToDateTime(long javaTime)
> 		{
> 			return DateTime.FromFileTime((javaTime + epochDiff) *
> TimeSpan.TicksPerMillisecond);
> 		}
> 	}
> 
> In IMessage.cs, change the interface declaration name for NMSExpiration to
> NMSTimeToLive.  In the ActiveMQMessage.cs file, add the following member
> variable to ActiveMQMessage:
> 
> 		protected DateTime expirationBaseTime;
> 
> In the same ActiveMQMessage.cs file, change the definition for
> NMSExpiration into the following definition for NMSTimeToLive (renaming
> the property field at the same time):
> 
>         /// <summary>
>         /// The time in milliseconds that this message should expire in
>         /// </summary>
> 		public TimeSpan NMSTimeToLive
> 		{
> 			get {
> 				if(0 != Expiration)
> 				{
> 					DateTime expirationTime = DateUtils.ToDateTime(Expiration);
> 					return expirationTime - expirationBaseTime;
> 				}
> 				else
> 				{
> 					return TimeSpan.FromMilliseconds(0);
> 				}
> 			}
> 			set {
> 				expirationBaseTime = DateTime.UtcNow;
> 				Expiration = DateUtils.ToJavaTime(expirationBaseTime + value);
> 			}
> 		}
> 
> I also changed the NMSTimeStamp property as follows.  I added a setter
> property, which makes things much easier on the client side:
> 
>         /// <summary>
>         /// The timestamp the broker added to the message
>         /// </summary>
>         public DateTime NMSTimestamp
>         {
>             get {
>                 return DateUtils.ToDateTime(Timestamp);
>             }
>             set {
>                 Timestamp = DateUtils.ToJavaTime(value);
>             }
>         }
> 
> Now, in the ActiveMQ MessageProducer.cs file, change the Send(...)
> function as follows:
> 
> 		protected void Send(IDestination destination, IMessage message, bool
> persistent, byte priority, TimeSpan timeToLive, bool specifiedTimeToLive)
> 		{
> 			ActiveMQMessage activeMessage = (ActiveMQMessage)message;
> 
> 			if (!disableMessageID)
> 			{
> 				MessageId id = new MessageId();
> 				id.ProducerId = info.ProducerId;
> 				lock (this)
> 				{
> 					id.ProducerSequenceId = ++messageCounter;
> 				}
> 
> 				activeMessage.MessageId = id;
> 			}
> 
> 			activeMessage.ProducerId = info.ProducerId;
> 			activeMessage.FromDestination = destination;
> 			activeMessage.NMSPersistent = persistent;
> 			activeMessage.NMSPriority = priority;
> 
> 			if (session.Transacted)
> 			{
> 				session.DoStartTransaction();
> 				activeMessage.TransactionId =
> session.TransactionContext.TransactionId;
> 			}
> 
> 			if (specifiedTimeToLive)
> 			{
> 				activeMessage.NMSTimeToLive = timeToLive;
> 			}
> 
> 			if (!disableMessageTimestamp)
> 			{
> 				activeMessage.NMSTimestamp = DateTime.UtcNow;
> 			}
> 
> 			session.DoSend(activeMessage);
> 		}
> 
> Notice that the setting of the NMSTimestamp property does not require
> conversion to Java time from .NET time format, as this is taken care of
> inside the new setter property.  At any level above this where you may
> have explicitly set the NMSExpiration property of the message, will need
> to be changed to NMSTimeToLive.  The NMSTimeToLive property is now
> correctly and consistently a timespan, instead of an absolute date/time. 
> These changes are much more logical to me, and I will continue with my
> version of the client.  These changes allow my .NET clients to deal
> compleletly in .NET date/time format and the conversion to/from Java
> date/time format is hidden.
> 
> Like I mentioned, you will need to make some trivial changes to MSMQ
> implementation and one or two test cases, but those are simple renames.  I
> hope that this can save you some time.  Since I am new to this list, I
> haven't found the directions on how to submit these changes back for
> possible commit into the actual source code.  Perhaps this is the first
> step in that process.
> 
> Thanks.  Please let me know if you have any questions or need further
> assistance in this area.
> 
> - Jim Gomes
> 
> 
> 
> PeterNilsson wrote:
>> 
>> Hi,
>> 
>> We have just started setting timeToLive when sending messages with the
>> ActiveMQ.Net client. However we get an exception when sending:
>> 
>> mscorlib.dll!System.DateTime.ToFileTimeUtc() + 0xad bytes	
>> mscorlib.dll!System.DateTime.ToFileTime() + 0x1c bytes	
>> NMS.ActiveMQ.DLL!ActiveMQ.Util.DateUtils.ToJavaTime(System.DateTime
>> dateTime = {0001-01-01 00:00:05}) Line 46 + 0x8 bytes	C#
>> NMS.ActiveMQ.DLL!ActiveMQ.Util.DateUtils.ToJavaTime(System.TimeSpan
>> timeToLive = {00:00:05}) Line 41 + 0x35 bytes	C#
>> NMS.ActiveMQ.DLL!ActiveMQ.MessageProducer.Send(NMS.IDestination
>> destination = {queue://test}, NMS.IMessage message =
>> {ActiveMQBytesMessage[ ProducerId= Destination= TransactionId=
>> OriginalDestination= MessageId=MessageId[ ProducerId=ProducerId[
>> ConnectionId=a5e10bc2-46ce-4b5b-be85-d1ed6e0c7575 Value=1 SessionId=1 ]
>> ProducerSequenceId=1 BrokerSequenceId=0 ] OriginalTransactionId= GroupID=
>> GroupSequence=0 CorrelationId=0 Persistent=False Expiration=0 Priority=0
>> ReplyTo=temp-queue://a5e10bc2-46ce-4b5b-be85-d1ed6e0c7575:1
>> Timestamp=128310953802143903 Type= Content=System.Byte[]
>> MarshalledProperties= DataStructure= TargetConsumerId= Compressed=False
>> RedeliveryCounter=0 BrokerPath= Arrival=0 UserID=
>> RecievedByDFBridge=False Droppable=False ]}, bool persistent = false,
>> byte priority = 5, System.TimeSpan timeToLive = {00:00:05}, bool
>> specifiedTimeToLive = true) Line 88 + 0x17 bytes	C#
>> NMS.ActiveMQ.DLL!ActiveMQ.MessageProducer.Send(NMS.IDestination
>> destination = {queue://test}, NMS.IMessage message =
>> {ActiveMQBytesMessage[ ProducerId= Destination= TransactionId=
>> OriginalDestination= MessageId=MessageId[ ProducerId=ProducerId[
>> ConnectionId=a5e10bc2-46ce-4b5b-be85-d1ed6e0c7575 Value=1 SessionId=1 ]
>> ProducerSequenceId=1 BrokerSequenceId=0 ] OriginalTransactionId= GroupID=
>> GroupSequence=0 CorrelationId=0 Persistent=False Expiration=0 Priority=0
>> ReplyTo=temp-queue://a5e10bc2-46ce-4b5b-be85-d1ed6e0c7575:1
>> Timestamp=128310953802143903 Type= Content=System.Byte[]
>> MarshalledProperties= DataStructure= TargetConsumerId= Compressed=False
>> RedeliveryCounter=0 BrokerPath= Arrival=0 UserID=
>> RecievedByDFBridge=False Droppable=False ]}, bool persistent = false,
>> byte priority = 5, System.TimeSpan timeToLive = {00:00:05}) Line 62 +
>> 0x27 bytes	C#
>>  	
>> When looking at the code for MessageProducer it looks like timestamp has
>> been omitted from the calculation of expiration: 
>> 
>> if (specifiedTimeToLive) {
>>   activeMessage.Expiration =
>> ActiveMQ.Util.DateUtils.ToJavaTime(timeToLive);
>> }
>> 
>> It looks like a bug to me, do you agree? 
>> 
>>    Peter
>> 
>> 
> 
> 

-- 
View this message in context: http://www.nabble.com/ActiveMQ.Net%3A-Client-does-not-include-timestamp-when-calculating-expiration-tf4312287s2354.html#a12472360
Sent from the ActiveMQ - Dev mailing list archive at Nabble.com.


Re: ActiveMQ.Net: Client does not include timestamp when calculating expiration

Posted by semog <e....@gmail.com>.
Hi Stefan,

Thanks for your feedback.  I wouldn't change the epoch times, because that
may cause discrepancies if any code is added later to reference the
individual epochs. I am in the GMT-8 (sometimes GMT-7) timezone.  I am
surprised that the call fails, but have theories as to why it would fail and
the Utc version of the function would succeed.  Your suggested change to the
calculation of epochDiff is correct.  Converting both to Utc time before the
subtraction operation will produce the same results.  I'll incorporate your
change in to my code as well and include that in the patch that I will
submit, unless you report back there are still problems with it.

- Jim


StefanL wrote:
> 
> Hi Jim,
> 
> Thanks for your fix which I am about to use. Just a note on the type
> initializer in DateUtils,
>     static DateUtils() 
>     { 
>         epochDiff = (javaEpoch.ToFileTime() - windowsEpoch.ToFileTime()) 
> / TimeSpan.TicksPerMillisecond; 
>     } 
> 
> This fails here in GMT+1 land when the 'windowsEpoch.ToFileTime()' is
> called with ArgumentException.
> If I instead do 'new DateTime(1601, 1, 1, 1, 0, 0, 0).ToFileTime()' this
> gives 0/zero.
> Note the 1 hour specified to the DateTime ctor.
> 
> Anyway, I would suggest that the row would be 
> epochDiff = (javaEpoch.ToFileTimeUtc() - windowsEpoch.ToFileTimeUtc())  /
> TimeSpan.TicksPerMillisecond;
> 
> Which runs without exceptions, hopefully it runs correct also..
> Regards
>  Stefan
> 
> 

-- 
View this message in context: http://www.nabble.com/ActiveMQ.Net%3A-Client-does-not-include-timestamp-when-calculating-expiration-tf4312287s2354.html#a12489276
Sent from the ActiveMQ - Dev mailing list archive at Nabble.com.