2019-05-15 14:56:09 +00:00
/ *
*
* ( c ) Copyright Ascensio System Limited 2010 - 2018
*
* This program is freeware . You can redistribute it and / or modify it under the terms of the GNU
* General Public License ( GPL ) version 3 as published by the Free Software Foundation ( https : //www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7 ( a ) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non - infringement of any third - party rights .
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY ; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE . For more details , see GNU GPL at https : //www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales @onlyoffice . com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices , as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3 ( b ) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software . If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons , you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute .
* Pursuant to Section 7 § 3 ( e ) we decline to grant you any rights under trademark law for use of our trademarks .
*
* /
2022-02-15 11:52:43 +00:00
namespace ASC.Notify.Engine ;
public class NotifyEngine : INotifyEngine
{
private readonly ILog _logger ;
private readonly Context _context ;
private readonly List < SendMethodWrapper > _sendMethods = new List < SendMethodWrapper > ( ) ;
private readonly Queue < NotifyRequest > _requests = new Queue < NotifyRequest > ( 1000 ) ;
private readonly Thread _notifyScheduler ;
private readonly Thread _notifySender ;
private readonly AutoResetEvent _requestsEvent = new AutoResetEvent ( false ) ;
private readonly AutoResetEvent _methodsEvent = new AutoResetEvent ( false ) ;
private readonly Dictionary < string , IPatternStyler > _stylers = new Dictionary < string , IPatternStyler > ( ) ;
private readonly IPatternFormatter _sysTagFormatter = new ReplacePatternFormatter ( @"_#(?<tagName>[A-Z0-9_\-.]+)#_" , true ) ;
private readonly TimeSpan _defaultSleep = TimeSpan . FromSeconds ( 10 ) ;
private readonly IServiceProvider _serviceProvider ;
public event Action < NotifyEngine , NotifyRequest , IServiceScope > BeforeTransferRequest ;
public event Action < NotifyEngine , NotifyRequest , IServiceScope > AfterTransferRequest ;
public NotifyEngine ( Context context , IServiceProvider serviceProvider )
{
_context = context ? ? throw new ArgumentNullException ( nameof ( context ) ) ;
_logger = serviceProvider . GetService < IOptionsMonitor < ILog > > ( ) . Get ( "ASC.Notify" ) ;
_serviceProvider = serviceProvider ;
_notifyScheduler = new Thread ( NotifyScheduler ) { IsBackground = true , Name = "NotifyScheduler" } ;
_notifySender = new Thread ( NotifySender ) { IsBackground = true , Name = "NotifySender" } ;
}
public virtual void QueueRequest ( NotifyRequest request , IServiceScope serviceScope )
{
BeforeTransferRequest ? . Invoke ( this , request , serviceScope ) ;
lock ( _requests )
{
if ( ! _notifySender . IsAlive )
{
_notifySender . Start ( ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
_requests . Enqueue ( request ) ;
}
_requestsEvent . Set ( ) ;
}
internal void RegisterSendMethod ( Action < DateTime > method , string cron )
{
2022-03-09 17:15:51 +00:00
ArgumentNullException . ThrowIfNull ( method ) ;
ArgumentNullOrEmptyException . ThrowIfNullOrEmpty ( cron ) ;
2022-02-15 11:52:43 +00:00
var w = new SendMethodWrapper ( method , cron , _logger ) ;
lock ( _sendMethods )
{
if ( ! _notifyScheduler . IsAlive )
2022-02-14 21:02:57 +00:00
{
2022-02-15 11:52:43 +00:00
_notifyScheduler . Start ( ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
_sendMethods . Remove ( w ) ;
_sendMethods . Add ( w ) ;
}
_methodsEvent . Set ( ) ;
}
internal void UnregisterSendMethod ( Action < DateTime > method )
{
2022-03-09 17:15:51 +00:00
ArgumentNullException . ThrowIfNull ( method ) ;
2022-02-15 11:52:43 +00:00
lock ( _sendMethods )
{
_sendMethods . Remove ( new SendMethodWrapper ( method , null , _logger ) ) ;
}
}
private void NotifyScheduler ( object state )
{
try
{
while ( true )
2022-02-14 21:02:57 +00:00
{
2022-02-15 11:52:43 +00:00
var min = DateTime . MaxValue ;
var now = DateTime . UtcNow ;
List < SendMethodWrapper > copy ;
lock ( _sendMethods )
{
copy = _sendMethods . ToList ( ) ;
}
foreach ( var w in copy )
{
if ( ! w . ScheduleDate . HasValue )
{
lock ( _sendMethods )
{
_sendMethods . Remove ( w ) ;
}
}
if ( w . ScheduleDate . Value < = now )
{
try
{
w . InvokeSendMethod ( now ) ;
}
catch ( Exception error )
{
_logger . Error ( error ) ;
}
w . UpdateScheduleDate ( now ) ;
}
if ( w . ScheduleDate . Value > now & & w . ScheduleDate . Value < min )
{
min = w . ScheduleDate . Value ;
}
}
var wait = min ! = DateTime . MaxValue ? min - DateTime . UtcNow : _defaultSleep ;
if ( wait < _defaultSleep )
{
wait = _defaultSleep ;
}
else if ( wait . Ticks > int . MaxValue )
{
wait = TimeSpan . FromTicks ( int . MaxValue ) ;
}
_methodsEvent . WaitOne ( wait , false ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
}
catch ( ThreadAbortException )
{
return ;
}
catch ( Exception e )
{
_logger . Error ( e ) ;
}
}
2022-02-14 21:02:57 +00:00
2022-02-15 11:52:43 +00:00
private void NotifySender ( object state )
{
try
{
while ( true )
2022-02-14 21:02:57 +00:00
{
2022-02-15 11:52:43 +00:00
NotifyRequest request = null ;
lock ( _requests )
{
if ( _requests . Count > 0 )
{
request = _requests . Dequeue ( ) ;
}
}
if ( request ! = null )
{
using var scope = _serviceProvider . CreateScope ( ) ;
AfterTransferRequest ? . Invoke ( this , request , scope ) ;
try
{
SendNotify ( request , scope ) ;
}
catch ( Exception e )
{
_logger . Error ( e ) ;
}
}
else
{
_requestsEvent . WaitOne ( ) ;
}
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
}
catch ( ThreadAbortException )
{
return ;
}
catch ( Exception e )
{
_logger . Error ( e ) ;
}
}
2022-02-14 21:02:57 +00:00
2022-02-15 11:52:43 +00:00
private NotifyResult SendNotify ( NotifyRequest request , IServiceScope serviceScope )
{
var sendResponces = new List < SendResponse > ( ) ;
var response = CheckPreventInterceptors ( request , InterceptorPlace . Prepare , serviceScope , null ) ;
if ( response ! = null )
{
sendResponces . Add ( response ) ;
}
else
{
sendResponces . AddRange ( SendGroupNotify ( request , serviceScope ) ) ;
}
NotifyResult result ;
if ( sendResponces . Count = = 0 )
{
result = new NotifyResult ( SendResult . OK , sendResponces ) ;
}
else
{
result = new NotifyResult ( sendResponces . Aggregate ( ( SendResult ) 0 , ( s , r ) = > r . Result ) , sendResponces ) ;
}
_logger . Debug ( result ) ;
return result ;
}
private SendResponse CheckPreventInterceptors ( NotifyRequest request , InterceptorPlace place , IServiceScope serviceScope , string sender )
{
return request . Intercept ( place , serviceScope ) ? new SendResponse ( request . NotifyAction , sender , request . Recipient , SendResult . Prevented ) : null ;
}
private List < SendResponse > SendGroupNotify ( NotifyRequest request , IServiceScope serviceScope )
{
var responces = new List < SendResponse > ( ) ;
SendGroupNotify ( request , responces , serviceScope ) ;
return responces ;
}
private void SendGroupNotify ( NotifyRequest request , List < SendResponse > responces , IServiceScope serviceScope )
{
if ( request . Recipient is IDirectRecipient )
{
var subscriptionSource = request . GetSubscriptionProvider ( serviceScope ) ;
2022-03-17 16:57:02 +00:00
if ( ! request . _isNeedCheckSubscriptions | | ! subscriptionSource . IsUnsubscribe ( request . Recipient as IDirectRecipient , request . NotifyAction , request . ObjectID ) )
2022-02-15 11:52:43 +00:00
{
var directresponses = new List < SendResponse > ( 1 ) ;
try
{
directresponses = SendDirectNotify ( request , serviceScope ) ;
}
catch ( Exception exc )
{
directresponses . Add ( new SendResponse ( request . NotifyAction , request . Recipient , exc ) ) ;
}
responces . AddRange ( directresponses ) ;
}
}
else
{
if ( request . Recipient is IRecipientsGroup )
{
var checkresp = CheckPreventInterceptors ( request , InterceptorPlace . GroupSend , serviceScope , null ) ;
if ( checkresp ! = null )
{
responces . Add ( checkresp ) ;
}
else
{
var recipientProvider = request . GetRecipientsProvider ( serviceScope ) ;
try
{
var recipients = recipientProvider . GetGroupEntries ( request . Recipient as IRecipientsGroup ) ? ? new IRecipient [ 0 ] ;
foreach ( var recipient in recipients )
{
try
{
var newRequest = request . Split ( recipient ) ;
SendGroupNotify ( newRequest , responces , serviceScope ) ;
}
catch ( Exception exc )
{
responces . Add ( new SendResponse ( request . NotifyAction , request . Recipient , exc ) ) ;
}
}
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
catch ( Exception exc )
{
responces . Add ( new SendResponse ( request . NotifyAction , request . Recipient , exc ) { Result = SendResult . IncorrectRecipient } ) ;
}
}
}
else
2022-02-14 21:02:57 +00:00
{
2022-02-15 11:52:43 +00:00
responces . Add ( new SendResponse ( request . NotifyAction , request . Recipient , null )
{
Result = SendResult . IncorrectRecipient ,
Exception = new NotifyException ( "recipient may be IRecipientsGroup or IDirectRecipient" )
} ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
}
}
2022-02-14 21:02:57 +00:00
2022-02-15 11:52:43 +00:00
private List < SendResponse > SendDirectNotify ( NotifyRequest request , IServiceScope serviceScope )
{
if ( ! ( request . Recipient is IDirectRecipient ) )
{
throw new ArgumentException ( "request.Recipient not IDirectRecipient" , nameof ( request ) ) ;
}
var responses = new List < SendResponse > ( ) ;
var response = CheckPreventInterceptors ( request , InterceptorPlace . DirectSend , serviceScope , null ) ;
if ( response ! = null )
{
responses . Add ( response ) ;
return responses ;
}
try
{
PrepareRequestFillSenders ( request , serviceScope ) ;
PrepareRequestFillPatterns ( request , serviceScope ) ;
PrepareRequestFillTags ( request , serviceScope ) ;
}
catch ( Exception ex )
{
responses . Add ( new SendResponse ( request . NotifyAction , null , request . Recipient , SendResult . Impossible ) ) ;
_logger . Error ( "Prepare" , ex ) ;
}
2022-03-17 16:57:02 +00:00
if ( request . _senderNames ! = null & & request . _senderNames . Length > 0 )
2022-02-15 11:52:43 +00:00
{
2022-03-17 16:57:02 +00:00
foreach ( var sendertag in request . _senderNames )
2022-02-15 11:52:43 +00:00
{
var channel = _context . NotifyService . GetSender ( sendertag ) ;
if ( channel ! = null )
{
try
{
response = SendDirectNotify ( request , channel , serviceScope ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
catch ( Exception exc )
{
response = new SendResponse ( request . NotifyAction , channel . SenderName , request . Recipient , exc ) ;
}
}
else
{
response = new SendResponse ( request . NotifyAction , sendertag , request . Recipient , new NotifyException ( $"Not registered sender \" { sendertag } \ "." ) ) ;
}
responses . Add ( response ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
}
else
{
response = new SendResponse ( request . NotifyAction , request . Recipient , new NotifyException ( "Notice hasn't any senders." ) ) ;
responses . Add ( response ) ;
}
return responses ;
}
private SendResponse SendDirectNotify ( NotifyRequest request , ISenderChannel channel , IServiceScope serviceScope )
{
if ( ! ( request . Recipient is IDirectRecipient ) )
{
throw new ArgumentException ( "request.Recipient not IDirectRecipient" , nameof ( request ) ) ;
}
request . CurrentSender = channel . SenderName ;
var oops = CreateNoticeMessageFromNotifyRequest ( request , channel . SenderName , serviceScope , out var noticeMessage ) ;
if ( oops ! = null )
{
return oops ;
}
request . CurrentMessage = noticeMessage ;
var preventresponse = CheckPreventInterceptors ( request , InterceptorPlace . MessageSend , serviceScope , channel . SenderName ) ;
if ( preventresponse ! = null )
{
return preventresponse ;
}
channel . SendAsync ( noticeMessage ) ;
return new SendResponse ( noticeMessage , channel . SenderName , SendResult . Inprogress ) ;
}
private SendResponse CreateNoticeMessageFromNotifyRequest ( NotifyRequest request , string sender , IServiceScope serviceScope , out NoticeMessage noticeMessage )
{
2022-03-09 17:15:51 +00:00
ArgumentNullException . ThrowIfNull ( request ) ;
2022-02-15 11:52:43 +00:00
var recipientProvider = request . GetRecipientsProvider ( serviceScope ) ;
var recipient = request . Recipient as IDirectRecipient ;
var addresses = recipient . Addresses ;
if ( addresses = = null | | addresses . Length = = 0 )
{
addresses = recipientProvider . GetRecipientAddresses ( request . Recipient as IDirectRecipient , sender ) ;
recipient = new DirectRecipient ( request . Recipient . ID , request . Recipient . Name , addresses ) ;
}
recipient = recipientProvider . FilterRecipientAddresses ( recipient ) ;
noticeMessage = request . CreateMessage ( recipient ) ;
addresses = recipient . Addresses ;
if ( addresses = = null | | ! addresses . Any ( a = > ! string . IsNullOrEmpty ( a ) ) )
{
//checking addresses
return new SendResponse ( request . NotifyAction , sender , recipient , new NotifyException ( string . Format ( "For recipient {0} by sender {1} no one addresses getted." , recipient , sender ) ) ) ;
}
var pattern = request . GetSenderPattern ( sender ) ;
if ( pattern = = null )
{
return new SendResponse ( request . NotifyAction , sender , recipient , new NotifyException ( string . Format ( "For action \"{0}\" by sender \"{1}\" no one patterns getted." , request . NotifyAction , sender ) ) ) ;
}
noticeMessage . Pattern = pattern ;
noticeMessage . ContentType = pattern . ContentType ;
noticeMessage . AddArgument ( request . Arguments . ToArray ( ) ) ;
var patternProvider = request . GetPatternProvider ( serviceScope ) ;
var formatter = patternProvider . GetFormatter ( pattern ) ;
try
{
if ( formatter ! = null )
2022-02-14 21:02:57 +00:00
{
2022-02-15 11:52:43 +00:00
formatter . FormatMessage ( noticeMessage , noticeMessage . Arguments ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
_sysTagFormatter . FormatMessage (
noticeMessage , new [ ]
{
2022-03-17 16:57:02 +00:00
new TagValue ( Context . _sysRecipientId , request . Recipient . ID ) ,
new TagValue ( Context . _sysRecipientName , request . Recipient . Name ) ,
new TagValue ( Context . _sysRecipientAddress , addresses ! = null & & addresses . Length > 0 ? addresses [ 0 ] : null )
2022-02-15 11:52:43 +00:00
}
) ;
//Do styling here
if ( ! string . IsNullOrEmpty ( pattern . Styler ) )
{
//We need to run through styler before templating
StyleMessage ( serviceScope , noticeMessage ) ;
}
}
catch ( Exception exc )
{
return new SendResponse ( request . NotifyAction , sender , recipient , exc ) ;
}
2022-02-14 21:02:57 +00:00
2022-02-15 11:52:43 +00:00
return null ;
}
private void StyleMessage ( IServiceScope scope , NoticeMessage message )
{
try
{
if ( ! _stylers . ContainsKey ( message . Pattern . Styler ) )
2022-02-14 21:02:57 +00:00
{
2022-02-15 11:52:43 +00:00
if ( scope . ServiceProvider . GetService ( Type . GetType ( message . Pattern . Styler , true ) ) is IPatternStyler styler )
{
_stylers . Add ( message . Pattern . Styler , styler ) ;
}
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
_stylers [ message . Pattern . Styler ] . ApplyFormating ( message ) ;
}
catch ( Exception exc )
{
_logger . Warn ( "error styling message" , exc ) ;
}
}
private void PrepareRequestFillSenders ( NotifyRequest request , IServiceScope serviceScope )
{
2022-03-17 16:57:02 +00:00
if ( request . _senderNames = = null )
2022-02-15 11:52:43 +00:00
{
var subscriptionProvider = request . GetSubscriptionProvider ( serviceScope ) ;
var senderNames = new List < string > ( ) ;
senderNames . AddRange ( subscriptionProvider . GetSubscriptionMethod ( request . NotifyAction , request . Recipient ) ? ? Array . Empty < string > ( ) ) ;
senderNames . AddRange ( request . Arguments . OfType < AdditionalSenderTag > ( ) . Select ( tag = > ( string ) tag . Value ) ) ;
2022-03-17 16:57:02 +00:00
request . _senderNames = senderNames . ToArray ( ) ;
2022-02-15 11:52:43 +00:00
}
}
2022-02-14 21:02:57 +00:00
2022-02-15 11:52:43 +00:00
private void PrepareRequestFillPatterns ( NotifyRequest request , IServiceScope serviceScope )
{
2022-03-17 16:57:02 +00:00
if ( request . _patterns = = null )
2022-02-15 11:52:43 +00:00
{
2022-03-17 16:57:02 +00:00
request . _patterns = new IPattern [ request . _senderNames . Length ] ;
if ( request . _patterns . Length = = 0 )
2022-02-14 21:02:57 +00:00
{
2022-02-15 11:52:43 +00:00
return ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
var apProvider = request . GetPatternProvider ( serviceScope ) ;
2022-03-17 16:57:02 +00:00
for ( var i = 0 ; i < request . _senderNames . Length ; i + + )
2022-02-14 21:02:57 +00:00
{
2022-03-17 16:57:02 +00:00
var senderName = request . _senderNames [ i ] ;
2022-02-15 11:52:43 +00:00
IPattern pattern = null ;
if ( apProvider . GetPatternMethod ! = null )
{
pattern = apProvider . GetPatternMethod ( request . NotifyAction , senderName , request ) ;
}
if ( pattern = = null )
{
pattern = apProvider . GetPattern ( request . NotifyAction , senderName ) ;
}
2022-03-17 16:57:02 +00:00
request . _patterns [ i ] = pattern ? ? throw new NotifyException ( $"For action \" { request . NotifyAction . ID } \ " by sender \"{senderName}\" no one patterns getted." ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
}
}
2022-02-14 21:02:57 +00:00
2022-02-15 11:52:43 +00:00
private void PrepareRequestFillTags ( NotifyRequest request , IServiceScope serviceScope )
{
var patternProvider = request . GetPatternProvider ( serviceScope ) ;
2022-03-17 16:57:02 +00:00
foreach ( var pattern in request . _patterns )
2022-02-15 11:52:43 +00:00
{
IPatternFormatter formatter ;
try
{
formatter = patternProvider . GetFormatter ( pattern ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
catch ( Exception exc )
{
throw new NotifyException ( string . Format ( "For pattern \"{0}\" formatter not instanced." , pattern ) , exc ) ;
}
var tags = Array . Empty < string > ( ) ;
try
{
if ( formatter ! = null )
2022-02-14 21:02:57 +00:00
{
2022-02-15 11:52:43 +00:00
tags = formatter . GetTags ( pattern ) ? ? Array . Empty < string > ( ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
}
catch ( Exception exc )
{
throw new NotifyException ( string . Format ( "Get tags from formatter of pattern \"{0}\" failed." , pattern ) , exc ) ;
}
2022-02-14 21:02:57 +00:00
2022-03-17 16:57:02 +00:00
foreach ( var tag in tags . Where ( tag = > ! request . Arguments . Exists ( tagValue = > Equals ( tagValue . Tag , tag ) ) & & ! request . _requaredTags . Exists ( rtag = > Equals ( rtag , tag ) ) ) )
2022-02-15 11:52:43 +00:00
{
2022-03-17 16:57:02 +00:00
request . _requaredTags . Add ( tag ) ;
2022-02-15 11:52:43 +00:00
}
}
}
private sealed class SendMethodWrapper
{
private readonly object _locker = new object ( ) ;
private readonly CronExpression _cronExpression ;
private readonly Action < DateTime > _method ;
public DateTime ? ScheduleDate { get ; private set ; }
public ILog Logger { get ; }
public SendMethodWrapper ( Action < DateTime > method , string cron , ILog log )
{
_method = method ;
Logger = log ;
if ( ! string . IsNullOrEmpty ( cron ) )
{
_cronExpression = new CronExpression ( cron ) ;
}
UpdateScheduleDate ( DateTime . UtcNow ) ;
}
public void UpdateScheduleDate ( DateTime d )
{
try
{
if ( _cronExpression ! = null )
{
ScheduleDate = _cronExpression . GetTimeAfter ( d ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
}
catch ( Exception e )
{
Logger . Error ( e ) ;
}
}
public void InvokeSendMethod ( DateTime d )
{
lock ( _locker )
{
Task . Run ( ( ) = >
{
try
{
_method ( d ) ;
}
catch ( Exception e )
{
Logger . Error ( e ) ;
}
} ) . Wait ( ) ;
}
}
public override bool Equals ( object obj )
{
return obj is SendMethodWrapper w & & _method . Equals ( w . _method ) ;
}
public override int GetHashCode ( )
{
return _method . GetHashCode ( ) ;
}
}
2019-05-15 14:56:09 +00:00
}