Componentes.Terceros.FastRe.../internal/4.2/1/Source/ClientServer/frxServerCache.pas
2007-11-18 19:40:07 +00:00

458 lines
11 KiB
ObjectPascal

{******************************************}
{ }
{ FastReport v4.0 }
{ Server cahce module }
{ }
{ Copyright (c) 1998-2007 }
{ by Alexander Fediachov, }
{ Fast Reports Inc. }
{ }
{******************************************}
unit frxServerCache;
{$I frx.inc}
interface
uses Windows, Classes, SysUtils, frxUtils, frxServerUtils, frxNetUtils,
frxVariables, frxClass, frxServerLog, SyncObjs;
type
TfrxServerCacheItem = class(TCollectionItem)
private
FReportName: String;
FVariables: TfrxVariables;
FFileName: String;
FExpTime: TDateTime;
FSessionId: String;
FStream: TStream;
public
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
published
property ReportName: String read FReportName write FReportName;
property Variables: TfrxVariables read FVariables write FVariables;
property FileName: String read FFileName write FFileName;
property ExpTime: TDateTime read FExpTime write FExpTime;
property SessionId: String read FSessionId write FSessionId;
property Stream: TStream read FStream write FStream;
end;
TfrxServerCacheSpool = class(TCollection)
private
function GetItems(Index: Integer): TfrxServerCacheItem;
function EqualVariables(const Var1: TfrxVariables; const Var2: TfrxVariables): Boolean;
public
constructor Create;
destructor Destroy; override;
property Items[Index: Integer]: TfrxServerCacheItem read GetItems;
procedure Clear;
published
function Add: TfrxServerCacheItem;
function Insert(Index: Integer): TfrxServerCacheItem;
function IndexOf(const ReportName: String; const Variables: TfrxVariables; const SessionId: String): Integer;
end;
TfrxServerCache = class (TThread)
private
FCachePath: String;
FActive: Boolean;
FLatency: Integer;
FHeap: TfrxServerCacheSpool;
FLatencyReports: TStrings;
FLatencyValues: TStrings;
FMemoryCache: Boolean;
FThreadActive: Boolean;
function GetReportLatency(const ReportName: String): Integer;
procedure SetActive(const Value: Boolean);
procedure CleanUpFiles;
procedure RemoveExpired;
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
function GetCachedStream(const Report: TfrxReport; const ReportName: String; const Variables: TfrxVariables; const Id: String): Boolean;
procedure Open;
procedure Close;
procedure Clear;
procedure AddReport(const Report: TfrxReport;
const ReportName: String; const Variables: TfrxVariables; const Id: String);
property Active: Boolean read FActive write SetActive;
property CachePath: String read FCachePath write FCachePath;
property DefaultLatency: Integer read FLatency write FLatency;
property Heap: TfrxServerCacheSpool read FHeap;
property LatencyReports: TStrings read FLatencyReports;
property LatencyValues: TStrings read FLatencyValues;
property MemoryCache: Boolean read FMemoryCache write FMemoryCache;
end;
const
CACHE_PREFIX = '$fr';
var
CacheCS1: TCriticalSection;
ReportCache: TfrxServerCache;
implementation
uses frxFileUtils, frxServerConfig;
{ TfrxServerCacheSpool }
function TfrxServerCacheSpool.Add: TfrxServerCacheItem;
begin
Result := TfrxServerCacheItem.Create(Self);
end;
procedure TfrxServerCacheSpool.Clear;
var
i: Integer;
begin
CacheCS1.Enter;
try
for i := 0 to Count - 1 do
if Assigned(Items[i].Stream) then
begin
Items[i].Stream.Free;
Items[i].Stream := nil;
end;
finally
CacheCS1.Leave;
end;
inherited Clear;
end;
constructor TfrxServerCacheSpool.Create;
begin
inherited Create(TfrxServerCacheItem);
end;
destructor TfrxServerCacheSpool.Destroy;
begin
inherited;
end;
function TfrxServerCacheSpool.EqualVariables(const Var1,
Var2: TfrxVariables): Boolean;
var
i, j, k: Integer;
begin
Result := False;
if Assigned(Var1) and Assigned(Var2) then
begin
j := Var1.Count;
if j = Var2.Count then
begin
Result := True;
for i := 0 to j - 1 do
begin
k := Var2.IndexOf(Var1.Items[i].Name);
if (k = -1) or (Var2.Items[k].Value <> Var1.Items[i].Value) then
begin
Result := False;
Break;
end;
end;
end;
end
else if Var1 = Var2 then
Result := True;
end;
function TfrxServerCacheSpool.GetItems(Index: Integer): TfrxServerCacheItem;
begin
Result := TfrxServerCacheItem(inherited Items[Index]);
end;
function TfrxServerCacheSpool.IndexOf(const ReportName: String;
const Variables: TfrxVariables; const SessionId: String): Integer;
var
i: Integer;
s: String;
begin
Result := -1;
for i := 0 to Count - 1 do
begin
s := Items[i].SessionId;
if ((AnsiCompareText(ReportName, Items[i].ReportName) = 0) and
EqualVariables(Items[i].Variables, Variables)) and (s = '') or
((AnsiCompareText(SessionId, s) = 0) and (s <> '')) then
begin
Result := i;
break;
end;
end;
end;
function TfrxServerCacheSpool.Insert(Index: Integer): TfrxServerCacheItem;
begin
Result := TfrxServerCacheItem(inherited Insert(Index));
end;
{ TfrxServerCacheItem }
constructor TfrxServerCacheItem.Create(Collection: TCollection);
begin
inherited Create(Collection);
FStream := nil;
end;
destructor TfrxServerCacheItem.Destroy;
begin
CacheCS1.Enter;
try
if Assigned(FStream) then
FStream.Free;
if Assigned(FVariables) then
FVariables.Free;
finally
CacheCS1.Leave;
end;
inherited;
end;
{ TfrxServerCache }
procedure TfrxServerCache.AddReport(const Report: TfrxReport;
const ReportName: String; const Variables: TfrxVariables; const Id: String);
var
Item: TfrxServerCacheItem;
Lat: TDateTime;
begin
if Active then
begin
CacheCS1.Enter;
try
Lat := GetReportLatency(ReportName) / 86400;
if Lat > 0 then
begin
Item := FHeap.Add;
Item.ReportName := ReportName;
if Assigned(Variables) then
begin
Item.Variables := TfrxVariables.Create;
Item.Variables.Assign(Variables);
end;
Item.ExpTime := Now + Lat;
if Id <> '' then
Item.ExpTime := Item.ExpTime * 20;
if FMemoryCache then
begin
Item.Stream := TMemoryStream.Create;
try
Report.PreviewPages.SaveToStream(Item.Stream);
Item.Stream.Position := 0;
except
Item.Stream.Free;
FMemoryCache := False;
end;
end;
if not FMemoryCache then
begin
Item.FileName := GetUniqueFileName(FCachePath, CACHE_PREFIX);
try
Report.PreviewPages.SaveToFile(Item.FileName);
except
Active := False;
end;
end;
Item.SessionId := Id;
end;
finally
CacheCS1.Leave;
end;
end;
end;
procedure TfrxServerCache.CleanUpFiles;
var
SRec: TSearchRec;
i: Integer;
begin
if DirectoryExists(FCachePath) then
begin
i := FindFirst(FCachePath + CACHE_PREFIX + '*.*', 0, SRec);
try
while i = 0 do
begin
try
DeleteFile(FCachePath + SRec.Name);
except
end;
i := FindNext(SRec);
end;
finally
FindClose(SRec);
end;
end;
end;
procedure TfrxServerCache.Clear;
begin
FHeap.Clear;
CleanUpFiles;
end;
procedure TfrxServerCache.Close;
begin
if FActive then
begin
Suspend;
Clear;
FActive := False;
end;
end;
constructor TfrxServerCache.Create;
begin
inherited Create(True);
FMemoryCache := ServerConfig.GetValue('server.cache.target') = 'memory';
FLatencyReports := TStringList.Create;
FLatencyValues := TStringList.Create;
FCachePath := frxGetAbsPathDir(ServerConfig.GetValue('server.cache.path'), ServerConfig.ConfigFolder);
FActive := ServerConfig.GetValue('server.cache.active') = 'yes';
FLatency := StrToInt(ServerConfig.GetValue('server.cache.defaultlatency'));
FHeap := TfrxServerCacheSpool.Create;
CleanUpFiles;
Resume;
end;
destructor TfrxServerCache.Destroy;
begin
Clear;
Terminate;
while FThreadActive do
Sleep(10);
// WaitFor;
PMessages;
FHeap.Free;
FLatencyReports.Free;
FLatencyValues.Free;
inherited;
end;
procedure TfrxServerCache.Execute;
var
i: Integer;
begin
FThreadActive := True;
while not Terminated do
begin
RemoveExpired;
i := 0;
while (not Terminated) and (i < 100) do
begin
Sleep(100);
Inc(i);
end;
end;
FThreadActive := False;
end;
function TfrxServerCache.GetCachedStream(const Report: TfrxReport;
const ReportName: String; const Variables: TfrxVariables; const Id: String): Boolean;
var
i: Integer;
begin
Result := False;
if Active then
begin
CacheCS1.Enter;
try
i := FHeap.IndexOf(ReportName, Variables, Id);
if i <> -1 then
begin
try
if Assigned(FHeap.Items[i].Stream) then
begin
FHeap.Items[i].Stream.Position := 0;
Report.PreviewPages.LoadFromStream(FHeap.Items[i].Stream);
Result := True;
end
else if FileExists(FHeap.Items[i].FileName) then
begin
Report.PreviewPages.LoadFromFile(FHeap.Items[i].FileName);
Result := True;
end;
except
on e: Exception do
begin
LogWriter.Write(ERROR_LEVEL, DateTimeToStr(Now) + #9 + Id + #9'cache read error: ' + FHeap.Items[i].FileName + ' ' + Report.Errors.Text + e.Message);
LogWriter.ErrorReached;
Result := False;
end;
end;
end
finally
CacheCS1.Leave;
end;
end;
if Result then
LogWriter.StatAddCacheHit;
end;
procedure TfrxServerCache.Open;
begin
if not FActive then
begin
Resume;
FActive := True;
end;
end;
procedure TfrxServerCache.RemoveExpired;
var
i: Integer;
begin
i := 0;
CacheCS1.Enter;
try
while i < FHeap.Count do
begin
if FHeap.Items[i].ExpTime <= Now then
begin
if Assigned(FHeap.Items[i].Stream) then
begin
FHeap.Items[i].Stream.Free;
FHeap.Items[i].Stream := nil;
end;
if FileExists(FHeap.Items[i].FileName) then
DeleteFile(FHeap.Items[i].FileName);
FHeap.Items[i].Free; // Delete(i);
end else Inc(i);
end;
finally
CacheCS1.Leave;
end;
end;
procedure TfrxServerCache.SetActive(const Value: Boolean);
begin
if Value <> FActive then
if Value then Open
else Close;
end;
function TfrxServerCache.GetReportLatency(const ReportName: String): Integer;
var
i: Integer;
begin
i := FLatencyReports.IndexOf(ReportName);
if i <> -1 then
Result := StrToInt(FLatencyValues[i])
else
Result := FLatency;
end;
initialization
CacheCS1 := TCriticalSection.Create;
finalization
CacheCS1.Free;
end.