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}