001/* 002 * Copyright 2022-2026 Revetware LLC. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.soklet; 018 019import org.jspecify.annotations.NonNull; 020import org.jspecify.annotations.Nullable; 021 022import java.io.PrintWriter; 023import java.io.StringWriter; 024import java.net.InetSocketAddress; 025import java.time.Duration; 026import java.util.List; 027 028/** 029 * Read-only hook methods for observing system and request lifecycle events. 030 * <p> 031 * Note: some of these methods are "fail-fast" - exceptions thrown will bubble out and stop execution - and for others, 032 * Soklet will catch exceptions and surface separately via {@link #didReceiveLogEvent(LogEvent)}. 033 * <p> 034 * A standard threadsafe implementation can be acquired via the {@link #defaultInstance()} factory method. 035 * <p> 036 * Full documentation is available at <a href="https://www.soklet.com/docs/request-lifecycle">https://www.soklet.com/docs/request-lifecycle</a>. 037 * 038 * @author <a href="https://www.revetkn.com">Mark Allen</a> 039 */ 040public interface LifecycleObserver { 041 /** 042 * Called before a {@link Soklet} instance starts. 043 */ 044 default void willStartSoklet(@NonNull Soklet soklet) { 045 // No-op by default 046 } 047 048 /** 049 * Called after a {@link Soklet} instance starts. 050 */ 051 default void didStartSoklet(@NonNull Soklet soklet) { 052 // No-op by default 053 } 054 055 /** 056 * Called after a {@link Soklet} instance was asked to start, but failed due to an exception. 057 */ 058 default void didFailToStartSoklet(@NonNull Soklet soklet, 059 @NonNull Throwable throwable) { 060 // No-op by default 061 } 062 063 /** 064 * Called before a {@link Soklet} instance stops. 065 */ 066 default void willStopSoklet(@NonNull Soklet soklet) { 067 // No-op by default 068 } 069 070 /** 071 * Called after a {@link Soklet} instance stops. 072 */ 073 default void didStopSoklet(@NonNull Soklet soklet) { 074 // No-op by default 075 } 076 077 /** 078 * Called after a {@link Soklet} instance was asked to stop, but failed due to an exception. 079 */ 080 default void didFailToStopSoklet(@NonNull Soklet soklet, 081 @NonNull Throwable throwable) { 082 // No-op by default 083 } 084 085 /** 086 * Called before the server starts. 087 */ 088 default void willStartServer(@NonNull Server server) { 089 // No-op by default 090 } 091 092 /** 093 * Called after the server starts. 094 */ 095 default void didStartServer(@NonNull Server server) { 096 // No-op by default 097 } 098 099 /** 100 * Called after a {@link Server} instance was asked to start, but failed due to an exception. 101 */ 102 default void didFailToStartServer(@NonNull Server server, 103 @NonNull Throwable throwable) { 104 // No-op by default 105 } 106 107 /** 108 * Called before the server stops. 109 */ 110 default void willStopServer(@NonNull Server server) { 111 // No-op by default 112 } 113 114 /** 115 * Called after the server stops. 116 */ 117 default void didStopServer(@NonNull Server server) { 118 // No-op by default 119 } 120 121 /** 122 * Called after a {@link Server} instance was asked to stop, but failed due to an exception. 123 */ 124 default void didFailToStopServer(@NonNull Server server, 125 @NonNull Throwable throwable) { 126 // No-op by default 127 } 128 129 /** 130 * Called when a server is about to accept a new TCP connection. 131 * 132 * @param serverType the server type that is accepting the connection 133 * @param remoteAddress the best-effort remote address, or {@code null} if unavailable 134 */ 135 default void willAcceptConnection(@NonNull ServerType serverType, 136 @Nullable InetSocketAddress remoteAddress) { 137 // No-op by default 138 } 139 140 /** 141 * Called after a server accepts a new TCP connection. 142 * 143 * @param serverType the server type that accepted the connection 144 * @param remoteAddress the best-effort remote address, or {@code null} if unavailable 145 */ 146 default void didAcceptConnection(@NonNull ServerType serverType, 147 @Nullable InetSocketAddress remoteAddress) { 148 // No-op by default 149 } 150 151 /** 152 * Called after a server fails to accept a new TCP connection. 153 * 154 * @param serverType the server type that failed to accept the connection 155 * @param remoteAddress the best-effort remote address, or {@code null} if unavailable 156 * @param reason the failure reason 157 * @param throwable an optional underlying cause, or {@code null} if not applicable 158 */ 159 default void didFailToAcceptConnection(@NonNull ServerType serverType, 160 @Nullable InetSocketAddress remoteAddress, 161 @NonNull ConnectionRejectionReason reason, 162 @Nullable Throwable throwable) { 163 // No-op by default 164 } 165 166 /** 167 * Called when a request is about to be accepted for application-level handling. 168 * 169 * @param serverType the server type that received the request 170 * @param remoteAddress the best-effort remote address, or {@code null} if unavailable 171 * @param requestTarget the raw request target (path + query) if known, or {@code null} if unavailable 172 */ 173 default void willAcceptRequest(@NonNull ServerType serverType, 174 @Nullable InetSocketAddress remoteAddress, 175 @Nullable String requestTarget) { 176 // No-op by default 177 } 178 179 /** 180 * Called after a request is accepted for application-level handling. 181 * 182 * @param serverType the server type that received the request 183 * @param remoteAddress the best-effort remote address, or {@code null} if unavailable 184 * @param requestTarget the raw request target (path + query) if known, or {@code null} if unavailable 185 */ 186 default void didAcceptRequest(@NonNull ServerType serverType, 187 @Nullable InetSocketAddress remoteAddress, 188 @Nullable String requestTarget) { 189 // No-op by default 190 } 191 192 /** 193 * Called when a request fails to be accepted before application-level handling begins. 194 * 195 * @param serverType the server type that received the request 196 * @param remoteAddress the best-effort remote address, or {@code null} if unavailable 197 * @param requestTarget the raw request target (path + query) if known, or {@code null} if unavailable 198 * @param reason the rejection reason 199 * @param throwable an optional underlying cause, or {@code null} if not applicable 200 */ 201 default void didFailToAcceptRequest(@NonNull ServerType serverType, 202 @Nullable InetSocketAddress remoteAddress, 203 @Nullable String requestTarget, 204 @NonNull RequestRejectionReason reason, 205 @Nullable Throwable throwable) { 206 // No-op by default 207 } 208 209 /** 210 * Called when Soklet is about to read or parse a request into a valid {@link Request}. 211 * 212 * @param serverType the server type that received the request 213 * @param remoteAddress the best-effort remote address, or {@code null} if unavailable 214 * @param requestTarget the raw request target (path + query) if known, or {@code null} if unavailable 215 */ 216 default void willReadRequest(@NonNull ServerType serverType, 217 @Nullable InetSocketAddress remoteAddress, 218 @Nullable String requestTarget) { 219 // No-op by default 220 } 221 222 /** 223 * Called when a request was successfully read or parsed into a valid {@link Request}. 224 * 225 * @param serverType the server type that received the request 226 * @param remoteAddress the best-effort remote address, or {@code null} if unavailable 227 * @param requestTarget the raw request target (path + query) if known, or {@code null} if unavailable 228 */ 229 default void didReadRequest(@NonNull ServerType serverType, 230 @Nullable InetSocketAddress remoteAddress, 231 @Nullable String requestTarget) { 232 // No-op by default 233 } 234 235 /** 236 * Called when a request could not be read or parsed into a valid {@link Request}. 237 * 238 * @param serverType the server type that received the request 239 * @param remoteAddress the best-effort remote address, or {@code null} if unavailable 240 * @param requestTarget the raw request target (path + query) if known, or {@code null} if unavailable 241 * @param reason the failure reason 242 * @param throwable an optional underlying cause, or {@code null} if not applicable 243 */ 244 default void didFailToReadRequest(@NonNull ServerType serverType, 245 @Nullable InetSocketAddress remoteAddress, 246 @Nullable String requestTarget, 247 @NonNull RequestReadFailureReason reason, 248 @Nullable Throwable throwable) { 249 // No-op by default 250 } 251 252 /** 253 * Called as soon as a request is received and a <em>Resource Method</em> has been resolved to handle it. 254 * 255 * @param serverType the server type that received the request 256 */ 257 default void didStartRequestHandling(@NonNull ServerType serverType, 258 @NonNull Request request, 259 @Nullable ResourceMethod resourceMethod) { 260 // No-op by default 261 } 262 263 /** 264 * Called after a request finishes processing. 265 */ 266 default void didFinishRequestHandling(@NonNull ServerType serverType, 267 @NonNull Request request, 268 @Nullable ResourceMethod resourceMethod, 269 @NonNull MarshaledResponse marshaledResponse, 270 @NonNull Duration duration, 271 @NonNull List<@NonNull Throwable> throwables) { 272 // No-op by default 273 } 274 275 /** 276 * Called before response data is written. 277 */ 278 default void willWriteResponse(@NonNull ServerType serverType, 279 @NonNull Request request, 280 @Nullable ResourceMethod resourceMethod, 281 @NonNull MarshaledResponse marshaledResponse) { 282 // No-op by default 283 } 284 285 /** 286 * Called after response data is written. 287 */ 288 default void didWriteResponse(@NonNull ServerType serverType, 289 @NonNull Request request, 290 @Nullable ResourceMethod resourceMethod, 291 @NonNull MarshaledResponse marshaledResponse, 292 @NonNull Duration responseWriteDuration) { 293 // No-op by default 294 } 295 296 /** 297 * Called after response data fails to write. 298 */ 299 default void didFailToWriteResponse(@NonNull ServerType serverType, 300 @NonNull Request request, 301 @Nullable ResourceMethod resourceMethod, 302 @NonNull MarshaledResponse marshaledResponse, 303 @NonNull Duration responseWriteDuration, 304 @NonNull Throwable throwable) { 305 // No-op by default 306 } 307 308 /** 309 * Called before the SSE server starts. 310 */ 311 default void willStartServerSentEventServer(@NonNull ServerSentEventServer serverSentEventServer) { 312 // No-op by default 313 } 314 315 /** 316 * Called after the SSE server starts. 317 */ 318 default void didStartServerSentEventServer(@NonNull ServerSentEventServer serverSentEventServer) { 319 // No-op by default 320 } 321 322 /** 323 * Called after a {@link ServerSentEventServer} instance was asked to start, but failed due to an exception. 324 */ 325 default void didFailToStartServerSentEventServer(@NonNull ServerSentEventServer serverSentEventServer, 326 @NonNull Throwable throwable) { 327 // No-op by default 328 } 329 330 /** 331 * Called before the SSE server stops. 332 */ 333 default void willStopServerSentEventServer(@NonNull ServerSentEventServer serverSentEventServer) { 334 // No-op by default 335 } 336 337 /** 338 * Called after the SSE server stops. 339 */ 340 default void didStopServerSentEventServer(@NonNull ServerSentEventServer serverSentEventServer) { 341 // No-op by default 342 } 343 344 /** 345 * Called after a {@link ServerSentEventServer} instance was asked to stop, but failed due to an exception. 346 */ 347 default void didFailToStopServerSentEventServer(@NonNull ServerSentEventServer serverSentEventServer, 348 @NonNull Throwable throwable) { 349 // No-op by default 350 } 351 352 /** 353 * Called before an SSE connection is established. 354 */ 355 default void willEstablishServerSentEventConnection(@NonNull Request request, 356 @Nullable ResourceMethod resourceMethod) { 357 // No-op by default 358 } 359 360 /** 361 * Called after an SSE connection is established. 362 */ 363 default void didEstablishServerSentEventConnection(@NonNull ServerSentEventConnection serverSentEventConnection) { 364 // No-op by default 365 } 366 367 /** 368 * Called if an SSE connection fails to establish. 369 * 370 * @param reason the handshake failure reason 371 * @param throwable an optional underlying cause, or {@code null} if not applicable 372 */ 373 default void didFailToEstablishServerSentEventConnection(@NonNull Request request, 374 @Nullable ResourceMethod resourceMethod, 375 ServerSentEventConnection.@NonNull HandshakeFailureReason reason, 376 @Nullable Throwable throwable) { 377 // No-op by default 378 } 379 380 /** 381 * Called before an SSE connection is terminated. 382 */ 383 default void willTerminateServerSentEventConnection(@NonNull ServerSentEventConnection serverSentEventConnection, 384 ServerSentEventConnection.@NonNull TerminationReason terminationReason, 385 @Nullable Throwable throwable) { 386 // No-op by default 387 } 388 389 /** 390 * Called after an SSE connection is terminated. 391 */ 392 default void didTerminateServerSentEventConnection(@NonNull ServerSentEventConnection serverSentEventConnection, 393 @NonNull Duration connectionDuration, 394 ServerSentEventConnection.@NonNull TerminationReason terminationReason, 395 @Nullable Throwable throwable) { 396 // No-op by default 397 } 398 399 /** 400 * Called before an SSE event is written. 401 */ 402 default void willWriteServerSentEvent(@NonNull ServerSentEventConnection serverSentEventConnection, 403 @NonNull ServerSentEvent serverSentEvent) { 404 // No-op by default 405 } 406 407 /** 408 * Called after an SSE event is written. 409 */ 410 default void didWriteServerSentEvent(@NonNull ServerSentEventConnection serverSentEventConnection, 411 @NonNull ServerSentEvent serverSentEvent, 412 @NonNull Duration writeDuration) { 413 // No-op by default 414 } 415 416 /** 417 * Called after an SSE event fails to write. 418 */ 419 default void didFailToWriteServerSentEvent(@NonNull ServerSentEventConnection serverSentEventConnection, 420 @NonNull ServerSentEvent serverSentEvent, 421 @NonNull Duration writeDuration, 422 @NonNull Throwable throwable) { 423 // No-op by default 424 } 425 426 /** 427 * Called before an SSE comment is written. 428 */ 429 default void willWriteServerSentEventComment(@NonNull ServerSentEventConnection serverSentEventConnection, 430 @NonNull ServerSentEventComment serverSentEventComment) { 431 // No-op by default 432 } 433 434 /** 435 * Called after an SSE comment is written. 436 */ 437 default void didWriteServerSentEventComment(@NonNull ServerSentEventConnection serverSentEventConnection, 438 @NonNull ServerSentEventComment serverSentEventComment, 439 @NonNull Duration writeDuration) { 440 // No-op by default 441 } 442 443 /** 444 * Called after an SSE comment fails to write. 445 */ 446 default void didFailToWriteServerSentEventComment(@NonNull ServerSentEventConnection serverSentEventConnection, 447 @NonNull ServerSentEventComment serverSentEventComment, 448 @NonNull Duration writeDuration, 449 @NonNull Throwable throwable) { 450 // No-op by default 451 } 452 453 /** 454 * Called when Soklet emits a log event. 455 */ 456 default void didReceiveLogEvent(@NonNull LogEvent logEvent) { 457 String message = logEvent.getMessage(); 458 Throwable throwable = logEvent.getThrowable().orElse(null); 459 460 if (throwable == null) { 461 System.err.printf("%s::didReceiveLogEvent [%s]: %s", LifecycleObserver.class.getSimpleName(), logEvent.getLogEventType().name(), message); 462 } else { 463 StringWriter stringWriter = new StringWriter(); 464 PrintWriter printWriter = new PrintWriter(stringWriter); 465 throwable.printStackTrace(printWriter); 466 String throwableWithStackTrace = stringWriter.toString(); 467 468 System.err.printf("%s::didReceiveLogEvent [%s]: %s\n%s\n", LifecycleObserver.class.getSimpleName(), logEvent.getLogEventType().name(), message, throwableWithStackTrace); 469 } 470 } 471 472 /** 473 * Acquires a threadsafe {@link LifecycleObserver} instance with sensible defaults. 474 * 475 * @return a {@code LifecycleObserver} with default settings 476 */ 477 @NonNull 478 static LifecycleObserver defaultInstance() { 479 return DefaultLifecycleObserver.defaultInstance(); 480 } 481}