update: updating files with detailed comments
This commit is contained in:
parent
7fa2bb0f7e
commit
e7f8083b25
8 changed files with 957 additions and 59 deletions
|
@ -7,6 +7,14 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/// This library exports various components of the application framework.
|
||||||
|
///
|
||||||
|
/// It includes:
|
||||||
|
/// - Application and ApplicationServer classes for managing the application lifecycle
|
||||||
|
/// - Channel for handling request/response cycles
|
||||||
|
/// - IsolateApplicationServer and IsolateSupervisor for managing isolates
|
||||||
|
/// - Options for configuring the application
|
||||||
|
/// - Starter for initializing and running the application
|
||||||
library;
|
library;
|
||||||
|
|
||||||
export 'src/application.dart';
|
export 'src/application.dart';
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
@ -11,56 +20,138 @@ export 'application_server.dart';
|
||||||
export 'options.dart';
|
export 'options.dart';
|
||||||
export 'starter.dart';
|
export 'starter.dart';
|
||||||
|
|
||||||
/// This object starts and stops instances of your [ApplicationChannel].
|
/// The Application class is responsible for starting and managing instances of an ApplicationChannel.
|
||||||
///
|
///
|
||||||
/// An application object opens HTTP listeners that forward requests to instances of your [ApplicationChannel].
|
/// An application object opens HTTP listeners that forward requests to instances of your [ApplicationChannel].
|
||||||
/// It is unlikely that you need to use this class directly - the `conduit serve` command creates an application object
|
/// It is unlikely that you need to use this class directly - the `conduit serve` command creates an application object
|
||||||
/// on your behalf.
|
/// on your behalf.
|
||||||
class Application<T extends ApplicationChannel> {
|
class Application<T extends ApplicationChannel> {
|
||||||
/// A list of isolates that this application supervises.
|
/// A list of isolates that this application supervises.
|
||||||
|
///
|
||||||
|
/// This list contains [ApplicationIsolateSupervisor] instances, each representing
|
||||||
|
/// an isolate running a separate instance of the application. These supervisors
|
||||||
|
/// are responsible for managing the lifecycle and communication with their
|
||||||
|
/// respective isolates. The list is populated when the application starts and
|
||||||
|
/// is cleared when the application stops.
|
||||||
List<ApplicationIsolateSupervisor> supervisors = [];
|
List<ApplicationIsolateSupervisor> supervisors = [];
|
||||||
|
|
||||||
/// The [ApplicationServer] listening for HTTP requests while under test.
|
/// The [ApplicationServer] listening for HTTP requests while under test.
|
||||||
///
|
///
|
||||||
/// This property is only valid when an application is started via [startOnCurrentIsolate].
|
/// This property is only valid when an application is started via [startOnCurrentIsolate].
|
||||||
|
/// It represents the server instance that handles incoming HTTP requests during testing.
|
||||||
|
/// The server is initialized when the application starts on the current isolate and
|
||||||
|
/// provides access to the underlying HTTP server and application channel.
|
||||||
|
///
|
||||||
|
/// Note: This property should not be accessed before calling [startOnCurrentIsolate],
|
||||||
|
/// as it will not be initialized until then.
|
||||||
late ApplicationServer server;
|
late ApplicationServer server;
|
||||||
|
|
||||||
/// The [ApplicationChannel] handling requests while under test.
|
/// The [ApplicationChannel] handling requests while under test.
|
||||||
///
|
///
|
||||||
/// This property is only valid when an application is started via [startOnCurrentIsolate]. You use
|
/// This property provides access to the application channel instance when the application
|
||||||
/// this value to access elements of your application channel during testing.
|
/// is started using [startOnCurrentIsolate]. It allows direct interaction with the channel
|
||||||
|
/// during testing, enabling access to its properties and methods.
|
||||||
|
///
|
||||||
|
/// The returned value is cast to type [T], which should match the generic type parameter
|
||||||
|
/// of the [Application] class.
|
||||||
|
///
|
||||||
|
/// This property is only valid and accessible after calling [startOnCurrentIsolate].
|
||||||
|
/// Attempting to access it before starting the application or when using [start] instead
|
||||||
|
/// of [startOnCurrentIsolate] may result in unexpected behavior or errors.
|
||||||
|
///
|
||||||
|
/// Usage example:
|
||||||
|
/// ```dart
|
||||||
|
/// final app = Application<MyChannel>();
|
||||||
|
/// await app.startOnCurrentIsolate();
|
||||||
|
/// final myChannel = app.channel;
|
||||||
|
/// // Now you can interact with myChannel for testing purposes
|
||||||
|
/// ```
|
||||||
T get channel => server.channel as T;
|
T get channel => server.channel as T;
|
||||||
|
|
||||||
/// The logger that this application will write messages to.
|
/// The logger that this application will write messages to.
|
||||||
///
|
///
|
||||||
/// This logger's name will appear as 'conduit'.
|
/// This logger is used throughout the application to record messages, errors,
|
||||||
Logger logger = Logger("conduit");
|
/// and other important information. It is configured with the name 'protevus',
|
||||||
|
/// which will appear as the source of all log messages generated by this logger.
|
||||||
|
///
|
||||||
|
/// The Logger class is likely from a logging package, providing various methods
|
||||||
|
/// for different log levels (e.g., info, warning, error) and potentially
|
||||||
|
/// supporting different output destinations or formatting options.
|
||||||
|
///
|
||||||
|
/// Usage of this logger helps in debugging, monitoring, and maintaining the
|
||||||
|
/// application by providing a centralized way to capture and analyze runtime
|
||||||
|
/// information.
|
||||||
|
Logger logger = Logger("protevus");
|
||||||
|
|
||||||
/// The options used to configure this application.
|
/// The options used to configure this application.
|
||||||
///
|
///
|
||||||
/// Changing these values once the application has started will have no effect.
|
/// This property holds an instance of [ApplicationOptions] that contains various
|
||||||
|
/// configuration settings for the application. These options can include things
|
||||||
|
/// like port numbers, database configurations, or any other application-specific
|
||||||
|
/// settings.
|
||||||
|
///
|
||||||
|
/// The options are typically set before the application is started. It's important
|
||||||
|
/// to note that modifying these options after the application has been started
|
||||||
|
/// will not have any effect on the running application.
|
||||||
|
///
|
||||||
|
/// Example usage:
|
||||||
|
/// ```dart
|
||||||
|
/// final app = Application<MyChannel>();
|
||||||
|
/// app.options.port = 8080;
|
||||||
|
/// app.options.configurationFilePath = 'config.yaml';
|
||||||
|
/// await app.start();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Default value is an instance of [ApplicationOptions] with default settings.
|
||||||
ApplicationOptions options = ApplicationOptions();
|
ApplicationOptions options = ApplicationOptions();
|
||||||
|
|
||||||
/// The duration to wait for each isolate during startup before failing.
|
/// The duration to wait for each isolate during startup before failing.
|
||||||
///
|
///
|
||||||
/// A [TimeoutException] is thrown if an isolate fails to startup in this time period.
|
/// This property sets the maximum time allowed for each isolate to start up
|
||||||
|
/// during the application's initialization process. If an isolate fails to
|
||||||
|
/// complete its startup within this time frame, a [TimeoutException] is thrown.
|
||||||
///
|
///
|
||||||
/// Defaults to 30 seconds.
|
/// Defaults to 30 seconds.
|
||||||
Duration isolateStartupTimeout = const Duration(seconds: 30);
|
Duration isolateStartupTimeout = const Duration(seconds: 30);
|
||||||
|
|
||||||
/// Whether or not this application is running.
|
/// Indicates whether the application is currently running.
|
||||||
///
|
///
|
||||||
/// This will return true if [start]/[startOnCurrentIsolate] have been invoked and completed; i.e. this is the synchronous version of the [Future] returned by [start]/[startOnCurrentIsolate].
|
/// This will return true if [start]/[startOnCurrentIsolate] have been invoked and completed; i.e. this is the synchronous version of the [Future] returned by [start]/[startOnCurrentIsolate].
|
||||||
///
|
///
|
||||||
/// This value will return to false after [stop] has completed.
|
/// This value will return to false after [stop] has completed.
|
||||||
bool get isRunning => _hasFinishedLaunching;
|
bool get isRunning => _hasFinishedLaunching;
|
||||||
|
|
||||||
|
/// Indicates whether the application has finished launching.
|
||||||
|
///
|
||||||
|
/// This boolean flag is set to true once the application has successfully
|
||||||
|
/// completed its startup process, including initializing all isolates and
|
||||||
|
/// opening HTTP listeners. It is used internally to track the application's
|
||||||
|
/// running state and is consulted by the [isRunning] getter.
|
||||||
|
///
|
||||||
|
/// The value is set to false initially and when the application is stopped,
|
||||||
|
/// and set to true at the end of successful [start] or [startOnCurrentIsolate]
|
||||||
|
/// method execution.
|
||||||
bool _hasFinishedLaunching = false;
|
bool _hasFinishedLaunching = false;
|
||||||
|
|
||||||
|
/// Retrieves the [ChannelRuntime] for the current application channel type.
|
||||||
|
///
|
||||||
|
/// This getter accesses the [RuntimeContext.current] map using the generic type [T]
|
||||||
|
/// (which represents the application's channel type) as the key. It then casts
|
||||||
|
/// the retrieved value to [ChannelRuntime].
|
||||||
|
///
|
||||||
|
/// The [ChannelRuntime] object contains runtime information and utilities
|
||||||
|
/// specific to the channel type, which are used in various parts of the
|
||||||
|
/// application for setup, initialization, and execution.
|
||||||
|
///
|
||||||
|
/// This getter is used internally by the Application class to access
|
||||||
|
/// channel-specific runtime information without exposing it publicly.
|
||||||
ChannelRuntime get _runtime => RuntimeContext.current[T] as ChannelRuntime;
|
ChannelRuntime get _runtime => RuntimeContext.current[T] as ChannelRuntime;
|
||||||
|
|
||||||
/// Starts this application, allowing it to handle HTTP requests.
|
/// Starts this application, allowing it to handle HTTP requests.
|
||||||
///
|
///
|
||||||
/// This method spawns [numberOfInstances] isolates, instantiates your application channel
|
/// This method initializes the application by spawning a specified number of isolates,
|
||||||
/// for each of these isolates, and opens an HTTP listener that sends requests to these instances.
|
/// each running an instance of the application channel. It then sets up an HTTP listener
|
||||||
|
/// to distribute incoming requests across these isolates.
|
||||||
///
|
///
|
||||||
/// The [Future] returned from this method will complete once all isolates have successfully started
|
/// The [Future] returned from this method will complete once all isolates have successfully started
|
||||||
/// and are available to handle requests.
|
/// and are available to handle requests.
|
||||||
|
@ -110,7 +201,7 @@ class Application<T extends ApplicationChannel> {
|
||||||
_hasFinishedLaunching = true;
|
_hasFinishedLaunching = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the application on the current isolate, and does not spawn additional isolates.
|
/// Starts the application on the current isolate without spawning additional isolates.
|
||||||
///
|
///
|
||||||
/// An application started in this way will run on the same isolate this method is invoked on.
|
/// An application started in this way will run on the same isolate this method is invoked on.
|
||||||
/// Performance is limited when running the application with this method; prefer to use [start].
|
/// Performance is limited when running the application with this method; prefer to use [start].
|
||||||
|
@ -139,8 +230,20 @@ class Application<T extends ApplicationChannel> {
|
||||||
|
|
||||||
/// Stops the application from running.
|
/// Stops the application from running.
|
||||||
///
|
///
|
||||||
/// Closes every isolate and their channel and stops listening for HTTP requests.
|
/// This method performs the following actions:
|
||||||
/// The [ServiceRegistry] will close any of its resources.
|
/// 1. Sets the '_hasFinishedLaunching' flag to false.
|
||||||
|
/// 2. Stops all supervisor isolates concurrently.
|
||||||
|
/// 3. Handles potential errors during supervisor shutdown, particularly checking for 'LateError'.
|
||||||
|
/// 4. Attempts to close the server forcefully.
|
||||||
|
/// 5. Logs any errors that occur during server closure.
|
||||||
|
/// 6. Resets the '_hasFinishedLaunching' flag and clears the supervisors list.
|
||||||
|
/// 7. Removes all listeners from the logger.
|
||||||
|
///
|
||||||
|
/// If a 'LateError' is encountered during supervisor shutdown, it throws a [StateError]
|
||||||
|
/// indicating that the channel type was not properly loaded.
|
||||||
|
///
|
||||||
|
/// This method ensures a clean shutdown of all application components and should be
|
||||||
|
/// called when the application needs to be terminated.
|
||||||
Future stop() async {
|
Future stop() async {
|
||||||
_hasFinishedLaunching = false;
|
_hasFinishedLaunching = false;
|
||||||
await Future.wait(supervisors.map((s) => s.stop()))
|
await Future.wait(supervisors.map((s) => s.stop()))
|
||||||
|
@ -167,6 +270,26 @@ class Application<T extends ApplicationChannel> {
|
||||||
|
|
||||||
/// Creates an [APIDocument] from an [ApplicationChannel].
|
/// Creates an [APIDocument] from an [ApplicationChannel].
|
||||||
///
|
///
|
||||||
|
/// This static method generates API documentation for a given application channel type.
|
||||||
|
/// It is primarily used by the 'conduit document' CLI command to create OpenAPI (formerly Swagger) documentation.
|
||||||
|
///
|
||||||
|
/// The method performs the following steps:
|
||||||
|
/// 1. Retrieves the runtime context for the specified channel type.
|
||||||
|
/// 2. Runs global initialization with the provided configuration.
|
||||||
|
/// 3. Creates an ApplicationServer instance.
|
||||||
|
/// 4. Prepares the channel.
|
||||||
|
/// 5. Generates the API documentation.
|
||||||
|
/// 6. Closes the channel.
|
||||||
|
/// 7. Returns the generated APIDocument.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [type]: The Type of the ApplicationChannel subclass.
|
||||||
|
/// - [config]: The ApplicationOptions containing configuration for the application.
|
||||||
|
/// - [projectSpec]: A Map containing additional project-specific information for the documentation.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A Future that resolves to an [APIDocument] containing the generated API documentation.
|
||||||
|
///
|
||||||
/// This method is called by the `conduit document` CLI.
|
/// This method is called by the `conduit document` CLI.
|
||||||
static Future<APIDocument> document(
|
static Future<APIDocument> document(
|
||||||
Type type,
|
Type type,
|
||||||
|
@ -188,6 +311,25 @@ class Application<T extends ApplicationChannel> {
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Spawns a new isolate to run an instance of the application.
|
||||||
|
///
|
||||||
|
/// This method creates a new isolate that runs an instance of the application channel.
|
||||||
|
/// It sets up the necessary communication channels and initializes the isolate with
|
||||||
|
/// the application's configuration and runtime information.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [application]: The main Application instance.
|
||||||
|
/// - [config]: ApplicationOptions containing the configuration for this instance.
|
||||||
|
/// - [identifier]: A unique identifier for this isolate.
|
||||||
|
/// - [logger]: The Logger instance for logging.
|
||||||
|
/// - [startupTimeout]: The maximum duration allowed for the isolate to start up.
|
||||||
|
/// - [logToConsole]: Whether to enable console logging for this isolate (default: false).
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A Future that resolves to an [ApplicationIsolateSupervisor] managing the new isolate.
|
||||||
|
///
|
||||||
|
/// This method is crucial for scaling the application across multiple isolates,
|
||||||
|
/// allowing for better performance and resource utilization.
|
||||||
Future<ApplicationIsolateSupervisor> _spawn(
|
Future<ApplicationIsolateSupervisor> _spawn(
|
||||||
Application application,
|
Application application,
|
||||||
ApplicationOptions config,
|
ApplicationOptions config,
|
||||||
|
@ -224,7 +366,7 @@ class Application<T extends ApplicationChannel> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Thrown when an application encounters an exception during startup.
|
/// Represents an exception that occurs during the startup process of an application.
|
||||||
///
|
///
|
||||||
/// Contains the original exception that halted startup.
|
/// Contains the original exception that halted startup.
|
||||||
class ApplicationStartupException implements Exception {
|
class ApplicationStartupException implements Exception {
|
||||||
|
|
|
@ -1,17 +1,38 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'package:protevus_application/application.dart';
|
import 'package:protevus_application/application.dart';
|
||||||
import 'package:protevus_http/http.dart';
|
import 'package:protevus_http/http.dart';
|
||||||
import 'package:protevus_runtime/runtime.dart';
|
import 'package:protevus_runtime/runtime.dart';
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
|
|
||||||
/// Listens for HTTP requests and delivers them to its [ApplicationChannel] instance.
|
/// A class representing an application server in the Conduit framework.
|
||||||
///
|
///
|
||||||
/// A Conduit application creates instances of this type to pair an HTTP server and an
|
/// The ApplicationServer class is responsible for managing the lifecycle of an HTTP server
|
||||||
/// instance of an [ApplicationChannel] subclass. Instances are created by [Application]
|
/// and its associated [ApplicationChannel]. It handles server creation, starting, and stopping,
|
||||||
/// and shouldn't be created otherwise.
|
/// as well as routing incoming requests to the appropriate handlers.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Creates and manages an instance of [ApplicationChannel]
|
||||||
|
/// - Configures and starts an HTTP or HTTPS server
|
||||||
|
/// - Handles incoming requests and routes them to the appropriate controller
|
||||||
|
/// - Manages server lifecycle (start, stop, close)
|
||||||
|
/// - Provides logging capabilities
|
||||||
|
/// - Supports both IPv4 and IPv6
|
||||||
|
/// - Handles secure connections with SSL/TLS
|
||||||
|
///
|
||||||
|
/// This class is typically instantiated and managed by the Application class and should not
|
||||||
|
/// be created directly in most cases
|
||||||
class ApplicationServer {
|
class ApplicationServer {
|
||||||
/// Creates a new server.
|
/// Creates a new server instance.
|
||||||
///
|
///
|
||||||
/// You should not need to invoke this method directly.
|
/// You should not need to invoke this method directly.
|
||||||
ApplicationServer(this.channelType, this.options, this.identifier) {
|
ApplicationServer(this.channelType, this.options, this.identifier) {
|
||||||
|
@ -21,41 +42,145 @@ class ApplicationServer {
|
||||||
..options = options;
|
..options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The configuration this instance used to start its [channel].
|
/// The configuration options used to start this server's [channel].
|
||||||
|
///
|
||||||
|
/// This property holds an instance of [ApplicationOptions] which contains
|
||||||
|
/// various settings used to configure the server, such as the address to bind to,
|
||||||
|
/// the port number, SSL/TLS settings, and other application-specific options.
|
||||||
|
/// These options are passed to the [ApplicationChannel] when it is initialized.
|
||||||
ApplicationOptions options;
|
ApplicationOptions options;
|
||||||
|
|
||||||
/// The underlying [HttpServer].
|
/// The underlying [HttpServer] instance used by this [ApplicationServer].
|
||||||
|
///
|
||||||
|
/// This property represents the core HTTP server that handles incoming requests.
|
||||||
|
/// It is initialized when the server starts and is used throughout the lifecycle
|
||||||
|
/// of the [ApplicationServer] to manage incoming connections and route requests
|
||||||
|
/// to the appropriate handlers.
|
||||||
|
///
|
||||||
|
/// The server can be either a standard HTTP server or an HTTPS server, depending
|
||||||
|
/// on the configuration and security context provided during initialization.
|
||||||
late final HttpServer server;
|
late final HttpServer server;
|
||||||
|
|
||||||
/// The instance of [ApplicationChannel] serving requests.
|
/// The instance of [ApplicationChannel] serving requests.
|
||||||
|
///
|
||||||
|
/// This property represents the primary request handling pipeline for the application.
|
||||||
|
/// It is instantiated when the ApplicationServer is created and is responsible for
|
||||||
|
/// processing incoming HTTP requests, routing them to appropriate controllers,
|
||||||
|
/// and generating responses.
|
||||||
|
///
|
||||||
|
/// The [ApplicationChannel] is a custom class defined by the application developer
|
||||||
|
/// that sets up the request handling logic, including middleware, controllers,
|
||||||
|
/// and other application-specific components.
|
||||||
late ApplicationChannel channel;
|
late ApplicationChannel channel;
|
||||||
|
|
||||||
/// The cached entrypoint of [channel].
|
/// The cached entrypoint of [channel].
|
||||||
|
///
|
||||||
|
/// This property stores the main [Controller] that serves as the entry point for request handling.
|
||||||
|
/// It is initialized when the server starts and is used to process incoming HTTP requests.
|
||||||
|
/// The entrypoint controller typically represents the root of the request handling pipeline
|
||||||
|
/// and may delegate to other controllers or middleware as needed.
|
||||||
late Controller entryPoint;
|
late Controller entryPoint;
|
||||||
|
|
||||||
|
/// The type of [ApplicationChannel] this server will use.
|
||||||
|
///
|
||||||
|
/// This property stores the Type of the ApplicationChannel subclass that will be
|
||||||
|
/// instantiated and used by this ApplicationServer. The ApplicationChannel
|
||||||
|
/// defines the request handling logic and routing for the application.
|
||||||
final Type channelType;
|
final Type channelType;
|
||||||
|
|
||||||
/// Target for sending messages to other [ApplicationChannel.messageHub]s.
|
/// Target for sending messages to other [ApplicationChannel.messageHub]s.
|
||||||
///
|
///
|
||||||
/// Events are added to this property by instances of [ApplicationMessageHub] and should not otherwise be used.
|
/// This property represents an [EventSink] that can be used to send messages
|
||||||
|
/// to other [ApplicationChannel.messageHub]s across different instances of
|
||||||
|
/// the application. It is primarily used for inter-server communication in
|
||||||
|
/// distributed setups.
|
||||||
|
///
|
||||||
|
/// The [hubSink] is typically set and managed by instances of [ApplicationMessageHub].
|
||||||
|
/// Application developers should not directly modify or use this property, as it is
|
||||||
|
/// intended for internal framework use.
|
||||||
|
///
|
||||||
|
/// The sink can be null if no message hub has been configured for this server.
|
||||||
EventSink<dynamic>? hubSink;
|
EventSink<dynamic>? hubSink;
|
||||||
|
|
||||||
/// Whether or not this server requires an HTTPS listener.
|
/// Indicates whether this server requires an HTTPS listener.
|
||||||
|
///
|
||||||
|
/// This getter returns a boolean value that determines if the server
|
||||||
|
/// should use HTTPS instead of HTTP. It is typically set to true when
|
||||||
|
/// a security context is provided during server initialization.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [bool]: true if the server requires HTTPS, false otherwise.
|
||||||
bool get requiresHTTPS => _requiresHTTPS;
|
bool get requiresHTTPS => _requiresHTTPS;
|
||||||
|
|
||||||
|
/// Indicates whether this server instance is configured to use HTTPS.
|
||||||
|
///
|
||||||
|
/// This private variable is set to true when a security context is provided
|
||||||
|
/// during server initialization, indicating that the server should use HTTPS.
|
||||||
|
/// It is used internally to determine the server's connection type and is
|
||||||
|
/// accessed through the public getter [requiresHTTPS].
|
||||||
|
///
|
||||||
|
/// The value is false by default, assuming HTTP connection, and is only set to
|
||||||
|
/// true when HTTPS is explicitly configured.
|
||||||
bool _requiresHTTPS = false;
|
bool _requiresHTTPS = false;
|
||||||
|
|
||||||
/// The unique identifier of this instance.
|
/// The unique identifier of this instance.
|
||||||
///
|
///
|
||||||
/// Each instance has its own identifier, a numeric value starting at 1, to identify it
|
/// Each instance has its own identifier, a numeric value starting at 1, to identify it
|
||||||
/// among other instances.
|
/// among other instances.
|
||||||
|
///
|
||||||
|
/// This identifier is used to distinguish between different [ApplicationServer] instances
|
||||||
|
/// when multiple servers are running concurrently. It's particularly useful for logging
|
||||||
|
/// and debugging purposes, allowing developers to trace which server instance is handling
|
||||||
|
/// specific requests or operations.
|
||||||
|
///
|
||||||
|
/// The identifier is typically assigned automatically by the [Application] class when
|
||||||
|
/// creating new server instances, ensuring that each server has a unique number.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// If three server instances are created, they might have identifiers 1, 2, and 3 respectively.
|
||||||
int identifier;
|
int identifier;
|
||||||
|
|
||||||
/// The logger of this instance
|
/// Returns the logger instance for this ApplicationServer.
|
||||||
Logger get logger => Logger("conduit");
|
///
|
||||||
|
/// This getter provides access to a [Logger] instance specifically configured
|
||||||
|
/// for the Conduit framework. The logger is named "conduit" and can be used
|
||||||
|
/// throughout the ApplicationServer and its associated classes for consistent
|
||||||
|
/// logging purposes.
|
||||||
|
///
|
||||||
|
/// The logger can be used to record various levels of information, warnings,
|
||||||
|
/// and errors during the server's operation, which is crucial for debugging
|
||||||
|
/// and monitoring the application's behavior.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [Logger] instance named "conduit".
|
||||||
|
Logger get logger => Logger("protevus");
|
||||||
|
|
||||||
/// Starts this instance, allowing it to receive HTTP requests.
|
/// Starts this instance, allowing it to receive HTTP requests.
|
||||||
///
|
///
|
||||||
/// Do not invoke this method directly.
|
/// This method initializes the server, preparing it to handle incoming HTTP requests.
|
||||||
|
/// It performs the following steps:
|
||||||
|
/// 1. Prepares the channel by calling [channel.prepare()].
|
||||||
|
/// 2. Sets up the entry point for request handling.
|
||||||
|
/// 3. Binds the HTTP server to the specified address and port.
|
||||||
|
/// 4. Configures HTTPS if a security context is provided.
|
||||||
|
///
|
||||||
|
/// The method supports both HTTP and HTTPS connections, determined by the presence
|
||||||
|
/// of a security context. It also handles IPv6 configuration and server sharing options.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// [shareHttpServer] - A boolean indicating whether to share the HTTP server
|
||||||
|
/// across multiple instances. Defaults to false.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [Future] that completes when the server has successfully started and is
|
||||||
|
/// ready to receive requests.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// May throw exceptions related to network binding or security context configuration.
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// This method should not be invoked directly under normal circumstances.
|
||||||
|
/// It is typically called by the framework during the application startup process.
|
||||||
Future start({bool shareHttpServer = false}) async {
|
Future start({bool shareHttpServer = false}) async {
|
||||||
logger.fine("ApplicationServer($identifier).start entry");
|
logger.fine("ApplicationServer($identifier).start entry");
|
||||||
|
|
||||||
|
@ -92,7 +217,21 @@ class ApplicationServer {
|
||||||
return didOpen();
|
return didOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Closes this HTTP server and channel.
|
/// Closes this HTTP server and associated channel.
|
||||||
|
///
|
||||||
|
/// This method performs the following steps:
|
||||||
|
/// 1. Closes the HTTP server, forcibly terminating any ongoing connections.
|
||||||
|
/// 2. Closes the associated [ApplicationChannel].
|
||||||
|
/// 3. Closes the [hubSink] if it exists.
|
||||||
|
///
|
||||||
|
/// The method logs the progress of each step for debugging purposes.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A [Future] that completes when all closing operations are finished.
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// The [hubSink] is actually closed by channel.messageHub.close, but it's
|
||||||
|
/// explicitly closed here to satisfy the Dart analyzer.
|
||||||
Future close() async {
|
Future close() async {
|
||||||
logger.fine("ApplicationServer($identifier).close Closing HTTP listener");
|
logger.fine("ApplicationServer($identifier).close Closing HTTP listener");
|
||||||
await server.close(force: true);
|
await server.close(force: true);
|
||||||
|
@ -104,7 +243,7 @@ class ApplicationServer {
|
||||||
logger.fine("ApplicationServer($identifier).close Closing complete");
|
logger.fine("ApplicationServer($identifier).close Closing complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoked when this server becomes ready receive requests.
|
/// Invoked when this server becomes ready to receive requests.
|
||||||
///
|
///
|
||||||
/// [ApplicationChannel.willStartReceivingRequests] is invoked after this opening has completed.
|
/// [ApplicationChannel.willStartReceivingRequests] is invoked after this opening has completed.
|
||||||
Future didOpen() async {
|
Future didOpen() async {
|
||||||
|
@ -117,6 +256,18 @@ class ApplicationServer {
|
||||||
logger.info("Server conduit/$identifier started.");
|
logger.info("Server conduit/$identifier started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends an application event.
|
||||||
|
///
|
||||||
|
/// This method is designed to handle application-wide events. By default,
|
||||||
|
/// it does nothing and serves as a placeholder for potential event handling
|
||||||
|
/// implementations in derived classes.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// [event]: A dynamic object representing the event to be sent.
|
||||||
|
/// It can be of any type, allowing flexibility in event structures.
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// Override this method in subclasses to implement specific event handling logic.
|
||||||
void sendApplicationEvent(dynamic event) {
|
void sendApplicationEvent(dynamic event) {
|
||||||
// By default, do nothing
|
// By default, do nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
@ -9,7 +18,7 @@ import 'package:protevus_runtime/runtime.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
/// An object that defines the behavior specific to your application.
|
/// An abstract class that defines the behavior specific to your application.
|
||||||
///
|
///
|
||||||
/// You create a subclass of [ApplicationChannel] to initialize your application's services and define how HTTP requests are handled by your application.
|
/// You create a subclass of [ApplicationChannel] to initialize your application's services and define how HTTP requests are handled by your application.
|
||||||
/// There *must* only be one subclass in an application and it must be visible to your application library file, e.g., 'package:my_app/my_app.dart'.
|
/// There *must* only be one subclass in an application and it must be visible to your application library file, e.g., 'package:my_app/my_app.dart'.
|
||||||
|
@ -21,7 +30,7 @@ import 'package:meta/meta.dart';
|
||||||
/// When your application is started, an instance of your application channel is created for each isolate (see [Application.start]). Each instance
|
/// When your application is started, an instance of your application channel is created for each isolate (see [Application.start]). Each instance
|
||||||
/// is a replica of your application that runs in its own memory isolated thread.
|
/// is a replica of your application that runs in its own memory isolated thread.
|
||||||
abstract class ApplicationChannel implements APIComponentDocumenter {
|
abstract class ApplicationChannel implements APIComponentDocumenter {
|
||||||
/// You implement this method to provide global initialization for your application.
|
/// Provides global initialization for the application.
|
||||||
///
|
///
|
||||||
/// Most of your application initialization code is written in [prepare], which is invoked for each isolate. For initialization that
|
/// Most of your application initialization code is written in [prepare], which is invoked for each isolate. For initialization that
|
||||||
/// needs to occur once per application start, you must provide an implementation for this method. This method is invoked prior
|
/// needs to occur once per application start, you must provide an implementation for this method. This method is invoked prior
|
||||||
|
@ -51,27 +60,43 @@ abstract class ApplicationChannel implements APIComponentDocumenter {
|
||||||
/// is for documentation purposes.
|
/// is for documentation purposes.
|
||||||
static Future initializeApplication(ApplicationOptions options) async {}
|
static Future initializeApplication(ApplicationOptions options) async {}
|
||||||
|
|
||||||
/// The logger that this object will write messages to.
|
/// Returns a Logger instance for this object.
|
||||||
///
|
///
|
||||||
/// This logger's name appears as 'conduit'.
|
/// This logger's name appears as 'conduit'.
|
||||||
Logger get logger => Logger("conduit");
|
Logger get logger => Logger("protevus");
|
||||||
|
|
||||||
/// The [ApplicationServer] that sends HTTP requests to this object.
|
/// Returns the [ApplicationServer] instance that sends HTTP requests to this object.
|
||||||
|
///
|
||||||
|
/// This getter provides access to the server associated with this ApplicationChannel.
|
||||||
|
/// The server is responsible for handling incoming HTTP requests and routing them
|
||||||
|
/// to the appropriate controllers within the channel.
|
||||||
ApplicationServer get server => _server;
|
ApplicationServer get server => _server;
|
||||||
|
|
||||||
|
/// Sets the ApplicationServer for this channel and establishes message hub connections.
|
||||||
|
///
|
||||||
|
/// This setter method performs two main tasks:
|
||||||
|
/// 1. It assigns the provided [server] to the private [_server] variable.
|
||||||
|
/// 2. It sets up the message hub connections:
|
||||||
|
/// - It adds a listener to the outbound stream of the messageHub, which sends
|
||||||
|
/// application events through the server.
|
||||||
|
/// - It sets the inbound sink of the messageHub as the hubSink of the server.
|
||||||
|
///
|
||||||
|
/// This setup allows for inter-isolate communication through the ApplicationMessageHub.
|
||||||
|
///
|
||||||
|
/// [server] The ApplicationServer instance to be set for this channel.
|
||||||
set server(ApplicationServer server) {
|
set server(ApplicationServer server) {
|
||||||
_server = server;
|
_server = server;
|
||||||
messageHub._outboundController.stream.listen(server.sendApplicationEvent);
|
messageHub._outboundController.stream.listen(server.sendApplicationEvent);
|
||||||
server.hubSink = messageHub._inboundController.sink;
|
server.hubSink = messageHub._inboundController.sink;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use this object to send data to channels running on other isolates.
|
/// A messaging hub for inter-isolate communication within the application.
|
||||||
///
|
///
|
||||||
/// You use this object to synchronize state across the isolates of an application. Any data sent
|
/// You use this object to synchronize state across the isolates of an application. Any data sent
|
||||||
/// through this object will be received by every other channel in your application (except the one that sent it).
|
/// through this object will be received by every other channel in your application (except the one that sent it).
|
||||||
final ApplicationMessageHub messageHub = ApplicationMessageHub();
|
final ApplicationMessageHub messageHub = ApplicationMessageHub();
|
||||||
|
|
||||||
/// The context used for setting up HTTPS in an application.
|
/// Returns a SecurityContext for HTTPS configuration if certificate and private key files are provided.
|
||||||
///
|
///
|
||||||
/// If this value is non-null, the [server] receiving HTTP requests will only accept requests over HTTPS.
|
/// If this value is non-null, the [server] receiving HTTP requests will only accept requests over HTTPS.
|
||||||
///
|
///
|
||||||
|
@ -93,6 +118,17 @@ abstract class ApplicationChannel implements APIComponentDocumenter {
|
||||||
///
|
///
|
||||||
/// These options are set when starting the application. Changes to this object have no effect
|
/// These options are set when starting the application. Changes to this object have no effect
|
||||||
/// on other isolates.
|
/// on other isolates.
|
||||||
|
///
|
||||||
|
/// This property holds an instance of [ApplicationOptions] which contains various
|
||||||
|
/// configuration settings for the application. These options are typically set
|
||||||
|
/// during the application's startup process.
|
||||||
|
///
|
||||||
|
/// The options stored here are specific to this channel instance and do not
|
||||||
|
/// affect other isolates running in the application. This means that modifying
|
||||||
|
/// these options at runtime will only impact the current isolate.
|
||||||
|
///
|
||||||
|
/// The property is nullable, allowing for cases where options might not be set
|
||||||
|
/// or where default configurations are used in the absence of specific options.
|
||||||
ApplicationOptions? options;
|
ApplicationOptions? options;
|
||||||
|
|
||||||
/// You implement this accessor to define how HTTP requests are handled by your application.
|
/// You implement this accessor to define how HTTP requests are handled by your application.
|
||||||
|
@ -112,9 +148,17 @@ abstract class ApplicationChannel implements APIComponentDocumenter {
|
||||||
/// }
|
/// }
|
||||||
Controller get entryPoint;
|
Controller get entryPoint;
|
||||||
|
|
||||||
|
/// The [ApplicationServer] instance associated with this channel.
|
||||||
|
///
|
||||||
|
/// This private variable stores the server that handles HTTP requests for this
|
||||||
|
/// ApplicationChannel. It is marked as 'late' because it will be initialized
|
||||||
|
/// after the channel is created, typically when the 'server' setter is called.
|
||||||
|
///
|
||||||
|
/// The server is responsible for managing incoming HTTP connections and
|
||||||
|
/// routing requests to the appropriate controllers within the channel.
|
||||||
late ApplicationServer _server;
|
late ApplicationServer _server;
|
||||||
|
|
||||||
/// You override this method to perform initialization tasks.
|
/// Performs initialization tasks for the application channel.
|
||||||
///
|
///
|
||||||
/// This method allows this instance to perform any initialization (other than setting up the [entryPoint]). This method
|
/// This method allows this instance to perform any initialization (other than setting up the [entryPoint]). This method
|
||||||
/// is often used to set up services that [Controller]s use to fulfill their duties. This method is invoked
|
/// is often used to set up services that [Controller]s use to fulfill their duties. This method is invoked
|
||||||
|
@ -123,12 +167,12 @@ abstract class ApplicationChannel implements APIComponentDocumenter {
|
||||||
/// By default, this method does nothing.
|
/// By default, this method does nothing.
|
||||||
Future prepare() async {}
|
Future prepare() async {}
|
||||||
|
|
||||||
/// You override this method to perform initialization tasks that occur after [entryPoint] has been established.
|
/// Overridable method called just before the application starts receiving requests.
|
||||||
///
|
///
|
||||||
/// Override this method to take action just before [entryPoint] starts receiving requests. By default, does nothing.
|
/// Override this method to take action just before [entryPoint] starts receiving requests. By default, does nothing.
|
||||||
void willStartReceivingRequests() {}
|
void willStartReceivingRequests() {}
|
||||||
|
|
||||||
/// You override this method to release any resources created in [prepare].
|
/// Releases resources and performs cleanup when the application channel is closing.
|
||||||
///
|
///
|
||||||
/// This method is invoked when the owning [Application] is stopped. It closes open ports
|
/// This method is invoked when the owning [Application] is stopped. It closes open ports
|
||||||
/// that this channel was using so that the application can be properly shut down.
|
/// that this channel was using so that the application can be properly shut down.
|
||||||
|
@ -146,7 +190,8 @@ abstract class ApplicationChannel implements APIComponentDocumenter {
|
||||||
|
|
||||||
/// Creates an OpenAPI document for the components and paths in this channel.
|
/// Creates an OpenAPI document for the components and paths in this channel.
|
||||||
///
|
///
|
||||||
/// This method invokes [entryPoint] and [prepare] before starting the documentation process.
|
/// This method generates a complete OpenAPI specification document for the application,
|
||||||
|
/// including all components, paths, and operations defined in the channel.
|
||||||
///
|
///
|
||||||
/// The documentation process first invokes [documentComponents] on this channel. Every controller in the channel will have its
|
/// The documentation process first invokes [documentComponents] on this channel. Every controller in the channel will have its
|
||||||
/// [documentComponents] methods invoked. Any declared property
|
/// [documentComponents] methods invoked. Any declared property
|
||||||
|
@ -181,6 +226,24 @@ abstract class ApplicationChannel implements APIComponentDocumenter {
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Documents the components of this ApplicationChannel and its controllers.
|
||||||
|
///
|
||||||
|
/// This method is responsible for generating API documentation for the components
|
||||||
|
/// of this ApplicationChannel and its associated controllers. It performs the following tasks:
|
||||||
|
///
|
||||||
|
/// 1. Calls `documentComponents` on the entry point controller, which typically
|
||||||
|
/// initiates the documentation process for all linked controllers.
|
||||||
|
///
|
||||||
|
/// 2. Retrieves all documentable channel components using the ChannelRuntime,
|
||||||
|
/// which are typically services or other objects that implement APIComponentDocumenter.
|
||||||
|
///
|
||||||
|
/// 3. Calls `documentComponents` on each of these channel components, allowing
|
||||||
|
/// them to add their own documentation to the API registry.
|
||||||
|
///
|
||||||
|
/// This method is marked with @mustCallSuper, indicating that subclasses
|
||||||
|
/// overriding this method must call the superclass implementation.
|
||||||
|
///
|
||||||
|
/// [registry] The APIDocumentContext used to store and organize the API documentation.
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
@override
|
@override
|
||||||
void documentComponents(APIDocumentContext registry) {
|
void documentComponents(APIDocumentContext registry) {
|
||||||
|
@ -194,7 +257,7 @@ abstract class ApplicationChannel implements APIComponentDocumenter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An object that sends and receives messages between [ApplicationChannel]s.
|
/// An object that facilitates message passing between [ApplicationChannel]s in different isolates.
|
||||||
///
|
///
|
||||||
/// You use this object to share information between isolates. Each [ApplicationChannel] has a property of this type. A message sent through this object
|
/// You use this object to share information between isolates. Each [ApplicationChannel] has a property of this type. A message sent through this object
|
||||||
/// is received by every other channel through its hub.
|
/// is received by every other channel through its hub.
|
||||||
|
@ -218,14 +281,44 @@ abstract class ApplicationChannel implements APIComponentDocumenter {
|
||||||
/// }
|
/// }
|
||||||
/// });
|
/// });
|
||||||
class ApplicationMessageHub extends Stream<dynamic> implements Sink<dynamic> {
|
class ApplicationMessageHub extends Stream<dynamic> implements Sink<dynamic> {
|
||||||
final Logger _logger = Logger("conduit");
|
/// A logger instance for the ApplicationMessageHub.
|
||||||
|
///
|
||||||
|
/// This logger is used to log messages and errors related to the ApplicationMessageHub.
|
||||||
|
/// It is named "protevus" to identify logs from this specific component.
|
||||||
|
final Logger _logger = Logger("protevus");
|
||||||
|
|
||||||
|
/// A StreamController for outbound messages.
|
||||||
|
///
|
||||||
|
/// This controller manages the stream of outbound messages sent from this
|
||||||
|
/// ApplicationMessageHub to other hubs. It is used internally to handle
|
||||||
|
/// the flow of messages being sent out to other isolates.
|
||||||
|
///
|
||||||
|
/// The stream is not broadcast, meaning it only allows a single subscriber.
|
||||||
|
/// This is typically used by the ApplicationServer to listen for outbound
|
||||||
|
/// messages and distribute them to other isolates.
|
||||||
final StreamController<dynamic> _outboundController =
|
final StreamController<dynamic> _outboundController =
|
||||||
StreamController<dynamic>();
|
StreamController<dynamic>();
|
||||||
|
|
||||||
|
/// A StreamController for inbound messages.
|
||||||
|
///
|
||||||
|
/// This controller manages the stream of inbound messages received by this
|
||||||
|
/// ApplicationMessageHub from other hubs. It is used internally to handle
|
||||||
|
/// the flow of messages coming in from other isolates.
|
||||||
|
///
|
||||||
|
/// The stream is broadcast, meaning it allows multiple subscribers. This allows
|
||||||
|
/// multiple parts of the application to listen for and react to incoming messages
|
||||||
|
/// independently.
|
||||||
final StreamController<dynamic> _inboundController =
|
final StreamController<dynamic> _inboundController =
|
||||||
StreamController<dynamic>.broadcast();
|
StreamController<dynamic>.broadcast();
|
||||||
|
|
||||||
/// Adds a listener for messages from other hubs.
|
/// Adds a listener for messages from other hubs.
|
||||||
///
|
///
|
||||||
|
/// A class that facilitates message passing between [ApplicationChannel]s in different isolates.
|
||||||
|
///
|
||||||
|
/// This class implements both [Stream] and [Sink] interfaces, allowing it to send and receive messages
|
||||||
|
/// across isolates. It uses separate controllers for inbound and outbound messages to manage the flow
|
||||||
|
/// of data.
|
||||||
|
///
|
||||||
/// You use this method to add listeners for messages from other hubs.
|
/// You use this method to add listeners for messages from other hubs.
|
||||||
/// When another hub [add]s a message, this hub will receive it on [onData].
|
/// When another hub [add]s a message, this hub will receive it on [onData].
|
||||||
///
|
///
|
||||||
|
@ -249,7 +342,8 @@ class ApplicationMessageHub extends Stream<dynamic> implements Sink<dynamic> {
|
||||||
|
|
||||||
/// Sends a message to all other hubs.
|
/// Sends a message to all other hubs.
|
||||||
///
|
///
|
||||||
/// [event] will be delivered to all other isolates that have set up a callback for [listen].
|
/// This method allows sending a message [event] to all other isolates in the application.
|
||||||
|
/// The message will be delivered to all other isolates that have set up a callback using [listen].
|
||||||
///
|
///
|
||||||
/// [event] must be isolate-safe data - in general, this means it may not be or contain a closure. Consult the API reference `dart:isolate` for more details. If [event]
|
/// [event] must be isolate-safe data - in general, this means it may not be or contain a closure. Consult the API reference `dart:isolate` for more details. If [event]
|
||||||
/// is not isolate-safe data, an error is delivered to [listen] on this isolate.
|
/// is not isolate-safe data, an error is delivered to [listen] on this isolate.
|
||||||
|
@ -258,6 +352,19 @@ class ApplicationMessageHub extends Stream<dynamic> implements Sink<dynamic> {
|
||||||
_outboundController.sink.add(event);
|
_outboundController.sink.add(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Closes the message hub and its associated stream controllers.
|
||||||
|
///
|
||||||
|
/// This method performs the following tasks:
|
||||||
|
/// 1. If the outbound controller has no listeners, it adds a dummy listener
|
||||||
|
/// to prevent potential issues with unhandled stream events.
|
||||||
|
/// 2. If the inbound controller has no listeners, it adds a dummy listener
|
||||||
|
/// for the same reason.
|
||||||
|
/// 3. Closes both the outbound and inbound controllers.
|
||||||
|
///
|
||||||
|
/// This method should be called when the application is shutting down or
|
||||||
|
/// when the message hub is no longer needed to ensure proper cleanup of resources.
|
||||||
|
///
|
||||||
|
/// Returns a Future that completes when both controllers have been closed.
|
||||||
@override
|
@override
|
||||||
Future close() async {
|
Future close() async {
|
||||||
if (!_outboundController.hasListener) {
|
if (!_outboundController.hasListener) {
|
||||||
|
@ -273,6 +380,10 @@ class ApplicationMessageHub extends Stream<dynamic> implements Sink<dynamic> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An abstract class that defines the runtime behavior of an ApplicationChannel.
|
||||||
|
///
|
||||||
|
/// This class provides methods and properties for managing the lifecycle,
|
||||||
|
/// documentation, and instantiation of an ApplicationChannel.
|
||||||
abstract class ChannelRuntime {
|
abstract class ChannelRuntime {
|
||||||
Iterable<APIComponentDocumenter> getDocumentableChannelComponents(
|
Iterable<APIComponentDocumenter> getDocumentableChannelComponents(
|
||||||
ApplicationChannel channel,
|
ApplicationChannel channel,
|
||||||
|
|
|
@ -1,10 +1,47 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:protevus_application/application.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:protevus_application/application.dart';
|
||||||
|
|
||||||
|
/// An isolated server implementation of the ApplicationServer class.
|
||||||
|
///
|
||||||
|
/// This class extends ApplicationServer to run in a separate isolate, allowing
|
||||||
|
/// for concurrent execution of multiple server instances. It manages communication
|
||||||
|
/// with a supervising application through message passing.
|
||||||
|
///
|
||||||
|
/// The server can be started, stopped, and can send and receive application events.
|
||||||
|
/// It also supports optional console logging for debugging purposes.
|
||||||
|
///
|
||||||
|
/// Constructor parameters:
|
||||||
|
/// - channelType: The type of channel to be used.
|
||||||
|
/// - configuration: ApplicationOptions for server configuration.
|
||||||
|
/// - identifier: A unique identifier for this server instance.
|
||||||
|
/// - supervisingApplicationPort: SendPort for communicating with the supervising application.
|
||||||
|
/// - logToConsole: Optional flag to enable console logging (default is false).
|
||||||
class ApplicationIsolateServer extends ApplicationServer {
|
class ApplicationIsolateServer extends ApplicationServer {
|
||||||
|
/// Constructor for ApplicationIsolateServer.
|
||||||
|
///
|
||||||
|
/// Creates a new instance of ApplicationIsolateServer with the specified parameters.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - channelType: The type of channel to be used for communication.
|
||||||
|
/// - configuration: ApplicationOptions for configuring the server.
|
||||||
|
/// - identifier: A unique identifier for this server instance.
|
||||||
|
/// - supervisingApplicationPort: SendPort for communicating with the supervising application.
|
||||||
|
/// - logToConsole: Optional flag to enable console logging (default is false).
|
||||||
|
///
|
||||||
|
/// This constructor initializes the server, sets up logging if enabled, and establishes
|
||||||
|
/// communication with the supervising application. It also sets up a listener for
|
||||||
|
/// incoming messages from the supervisor.
|
||||||
ApplicationIsolateServer(
|
ApplicationIsolateServer(
|
||||||
Type channelType,
|
Type channelType,
|
||||||
ApplicationOptions configuration,
|
ApplicationOptions configuration,
|
||||||
|
@ -26,9 +63,40 @@ class ApplicationIsolateServer extends ApplicationServer {
|
||||||
supervisingApplicationPort.send(supervisingReceivePort.sendPort);
|
supervisingApplicationPort.send(supervisingReceivePort.sendPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A SendPort used for communication with the supervising application.
|
||||||
|
///
|
||||||
|
/// This SendPort allows the ApplicationIsolateServer to send messages and events
|
||||||
|
/// back to the supervising application, enabling bidirectional communication
|
||||||
|
/// between the isolated server and its parent process.
|
||||||
SendPort supervisingApplicationPort;
|
SendPort supervisingApplicationPort;
|
||||||
|
|
||||||
|
/// A ReceivePort for receiving messages from the supervising application.
|
||||||
|
///
|
||||||
|
/// This ReceivePort is used to listen for incoming messages from the supervising
|
||||||
|
/// application. It's initialized in the constructor and is used to set up a
|
||||||
|
/// listener for handling various commands and messages, such as stop requests
|
||||||
|
/// or application events.
|
||||||
|
///
|
||||||
|
/// The 'late' keyword indicates that this variable will be initialized after
|
||||||
|
/// the constructor body, but before it's used.
|
||||||
late ReceivePort supervisingReceivePort;
|
late ReceivePort supervisingReceivePort;
|
||||||
|
|
||||||
|
/// Starts the ApplicationIsolateServer.
|
||||||
|
///
|
||||||
|
/// This method overrides the base class's start method to add functionality
|
||||||
|
/// specific to the isolated server. It performs the following steps:
|
||||||
|
/// 1. Calls the superclass's start method with the provided shareHttpServer parameter.
|
||||||
|
/// 2. Logs a fine-level message indicating that the server has started.
|
||||||
|
/// 3. Sends a 'listening' message to the supervising application.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - shareHttpServer: A boolean indicating whether to share the HTTP server (default is false).
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A Future that completes with the result of the superclass's start method.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// Any exceptions that may be thrown by the superclass's start method.
|
||||||
@override
|
@override
|
||||||
Future start({bool shareHttpServer = false}) async {
|
Future start({bool shareHttpServer = false}) async {
|
||||||
final result = await super.start(shareHttpServer: shareHttpServer);
|
final result = await super.start(shareHttpServer: shareHttpServer);
|
||||||
|
@ -41,6 +109,21 @@ class ApplicationIsolateServer extends ApplicationServer {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends an application event to the supervising application.
|
||||||
|
///
|
||||||
|
/// This method overrides the base class's sendApplicationEvent method to
|
||||||
|
/// implement event sending in the context of an isolated server. It wraps
|
||||||
|
/// the event in a MessageHubMessage and sends it through the
|
||||||
|
/// supervisingApplicationPort.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - event: The application event to be sent. Can be of any type.
|
||||||
|
///
|
||||||
|
/// If an error occurs during the sending process, it is caught and added
|
||||||
|
/// to the hubSink as an error, along with the stack trace.
|
||||||
|
///
|
||||||
|
/// Note: This method does not throw exceptions directly; instead, it
|
||||||
|
/// reports errors through the hubSink.
|
||||||
@override
|
@override
|
||||||
void sendApplicationEvent(dynamic event) {
|
void sendApplicationEvent(dynamic event) {
|
||||||
try {
|
try {
|
||||||
|
@ -50,6 +133,22 @@ class ApplicationIsolateServer extends ApplicationServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Listener method for handling incoming messages from the supervising application.
|
||||||
|
///
|
||||||
|
/// This method processes two types of messages:
|
||||||
|
/// 1. A stop message (ApplicationIsolateSupervisor.messageKeyStop):
|
||||||
|
/// When received, it calls the stop() method to shut down the server.
|
||||||
|
/// 2. A MessageHubMessage:
|
||||||
|
/// When received, it adds the payload of the message to the hubSink.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - message: The incoming message. Can be either a stop command or a MessageHubMessage.
|
||||||
|
///
|
||||||
|
/// This method doesn't return any value but performs actions based on the message type:
|
||||||
|
/// - For a stop message, it initiates the server shutdown process.
|
||||||
|
/// - For a MessageHubMessage, it propagates the payload to the hubSink if it exists.
|
||||||
|
///
|
||||||
|
/// Note: This method assumes that hubSink is properly initialized elsewhere in the class.
|
||||||
void listener(dynamic message) {
|
void listener(dynamic message) {
|
||||||
if (message == ApplicationIsolateSupervisor.messageKeyStop) {
|
if (message == ApplicationIsolateSupervisor.messageKeyStop) {
|
||||||
stop();
|
stop();
|
||||||
|
@ -58,6 +157,21 @@ class ApplicationIsolateServer extends ApplicationServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stops the ApplicationIsolateServer and performs cleanup operations.
|
||||||
|
///
|
||||||
|
/// This method performs the following steps:
|
||||||
|
/// 1. Closes the supervisingReceivePort to stop receiving messages.
|
||||||
|
/// 2. Logs a fine-level message indicating the server is closing.
|
||||||
|
/// 3. Calls the close() method to shut down the server.
|
||||||
|
/// 4. Logs a fine-level message confirming the server has closed.
|
||||||
|
/// 5. Clears all listeners from the logger.
|
||||||
|
/// 6. Logs a fine-level message indicating it's sending a stop acknowledgement.
|
||||||
|
/// 7. Sends a stop acknowledgement message to the supervising application.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// A Future that completes when all stop operations are finished.
|
||||||
|
///
|
||||||
|
/// Note: This method is asynchronous and should be awaited when called.
|
||||||
Future stop() async {
|
Future stop() async {
|
||||||
supervisingReceivePort.close();
|
supervisingReceivePort.close();
|
||||||
logger.fine("ApplicationIsolateServer($identifier) closing server");
|
logger.fine("ApplicationIsolateServer($identifier) closing server");
|
||||||
|
@ -72,10 +186,40 @@ class ApplicationIsolateServer extends ApplicationServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A typedef defining the signature for an isolate entry function.
|
||||||
|
///
|
||||||
|
/// This function type is used to define the entry point for an isolate in the context
|
||||||
|
/// of an ApplicationIsolateServer. It takes a single parameter of type
|
||||||
|
/// ApplicationInitialServerMessage, which contains all the necessary information
|
||||||
|
/// to initialize and run the server within the isolate.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - message: An ApplicationInitialServerMessage object containing configuration
|
||||||
|
/// details, identifiers, and communication ports needed to set up the server
|
||||||
|
/// in the isolate.
|
||||||
|
///
|
||||||
|
/// This typedef is typically used when spawning new isolates for server instances,
|
||||||
|
/// allowing for a standardized way of passing initial setup information to the isolate.
|
||||||
typedef IsolateEntryFunction = void Function(
|
typedef IsolateEntryFunction = void Function(
|
||||||
ApplicationInitialServerMessage message,
|
ApplicationInitialServerMessage message,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Represents the initial message sent to an ApplicationIsolateServer when it's created.
|
||||||
|
///
|
||||||
|
/// This class encapsulates all the necessary information needed to initialize and
|
||||||
|
/// configure an ApplicationIsolateServer within an isolate. It includes details about
|
||||||
|
/// the stream type, configuration options, communication ports, and logging preferences.
|
||||||
|
///
|
||||||
|
/// Properties:
|
||||||
|
/// - streamTypeName: The name of the stream type to be used by the server.
|
||||||
|
/// - streamLibraryURI: The URI of the library containing the stream implementation.
|
||||||
|
/// - configuration: ApplicationOptions object containing server configuration details.
|
||||||
|
/// - parentMessagePort: SendPort for communicating with the parent (supervising) application.
|
||||||
|
/// - identifier: A unique identifier for the server instance.
|
||||||
|
/// - logToConsole: A boolean flag indicating whether to enable console logging (default is false).
|
||||||
|
///
|
||||||
|
/// This class is typically used when spawning a new isolate for an ApplicationIsolateServer,
|
||||||
|
/// providing all the necessary information in a single, structured message.
|
||||||
class ApplicationInitialServerMessage {
|
class ApplicationInitialServerMessage {
|
||||||
ApplicationInitialServerMessage(
|
ApplicationInitialServerMessage(
|
||||||
this.streamTypeName,
|
this.streamTypeName,
|
||||||
|
@ -94,6 +238,16 @@ class ApplicationInitialServerMessage {
|
||||||
bool logToConsole = false;
|
bool logToConsole = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a message that can be sent through the message hub.
|
||||||
|
///
|
||||||
|
/// This class encapsulates a payload of any type, allowing for flexible
|
||||||
|
/// communication between different parts of the application.
|
||||||
|
///
|
||||||
|
/// The payload can be of any type (dynamic), making this class versatile
|
||||||
|
/// for various types of messages.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - payload: The content of the message, which can be of any type.
|
||||||
class MessageHubMessage {
|
class MessageHubMessage {
|
||||||
MessageHubMessage(this.payload);
|
MessageHubMessage(this.payload);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
|
@ -6,9 +15,26 @@ import 'package:logging/logging.dart';
|
||||||
|
|
||||||
/// Represents the supervision of a [ApplicationIsolateServer].
|
/// Represents the supervision of a [ApplicationIsolateServer].
|
||||||
///
|
///
|
||||||
|
/// This class, ApplicationIsolateSupervisor, is responsible for supervising and managing
|
||||||
|
/// an [ApplicationIsolateServer]. It handles the lifecycle of the isolate, including
|
||||||
|
/// starting, stopping, and communicating with it. The supervisor also manages error
|
||||||
|
/// handling, message passing between isolates, and ensures proper startup and shutdown
|
||||||
|
/// of the supervised isolate.
|
||||||
|
///
|
||||||
/// You should not use this class directly.
|
/// You should not use this class directly.
|
||||||
class ApplicationIsolateSupervisor {
|
class ApplicationIsolateSupervisor {
|
||||||
/// Create an instance of [ApplicationIsolateSupervisor].
|
/// Creates an instance of [ApplicationIsolateSupervisor].
|
||||||
|
///
|
||||||
|
/// This constructor initializes a new [ApplicationIsolateSupervisor] with the provided parameters.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [supervisingApplication]: The [Application] instance that owns this supervisor.
|
||||||
|
/// - [isolate]: The [Isolate] being supervised.
|
||||||
|
/// - [receivePort]: The [ReceivePort] for receiving messages from the supervised isolate.
|
||||||
|
/// - [identifier]: A numeric identifier for the isolate relative to the [Application].
|
||||||
|
/// - [logger]: The [Logger] instance used for logging.
|
||||||
|
/// - [startupTimeout]: Optional. The maximum duration to wait for the isolate to start up.
|
||||||
|
/// Defaults to 30 seconds.
|
||||||
ApplicationIsolateSupervisor(
|
ApplicationIsolateSupervisor(
|
||||||
this.supervisingApplication,
|
this.supervisingApplication,
|
||||||
this.isolate,
|
this.isolate,
|
||||||
|
@ -18,34 +44,168 @@ class ApplicationIsolateSupervisor {
|
||||||
this.startupTimeout = const Duration(seconds: 30),
|
this.startupTimeout = const Duration(seconds: 30),
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The [Isolate] being supervised.
|
/// The [Isolate] being supervised by this [ApplicationIsolateSupervisor].
|
||||||
|
///
|
||||||
|
/// This isolate represents a separate thread of execution where the
|
||||||
|
/// [ApplicationIsolateServer] runs. The supervisor manages the lifecycle
|
||||||
|
/// and communication with this isolate.
|
||||||
final Isolate isolate;
|
final Isolate isolate;
|
||||||
|
|
||||||
/// The [ReceivePort] for which messages coming from [isolate] will be received.
|
/// The [ReceivePort] for receiving messages from the supervised isolate.
|
||||||
|
///
|
||||||
|
/// This [ReceivePort] is used to establish a communication channel between
|
||||||
|
/// the supervisor and the supervised isolate. It allows the supervisor to
|
||||||
|
/// receive various types of messages, including startup notifications,
|
||||||
|
/// error reports, and custom messages from the isolate.
|
||||||
|
///
|
||||||
|
/// The [receivePort] is crucial for managing the lifecycle of the isolate
|
||||||
|
/// and handling inter-isolate communication. It is used in conjunction with
|
||||||
|
/// the [listener] method to process incoming messages from the isolate.
|
||||||
final ReceivePort receivePort;
|
final ReceivePort receivePort;
|
||||||
|
|
||||||
/// A numeric identifier for the isolate relative to the [Application].
|
/// A numeric identifier for the isolate relative to the [Application].
|
||||||
|
///
|
||||||
|
/// This identifier is unique within the context of the parent [Application].
|
||||||
|
/// It is used to distinguish between different isolates managed by the same
|
||||||
|
/// application, facilitating logging, debugging, and isolate management.
|
||||||
|
/// The identifier is typically assigned sequentially when creating new isolates.
|
||||||
final int identifier;
|
final int identifier;
|
||||||
|
|
||||||
|
/// The maximum duration to wait for the isolate to start up.
|
||||||
|
///
|
||||||
|
/// This duration specifies the time limit for the supervised isolate to complete its
|
||||||
|
/// startup process. If the isolate fails to start within this timeout period, an
|
||||||
|
/// exception will be thrown. This helps prevent indefinite waiting in case of startup
|
||||||
|
/// issues.
|
||||||
|
///
|
||||||
|
/// The default value is typically set in the constructor, often to 30 seconds.
|
||||||
final Duration startupTimeout;
|
final Duration startupTimeout;
|
||||||
|
|
||||||
/// A reference to the owning [Application]
|
/// A reference to the owning [Application].
|
||||||
|
///
|
||||||
|
/// This property holds a reference to the [Application] instance that owns and manages
|
||||||
|
/// this [ApplicationIsolateSupervisor]. It allows the supervisor to interact with
|
||||||
|
/// the main application, access shared resources, and coordinate activities across
|
||||||
|
/// multiple isolates.
|
||||||
|
///
|
||||||
|
/// The supervising application is responsible for creating and managing the lifecycle
|
||||||
|
/// of this supervisor and its associated isolate. It also provides context for
|
||||||
|
/// operations such as logging, configuration, and inter-isolate communication.
|
||||||
Application supervisingApplication;
|
Application supervisingApplication;
|
||||||
|
|
||||||
/// A reference to the [Logger] used by the [supervisingApplication].
|
/// The logger instance used for recording events and errors.
|
||||||
|
///
|
||||||
|
/// This [Logger] is typically shared with the [supervisingApplication] and is used
|
||||||
|
/// to log various events, warnings, and errors related to the isolate supervision
|
||||||
|
/// process. It helps in debugging and monitoring the behavior of the supervised
|
||||||
|
/// isolate and the supervisor itself.
|
||||||
|
///
|
||||||
|
/// The logger can be used to record information at different severity levels,
|
||||||
|
/// such as fine, info, warning, and severe, depending on the nature of the event
|
||||||
|
/// being logged.
|
||||||
Logger logger;
|
Logger logger;
|
||||||
|
|
||||||
|
/// A list to store pending [MessageHubMessage] objects.
|
||||||
|
///
|
||||||
|
/// This queue is used to temporarily hold messages that are received when the
|
||||||
|
/// supervising application is not running. Once the application starts running,
|
||||||
|
/// these messages are processed and sent to other supervisors.
|
||||||
|
///
|
||||||
|
/// The queue helps ensure that no messages are lost during the startup phase
|
||||||
|
/// of the application, maintaining message integrity across isolates.
|
||||||
final List<MessageHubMessage> _pendingMessageQueue = [];
|
final List<MessageHubMessage> _pendingMessageQueue = [];
|
||||||
|
|
||||||
|
/// Indicates whether the isolate is currently in the process of launching.
|
||||||
|
///
|
||||||
|
/// This getter returns `true` if the isolate is still in the startup phase,
|
||||||
|
/// and `false` if the launch process has completed.
|
||||||
|
///
|
||||||
|
/// It checks the state of the [_launchCompleter] to determine if the
|
||||||
|
/// launch process is still ongoing. If the completer is not yet completed,
|
||||||
|
/// it means the isolate is still launching.
|
||||||
|
///
|
||||||
|
/// This property is useful for handling different behaviors or error states
|
||||||
|
/// depending on whether the isolate is in its launch phase or has already
|
||||||
|
/// started running normally.
|
||||||
bool get _isLaunching => !_launchCompleter.isCompleted;
|
bool get _isLaunching => !_launchCompleter.isCompleted;
|
||||||
|
|
||||||
|
/// The [SendPort] used to send messages to the supervised isolate.
|
||||||
|
///
|
||||||
|
/// This [SendPort] is initialized when the supervisor receives the corresponding
|
||||||
|
/// [SendPort] from the supervised isolate during the startup process. It enables
|
||||||
|
/// bi-directional communication between the supervisor and the supervised isolate.
|
||||||
|
///
|
||||||
|
/// The [_serverSendPort] is used to send various messages to the isolate, including
|
||||||
|
/// stop signals, custom application messages, and other control commands. It plays
|
||||||
|
/// a crucial role in managing the lifecycle and behavior of the supervised isolate.
|
||||||
late SendPort _serverSendPort;
|
late SendPort _serverSendPort;
|
||||||
|
|
||||||
|
/// A [Completer] used to manage the launch process of the supervised isolate.
|
||||||
|
///
|
||||||
|
/// This completer is initialized when the isolate is being launched and is completed
|
||||||
|
/// when the isolate has successfully started up. It's used in conjunction with
|
||||||
|
/// [resume] method to handle the asynchronous nature of isolate startup.
|
||||||
|
///
|
||||||
|
/// The completer allows other parts of the supervisor to wait for the isolate
|
||||||
|
/// to finish launching before proceeding with further operations. It's also used
|
||||||
|
/// to implement timeout functionality in case the isolate fails to start within
|
||||||
|
/// the specified [startupTimeout].
|
||||||
late Completer _launchCompleter;
|
late Completer _launchCompleter;
|
||||||
|
|
||||||
|
/// A [Completer] used to manage the stop process of the supervised isolate.
|
||||||
|
///
|
||||||
|
/// This nullable [Completer] is initialized when the [stop] method is called
|
||||||
|
/// and is used to handle the asynchronous nature of stopping the isolate.
|
||||||
|
/// It allows the supervisor to wait for the isolate to acknowledge the stop
|
||||||
|
/// message before proceeding with the termination process.
|
||||||
|
///
|
||||||
|
/// The completer is set to null after the stop process is complete, indicating
|
||||||
|
/// that the isolate has been successfully stopped. This helps manage the state
|
||||||
|
/// of the stop operation and prevents multiple stop attempts from interfering
|
||||||
|
/// with each other.
|
||||||
Completer? _stopCompleter;
|
Completer? _stopCompleter;
|
||||||
|
|
||||||
|
/// A constant string used as a message key to signal the supervised isolate to stop.
|
||||||
|
///
|
||||||
|
/// This constant is used in the communication protocol between the supervisor
|
||||||
|
/// and the supervised isolate. When sent to the isolate, it indicates that
|
||||||
|
/// the isolate should begin its shutdown process.
|
||||||
|
///
|
||||||
|
/// The underscore prefix in the value suggests that this is intended for
|
||||||
|
/// internal use within the isolate communication system.
|
||||||
static const String messageKeyStop = "_MessageStop";
|
static const String messageKeyStop = "_MessageStop";
|
||||||
|
|
||||||
|
/// A constant string used as a message key to indicate that the supervised isolate is listening.
|
||||||
|
///
|
||||||
|
/// This constant is part of the communication protocol between the supervisor
|
||||||
|
/// and the supervised isolate. When the isolate sends this message to the
|
||||||
|
/// supervisor, it signals that the isolate has completed its startup process
|
||||||
|
/// and is ready to receive and process messages.
|
||||||
|
///
|
||||||
|
/// The underscore prefix in the value suggests that this is intended for
|
||||||
|
/// internal use within the isolate communication system.
|
||||||
static const String messageKeyListening = "_MessageListening";
|
static const String messageKeyListening = "_MessageListening";
|
||||||
|
|
||||||
/// Resumes the [Isolate] being supervised.
|
/// Resumes the [Isolate] being supervised.
|
||||||
|
///
|
||||||
|
/// This method initiates the process of resuming the supervised isolate and
|
||||||
|
/// sets up the necessary listeners and error handlers. It performs the following steps:
|
||||||
|
///
|
||||||
|
/// 1. Initializes a new [Completer] for managing the launch process.
|
||||||
|
/// 2. Sets up a listener for the [receivePort] to handle incoming messages.
|
||||||
|
/// 3. Configures the isolate to handle errors non-fatally.
|
||||||
|
/// 4. Adds an error listener to the isolate.
|
||||||
|
/// 5. Resumes the isolate from its paused state.
|
||||||
|
/// 6. Waits for the isolate to complete its startup process.
|
||||||
|
///
|
||||||
|
/// If the isolate fails to start within the specified [startupTimeout], a
|
||||||
|
/// [TimeoutException] is thrown with a detailed error message.
|
||||||
|
///
|
||||||
|
/// Returns a [Future] that completes when the isolate has successfully started,
|
||||||
|
/// or throws an exception if the startup process fails or times out.
|
||||||
|
///
|
||||||
|
/// Throws:
|
||||||
|
/// - [TimeoutException] if the isolate doesn't start within the [startupTimeout].
|
||||||
Future resume() {
|
Future resume() {
|
||||||
_launchCompleter = Completer();
|
_launchCompleter = Completer();
|
||||||
receivePort.listen(listener);
|
receivePort.listen(listener);
|
||||||
|
@ -71,6 +231,24 @@ class ApplicationIsolateSupervisor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops the [Isolate] being supervised.
|
/// Stops the [Isolate] being supervised.
|
||||||
|
///
|
||||||
|
/// This method initiates the process of stopping the supervised isolate. It performs the following steps:
|
||||||
|
///
|
||||||
|
/// 1. Creates a new [Completer] to manage the stop process.
|
||||||
|
/// 2. Sends a stop message to the supervised isolate using [_serverSendPort].
|
||||||
|
/// 3. Waits for the isolate to acknowledge the stop message.
|
||||||
|
/// 4. If the isolate doesn't respond within 5 seconds, logs a severe message.
|
||||||
|
/// 5. Forcefully kills the isolate using [isolate.kill()].
|
||||||
|
/// 6. Closes the [receivePort] to clean up resources.
|
||||||
|
///
|
||||||
|
/// The method uses a timeout of 5 seconds to wait for the isolate's acknowledgment.
|
||||||
|
/// If the timeout occurs, it assumes the isolate is not responding and proceeds to terminate it.
|
||||||
|
///
|
||||||
|
/// This method ensures that the isolate is stopped one way or another, either gracefully
|
||||||
|
/// or by force, and properly cleans up associated resources.
|
||||||
|
///
|
||||||
|
/// Returns a [Future] that completes when the stop process is finished, regardless of
|
||||||
|
/// whether the isolate responded to the stop message or was forcefully terminated.
|
||||||
Future stop() async {
|
Future stop() async {
|
||||||
_stopCompleter = Completer();
|
_stopCompleter = Completer();
|
||||||
logger.fine(
|
logger.fine(
|
||||||
|
@ -91,6 +269,22 @@ class ApplicationIsolateSupervisor {
|
||||||
receivePort.close();
|
receivePort.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles incoming messages from the supervised isolate.
|
||||||
|
///
|
||||||
|
/// This method is the central message processing function for the supervisor.
|
||||||
|
/// It handles various types of messages:
|
||||||
|
///
|
||||||
|
/// - [SendPort]: Stores the send port for communicating with the isolate.
|
||||||
|
/// - [messageKeyListening]: Indicates the isolate has started and is listening.
|
||||||
|
/// - [messageKeyStop]: Acknowledges that the isolate has received a stop message.
|
||||||
|
/// - [List]: Represents an error from the isolate, which is then handled.
|
||||||
|
/// - [MessageHubMessage]: Inter-isolate communication message.
|
||||||
|
///
|
||||||
|
/// For [MessageHubMessage], if the supervising application is not running,
|
||||||
|
/// the message is queued. Otherwise, it's immediately sent to other supervisors.
|
||||||
|
///
|
||||||
|
/// This method is crucial for managing the lifecycle and communication of the
|
||||||
|
/// supervised isolate, handling startup, shutdown, errors, and inter-isolate messaging.
|
||||||
void listener(dynamic message) {
|
void listener(dynamic message) {
|
||||||
if (message is SendPort) {
|
if (message is SendPort) {
|
||||||
_serverSendPort = message;
|
_serverSendPort = message;
|
||||||
|
@ -122,12 +316,38 @@ class ApplicationIsolateSupervisor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends all pending messages stored in the [_pendingMessageQueue] to other supervisors.
|
||||||
|
///
|
||||||
|
/// This method is typically called when the supervising application starts running
|
||||||
|
/// to process any messages that were received while the application was not active.
|
||||||
|
/// It performs the following steps:
|
||||||
|
///
|
||||||
|
/// 1. Creates a copy of the [_pendingMessageQueue] to safely iterate over it.
|
||||||
|
/// 2. Clears the original [_pendingMessageQueue].
|
||||||
|
/// 3. Sends each message in the copied list to other supervisors using [_sendMessageToOtherSupervisors].
|
||||||
|
///
|
||||||
|
/// This ensures that no messages are lost during the startup phase of the application
|
||||||
|
/// and maintains message integrity across isolates.
|
||||||
void sendPendingMessages() {
|
void sendPendingMessages() {
|
||||||
final list = List<MessageHubMessage>.from(_pendingMessageQueue);
|
final list = List<MessageHubMessage>.from(_pendingMessageQueue);
|
||||||
_pendingMessageQueue.clear();
|
_pendingMessageQueue.clear();
|
||||||
list.forEach(_sendMessageToOtherSupervisors);
|
list.forEach(_sendMessageToOtherSupervisors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends a [MessageHubMessage] to all other supervisors managed by the supervising application.
|
||||||
|
///
|
||||||
|
/// This method is responsible for propagating messages across different isolates
|
||||||
|
/// managed by the same application. It performs the following actions:
|
||||||
|
///
|
||||||
|
/// 1. Iterates through all supervisors in the supervising application.
|
||||||
|
/// 2. Excludes the current supervisor from the recipients.
|
||||||
|
/// 3. Sends the provided [message] to each of the other supervisors' isolates.
|
||||||
|
///
|
||||||
|
/// This method is crucial for maintaining communication and synchronization
|
||||||
|
/// between different isolates within the application.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [message]: The [MessageHubMessage] to be sent to other supervisors.
|
||||||
void _sendMessageToOtherSupervisors(MessageHubMessage message) {
|
void _sendMessageToOtherSupervisors(MessageHubMessage message) {
|
||||||
supervisingApplication.supervisors
|
supervisingApplication.supervisors
|
||||||
.where((sup) => sup != this)
|
.where((sup) => sup != this)
|
||||||
|
@ -136,6 +356,25 @@ class ApplicationIsolateSupervisor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles exceptions thrown by the supervised isolate.
|
||||||
|
///
|
||||||
|
/// This method is responsible for processing and responding to exceptions
|
||||||
|
/// that occur within the supervised isolate. It behaves differently depending
|
||||||
|
/// on whether the isolate is still in the process of launching or not:
|
||||||
|
///
|
||||||
|
/// - If the isolate is launching ([_isLaunching] is true):
|
||||||
|
/// It wraps the error in an [ApplicationStartupException] and completes
|
||||||
|
/// the [_launchCompleter] with this error, effectively failing the launch process.
|
||||||
|
///
|
||||||
|
/// - If the isolate has already launched:
|
||||||
|
/// It logs the error as a severe uncaught exception using the supervisor's logger.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - [error]: The error or exception object thrown by the isolate.
|
||||||
|
/// - [stacktrace]: The [StackTrace] associated with the error.
|
||||||
|
///
|
||||||
|
/// This method is crucial for maintaining the stability and error handling
|
||||||
|
/// of the isolate, especially during its startup phase.
|
||||||
void _handleIsolateException(dynamic error, StackTrace stacktrace) {
|
void _handleIsolateException(dynamic error, StackTrace stacktrace) {
|
||||||
if (_isLaunching) {
|
if (_isLaunching) {
|
||||||
final appException = ApplicationStartupException(error);
|
final appException = ApplicationStartupException(error);
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:args/args.dart';
|
import 'package:args/args.dart';
|
||||||
|
@ -5,45 +14,73 @@ import 'package:protevus_application/application.dart';
|
||||||
|
|
||||||
/// An object that contains configuration values for an [Application].
|
/// An object that contains configuration values for an [Application].
|
||||||
///
|
///
|
||||||
/// You use this object in an [ApplicationChannel] to manage external configuration data for your application.
|
/// This class provides a set of options that can be used to configure an application,
|
||||||
|
/// including network settings, SSL configuration, and custom context values.
|
||||||
|
/// It also includes a static [ArgParser] for parsing command-line arguments.
|
||||||
|
///
|
||||||
|
/// Key features:
|
||||||
|
/// - Configurable address and port for HTTP requests
|
||||||
|
/// - IPv6 support
|
||||||
|
/// - SSL/HTTPS configuration options
|
||||||
|
/// - Client-side certificate usage flag
|
||||||
|
/// - Custom context for application-specific configuration
|
||||||
|
/// - Command-line argument parsing for easy configuration
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// This class is typically used in conjunction with [ApplicationChannel] to set up
|
||||||
|
/// and configure an application based on external inputs or command-line arguments.
|
||||||
class ApplicationOptions {
|
class ApplicationOptions {
|
||||||
/// The absolute path of the configuration file for this application.
|
/// The absolute path of the configuration file for this application.
|
||||||
///
|
///
|
||||||
/// This path is provided when an application is started by the `--config-path` option to `conduit serve`.
|
/// This property stores the file path of the configuration file used by the application.
|
||||||
/// You may load the file at this path in [ApplicationChannel] to use configuration values.
|
/// The path is typically set when the application is started using the `--config-path` option
|
||||||
|
/// with the `conduit serve` command.
|
||||||
|
///
|
||||||
|
/// The configuration file can contain application-specific settings and can be loaded
|
||||||
|
/// in the [ApplicationChannel] to access these configuration values.
|
||||||
|
///
|
||||||
|
/// This property may be null if no configuration file path was specified when starting the application.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// - Access this property to get the path of the configuration file.
|
||||||
|
/// - Use the path to load and parse the configuration file in your application logic.
|
||||||
|
/// - Ensure to handle cases where this property might be null.
|
||||||
String? configurationFilePath;
|
String? configurationFilePath;
|
||||||
|
|
||||||
/// The address to listen for HTTP requests on.
|
/// The address to listen for HTTP requests on.
|
||||||
///
|
///
|
||||||
/// By default, this address will default to 'any' address (0.0.0.0). If [isIpv6Only] is true,
|
/// This property specifies the network address on which the application will listen for incoming HTTP requests.
|
||||||
/// 'any' will be any IPv6 address, otherwise, it will be any IPv4 or IPv6 address.
|
|
||||||
///
|
///
|
||||||
/// This value may be an [InternetAddress] or a [String].
|
/// This value may be an [InternetAddress] or a [String].
|
||||||
dynamic address;
|
dynamic address;
|
||||||
|
|
||||||
/// The port to listen for HTTP requests on.
|
/// The port number on which the application will listen for HTTP requests.
|
||||||
///
|
///
|
||||||
/// Defaults to 8888.
|
/// Defaults to 8888.
|
||||||
int port = 8888;
|
int port = 8888;
|
||||||
|
|
||||||
/// Whether or not the application should only receive connections over IPv6.
|
/// Whether or not the application should only receive connections over IPv6.
|
||||||
///
|
///
|
||||||
|
/// This flag determines if the application should exclusively use IPv6 for incoming connections.
|
||||||
|
/// When set to true, the application will only accept IPv6 connections and reject IPv4 connections.
|
||||||
|
/// This setting can be useful in environments that require IPv6-only communication.
|
||||||
|
///
|
||||||
/// Defaults to false. This flag impacts the default value of the [address] property.
|
/// Defaults to false. This flag impacts the default value of the [address] property.
|
||||||
bool isIpv6Only = false;
|
bool isIpv6Only = false;
|
||||||
|
|
||||||
/// Whether or not the application's request controllers should use client-side HTTPS certificates.
|
/// Indicates whether the application's request controllers should use client-side HTTPS certificates.
|
||||||
///
|
///
|
||||||
/// Defaults to false.
|
/// Defaults to false.
|
||||||
bool isUsingClientCertificate = false;
|
bool isUsingClientCertificate = false;
|
||||||
|
|
||||||
/// The path to a SSL certificate.
|
/// The path to a SSL certificate file.
|
||||||
///
|
///
|
||||||
/// If specified - along with [privateKeyFilePath] - an [Application] will only allow secure connections over HTTPS.
|
/// If specified - along with [privateKeyFilePath] - an [Application] will only allow secure connections over HTTPS.
|
||||||
/// This value is often set through the `--ssl-certificate-path` command line option of `conduit serve`. For finer control
|
/// This value is often set through the `--ssl-certificate-path` command line option of `conduit serve`. For finer control
|
||||||
/// over how HTTPS is configured for an application, see [ApplicationChannel.securityContext].
|
/// over how HTTPS is configured for an application, see [ApplicationChannel.securityContext].
|
||||||
String? certificateFilePath;
|
String? certificateFilePath;
|
||||||
|
|
||||||
/// The path to a private key.
|
/// The path to a private key file for SSL/TLS encryption.
|
||||||
///
|
///
|
||||||
/// If specified - along with [certificateFilePath] - an [Application] will only allow secure connections over HTTPS.
|
/// If specified - along with [certificateFilePath] - an [Application] will only allow secure connections over HTTPS.
|
||||||
/// This value is often set through the `--ssl-key-path` command line option of `conduit serve`. For finer control
|
/// This value is often set through the `--ssl-key-path` command line option of `conduit serve`. For finer control
|
||||||
|
@ -54,8 +91,40 @@ class ApplicationOptions {
|
||||||
///
|
///
|
||||||
/// This is a user-specific set of configuration options provided by [ApplicationChannel.initializeApplication].
|
/// This is a user-specific set of configuration options provided by [ApplicationChannel.initializeApplication].
|
||||||
/// Each instance of [ApplicationChannel] has access to these values if set.
|
/// Each instance of [ApplicationChannel] has access to these values if set.
|
||||||
|
///
|
||||||
|
/// This map allows for storing and retrieving custom configuration values that can be used
|
||||||
|
/// throughout the application. It provides a flexible way to pass application-specific
|
||||||
|
/// settings to different parts of the system.
|
||||||
|
///
|
||||||
|
/// The context can be populated during the application's initialization phase and
|
||||||
|
/// can contain any type of data that adheres to the dynamic type.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// - Add configuration values: `context['databaseUrl'] = 'postgres://...';`
|
||||||
|
/// - Retrieve values: `final dbUrl = options.context['databaseUrl'];`
|
||||||
|
///
|
||||||
|
/// Note: It's important to ensure type safety when retrieving values from this map,
|
||||||
|
/// as it uses dynamic typing.
|
||||||
final Map<String, dynamic> context = {};
|
final Map<String, dynamic> context = {};
|
||||||
|
|
||||||
|
/// A static [ArgParser] for parsing command-line arguments for the application.
|
||||||
|
///
|
||||||
|
/// This parser defines several options and flags that can be used to configure
|
||||||
|
/// the application when it is launched from the command line. The available
|
||||||
|
/// options include:
|
||||||
|
///
|
||||||
|
/// - address: The address to listen on for HTTP requests.
|
||||||
|
/// - config-path: The path to a configuration file.
|
||||||
|
/// - isolates: Number of isolates for handling requests.
|
||||||
|
/// - port: The port number to listen for HTTP requests on.
|
||||||
|
/// - ipv6-only: Flag to limit listening to IPv6 connections only.
|
||||||
|
/// - ssl-certificate-path: The path to an SSL certificate file.
|
||||||
|
/// - ssl-key-path: The path to an SSL private key file.
|
||||||
|
/// - timeout: Number of seconds to wait to ensure startup succeeded.
|
||||||
|
/// - help: Flag to display help information.
|
||||||
|
///
|
||||||
|
/// Each option is configured with a description, and some include default values
|
||||||
|
/// or abbreviations for easier command-line usage.
|
||||||
static final parser = ArgParser()
|
static final parser = ArgParser()
|
||||||
..addOption(
|
..addOption(
|
||||||
"address",
|
"address",
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Protevus Platform.
|
||||||
|
*
|
||||||
|
* (C) Protevus <developers@protevus.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
|
@ -7,6 +16,21 @@ import 'package:protevus_application/application.dart';
|
||||||
Warning: do not remove. This method is invoked by a generated script.
|
Warning: do not remove. This method is invoked by a generated script.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/// Starts the application either on the current isolate or across multiple isolates.
|
||||||
|
///
|
||||||
|
/// This function initializes and starts the application, setting up communication
|
||||||
|
/// between isolates using ports. It responds to stop commands and reports the
|
||||||
|
/// application's status back to the parent isolate.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - app: The Application instance to be started.
|
||||||
|
/// - isolateCount: The number of isolates to start the application on. If 0, starts on the current isolate.
|
||||||
|
/// - parentPort: The SendPort of the parent isolate for communication.
|
||||||
|
///
|
||||||
|
/// The function sets up a ReceivePort to listen for commands, particularly the "stop" command.
|
||||||
|
/// It then starts the application either on the current isolate or across multiple isolates
|
||||||
|
/// based on the isolateCount parameter. Finally, it sends a status message back to the parent isolate.
|
||||||
Future startApplication<T extends ApplicationChannel>(
|
Future startApplication<T extends ApplicationChannel>(
|
||||||
Application<T> app,
|
Application<T> app,
|
||||||
int isolateCount,
|
int isolateCount,
|
||||||
|
|
Loading…
Reference in a new issue