delphimvcframework/sources/MVCFramework.Session.pas

269 lines
6.9 KiB
ObjectPascal
Raw Normal View History

// ***************************************************************************
//
// Delphi MVC Framework
//
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team
//
// https://github.com/danieleteti/delphimvcframework
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ***************************************************************************
2015-12-22 12:38:17 +01:00
2013-10-30 00:48:23 +01:00
unit MVCFramework.Session;
{$I dmvcframework.inc}
2013-10-30 00:48:23 +01:00
interface
uses
System.SyncObjs,
System.SysUtils,
System.DateUtils,
2013-10-30 00:48:23 +01:00
System.Generics.Collections;
const
DEFAULT_SESSION_INACTIVITY = 60; // in minutes
2013-10-30 00:48:23 +01:00
type
2013-10-30 00:48:23 +01:00
TWebSession = class abstract
private
FSessionId: string;
2013-10-30 00:48:23 +01:00
FLastAccess: TDateTime;
2014-04-01 19:39:28 +02:00
FTimeout: UInt64;
protected
function GetItems(const AKey: string): string; virtual; abstract;
procedure SetItems(const AKey, AValue: string); virtual; abstract;
2013-10-30 00:48:23 +01:00
public
constructor Create(const ASessionId: string; const ATimeout: UInt64); virtual;
2013-10-30 00:48:23 +01:00
destructor Destroy; override;
2013-10-30 00:48:23 +01:00
procedure MarkAsUsed;
function ToString: string; override;
function IsExpired: Boolean;
property Items[const AKey: string]: string read GetItems write SetItems; default;
property SessionId: string read FSessionId;
2013-10-30 00:48:23 +01:00
property LastAccess: TDateTime read FLastAccess;
property Timeout: UInt64 read FTimeout;
end;
TWebSessionClass = class of TWebSession;
TWebSessionMemory = class(TWebSession)
private
2013-10-30 00:48:23 +01:00
FData: TDictionary<string, string>;
protected
function GetItems(const AKey: string): string; override;
procedure SetItems(const AKey, AValue: string); override;
2013-10-30 00:48:23 +01:00
public
constructor Create(const ASessionId: string; const ATimeout: UInt64); override;
2013-10-30 00:48:23 +01:00
destructor Destroy; override;
function ToString: String; override;
property Data: TDictionary<string, string> read FData;
2013-10-30 00:48:23 +01:00
end;
TMVCSessionFactory = class sealed
private
FRegisteredSessionTypes: TDictionary<String, TWebSessionClass>;
private
class var Instance: TMVCSessionFactory;
2013-10-30 00:48:23 +01:00
public
constructor Create;
2015-04-01 17:01:23 +02:00
destructor Destroy; override;
procedure RegisterSessionType(const AName: String; AWebSessionClass: TWebSessionClass);
function CreateNewByType(const AName, ASessionId: string; const ATimeout: UInt64): TWebSession;
class function GetInstance: TMVCSessionFactory; static;
class procedure DestroyInstance; static;
2013-10-30 00:48:23 +01:00
end;
function GlobalSessionList: TObjectDictionary<string, TWebSession>;
2013-10-30 00:48:23 +01:00
implementation
var
GlSessionList: TObjectDictionary<string, TWebSession> = nil;
GlLastSessionListClear: TDateTime;
GlCriticalSection: TCriticalSection;
2013-10-30 00:48:23 +01:00
function GlobalSessionList: TObjectDictionary<string, TWebSession>;
2013-10-30 00:48:23 +01:00
var
S: string;
2013-10-30 00:48:23 +01:00
begin
if not Assigned(GlSessionList) then
2013-10-30 00:48:23 +01:00
begin
GlCriticalSection.Enter;
2013-10-30 00:48:23 +01:00
try
if not Assigned(GlSessionList) then
GlSessionList := TObjectDictionary<string, TWebSession>.Create([doOwnsValues]);
2013-10-30 00:48:23 +01:00
finally
GlCriticalSection.Leave;
2013-10-30 00:48:23 +01:00
end;
end;
if MinutesBetween(Now, GlLastSessionListClear) >= 1 then
2013-10-30 00:48:23 +01:00
begin
TMonitor.Enter(GlSessionList);
2013-10-30 00:48:23 +01:00
try
for S in GlSessionList.Keys do
if TWebSession(GlSessionList.Items[S]).IsExpired then
GlSessionList.Remove(S);
GlLastSessionListClear := Now;
2013-10-30 00:48:23 +01:00
finally
TMonitor.Exit(GlSessionList);
2013-10-30 00:48:23 +01:00
end;
end;
Result := GlSessionList;
2013-10-30 00:48:23 +01:00
end;
{ TWebSession }
constructor TWebSession.Create(const ASessionId: string; const ATimeout: UInt64);
2013-10-30 00:48:23 +01:00
begin
inherited Create;
FSessionId := ASessionId;
FTimeout := ATimeout;
2013-10-30 00:48:23 +01:00
end;
destructor TWebSession.Destroy;
begin
inherited Destroy;
2013-10-30 00:48:23 +01:00
end;
function TWebSession.IsExpired: Boolean;
begin
if (FTimeout = 0) then
Result := MinutesBetween(Now, LastAccess) > DEFAULT_SESSION_INACTIVITY
else
Result := MinutesBetween(Now, LastAccess) > FTimeout;
2013-10-30 00:48:23 +01:00
end;
procedure TWebSession.MarkAsUsed;
begin
FLastAccess := Now;
2013-10-30 00:48:23 +01:00
end;
function TWebSession.ToString: string;
begin
Result := '';
end;
{ TWebSessionMemory }
constructor TWebSessionMemory.Create(const ASessionId: string; const ATimeout: UInt64);
2013-10-30 00:48:23 +01:00
begin
inherited Create(ASessionId, ATimeout);
2013-10-30 00:48:23 +01:00
FData := TDictionary<string, string>.Create;
end;
destructor TWebSessionMemory.Destroy;
begin
FData.Free;
inherited Destroy;
2013-10-30 00:48:23 +01:00
end;
function TWebSessionMemory.GetItems(const AKey: string): string;
2013-10-30 00:48:23 +01:00
begin
2014-04-01 19:39:28 +02:00
TMonitor.Enter(Self);
try
if not FData.TryGetValue(AKey, Result) then
2014-04-01 19:39:28 +02:00
Result := '';
finally
TMonitor.Exit(Self);
end;
2013-10-30 00:48:23 +01:00
end;
procedure TWebSessionMemory.SetItems(const AKey, AValue: string);
2013-10-30 00:48:23 +01:00
begin
2014-04-01 19:39:28 +02:00
TMonitor.Enter(Self);
try
FData.AddOrSetValue(AKey, AValue);
2014-04-01 19:39:28 +02:00
finally
TMonitor.Exit(Self);
end;
2013-10-30 00:48:23 +01:00
end;
function TWebSessionMemory.ToString: String;
2013-10-30 00:48:23 +01:00
var
LKey: string;
2013-10-30 00:48:23 +01:00
begin
Result := '';
for LKey in FData.Keys do
Result := Result + LKey + '=' + QuotedStr(FData.Items[LKey]) + sLineBreak;
2013-10-30 00:48:23 +01:00
end;
{ TMVCSessionFactory }
2013-10-30 00:48:23 +01:00
constructor TMVCSessionFactory.Create;
begin
inherited Create;
2013-10-30 00:48:23 +01:00
FRegisteredSessionTypes := TDictionary<string, TWebSessionClass>.Create;
end;
function TMVCSessionFactory.CreateNewByType(const AName, ASessionId: string; const ATimeout: UInt64): TWebSession;
2013-10-30 00:48:23 +01:00
var
Clazz: TWebSessionClass;
2013-10-30 00:48:23 +01:00
begin
if not FRegisteredSessionTypes.TryGetValue(AName, Clazz) then
raise Exception.Create('Unknown application session type');
Result := Clazz.Create(ASessionId, ATimeout);
2013-10-30 00:48:23 +01:00
end;
destructor TMVCSessionFactory.Destroy;
begin
FRegisteredSessionTypes.Free;
inherited Destroy;
2013-10-30 00:48:23 +01:00
end;
class procedure TMVCSessionFactory.DestroyInstance;
2013-10-30 00:48:23 +01:00
begin
if Assigned(Instance) then
Instance.Free;
2013-10-30 00:48:23 +01:00
end;
class function TMVCSessionFactory.GetInstance: TMVCSessionFactory;
begin
if not Assigned(Instance) then
Instance := TMVCSessionFactory.Create;
Result := Instance;
end;
procedure TMVCSessionFactory.RegisterSessionType(const AName: String; AWebSessionClass: TWebSessionClass);
2013-10-30 00:48:23 +01:00
begin
FRegisteredSessionTypes.AddOrSetValue(AName, AWebSessionClass);
end;
initialization
TMVCSessionFactory.GetInstance.RegisterSessionType('memory', TWebSessionMemory);
GlCriticalSection := TCriticalSection.Create;
2013-10-30 00:48:23 +01:00
finalization
TMVCSessionFactory.DestroyInstance;
FreeAndNil(GlCriticalSection);
if Assigned(GlSessionList) then
FreeAndNil(GlSessionList);
2013-10-30 00:48:23 +01:00
end.