@@ -90,25 +90,73 @@ private async Task RunWebSocketsAsync(CancellationToken cancellationToken)
90
90
PropertyNameCaseInsensitive = true
91
91
} ) ;
92
92
93
- string remoteStoragePath = Mapping . GetAbsoluteUri ( webSocketMessage . ItemPath ) ;
94
-
95
- // Just in case there is more than one WebSockets server/virtual folder that
96
- // is sending notifications (like with webdavserver.net, webdavserver.com),
97
- // here we filter notifications that come from a different server/virtual folder.
98
- if ( remoteStoragePath . StartsWith ( Program . Settings . WebDAVServerUrl , StringComparison . InvariantCultureIgnoreCase ) )
93
+ // Because of the on-demand loading, item or its parent may not exists or be offline.
94
+ // We can ignore notifiction in this case and avoid many requests to the remote storage.
95
+ if ( ShouldUpdate ( webSocketMessage ) )
99
96
{
100
- Logger . LogDebug ( $ "EventType: { webSocketMessage . EventType } ", webSocketMessage . ItemPath , webSocketMessage . TargetPath ) ;
101
97
await ProcessAsync ( ) ;
102
98
}
103
99
}
104
100
}
105
101
102
+ /// <summary>
103
+ /// Verifies that the item exists in the user file system and should be updated.
104
+ /// </summary>
105
+ /// <param name="webSocketMessage">Information about change in the remote storage.</param>
106
+ /// <returns>True if the item exists and should be updated. False otherwise.</returns>
107
+ private bool ShouldUpdate ( WebSocketMessage webSocketMessage )
108
+ {
109
+ string remoteStoragePath = Mapping . GetAbsoluteUri ( webSocketMessage . ItemPath ) ;
110
+
111
+ // Just in case there is more than one WebSockets server/virtual folder that
112
+ // is sending notifications (like with webdavserver.net, webdavserver.com),
113
+ // here we filter notifications that come from a different server/virtual folder.
114
+ if ( remoteStoragePath . StartsWith ( Program . Settings . WebDAVServerUrl , StringComparison . InvariantCultureIgnoreCase ) )
115
+ {
116
+ Logger . LogDebug ( $ "EventType: { webSocketMessage . EventType } ", webSocketMessage . ItemPath , webSocketMessage . TargetPath ) ;
117
+
118
+ string userFileSystemPath = Mapping . ReverseMapPath ( remoteStoragePath ) ;
119
+ switch ( webSocketMessage . EventType )
120
+ {
121
+ case "created" :
122
+ case "deleted" :
123
+ // Verify that parent folder exists and is not offline.
124
+ string userFileSystemParentPath = Path . GetDirectoryName ( userFileSystemPath ) ;
125
+ return Directory . Exists ( userFileSystemParentPath )
126
+ && ! new DirectoryInfo ( userFileSystemParentPath ) . Attributes . HasFlag ( FileAttributes . Offline ) ;
127
+
128
+ case "moved" :
129
+ // Verify that source exists OR target folder exists and is not offline.
130
+ if ( File . Exists ( userFileSystemPath ) )
131
+ {
132
+ return true ;
133
+ }
134
+ else
135
+ {
136
+ string remoteStorageNewPath = Mapping . GetAbsoluteUri ( webSocketMessage . TargetPath ) ;
137
+ string userFileSystemNewPath = Mapping . ReverseMapPath ( remoteStorageNewPath ) ;
138
+ string userFileSystemNewParentPath = Path . GetDirectoryName ( userFileSystemNewPath ) ;
139
+ return Directory . Exists ( userFileSystemNewParentPath )
140
+ && ! new DirectoryInfo ( userFileSystemNewParentPath ) . Attributes . HasFlag ( FileAttributes . Offline ) ;
141
+ }
142
+
143
+ case "updated" :
144
+ default :
145
+ // Any other notifications.
146
+ return File . Exists ( userFileSystemPath ) ;
147
+ }
148
+ }
149
+
150
+ return false ;
151
+ }
152
+
106
153
/// <summary>
107
154
/// Starts monitoring changes in the remote storage.
108
155
/// </summary>
109
156
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
110
157
public async Task StartAsync ( CancellationToken cancellationToken = default )
111
158
{
159
+ Logger . LogDebug ( "Starting" , webSocketServerUrl ) ;
112
160
await Task . Factory . StartNew (
113
161
async ( ) =>
114
162
{
@@ -123,21 +171,19 @@ await Task.Factory.StartNew(
123
171
{
124
172
repeat = false ;
125
173
126
- Logger . LogDebug ( "Starting" , webSocketServerUrl ) ;
127
-
128
174
// Configure web sockets and connect to the server.
129
175
clientWebSocket . Options . KeepAliveInterval = TimeSpan . FromSeconds ( 10 ) ;
130
176
clientWebSocket . Options . Credentials = Engine . Credentials ;
131
177
clientWebSocket . Options . Cookies = new CookieContainer ( ) ;
132
178
clientWebSocket . Options . Cookies . Add ( Engine . Cookies ) ;
133
179
clientWebSocket . Options . SetRequestHeader ( "InstanceId" , Engine . InstanceId . ToString ( ) ) ;
134
180
await clientWebSocket . ConnectAsync ( new Uri ( webSocketServerUrl ) , cancellationToken ) ;
135
- Logger . LogDebug ( "Connected" , webSocketServerUrl ) ;
181
+ Logger . LogMessage ( "Connected" , webSocketServerUrl ) ;
136
182
137
183
// After esteblishing connection with a server we must get all changes from the remote storage.
138
184
// This is required on Engine start, server recovery, network recovery, etc.
139
185
Logger . LogDebug ( "Getting all changes from server" , webSocketServerUrl ) ;
140
- await ProcessAsync ( ) ;
186
+ await ProcessAsync ( Logger ) ;
141
187
142
188
Logger . LogMessage ( "Started" , webSocketServerUrl ) ;
143
189
@@ -193,20 +239,27 @@ public async Task StopAsync()
193
239
/// web sockets should not stop processing changes.
194
240
/// To stop processing changes that are already received the Engine must be stopped.
195
241
/// </remarks>
196
- private async Task ProcessAsync ( )
242
+ private async Task ProcessAsync ( ILogger logger = null )
197
243
{
198
244
try
199
245
{
200
- await Engine . ServerNotifications ( Engine . Path , Logger )
201
- . ProcessChangesAsync ( async ( metadata , userFileSystemPath ) =>
202
- await Engine . Placeholders . GetItem ( userFileSystemPath ) . SavePropertiesAsync ( metadata as FileSystemItemMetadataExt , Logger ) ) ;
246
+ await Engine . ServerNotifications ( Engine . Path , logger ) . ProcessChangesAsync ( SavePropertiesAsync ) ;
203
247
}
204
248
catch ( Exception ex )
205
249
{
206
250
Logger . LogError ( "Failed to process changes" , Engine . Path , null , ex ) ;
207
251
}
208
252
}
209
253
254
+ private async Task SavePropertiesAsync ( IFileSystemItemMetadata metadata , string userFileSystemPath )
255
+ {
256
+ if ( Engine . Placeholders . TryGetItem ( userFileSystemPath , out PlaceholderItem placeholderItem ) )
257
+ {
258
+ await placeholderItem . SavePropertiesAsync ( metadata as FileSystemItemMetadataExt ) ;
259
+ }
260
+ }
261
+
262
+
210
263
private bool disposedValue = false ; // To detect redundant calls
211
264
212
265
protected virtual void Dispose ( bool disposing )
0 commit comments