Common/DocumentResultList: Cache opened files and load as stream

This commit is contained in:
Jonathan Jenne 2021-02-24 16:40:53 +01:00
parent d63d90f0d2
commit 1277c393ba
4 changed files with 602 additions and 342 deletions

View File

@ -104,6 +104,7 @@
</Compile>
<Compile Include="DocumentPropertyMenu\DocumentPropertyMenu.vb" />
<Compile Include="DocumentResultList\DocumentResultConfig.vb" />
<Compile Include="DocumentResultList\DocumentResultInfo.vb" />
<Compile Include="DocumentResultList\DocumentResultList.vb" />
<Compile Include="DocumentResultList\DocumentResultParams.vb" />
<Compile Include="DocumentResultList\frmDocumentResultList.Designer.vb">
@ -112,6 +113,7 @@
<Compile Include="DocumentResultList\frmDocumentResultList.vb">
<SubType>Form</SubType>
</Compile>
<Compile Include="DocumentResultList\DocumentResultCache.vb" />
<Compile Include="IResultForm.vb" />
<Compile Include="My Project\AssemblyInfo.vb" />
<Compile Include="My Project\Application.Designer.vb">

View File

@ -0,0 +1,117 @@
Imports DigitalData.Modules.EDMI.API.Client
Public Class DocumentResultCache
Implements ICollection(Of DocumentResultInfo)
Private Const _DefaultCapacity As Long = 1000
Public Shared ReadOnly Property DefaultCapacity As Long
Get
Return _DefaultCapacity
End Get
End Property
Friend ReadOnly List As New LinkedList(Of DocumentResultInfo)
Private ReadOnly Index As New Dictionary(Of String, LinkedListNode(Of DocumentResultInfo))
Private ReadOnly Lock As New Object
Public Sub New()
Me.New(_DefaultCapacity)
End Sub
Public Sub New(capacity As Long)
If capacity < 0 Then
Throw New InvalidOperationException("DocumentResultCache capacity must be positive.")
End If
Me.Capacity = capacity
End Sub
Public Event DiscardingOldestItem As EventHandler
Public Property Capacity As Long
Public Property Count As Integer Implements ICollection(Of DocumentResultInfo).Count
Public ReadOnly Property Oldest As DocumentResultInfo
Get
Return List.First.Value
End Get
End Property
Public Sub Add(item As DocumentResultInfo) Implements ICollection(Of DocumentResultInfo).Add
SyncLock Lock
If Index.ContainsKey(item.FullPath) Then
List.Remove(Index(item.FullPath))
Index(item.FullPath) = List.AddLast(item)
Return
End If
If Count >= Capacity AndAlso Capacity <> 0 Then
RaiseEvent DiscardingOldestItem(Me, New EventArgs())
Remove(Oldest)
End If
Index.Add(item.FullPath, List.AddLast(item))
If item.Contents IsNot Nothing Then
Count = Count + item.Contents?.Length
End If
End SyncLock
End Sub
Public Function Contains(item As DocumentResultInfo) As Boolean Implements ICollection(Of DocumentResultInfo).Contains
Return Index.ContainsKey(item.FullPath)
End Function
Public Sub CopyTo(array As DocumentResultInfo(), ByVal arrayIndex As Integer) Implements ICollection(Of DocumentResultInfo).CopyTo
SyncLock Lock
For Each item As DocumentResultInfo In Me
array(Math.Min(System.Threading.Interlocked.Increment(arrayIndex), arrayIndex - 1)) = item
Next
End SyncLock
End Sub
Public Sub Clear() Implements ICollection(Of DocumentResultInfo).Clear
SyncLock Lock
List.Clear()
Index.Clear()
End SyncLock
End Sub
Public ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of DocumentResultInfo).IsReadOnly
Get
Return False
End Get
End Property
Public Function Remove(item As DocumentResultInfo) As Boolean Implements ICollection(Of DocumentResultInfo).Remove
SyncLock Lock
If Index.ContainsKey(item.FullPath) Then
List.Remove(Index(item.FullPath))
Index.Remove(item.FullPath)
If item.Contents IsNot Nothing Then
Count -= item.Contents.Length
End If
Return True
End If
Return False
End SyncLock
End Function
Private Iterator Function GetEnumerator() As IEnumerator(Of DocumentResultInfo) Implements ICollection(Of DocumentResultInfo).GetEnumerator
Dim node As LinkedListNode(Of DocumentResultInfo) = List.First
While node IsNot Nothing
Yield node.Value
node = node.[Next]
End While
End Function
Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Return DirectCast(List, IEnumerable).GetEnumerator()
End Function
End Class

View File

@ -0,0 +1,8 @@
Imports DigitalData.Modules.EDMI.API.Client
Public Class DocumentResultInfo
Inherits DocumentInfo
Public Contents As Byte()
Public LastWriteTime As Date
End Class

View File

@ -45,14 +45,14 @@ Public Class frmDocumentResultList
Private _IsLoading As Boolean = True
Private _ActiveGrid As GridControl = Nothing
Private _ActiveGridBand As GridBand = Nothing
Private _DocumentInfo As DocumentInfo = Nothing
' TODO: Hashes for checking if the opened file was modified externally
Private _HashOriginalFile As String = Nothing
Private _HashOpenedFile As String = Nothing
Private WithEvents _FileOpenTimer As New Timer
Private _FileOpenList As New Dictionary(Of Integer, String)
Private _OpenDocuments As New DocumentResultCache(50000000)
Private _CurrentDocument As DocumentResultInfo = Nothing
Private Property OperationMode As IResultForm.Mode Implements IResultForm.OperationMode
@ -79,8 +79,8 @@ Public Class frmDocumentResultList
_Params = Params
_ResultLists = Params.Results
_FileOpenTimer.Interval = FILE_OPEN_TIMER_INTERVAL
_FileOpenTimer.Start()
'_FileOpenTimer.Interval = FILE_OPEN_TIMER_INTERVAL
'_FileOpenTimer.Start()
End Sub
Private Sub frmDocumentResultList_Load(sender As Object, e As EventArgs) Handles MyBase.Load
@ -145,40 +145,45 @@ Public Class frmDocumentResultList
If e.FocusedRowHandle >= 0 Then
Dim oRow = sender.GetDataRow(e.FocusedRowHandle)
Dim oDocumentInfo As DocumentResultInfo = Nothing
DocumentViewer1.CloseDocument()
If OperationMode = IResultForm.Mode.NoAppServer Then
LoadFile_Legacy(oRow)
oDocumentInfo = LoadFile_Legacy(oRow)
ElseIf OperationMode = IResultForm.Mode.WithAppServer Then
LoadFile_IDB(oRow)
oDocumentInfo = LoadFile_IDB(oRow)
End If
If IsNothing(_DocumentInfo) Then
If IsNothing(oDocumentInfo) Then
Show_Warning("File could not be loaded!")
Exit Sub
End If
If Not IsNothing(_DocumentInfo) Then
If _FileOpenList.ContainsValue(_DocumentInfo.FullPath) Then
Show_Warning("Die ausgewählte Datei befindet sich im Zugriff!")
Else
DocumentViewer1.LoadFile(_DocumentInfo.FullPath)
If Not File.Exists(oDocumentInfo.FullPath) Then
Show_Warning("File does not exist!")
_HashOriginalFile = Nothing
Exit Sub
End If
If _DocumentInfo.AccessRight = Rights.AccessRight.VIEW_ONLY Then
If oDocumentInfo.Contents IsNot Nothing Then
Dim oFileInfo As New FileInfo(oDocumentInfo.FullPath)
DocumentViewer1.LoadFile(oFileInfo.Name, New MemoryStream(oDocumentInfo.Contents))
Else
DocumentViewer1.LoadFile(oDocumentInfo.FullPath)
End If
If oDocumentInfo.AccessRight = Rights.AccessRight.VIEW_ONLY Then
DocumentViewer1.SetViewOnly(True)
RibbonPageGroup_Export.Visible = False
Else
DocumentViewer1.SetViewOnly(False)
RibbonPageGroup_Export.Visible = True
End If
End If
End If
' TODO: Create checksum after closing, compare and take action
If File.Exists(_DocumentInfo.FullPath) Then
_HashOriginalFile = _Filesystem.GetChecksum(_DocumentInfo.FullPath)
Else
_HashOriginalFile = Nothing
End If
_HashOriginalFile = _Filesystem.GetChecksum(oDocumentInfo.FullPath)
End If
Catch ex As Exception
_Logger.Error(ex)
@ -203,31 +208,160 @@ Public Class frmDocumentResultList
Return True
End Function
Private Sub LoadFile_Legacy(GridRow As DataRow)
Private Function LoadFile_Legacy(GridRow As DataRow) As DocumentResultInfo
Try
Dim oFullPath = GridRow.Item(COLUMN_FILEPATH)
_DocumentInfo = New DocumentInfo() With {
Dim oDocumentInfo = New DocumentResultInfo() With {
.AccessRight = Rights.AccessRight.FULL,
.FullPath = oFullPath
}
If File.Exists(oDocumentInfo.FullPath) Then
oDocumentInfo = LoadFile_AsByteArray(oDocumentInfo)
_OpenDocuments.Add(oDocumentInfo)
_CurrentDocument = oDocumentInfo
End If
Return oDocumentInfo
Catch ex As Exception
_Logger.Error(ex)
MsgBox("Error while loading file", MsgBoxStyle.Critical, Text)
Return Nothing
End Try
End Sub
End Function
Private Sub LoadFile_IDB(GridRow As DataRow)
Private Function LoadFile_IDB(GridRow As DataRow) As DocumentResultInfo
Try
Dim oObjectId = GridRow.Item(COLUMN_DOCID)
_Logger.Debug($"Gettin' Infor for oObjectId: {oObjectId}")
_Logger.Debug($"Getting Information for oObjectId: {oObjectId}")
' This needs to be Sync bc otherwise the PopupMenuShowing event will fire before this method loaded the Document Info
_DocumentInfo = _IDBClient.GetDocumentInfo(_Environment.User.UserId, oObjectId)
Dim oDocumentInfo As DocumentInfo = _IDBClient.GetDocumentInfo(_Environment.User.UserId, oObjectId)
Dim oResultDocumentInfo As New DocumentResultInfo() With {
.AccessRight = oDocumentInfo.AccessRight,
.FullPath = oDocumentInfo.FullPath
}
If File.Exists(oResultDocumentInfo.FullPath) Then
oResultDocumentInfo = LoadFile_AsByteArray(oResultDocumentInfo)
_OpenDocuments.Add(oResultDocumentInfo)
_CurrentDocument = oResultDocumentInfo
End If
Return oResultDocumentInfo
Catch ex As Exception
_Logger.Error(ex)
Return Nothing
End Try
End Sub
End Function
Private Function LoadFile_AsByteArray(DocumentInfo As DocumentResultInfo) As DocumentResultInfo
Try
Dim oFullPath As String = DocumentInfo.FullPath
Dim oPathExists = From oFile In _OpenDocuments
Where oFile.FullPath = oFullPath And oFile.Contents IsNot Nothing
Select oFile
Dim oExistsInCache = oPathExists.Count() > 0
Dim oSizeChanged = False
Dim oWriteTimeChanged = False
' Get Information about the file on the filesystem
Dim oFileInfo As New FileInfo(oFullPath)
If oExistsInCache Then
Dim oCachedItem = oPathExists.First()
If oCachedItem.Contents Is Nothing Then
oSizeChanged = False
Else
oSizeChanged = Not (oFileInfo.Length = oCachedItem.Contents.Length)
End If
If oCachedItem.LastWriteTime = Nothing Then
oWriteTimeChanged = False
Else
oWriteTimeChanged = Not oFileInfo.LastWriteTime.Equals(oCachedItem.LastWriteTime)
End If
If oSizeChanged Or oWriteTimeChanged Then
Using oStream = File.OpenRead(DocumentInfo.FullPath)
Using oMemoryStream = New MemoryStream()
oStream.CopyTo(oMemoryStream)
DocumentInfo.Contents = oMemoryStream.ToArray()
DocumentInfo.LastWriteTime = oFileInfo.LastWriteTime
End Using
End Using
Return DocumentInfo
Else
Return oCachedItem
End If
Else
Using oStream = File.OpenRead(DocumentInfo.FullPath)
Using oMemoryStream = New MemoryStream()
oStream.CopyTo(oMemoryStream)
DocumentInfo.Contents = oMemoryStream.ToArray()
DocumentInfo.LastWriteTime = oFileInfo.LastWriteTime
End Using
End Using
Return DocumentInfo
End If
Catch ex As Exception
End Try
End Function
'Private Function LoadFile_AsByteArray(DocumentInfo As DocumentResultInfo)
' Try
' Dim oLoadedInfo As DocumentResultInfo = Nothing
' Dim oFileContents As Byte()
' Dim oFullPath As String = DocumentInfo.FullPath
' Dim oPathExists = From oFile In _OpenDocuments
' Where oFile.FullPath = oFullPath And oFile.Contents IsNot Nothing
' Select oFile
' If oPathExists.Count > 0 Then
' Dim oDocumentInfo = oPathExists.First()
' Dim oFileInfo As New FileInfo(DocumentInfo.FullPath)
' Dim oSizeChanged = False
' Dim oWriteTimeChanged = False
' If DocumentInfo.Contents Is Nothing Then
' oSizeChanged = False
' Else
' oSizeChanged = Not oFileInfo.Length.Equals(DocumentInfo.Contents?.Length)
' End If
' If DocumentInfo.LastWriteTime = Nothing Then
' oWriteTimeChanged = False
' Else
' oWriteTimeChanged = Not oFileInfo.LastWriteTime.Equals(DocumentInfo.LastWriteTime)
' End If
' If oSizeChanged Or oWriteTimeChanged Then
' Else
' Using oStream = File.OpenRead(oFullPath)
' Using oMemoryStream = New MemoryStream()
' oStream.CopyTo(oMemoryStream)
' oFileContents = oMemoryStream.ToArray()
' End Using
' End Using
' End If
' End If
' DocumentInfo.Contents = oFileContents
' DocumentInfo.LastWriteTime = oFileInfo.LastWriteTime
' Return DocumentInfo
' Catch ex As Exception
' _Logger.Error(ex)
' Return Nothing
' End Try
'End Function
Public Function RefreshResults(pResults As IEnumerable(Of BaseResult)) As Boolean Implements IResultForm.RefreshResults
_IsLoading = True
@ -576,7 +710,7 @@ Public Class frmDocumentResultList
Dim oRow = GetActiveRow()
If oRow IsNot Nothing Then
Dim oFilename = _DocumentInfo.FullPath
Dim oFilename = _CurrentDocument.FullPath
Dim oDirectory = IO.Path.GetDirectoryName(oFilename)
Process.Start(oDirectory)
End If
@ -587,8 +721,8 @@ Public Class frmDocumentResultList
Private Sub OpenFile()
Try
If _DocumentInfo IsNot Nothing Then
Dim oFilename = _DocumentInfo.FullPath
If _CurrentDocument IsNot Nothing Then
Dim oFilename = _CurrentDocument.FullPath
DocumentPropertyMenu_FileOpened(Me, oFilename)
End If
Catch ex As Exception
@ -603,7 +737,7 @@ Public Class frmDocumentResultList
Dim oRow = GetActiveRow()
If oRow IsNot Nothing Then
Dim oFilename = _DocumentInfo.FullPath
Dim oFilename = _CurrentDocument.FullPath
Clipboard.SetText(oFilename)
End If
Catch ex As Exception
@ -658,7 +792,7 @@ Public Class frmDocumentResultList
End Sub
Private Sub GridControl_DoubleClick(sender As Object, e As EventArgs) Handles GridControl1.DoubleClick, GridControl2.DoubleClick, GridControl3.DoubleClick
If _DocumentInfo IsNot Nothing And _DocumentInfo.AccessRight > Rights.AccessRight.VIEW_ONLY Then
If _CurrentDocument IsNot Nothing AndAlso _CurrentDocument.AccessRight > Rights.AccessRight.VIEW_ONLY Then
OpenFile()
End If
End Sub
@ -671,13 +805,13 @@ Public Class frmDocumentResultList
If e.MenuType = GridMenuType.Row Then
Dim oRowHandle = e.HitInfo.RowHandle
Dim oRow As DataRow = oView.GetDataRow(oRowHandle)
Dim oFilepath As String = _DocumentInfo.FullPath
Dim oFilepath As String = _CurrentDocument.FullPath
Dim oObjectId As Long = oRow.Item(COLUMN_DOCID)
Dim oMenu As New DocumentPropertyMenu(_LogConfig, _Environment, _IDBClient, oFilepath, oObjectId)
e.Menu.Items.Clear()
For Each oItem In oMenu.GetMenuItems(OperationMode, _DocumentInfo.AccessRight)
For Each oItem In oMenu.GetMenuItems(OperationMode, _CurrentDocument.AccessRight)
e.Menu.Items.Add(oItem)
Next
@ -691,46 +825,45 @@ Public Class frmDocumentResultList
End Sub
Public Sub DocumentPropertyMenu_FileOpened(sender As Object, FilePath As String)
DocumentViewer1.CloseDocument()
'DocumentViewer1.CloseDocument()
Dim oProcess = Process.Start(New ProcessStartInfo With {
.FileName = FilePath
})
_FileOpenList.Add(oProcess.Id, FilePath)
End Sub
Public Sub FileOpenTimer_Elapsed() Handles _FileOpenTimer.Tick
Try
Dim oProcesses = Process.GetProcesses()
Dim oIds = (From oProc In oProcesses
Select oProc.Id).
ToList()
'Try
' Dim oProcesses = Process.GetProcesses()
' Dim oIds = (From oProc In oProcesses
' Select oProc.Id).
' ToList()
Dim oNewFileOpenList As New Dictionary(Of Integer, String)
For Each oOpenFile In _FileOpenList
If oIds.Contains(oOpenFile.Key) Then
oNewFileOpenList.Add(oOpenFile.Key, oOpenFile.Value)
End If
Next
' Dim oNewFileOpenList As New Dictionary(Of Integer, String)
' For Each oOpenFile In _FileOpenList
' If oIds.Contains(oOpenFile.Key) Then
' oNewFileOpenList.Add(oOpenFile.Key, oOpenFile.Value)
' End If
' Next
If oNewFileOpenList.Count < _FileOpenList.Count Then
Dim oClosedFiles = _FileOpenList.
Except(oNewFileOpenList).
ToList()
' If oNewFileOpenList.Count < _FileOpenList.Count Then
' Dim oClosedFiles = _FileOpenList.
' Except(oNewFileOpenList).
' ToList()
If oClosedFiles.Count = 1 Then
Dim oOpenFile = oClosedFiles.First()
DocumentViewer1.LoadFile(oOpenFile.Value)
Else
ClearGridData()
UpdateGridData()
End If
' If oClosedFiles.Count = 1 Then
' Dim oOpenFile = oClosedFiles.First()
' DocumentViewer1.LoadFile(oOpenFile.Value)
' Else
' ClearGridData()
' UpdateGridData()
' End If
_FileOpenList = oNewFileOpenList
End If
Catch ex As Exception
_Logger.Error(ex)
End Try
' _FileOpenList = oNewFileOpenList
' End If
'Catch ex As Exception
' _Logger.Error(ex)
'End Try
End Sub
Public Sub Show_CriticalError(Message As String)
@ -792,10 +925,10 @@ Public Class frmDocumentResultList
Dim oHitInfo = sender.CalcHitInfo(e.Location)
If oHitInfo.InRow Then
If _DocumentInfo IsNot Nothing AndAlso _DocumentInfo.AccessRight >= Rights.AccessRight.VIEW_EXPORT Then
If _CurrentDocument IsNot Nothing AndAlso _CurrentDocument.AccessRight >= Rights.AccessRight.VIEW_EXPORT Then
_ScreenOffset = SystemInformation.WorkingArea.Location
Dim oFullPath As String = _DocumentInfo.FullPath
Dim oFullPath As String = _CurrentDocument.FullPath
Dim oFiles As String() = {oFullPath}
Dim oData As New DataObject(DataFormats.FileDrop, oFiles)