PrimeLoader System Architecture
Multi-Tenant Hydrology Sensor SaaS Platform
System Overview
PrimeLoader is a Linux daemon-based multi-tenant application server built with Delphi. It serves as the core backend for a hydrology sensor SaaS platform, managing multiple tenant databases, dynamic plugin loading, and OAuth2 authentication.
Key Architectural Patterns:
- Dependency Injection — Spring4D container for IoC
- Multi-Tenancy — Separate database per tenant with connection pooling
- Plugin Architecture — Dynamic .so library loading at runtime
- Factory Pattern — Tenant and Plugin factories for object creation
- Thread-Safe Pooling — Critical sections protecting shared resources
Class Diagram
%%{init: {'theme': 'dark'}}%%
classDiagram
direction TB
class PrimeLoader_Apache {
<<Linux Daemon>>
-SID: Integer
-ControllerModule: TMainServer
-TerminateRequested: Boolean
+RunDaemon()
+Daemonize()
+StartServer()
+CleanupAndShutdown()
}
class IServer {
<<Interface>>
+HttpServer: TIndySparkleHTTPServer
+TenantFactory: ITenantFactory
+PluginFactory: IPluginFactory
+Logger: ILinuxLog
+TenantPoolManager: TTenantPoolManager
+CreateServer()
+StartServer()
+StopServer()
+AddModule()
}
class TServer {
<<Implementation>>
-FHTTPServer: TIndySparkleHTTPServer
-FTenantFactory: ITenantFactory
-FPluginFactory: IPluginFactory
+CreateServer()
+DoOnCommand()
+DoOnError()
}
class TMainServer {
<<DataModule>>
+AdminServer: TXDataServer
+AdminDatabase: TUniConnection
+PrimeRadiantSphinxServer: TSphinxServer
+InitialiseServer()
+LoadPlugins()
+InstallTenants()
}
class ITenantFactory {
<<Interface>>
+TenantList: TDictionary
+Add()
+FindTenant()
+Remove()
}
class TTenantPoolManager {
<<Singleton>>
-FPools: TDictionary
-FConfigs: TDictionary
-FLock: TCriticalSection
+Initialize()
+GetPool(): TTenantConnectionPool
+Shutdown()
}
class TTenantConnectionPool {
<<Thread-Safe>>
-FPool: TList
-FInUse: TList
-FLock: TCriticalSection
+Acquire(): TUniConnection
+Release()
}
class IPluginFactory {
<<Interface>>
+PluginList: TDictionary
+ManagePluginLibraries()
+FindPlugin()
+RemovePlugin()
}
class IPlugin {
<<Interface>>
+LibraryName: String
+LibraryHandle: HMODULE
+BaseURL: String
+IsLoaded: Boolean
+StartDataModule()
}
class TSphinxServer {
<<OAuth2>>
+CreateModule()
+CreateContext()
}
class TXDataServer {
<<REST API>>
+BaseURL: String
+CreateModule()
}
PrimeLoader_Apache --> IServer : creates
PrimeLoader_Apache --> TMainServer : creates
IServer <|.. TServer : implements
TServer --> ITenantFactory
TServer --> IPluginFactory
TServer --> TTenantPoolManager
TMainServer --> TXDataServer
TMainServer --> TSphinxServer
TMainServer --> TTenantPoolManager : initializes
ITenantFactory o-- ITenant
IPluginFactory o-- IPlugin
TTenantPoolManager o-- TTenantConnectionPool
Daemon Startup Sequence
%%{init: {'theme': 'dark'}}%%
sequenceDiagram
participant OS as Linux OS
participant D as PrimeLoader
participant IC as DI Container
participant S as IServer
participant MS as TMainServer
participant TPM as TenantPoolManager
participant PF as PluginFactory
OS->>D: Start Process
D->>D: Daemonize()
D->>D: SetupSignalHandlers()
D->>IC: RegisterClassesAndInterfaces()
D->>IC: Resolve IServer
IC-->>S: TServer instance
D->>MS: Create TMainServer
MS->>MS: LoadConfigs()
MS->>MS: ConnectPrimaryServers()
D->>S: CreateServer()
S->>S: Create HTTP Server
D->>MS: InitialiseServer()
MS->>MS: SetupServerModules()
MS->>TPM: Initialize(LoadSitesProc)
TPM->>TPM: Create connection pools
MS->>PF: ManagePluginLibraries()
PF->>PF: Load plugin .so files
PF->>PF: DoInject() for each
D->>S: StartServer()
Note over D: Enter main loop
HTTP Request Flow
%%{init: {'theme': 'dark'}}%%
sequenceDiagram
participant C as Client
participant H as HTTP Server
participant JWT as JWT Middleware
participant M as XData Module
participant TPM as TenantPoolManager
participant Pool as ConnectionPool
participant Svc as Service
C->>H: HTTP Request
H->>JWT: Validate Token
JWT->>JWT: Extract tenant_id
JWT->>M: Forward Request
M->>TPM: GetPool(tenantURL)
TPM-->>M: TTenantConnectionPool
M->>Pool: Acquire()
Pool-->>M: TUniConnection
M->>Svc: Execute Service
Svc-->>M: Response
M->>Pool: Release(conn)
M-->>C: HTTP Response
Plugin Loading Process
%%{init: {'theme': 'dark'}}%%
sequenceDiagram
participant MS as TMainServer
participant PF as PluginFactory
participant DB as Database
participant SO as .so Library
participant S as IServer
MS->>PF: ManagePluginLibraries()
PF->>DB: GetActiveModuleList
DB-->>PF: Plugin records
loop Each plugin
PF->>PF: Create IPlugin
PF->>SO: LoadLibrary(name.so)
SO-->>PF: HMODULE handle
PF->>DB: RegisterModuleHandle()
PF->>SO: GetProcAddress('Inject')
PF->>SO: Inject(AppServer, Plugin)
Note over SO: Plugin registers services
end
Connection Pool Management
%%{init: {'theme': 'dark'}}%%
sequenceDiagram
participant Svc as Service
participant TPM as TenantPoolManager
participant Pool as TTenantConnectionPool
participant Conn as TUniConnection
Svc->>TPM: GetPool(url)
TPM-->>Svc: Pool instance
Svc->>Pool: Acquire()
Pool->>Pool: Lock
alt Has available
Pool->>Pool: Validate connection
Pool-->>Svc: Connection
else Empty, under max
Pool->>Conn: Create new
Conn->>Conn: Connect to DB
Pool-->>Svc: Connection
else Pool exhausted
Pool-->>Svc: Exception
end
Note over Svc: Use connection
Svc->>Pool: Release(conn)
Pool->>Pool: Validate & return to pool
OAuth2 Authentication Flow
%%{init: {'theme': 'dark'}}%%
sequenceDiagram
participant C as Client
participant S as Sphinx Server
participant Cfg as SphinxConfig
participant UM as UserManager
C->>C: Generate PKCE codes
C->>S: /authorize (code_challenge)
S->>Cfg: Validate client
S->>C: Redirect to login
C->>S: POST credentials
S->>UM: Validate user
UM-->>S: Valid
S->>S: Generate auth code
S->>C: Redirect with code
C->>S: POST /token (code_verifier)
S->>S: Verify PKCE
S->>S: Generate JWT
S-->>C: access_token, id_token
Component Reference
Core Components
PrimeLoader_Apache
Main daemon entry point. Handles daemonization, signal handling, and lifecycle management.
Framework/PRIMELOADER/PrimeLoader_Apache.dprTServer (IServer)
Core server implementation managing HTTP server, factories, and modules.
Framework/COMMON/SERVER/OBJECTS/Obj.Server.pasTMainServer
Primary controller DataModule handling initialization and database connections.
Framework/COMMON/DATAMODULES/BackOffice.Data.Main.pasMulti-Tenancy Components
TTenantPoolManager
Singleton managing all tenant connection pools. Thread-safe initialization and access.
Framework/COMMON/USERS/OBJECTS/Obj.Tenant.PoolManager.pasTTenantConnectionPool
Per-tenant thread-safe connection pool with acquire/release pattern.
Framework/COMMON/USERS/OBJECTS/Obj.Tenant.ConnectionPool.pasTTenant (ITenant)
Tenant entity with database configuration and data manager.
Framework/COMMON/USERS/OBJECTS/Obj.Tenant.pasTTenantFactory (ITenantFactory)
Factory for creating and managing tenant instances.
Framework/COMMON/FACTORIES/OBJECTS/Obj.Factory.Tenant.pasPlugin System Components
TPluginFactory (IPluginFactory)
Manages plugin lifecycle: loading, injection, and removal.
Framework/COMMON/FACTORIES/OBJECTS/Obj.Factory.Plugin.pasTPlugin (IPlugin)
Plugin entity representing a dynamically loaded .so library.
Framework/COMMON/PLUGIN/OBJECTS/Obj.Plugin.pasInterface Summary
| Interface | Implementation | Purpose |
|---|---|---|
| IServer | TServer | Main application server abstraction |
| ITenant | TTenant | Tenant entity with database config |
| ITenantFactory | TTenantFactory | Tenant creation and lookup |
| IPlugin | TPlugin | Plugin entity with module info |
| IPluginFactory | TPluginFactory | Plugin loading and management |
| IDataManager | TDataManager | Database operations abstraction |
| ILinuxLog | TLinuxLog | Logging service |
Key Data Flows
| Flow | Entry Point | Key Components | Output |
|---|---|---|---|
| Startup | PrimeLoader_Apache.Main | InjectionContainer → TServer → TMainServer | Running HTTP server with modules |
| Tenant Init | InstallTenants() | LoadSitesProc → TenantPoolManager → ConnectionPools | Pools ready for all tenants |
| Plugin Load | LoadPlugins() | GetActiveModuleList → PluginFactory → LoadLibrary | Plugins injected and active |
| Request | HTTP Request | JWT Middleware → XData Module → Service | HTTP Response |
| Auth | /authorize | SphinxServer → SphinxConfig → UserManager | JWT tokens |