1- // SPDX-FileCopyrightText: Copyright 2014-2023 Mark Rotteveel
1+ // SPDX-FileCopyrightText: Copyright 2014-2026 Mark Rotteveel
22// SPDX-License-Identifier: LGPL-2.1-or-later OR BSD-3-Clause
33package org .firebirdsql .gds .ng .wire ;
44
55import org .firebirdsql .gds .ng .DeferredResponse ;
66import org .firebirdsql .gds .ng .WarningMessageCallback ;
7+ import org .jspecify .annotations .NullMarked ;
8+ import org .jspecify .annotations .Nullable ;
79
810import java .util .function .Consumer ;
911import java .util .function .Function ;
1012
1113import static java .lang .System .Logger .Level .DEBUG ;
14+ import static java .util .Objects .requireNonNull ;
1215
1316/**
1417 * Interface for processing deferred responses from the server.
1518 * <p>
16- * This interfaces is used in protocol 11 or higher.
19+ * This interface is used in protocol 11 or higher.
1720 * </p>
1821 *
1922 * @author Mark Rotteveel
2023 * @since 3.0
2124 */
25+ @ NullMarked
2226public interface DeferredAction {
2327
2428 /**
@@ -62,7 +66,7 @@ default void onException(Exception exception) {
6266 *
6367 * @return warning callback to use when executing this deferred action, {@code null} signals to use the default
6468 */
65- default WarningMessageCallback getWarningMessageCallback () {
69+ default @ Nullable WarningMessageCallback getWarningMessageCallback () {
6670 return null ;
6771 }
6872
@@ -97,25 +101,99 @@ default boolean requiresSync() {
97101 * @return deferred action
98102 */
99103 static <T > DeferredAction wrapDeferredResponse (DeferredResponse <T > deferredResponse ,
100- Function <Response , T > responseMapper , WarningMessageCallback warningMessageCallback ,
104+ Function <Response , T > responseMapper , @ Nullable WarningMessageCallback warningMessageCallback ,
101105 Consumer <Exception > exceptionConsumer , boolean requiresSync ) {
102- return new DeferredAction () {
106+ return builder ()
107+ .withProcessResponse (response -> deferredResponse .onResponse (responseMapper .apply (response )))
108+ .withOnException (exception -> {
109+ try {
110+ deferredResponse .onException (exception );
111+ } finally {
112+ exceptionConsumer .accept (exception );
113+ }
114+ })
115+ .withWarningMessageCallback (warningMessageCallback )
116+ .withRequiresSync (requiresSync )
117+ .build ();
118+ }
119+
120+ /**
121+ * @return builder for a deferred action instance.
122+ * @since 7
123+ */
124+ static Builder builder () {
125+ return new Builder ();
126+ }
127+
128+ /**
129+ * Builder for lambda-based deferred actions.
130+ *
131+ * @since 7
132+ */
133+ final class Builder {
134+
135+ private static final Consumer <Response > RESPONSE_NO_OP = r -> {};
136+ private static final Consumer <Exception > EXCEPTION_NO_OP = e ->
137+ System .getLogger (DeferredAction .class .getName ()).log (DEBUG , "Exception in processDeferredActions" , e );
138+
139+ private Consumer <Response > processResponse = RESPONSE_NO_OP ;
140+ private Consumer <Exception > onException = EXCEPTION_NO_OP ;
141+ private @ Nullable WarningMessageCallback warningMessageCallback ;
142+ private boolean requiresSync ;
143+
144+ private Builder () {
145+ }
146+
147+ public Builder withProcessResponse (Consumer <Response > processResponse ) {
148+ this .processResponse = processResponse ;
149+ return this ;
150+ }
151+
152+ public Builder withOnException (Consumer <Exception > onException ) {
153+ this .onException = onException ;
154+ return this ;
155+ }
156+
157+ public Builder withWarningMessageCallback (@ Nullable WarningMessageCallback warningMessageCallback ) {
158+ this .warningMessageCallback = warningMessageCallback ;
159+ return this ;
160+ }
161+
162+ public Builder withRequiresSync (boolean requiresSync ) {
163+ this .requiresSync = requiresSync ;
164+ return this ;
165+ }
166+
167+ public DeferredAction build () {
168+ if (processResponse == RESPONSE_NO_OP && onException == EXCEPTION_NO_OP && warningMessageCallback == null
169+ && !requiresSync ) {
170+ return NO_OP_INSTANCE ;
171+ }
172+
173+ return new DeferredActionImpl (processResponse , onException , warningMessageCallback , requiresSync );
174+ }
175+
176+ /**
177+ * Lambda-based deferred action implementation.
178+ *
179+ * @since 7
180+ */
181+ private record DeferredActionImpl (Consumer <Response > processResponse , Consumer <Exception > onException ,
182+ @ Nullable WarningMessageCallback warningMessageCallback , boolean requiresSync )
183+ implements DeferredAction {
184+
103185 @ Override
104186 public void processResponse (Response response ) {
105- deferredResponse . onResponse ( responseMapper . apply ( response ) );
187+ processResponse . accept ( response );
106188 }
107189
108190 @ Override
109191 public void onException (Exception exception ) {
110- try {
111- deferredResponse .onException (exception );
112- } finally {
113- exceptionConsumer .accept (exception );
114- }
192+ onException .accept (exception );
115193 }
116194
117195 @ Override
118- public WarningMessageCallback getWarningMessageCallback () {
196+ public @ Nullable WarningMessageCallback getWarningMessageCallback () {
119197 return warningMessageCallback ;
120198 }
121199
@@ -124,7 +202,46 @@ public boolean requiresSync() {
124202 return requiresSync ;
125203 }
126204
127- };
205+ }
206+
207+ }
208+
209+ /**
210+ * Deferred action implementation that delegates to another deferred action.
211+ * <p>
212+ * This class is intended as a base class for implementations that want to decorate method calls. To decorate it,
213+ * subclass this class, and override the method, and ensure you call {@code super.<overridden-method>} in such a
214+ * way that it is always called, even if the decoration fails.
215+ * </p>
216+ * @since 7
217+ */
218+ abstract class DelegatingDeferredAction implements DeferredAction {
219+
220+ private final DeferredAction delegate ;
221+
222+ DelegatingDeferredAction (DeferredAction delegate ) {
223+ this .delegate = requireNonNull (delegate , "delegate" );
224+ }
225+
226+ @ Override
227+ public void processResponse (Response response ) {
228+ delegate .processResponse (response );
229+ }
230+
231+ @ Override
232+ public void onException (Exception exception ) {
233+ delegate .onException (exception );
234+ }
235+
236+ @ Override
237+ public @ Nullable WarningMessageCallback getWarningMessageCallback () {
238+ return delegate .getWarningMessageCallback ();
239+ }
240+
241+ @ Override
242+ public boolean requiresSync () {
243+ return delegate .requiresSync ();
244+ }
128245 }
129246
130247}
0 commit comments