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.

Reply via email to