1818 */
1919package org .firebirdsql .management ;
2020
21+ import jdk .jfr .StackTrace ;
2122import org .firebirdsql .gds .ServiceRequestBuffer ;
2223import org .firebirdsql .gds .impl .DbAttachInfo ;
2324import org .firebirdsql .gds .impl .GDSFactory ;
3132import java .io .IOException ;
3233import java .io .OutputStream ;
3334import java .sql .SQLException ;
35+ import java .util .Arrays ;
3436import java .util .Map ;
3537
3638import static org .firebirdsql .gds .ISCConstants .isc_info_end ;
3739import static org .firebirdsql .gds .ISCConstants .isc_info_svc_to_eof ;
3840import static org .firebirdsql .gds .ISCConstants .isc_info_truncated ;
41+ import static org .firebirdsql .gds .VaxEncoding .iscVaxInteger2 ;
3942import static org .firebirdsql .jaybird .fb .constants .SpbItems .isc_spb_dbname ;
4043import static org .firebirdsql .jaybird .fb .constants .SpbItems .isc_spb_options ;
41- import static org .firebirdsql .gds .VaxEncoding .iscVaxInteger2 ;
4244
4345/**
4446 * An implementation of the basic Firebird Service API functionality.
@@ -52,6 +54,7 @@ public class FBServiceManager implements ServiceManager {
5254 private final FbDatabaseFactory dbFactory ;
5355 private String database ;
5456 private OutputStream logger ;
57+ private ServiceRequestCustomizer serviceRequestCustomizer ;
5558
5659 public final static int BUFFER_SIZE = 1024 ; //1K
5760
@@ -305,6 +308,16 @@ public synchronized void setLogger(OutputStream logger) {
305308 this .logger = logger ;
306309 }
307310
311+ @ Override
312+ public void setServiceRequestCustomizer (ServiceRequestCustomizer serviceRequestCustomizer ) {
313+ this .serviceRequestCustomizer = serviceRequestCustomizer ;
314+ }
315+
316+ @ Override
317+ public ServiceRequestCustomizer getServiceRequestCustomizer () {
318+ return serviceRequestCustomizer ;
319+ }
320+
308321 public FbService attachServiceManager () throws SQLException {
309322 FbService fbService = dbFactory .serviceConnect (serviceProperties );
310323 fbService .attach ();
@@ -410,22 +423,81 @@ public void queueService(FbService service) throws SQLException, IOException {
410423 @ Deprecated
411424 protected void executeServicesOperation (ServiceRequestBuffer srb ) throws SQLException {
412425 try (FbService service = attachServiceManager ()) {
413- service .startServiceAction (srb );
414- queueService (service );
415- } catch (IOException ioe ) {
416- throw new SQLException (ioe );
426+ executeServicesOperation (service , srb );
417427 }
418428 }
419429
420430 protected final void executeServicesOperation (FbService service , ServiceRequestBuffer srb ) throws SQLException {
421431 try {
422- service .startServiceAction (srb );
432+ service .startServiceAction (customize ( srb ) );
423433 queueService (service );
424434 } catch (IOException ioe ) {
425435 throw new SQLException (ioe );
426436 }
427437 }
428438
439+ /**
440+ * Customizes the service request buffer, if a customizer was set.
441+ *
442+ * @param srb
443+ * service request buffer
444+ * @return {@code srb}, possibly modified
445+ * @throws SQLException
446+ * for exceptions thrown by the service request customizer
447+ * @see #setServiceRequestCustomizer(ServiceRequestCustomizer)
448+ * @since 7
449+ */
450+ protected final ServiceRequestBuffer customize (ServiceRequestBuffer srb ) throws SQLException {
451+ ServiceRequestCustomizer customizer = serviceRequestCustomizer ;
452+ if (customizer != null ) {
453+ ServiceRequestContext context = new ServiceRequestContext (determineOperation ());
454+ try {
455+ customizer .customize (srb , context );
456+ } catch (RuntimeException e ) {
457+ throw new SQLException ("Service request terminated by ServiceRequestCustomizer" , e );
458+ }
459+ }
460+ return srb ;
461+ }
462+
463+ /**
464+ * Determines the service operation in this call chain.
465+ *
466+ * @return service operation
467+ */
468+ private String determineOperation () {
469+ class StackFrame {
470+ final Class <?> clazz ;
471+ final String methodName ;
472+
473+ StackFrame (String className , String methodName ) {
474+ Class <?> tempClass ;
475+ try {
476+ tempClass = Class .forName (className );
477+ } catch (ClassNotFoundException e ) {
478+ // Shouldn't happen, and if it does, it is likely a class we're not interested in
479+ tempClass = Object .class ;
480+ }
481+ clazz = tempClass ;
482+ this .methodName = methodName ;
483+ }
484+ }
485+ Class <?> serviceManagerClass = getClass ();
486+ StackFrame lastFrame = null ;
487+ StackTraceElement [] stackTrace = Thread .currentThread ().getStackTrace ();
488+ // Start at 3 to skip getStackTrace, determineOperation and customize
489+ for (int idx = 3 ; idx < stackTrace .length ; idx ++) {
490+ StackTraceElement currentElement = stackTrace [idx ];
491+ StackFrame currentFrame = new StackFrame (currentElement .getClassName (), currentElement .getMethodName ());
492+ if (!currentFrame .clazz .isAssignableFrom (serviceManagerClass )
493+ || !ServiceManager .class .isAssignableFrom (currentFrame .clazz )) {
494+ break ;
495+ }
496+ lastFrame = currentFrame ;
497+ }
498+ return lastFrame != null ? lastFrame .methodName : "unknown" ;
499+ }
500+
429501 protected ServiceRequestBuffer createRequestBuffer (FbService service , int operation , int options ) {
430502 ServiceRequestBuffer srb = service .createServiceRequestBuffer ();
431503 srb .addArgument (operation );
0 commit comments