### Installation Dependencies Source: https://github.com/gruelbox/transaction-outbox/blob/master/transactionoutbox-quarkus/README.md Add the transactionoutbox-quarkus dependency to your project build file. ```xml com.gruelbox transactionoutbox-quarkus 7.0.707 ``` ```groovy implementation 'com.gruelbox:transactionoutbox-quarkus:7.0.707' ``` -------------------------------- ### Usage Example - Tracking scheduled tasks Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Implement the scheduled() method to track when a task is scheduled. This example records a metric and logs the event. ```java @Override public void scheduled(TransactionOutboxEntry entry) { metricsService.recordScheduledTask(entry.getDescription()); logger.info("Scheduled task: {}", entry.description()); } ``` -------------------------------- ### ReflectionInstantiator Implementation Example Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md An example implementation of the getInstance method using Java reflection to instantiate a class via its no-args constructor. ```java @Override public Object getInstance(String name) { try { Class clazz = Class.forName(name); return clazz.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new RuntimeException("Failed to instantiate " + name, e); } } ``` -------------------------------- ### Initialize Transaction Outbox Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-outbox.md Performs initial setup to make the TransactionOutbox instance usable. Can be called explicitly if initialization was deferred. ```java void initialize() ``` -------------------------------- ### Usage Example - Cleanup after task success Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Implement the success() method to perform cleanup actions after a task has successfully completed. This example cleans up temporary state. ```java @Override public void success(TransactionOutboxEntry entry) { // Clean up temporary state created during scheduling tempStateService.cleanup(entry.getId()); logger.debug("Task completed: {}", entry.description()); } ``` -------------------------------- ### initialize Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-outbox.md Performs initial setup, making the instance usable. Called automatically if `initializeImmediately(true)` is set (the default). Can be called explicitly if initialization was deferred. ```APIDOC ## initialize() ### Description Performs initial setup, making the instance usable. Called automatically if `initializeImmediately(true)` is set (the default). Can be called explicitly if initialization was deferred. ### Method (Not specified, likely a method call in a Java SDK) ### Parameters (None specified) ### Request Example (None specified) ### Response #### Success Response (No return value specified, likely void) #### Response Example (None specified) ### Throws - `Exception` - Any initialization errors (e.g., database migration failures) ``` -------------------------------- ### Install transaction-outbox-jooq dependency Source: https://github.com/gruelbox/transaction-outbox/blob/master/transactionoutbox-jooq/README.md Add the dependency to your project using Maven or Gradle. ```xml com.gruelbox transactionoutbox-jooq 7.0.707 ``` ```groovy implementation 'com.gruelbox:transactionoutbox-jooq:7.0.707' ``` -------------------------------- ### SpringInstantiator Implementation Example Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md An example implementation of the getInstance method using Spring's ApplicationContext to retrieve a bean by name. ```java @Override public Object getInstance(String name) { return applicationContext.getBean(name); } ``` -------------------------------- ### Usage Example - Monitoring task failures Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Implement the failure() method to monitor task failures. This example logs the failure, checks the attempt count, and alerts if the task is approaching the maximum retry limit. ```java @Override public void failure(TransactionOutboxEntry entry, Throwable cause) { logger.warn("Task failed (attempt {}): {} - {}", entry.getAttempts() + 1, entry.description(), cause.getMessage()); if (entry.getAttempts() + 1 == maxAttempts - 1) { // One more failure will block - alert now alertService.warnTaskApproachingLimit(entry); } } ``` -------------------------------- ### Configure explicit transaction context Source: https://github.com/gruelbox/transaction-outbox/blob/master/transactionoutbox-jooq/README.md Setup the outbox without thread-local synchronization by passing the DSLContext directly. ```java // Create the DSLContext and connect the listener var dsl = DSL.using(dataSource, SQLDialect.H2); dsl.configuration().set(JooqTransactionManager.createListener()); // Create the outbox var outbox = TransactionOutbox.builder() .transactionManager(JooqTransactionManager.create(dsl)) .persistor(Persistor.forDialect(Dialect.MY_SQL_8)) .build(); ``` -------------------------------- ### Usage Example: Transaction Logging Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Logs the start and end of a task execution, including its duration and any errors. Useful for monitoring and debugging. ```java @Override public void wrapInvocation(Invocator invocator) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { Invocation inv = invocator.getInvocation(); long startTime = System.currentTimeMillis(); try { invocator.run(); long duration = System.currentTimeMillis() - startTime; logger.info("Task executed: {} in {}ms", inv.getMethodName(), duration); } catch (Exception e) { logger.error("Task failed: {} - {}", inv.getMethodName(), e.getMessage()); throw e; } } ``` -------------------------------- ### Configure and use TransactionOutbox Source: https://github.com/gruelbox/transaction-outbox/blob/master/README.md Demonstrates advanced configuration of the TransactionOutbox builder, including custom persistence, serialization, and event handling. Includes a usage example showing how to schedule tasks within a transaction. ```java TransactionManager transactionManager = TransactionManager.fromDataSource(dataSource); TransactionOutbox outbox = TransactionOutbox.builder() // The most complex part to set up for most will be synchronizing with your existing transaction // management. Pre-rolled implementations are available for jOOQ and Spring (see above for more information) // and you can use those examples to synchronize with anything else by defining your own TransactionManager. // Or, if you have no formal transaction management at the moment, why not start, using transaction-outbox's // built-in one? .transactionManager(transactionManager) // Modify how requests are persisted to the database. For more complex modifications, you may wish to subclass // DefaultPersistor, or create a completely new Persistor implementation. .persistor(DefaultPersistor.builder() // Selecting the right SQL dialect ensures that features such as SKIP LOCKED are used correctly. .dialect(Dialect.POSTGRESQL_9) // Override the table name (defaults to "TXNO_OUTBOX") .tableName("transactionOutbox") // Shorten the time we will wait for write locks (defaults to 2) .writeLockTimeoutSeconds(1) // Disable automatic creation and migration of the outbox table, forcing the application to manage // migrations itself .migrate(false) // Allow the SaleType enum and Money class to be used in arguments (see example below) .serializer(DefaultInvocationSerializer.builder() .serializableTypes(Set.of(SaleType.class, Money.class)) .build()) .build()) // GuiceInstantiator and SpringInstantiator are great if you are using Guice or Spring DI, but what if you // have your own service locator? Wire it in here. Fully-custom Instantiator implementations are easy to // implement. .instantiator(Instantiator.using(myServiceLocator::createInstance)) // Change the log level used when work cannot be submitted to a saturated queue to INFO level (the default // is WARN, which you should probably consider a production incident). You can also change the Executor used // for submitting work to a shared thread pool used by the rest of your application. Fully-custom Submitter // implementations are also easy to implement, for example to cluster work. .submitter(ExecutorSubmitter.builder() .executor(ForkJoinPool.commonPool()) .logLevelWorkQueueSaturation(Level.INFO) .build()) // Lower the log level when a task fails temporarily from the default WARN. .logLevelTemporaryFailure(Level.INFO) // 10 attempts at a task before blocking it. .blockAfterAttempts(10) // When calling flush(), select 0.5m records at a time. .flushBatchSize(500_000) // Flush once every 15 minutes only .attemptFrequency(Duration.ofMinutes(15)) // Include Slf4j's Mapped Diagnostic Context in tasks. This means that anything in the MDC when schedule() // is called will be recreated in the task when it runs. Very useful for tracking things like user ids and // request ids across invocations. .serializeMdc(true) // Sets how long we should keep records of requests with a unique request id so duplicate requests // can be rejected. Defaults to 7 days. .retentionThreshold(Duration.ofDays(1)) // We can intercept and modify numerous events. The most common use is to catch blocked tasks // and raise alerts for these to be investigated. A Slack interactive message is particularly effective here // since it can be wired up to call unblock() automatically. .listener(new TransactionOutboxListener() { @Override public void success(TransactionOutboxEntry entry) { eventPublisher.publish(new OutboxTaskProcessedEvent(entry.getId())); } @Override public void blocked(TransactionOutboxEntry entry, Throwable cause) { eventPublisher.publish(new BlockedOutboxTaskEvent(entry.getId())); } @Override public Map extractSession() { return Map.of(); } @Override public void wrapInvocationAndInit(Invocator invocator) { invocator.runUnchecked(); } @Override public void wrapInvocation(Invocator invocator) throws Exception { invocator.run(); } }) .build(); // Usage example, using the in-built transaction manager MDC.put("SESSIONKEY", "Foo"); try { transactionManager.inTransaction(tx -> { writeSomeChanges(tx.connection()); outbox.schedule(getClass()) .performRemoteCall(SaleType.SALE, Money.of(10, Currency.getInstance("USD"))); }); } finally { MDC.clear(); } ``` -------------------------------- ### Production-Ready TransactionOutboxListener Implementation Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md An example of a complete listener implementation for production environments. It demonstrates how to integrate with metrics, alerting, and auditing services, and includes session extraction and invocation wrapping logic. ```java public class ProductionListener implements TransactionOutboxListener { private final MetricsService metrics; private final AlertService alerts; private final AuditService audit; @Override public void scheduled(TransactionOutboxEntry entry) { metrics.incrementScheduledTasks(); } @Override public void success(TransactionOutboxEntry entry) { metrics.recordTaskSuccess(entry.getDescription()); audit.logTaskCompletion(entry.getId()); } @Override public void failure(TransactionOutboxEntry entry, Throwable cause) { metrics.recordTaskFailure(entry.getDescription(), entry.getAttempts()); if (entry.getAttempts() + 1 >= maxAttempts - 1) { alerts.warn("Task approaching max attempts: " + entry.description()); } } @Override public void blocked(TransactionOutboxEntry entry, Throwable cause) { metrics.recordBlockedTask(entry.getDescription()); alerts.critical("BLOCKED: " + entry.description(), cause); slackService.postAlert(entry, cause); } @Override public Map extractSession() { Map session = new HashMap<>(); session.put("traceId", Span.current().getSpanContext().getTraceId()); session.put("userId", getCurrentUserId()); session.put("tenantId", getCurrentTenantId()); return session; } @Override public void wrapInvocationAndInit(Invocator invocator) { Invocation inv = invocator.getInvocation(); String tenantId = inv.getSession().get("tenantId"); TenantContext.set(tenantId); try { invocator.runUnchecked(); } finally { TenantContext.clear(); } } } ``` -------------------------------- ### Usage Example: Tenant Context Restoration Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Restores tenant context before executing a task and clears it afterward. Essential for multi-tenant applications. ```java @Override public void wrapInvocationAndInit(Invocator invocator) { Invocation inv = invocator.getInvocation(); String tenantId = inv.getSession().get("tenantId"); TenantContext.setCurrentTenant(tenantId); try { invocator.runUnchecked(); } finally { TenantContext.clear(); } } ``` -------------------------------- ### Get Class Name for Serialization (Reflection) Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Implementation example for mapping a class to its fully-qualified name using reflection. This name is used for serialization and lookup. ```java String getName(Class clazz) ``` ```java @Override public String getName(Class clazz) { return clazz.getName(); // e.g., "com.example.UserService" } ``` -------------------------------- ### Invocation Constructors Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/invocation.md Details on the different constructors available for creating Invocation objects, including parameters and usage examples. ```APIDOC ## Invocation(String className, String methodName, Class[] parameterTypes, Object[] args) ### Description Basic constructor without context. ### Parameters #### Path Parameters - **className** (String) - Required - Class name from instantiator - **methodName** (String) - Required - Method name to invoke - **parameterTypes** (Class[]) - Required - Parameter types for method resolution - **args** (Object[]) - Required - Method arguments ### Request Example ```java Invocation inv = new Invocation( "com.example.UserService", "createUser", new Class[] { String.class, String.class }, new Object[] { "John", "john@example.com" } ); ``` ## Invocation(String className, String methodName, Class[] parameterTypes, Object[] args, Map mdc) ### Description Legacy constructor with MDC only. Deprecated - use full constructor. ### Parameters #### Path Parameters - **className** (String) - Required - Class name from instantiator - **methodName** (String) - Required - Method name to invoke - **parameterTypes** (Class[]) - Required - Parameter types for method resolution - **args** (Object[]) - Required - Method arguments - **mdc** (Map) - Required - SLF4J MDC (context map) ## Invocation(String className, String methodName, Class[] parameterTypes, Object[] args, Map mdc, Map session) ### Description Full constructor with both MDC and custom session state. ### Parameters #### Path Parameters - **className** (String) - Required - Class name from instantiator - **methodName** (String) - Required - Method name to invoke - **parameterTypes** (Class[]) - Required - Parameter types for method resolution - **args** (Object[]) - Required - Method arguments - **mdc** (Map) - Required - SLF4J MDC, restored at execution - **session** (Map) - Required - Custom session data from listener ### Request Example ```java Map mdc = MDC.getCopyOfContextMap(); Map session = listener.extractSession(); Invocation inv = new Invocation( "com.example.UserService", "createUser", new Class[] { String.class }, new Object[] { "data" }, mdc, session ); ``` ``` -------------------------------- ### Handle incoming remote requests with JAX-RS Source: https://github.com/gruelbox/transaction-outbox/blob/master/README.md An example endpoint to receive serialized entries and process them locally. ```java @POST @Path("/outbox/process") void processOutboxEntry(String entry) { TransactionOutboxEntry entry = somethingWhichCanSerializeTransactionOutboxEntries.deserialize(entry); Submitter submitter = ExecutorSubmitter.builder().executor(localExecutor).logLevelWorkQueueSaturation(Level.INFO).build(); submitter.submit(entry, outbox.get()::processNow); } ``` -------------------------------- ### Usage Example - Extracting Tenant Context Session Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Implement extractSession to capture and serialize the current tenant ID if it exists in the TenantContext. ```java @Override public Map extractSession() { String tenantId = TenantContext.getCurrentTenant(); if (tenantId != null) { return Map.of("tenantId", tenantId); } return null; } ``` -------------------------------- ### TransactionOutbox.builder() Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-outbox.md Creates a new builder for constructing a TransactionOutbox instance. This is the starting point for configuring and initializing the TransactionOutbox. ```APIDOC ## TransactionOutbox.builder() ### Description Creates a new builder for constructing a `TransactionOutbox` instance. ### Method `static TransactionOutboxBuilder builder()` ### Returns `TransactionOutboxBuilder` instance for fluent configuration ### Usage ```java TransactionOutbox outbox = TransactionOutbox.builder() .transactionManager(txManager) .persistor(Persistor.forDialect(Dialect.POSTGRESQL_9)) .build(); ``` ``` -------------------------------- ### Handle incoming events with TransactionOutbox Source: https://github.com/gruelbox/transaction-outbox/blob/master/README.md Example of using TransactionOutbox within an SQS event handler to schedule asynchronous processing. ```java public class FooEventHandler implements SQSEventHandler { @Inject private TransactionOutbox outbox; public void handle(ThingHappenedEvent event) { outbox.schedule(FooService.class).handleEvent(event.getThingId()); } } ``` -------------------------------- ### Usage Example: Database Audit Trail Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Logs audit entries before and after task execution to track operations. Useful for security and compliance. ```java @Override public void wrapInvocation(Invocator invocator) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { Invocation inv = invocator.getInvocation(); // Log audit entry before execution auditService.logTaskStart(inv.getId(), inv.getClassName(), inv.getMethodName()); invocator.run(); // Log audit entry after execution auditService.logTaskEnd(inv.getId(), "SUCCESS"); } ``` -------------------------------- ### Initialize Transaction Outbox with H2 In-Memory Database Source: https://github.com/gruelbox/transaction-outbox/blob/master/README.md Use this setup when no existing transaction management or dependency injection is present. It configures an in-memory H2 database and the outbox for scheduling tasks. ```java TransactionManager transactionManager = TransactionManager.fromConnectionDetails( "org.h2.Driver", "jdbc:h2:mem:test;MV_STORE=TRUE", "test", "test")); TransactionOutbox outbox = TransactionOutbox.builder() .transactionManager(transactionManager) .persistor(Persistor.forDialect(Dialect.H2)) .build(); transactionManager.inTransaction(tx -> { tx.connection().createStatement().execute("INSERT INTO..."); outbox.schedule(MyClass.class).myMethod("Foo", "Bar")); }); ``` -------------------------------- ### Implement a transactional sale endpoint Source: https://github.com/gruelbox/transaction-outbox/blob/master/README.md An example of a problematic implementation where a database transaction does not cover external message queue operations. ```java @POST @Path("/sales") @Transactional public SaleId createWidget(Sale sale) { var saleId = saleRepository.save(sale); messageQueue.postMessage(StockReductionEvent.of(sale.item(), sale.amount())); messageQueue.postMessage(IncomeEvent.of(sale.value())); return saleId; } ``` -------------------------------- ### Schedule a Transaction Outbox Task with Various Argument Types Source: https://github.com/gruelbox/transaction-outbox/blob/master/transactionoutbox-jackson/README.md Example of scheduling a task using the outbox.schedule method, demonstrating support for diverse argument types like LocalDate, String, and Integer. ```java outbox.schedule(getClass()) .process(List.of(LocalDate.of(2000,1,1), "a", "b", 2)); ``` -------------------------------- ### Usage Example: OTEL Context Restoration Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Restores OpenTelemetry context (trace and span IDs) before executing a task. Useful for distributed tracing. ```java @Override public void wrapInvocationAndInit(Invocator invocator) { Invocation inv = invocator.getInvocation(); Map session = inv.getSession(); if (session != null) { String traceId = session.get("traceId"); String spanId = session.get("spanId"); // Create child span linked to parent Span span = tracer.spanBuilder(inv.getClassName() + "." + inv.getMethodName()) .addLink(SpanContext.createFromRemoteParent(traceId, spanId, ...)) .startSpan(); try (Scope scope = span.makeCurrent()) { invocator.runUnchecked(); } finally { span.end(); } } else { invocator.runUnchecked(); } } ``` -------------------------------- ### Example Serialized JSON with Field Abbreviations Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/invocation.md Illustrates the compact JSON format used for serializing invocations, employing abbreviations for field names like className, methodName, parameterTypes, args, mdc, and session. ```json { "c": "com.example.UserService", "m": "createUser", "p": ["java.lang.String", "java.lang.String"], "a": ["John", "john@example.com"], "x": {"requestId": "req-123", "userId": "user-456"}, "s": {"tenantId": "tenant-1"} } ``` -------------------------------- ### Usage Example - Alerting on blocked tasks Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Implement the blocked method to log errors, send critical alerts, and post notifications to Slack when a task is blocked. This requires operational response. ```java @Override public void blocked(TransactionOutboxEntry entry, Throwable cause) { logger.error("BLOCKED TASK - requires manual intervention: {} - {}", entry.description(), cause.getMessage()); // Send alert to ops team alertService.sendCriticalAlert(new BlockedTaskAlert( entry.getId(), entry.description(), cause )); // Post to Slack with unblock button slackService.postBlockedTaskAlert(entry, cause); } ``` -------------------------------- ### Execute Transactional Work with Return Value and Checked Exceptions Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-manager.md Use this method to start a new transaction, execute work that may throw a checked exception and return a value. The return value is preserved, and the exception is re-thrown. ```java T inTransactionReturnsThrows(ThrowingTransactionalSupplier work) throws E ``` ```java String result = transactionManager.inTransactionReturnsThrows(tx -> { doSomethingThatThrows(); return "success"; }); ``` -------------------------------- ### Execute Transactional Work with Checked Exceptions Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-manager.md Use this method to start a new transaction and execute work that might throw a checked exception. The exception will be re-thrown after the transaction. ```java default void inTransactionThrows(ThrowingTransactionalWork work) throws E ``` ```java transactionManager.inTransactionThrows(tx -> { someMethodThatThrowsCheckedException(); }); ``` -------------------------------- ### Get Class Name for Serialization (Spring) Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Implementation example for mapping a class to its Spring bean name. This name is used for serialization and lookup, providing an alternative to fully-qualified class names. ```java @Override public String getName(Class clazz) { String[] beanNames = applicationContext.getBeanNamesForType(clazz); return beanNames[0]; // Use Spring bean name instead of class name } ``` -------------------------------- ### Using ReflectionInstantiator Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Demonstrates how to create an Instantiator instance that uses reflection for class instantiation. ```java // Created by Instantiator.usingReflection() Instantiator instantiator = Instantiator.usingReflection(); ``` -------------------------------- ### Configuring FunctionInstantiator Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Shows how to configure and build a FunctionInstantiator with a custom instantiation function. ```java FunctionInstantiator instantiator = FunctionInstantiator.builder() .fn(myCreationFunction) .build(); ``` -------------------------------- ### Configuring ReflectionInstantiator Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Shows how to configure and build a ReflectionInstantiator using its builder pattern. ```java ReflectionInstantiator instantiator = ReflectionInstantiator.builder() .build(); ``` -------------------------------- ### Default wrapInvocationAndInit Method Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Provides a default implementation for wrapping invocation and initialization logic. It handles common exceptions during task execution. ```java default void wrapInvocationAndInit(Invocator invocator) { try { invocator.run(); } catch (IllegalAccessException | InvocationTargetException e) { throw new UncheckedException(e); } } ``` -------------------------------- ### Get Method Name Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/invocation.md Retrieves the name of the method to be invoked. ```java Method method = clazz.getDeclaredMethod( invocation.getMethodName(), invocation.getParameterTypes() ); ``` -------------------------------- ### Proxying Interface as Preferred Method Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/errors.md Demonstrates the preferred approach of using interfaces for scheduling tasks with `outbox.schedule()`, which works without the ByteBuddy dependency. ```java // Preferred - interface public interface IMyService { ... } outbox.schedule(IMyService.class).doWork(); // Works without ByteBuddy ``` -------------------------------- ### Configure GitHub Repository for Snapshots Source: https://github.com/gruelbox/transaction-outbox/blob/master/README.md Add the GitHub Package Repository to your build system to access continuously-delivered releases. ```xml github-transaction-outbox Gruelbox Github Repository https://maven.pkg.github.com/gruelbox/transaction-outbox ``` ```groovy repositories { maven { name = "github-transaction-outbox" url = uri("https://maven.pkg.github.com/gruelbox/transaction-outboxY") credentials { username = $githubUserName password = $githubToken } } } ``` -------------------------------- ### Get Class Name Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/invocation.md Retrieves the class identifier used for instantiating the target object. ```java String name = invocation.getClassName(); Object instance = instantiator.getInstance(name); ``` -------------------------------- ### Configure DefaultPersistor Builder Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/persistor.md Demonstrates how to configure the DefaultPersistor using its builder. Required options include the SQL dialect and optionally the table name, migration settings, timeouts, and serializers. ```java DefaultPersistor.builder() .dialect(Dialect.POSTGRESQL_9) // Required .tableName("TXNO_OUTBOX") // Optional, defaults to TXNO_OUTBOX .migrate(true) // Optional, defaults to true .writeLockTimeoutSeconds(2) // Optional, defaults to 2 .serializer(invocationSerializer) // Optional, defaults to GSON .build() ``` -------------------------------- ### TransactionManager.inTransactionReturns(TransactionalSupplier supplier) Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-manager.md Starts a new transaction and returns a value computed within it. ```APIDOC ## TransactionManager.inTransactionReturns(TransactionalSupplier supplier) ### Description Starts a new transaction and returns a value computed within it. ### Method T ### Parameters #### Path Parameters - **supplier** (TransactionalSupplier) - Required - Supplier computing a value in the transaction ### Returns The value returned by the supplier ### Type Parameters - **T** - The return type ### Usage ```java User createdUser = transactionManager.inTransactionReturns(tx -> { User user = new User("John", "john@example.com"); userDao.save(user); return user; }); ``` ``` -------------------------------- ### Invocation Properties Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/invocation.md Details on the immutable properties of the Invocation class, including their getters and usage examples. ```APIDOC ## className ### Description The class identifier (from `Instantiator.getName()`), not necessarily the actual class name. ### Method ```java String getClassName() ``` ### Usage ```java String name = invocation.getClassName(); Object instance = instantiator.getInstance(name); ``` ## methodName ### Description The method name to invoke. Combined with `parameterTypes` for method resolution. ### Method ```java String getMethodName() ``` ### Usage ```java Method method = clazz.getDeclaredMethod( invocation.getMethodName(), invocation.getParameterTypes() ); ``` ## parameterTypes ### Description Array of parameter types for resolving method overloads. **Important:** - Must match the actual parameter types exactly - Used by reflection to find the correct method - Empty array `{}` for no-argument methods ### Method ```java Class[] getParameterTypes() ``` ### Usage ```java Method method = clazz.getDeclaredMethod( methodName, invocation.getParameterTypes() ); ``` ## args ### Description Method arguments. Must match length and types of `parameterTypes`. **Important:** - Null values are preserved - Each argument is serialized individually - Serializable types determined by `InvocationSerializer` ### Method ```java Object[] getArgs() ``` ### Usage ```java method.invoke(instance, invocation.getArgs()); ``` ## mdc ### Description SLF4J Mapped Diagnostic Context captured at scheduling time. **Contents (typical):** - Request ID - User ID - Session ID - Correlation ID - Any application-specific context **Restoration at Execution:** ```java // During task execution if (invocation.getMdc() != null) { MDC.setContextMap(invocation.getMdc()); } ``` **Population (at scheduling):** ```java // In TransactionOutboxListener @Override public Map extractSession() { return MDC.getCopyOfContextMap(); } ``` ### Method ```java Map getMdc() ``` ``` -------------------------------- ### SpringInstantiator Usage in TransactionOutbox Builder Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Example of integrating SpringInstantiator into the TransactionOutbox builder, requiring an ApplicationContext. ```java @Bean public TransactionOutbox transactionOutbox( ApplicationContext context) { return TransactionOutbox.builder() .instantiator(new SpringInstantiator(context)) .build(); } ``` -------------------------------- ### Build TransactionOutbox Instance Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-outbox.md Creates and initializes a TransactionOutbox instance. This is the entry point for configuring and scheduling outbox operations. ```java abstract TransactionOutbox build() ``` -------------------------------- ### Get Arguments Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/invocation.md Retrieves the arguments for the method call. Must match the length and types of parameterTypes. ```java Object[] getArgs() ``` ```java method.invoke(instance, invocation.getArgs()); ``` -------------------------------- ### Transactional Usage Example Source: https://github.com/gruelbox/transaction-outbox/blob/master/transactionoutbox-quarkus/README.md Use the @Transactional annotation to manage database operations and schedule outbox events. ```java @Transactional public void doStuff() { customerRepository.save(new Customer(1L, "Martin", "Carthy")); customerRepository.save(new Customer(2L, "Dave", "Pegg")); outbox.get().schedule(getClass()).publishCustomerCreatedEvent(1L); outbox.get().schedule(getClass()).publishCustomerCreatedEvent(2L); } void publishCustomerCreatedEvent(long id) { // Remote call here } ``` -------------------------------- ### Initialize TransactionOutbox with thread-local manager Source: https://github.com/gruelbox/transaction-outbox/blob/master/transactionoutbox-jooq/README.md Build the outbox instance using the JooqTransactionManager. ```java var outbox = TransactionOutbox.builder() .transactionManager(JooqTransactionManager.create(dsl, listener)) .persistor(Persistor.forDialect(Dialect.MY_SQL_8)) .build(); } ``` -------------------------------- ### Schedule Service with Parameters Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-outbox.md Returns a proxy of the specified class with all configured parameters applied. Method calls on this proxy are scheduled according to the outbox configuration. ```java T schedule(Class clazz) ``` ```java outbox.with() .uniqueRequestId("request-1") .ordered("topic-1") .delayForAtLeast(Duration.ofMinutes(1)) .schedule(MyService.class) .doWork(data); ``` -------------------------------- ### wrapInvocationAndInit Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Wraps the entire task execution including lock acquisition. This method is a good place to initialize session state before any database locks are acquired or important checks are performed. ```APIDOC ## wrapInvocationAndInit(Invocator invocator) ### Description Wraps the entire task execution including lock acquisition. Good place to initialize session state. ### Parameters #### Path Parameters - **invocator** (Invocator) - Required - Task execution handle ### Usage Example - OTEL Context Restoration: ```java @Override public void wrapInvocationAndInit(Invocator invocator) { Invocation inv = invocator.getInvocation(); Map session = inv.getSession(); if (session != null) { String traceId = session.get("traceId"); String spanId = session.get("spanId"); // Create child span linked to parent Span span = tracer.spanBuilder(inv.getClassName() + "." + inv.getMethodName()) .addLink(SpanContext.createFromRemoteParent(traceId, spanId, ...)) .startSpan(); try (Scope scope = span.makeCurrent()) { invocator.runUnchecked(); } finally { span.end(); } } else { invocator.runUnchecked(); } } ``` ### Usage Example - Tenant Context Restoration: ```java @Override public void wrapInvocationAndInit(Invocator invocator) { Invocation inv = invocator.getInvocation(); String tenantId = inv.getSession().get("tenantId"); TenantContext.setCurrentTenant(tenantId); try { invocator.runUnchecked(); } finally { TenantContext.clear(); } } ``` ``` -------------------------------- ### Initialize Transaction Outbox with DataSource Source: https://github.com/gruelbox/transaction-outbox/blob/master/README.md Configure Transaction Outbox using an existing DataSource, which allows integration with connection pooling libraries like Hikari. ```java TransactionManager transactionManager = TransactionManager.fromDataSource(dataSource); ``` -------------------------------- ### Identify unsupported polymorphic collections Source: https://github.com/gruelbox/transaction-outbox/blob/master/README.md Example of a call that fails with the default serializer due to type limitations in polymorphic collections. ```java outbox.schedule(Service.class).processList(List.of(1, "2", 3L)); ``` -------------------------------- ### Instantiator.using(Function, Object> fn) Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Creates an Instantiator using a custom function for instance creation. This function receives a Class object and is responsible for returning an instance of that class, often used with dependency injection frameworks. ```APIDOC ## Instantiator.using(Function, Object> fn) ### Description Creates an instantiator using a custom function that creates instances. The function receives the class and must return an instance. ### Method `static Instantiator using(Function, Object> fn)` ### Parameters #### Request Body - **fn** (Function, Object>) - Required - Function to create instances ### Returns `Instantiator` using the provided function ### Usage Examples ```java // Guice example Injector injector = Guice.createInjector(...); TransactionOutbox outbox = TransactionOutbox.builder() .instantiator(Instantiator.using(injector::getInstance)) .build(); // Spring example ApplicationContext context = ...; TransactionOutbox outbox = TransactionOutbox.builder() .instantiator(Instantiator.using(context::getBean)) .build(); // Custom Service Locator Example Instantiator customInstantiator = Instantiator.using(clazz -> { // Custom logic to instantiate if (clazz == UserService.class) { return new UserService(dependencies); } // Fallback return clazz.getDeclaredConstructor().newInstance(); }); ``` ``` -------------------------------- ### TransactionOutbox.with() Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-outbox.md Creates a builder for scheduling requests with additional parameterization, such as unique request IDs, topics for ordered processing, and delays. ```APIDOC ## TransactionOutbox.with() ### Description Creates a builder for scheduling requests with additional parameterization such as unique request IDs, topics for ordered processing, and delays. ### Method `ParameterizedScheduleBuilder with()` ### Returns `ParameterizedScheduleBuilder` for fluent parameter configuration ### Usage ```java outbox.with() .uniqueRequestId("request-123") .schedule(Service.class) .process("data"); ``` ``` -------------------------------- ### TransactionManager.inTransaction(TransactionalWork work) Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-manager.md Starts a new transaction and executes the transactional work, which receives a Transaction object for database access. ```APIDOC ## TransactionManager.inTransaction(TransactionalWork work) ### Description Starts a new transaction and executes the transactional work, which receives a `Transaction` object for database access. ### Method void ### Parameters #### Path Parameters - **work** (TransactionalWork) - Required - Work with access to transaction context ### Usage ```java transactionManager.inTransaction(tx -> { tx.connection().createStatement().execute("INSERT INTO ..."); }); ``` ``` -------------------------------- ### TransactionManager.inTransaction(Runnable runnable) Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-manager.md Starts a new transaction, executes the runnable within it, and commits on success or rolls back on failure. ```APIDOC ## TransactionManager.inTransaction(Runnable runnable) ### Description Starts a new transaction, executes the runnable within it, and commits on success or rolls back on failure. ### Method void ### Parameters #### Path Parameters - **runnable** (Runnable) - Required - Code to execute within the transaction ### Throws - Any exception thrown by the runnable (wrapped if checked) ### Usage ```java transactionManager.inTransaction(() -> { customerDao.save(customer); outbox.schedule(NotificationService.class).sendEmail(customer.getId()); }); ``` ``` -------------------------------- ### Configure Transaction Outbox with Jackson for Fresh Projects Source: https://github.com/gruelbox/transaction-outbox/blob/master/transactionoutbox-jackson/README.md Configure the Transaction Outbox builder with JacksonInvocationSerializer for new projects. Ensure Jackson's ObjectMapper is provided. ```java var outbox = TransactionOutbox.builder() .persistor(DefaultPersistor.builder() .dialect(Dialect.H2) .serializer(JacksonInvocationSerializer.builder() .mapper(new ObjectMapper()) .build()) .build()) ``` -------------------------------- ### Using FunctionInstantiator with Custom Logic Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Illustrates creating an Instantiator that uses a custom function for instantiation logic. ```java Instantiator instantiator = Instantiator.using(clazz -> { // Custom instantiation logic return new CustomFactory().create(clazz); }); ``` -------------------------------- ### Instantiator.usingReflection() Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Creates an Instantiator that uses Java reflection to instantiate classes via their public no-args constructor. Class names are represented by their fully-qualified names. ```APIDOC ## Instantiator.usingReflection() ### Description Creates an instantiator that uses reflection to create instances with a no-args constructor. Class names are their fully-qualified names. ### Method `static Instantiator usingReflection()` ### Returns `Instantiator` using reflection-based instantiation ### Requirements - All classes must have a public no-args constructor - Classes must be on the application classpath at execution time ### Class Name Format `com.example.MyService` ### Usage Example ```java TransactionOutbox outbox = TransactionOutbox.builder() .instantiator(Instantiator.usingReflection()) .build(); ``` ``` -------------------------------- ### Instantiator for Multi-Version Support Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Create an Instantiator that selects the correct factory based on the service class version. This is useful when dealing with different versions of the same service. ```java Instantiator multiVersion = Instantiator.using(clazz -> { String versionedName = extractVersion(clazz); switch (versionedName) { case "com.example.UserService@v1": return userServiceV1Factory.create(); case "com.example.UserService@v2": return userServiceV2Factory.create(); default: throw new IllegalArgumentException("Unknown version: " + versionedName); } }); ``` -------------------------------- ### Extract Session Data Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/invocation.md Example of extracting custom session data, typically from a listener, to be included in the Invocation's session map. ```java // In TransactionOutboxListener @Override public Map extractSession() { return MDC.getCopyOfContextMap(); } ``` -------------------------------- ### Add Stable Dependency Source: https://github.com/gruelbox/transaction-outbox/blob/master/README.md Include the core library in your project build configuration. ```xml com.gruelbox transactionoutbox-core 7.0.707 ``` ```groovy implementation 'com.gruelbox:transactionoutbox-core:7.0.707' ``` -------------------------------- ### Get Parameter Types Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/invocation.md Retrieves the array of parameter types used for method resolution. Must match actual types exactly. ```java Class[] getParameterTypes() ``` ```java Method method = clazz.getDeclaredMethod( methodName, invocation.getParameterTypes() ); ``` -------------------------------- ### GuiceInstantiator Usage with TransactionOutbox Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Demonstrates how to use GuiceInstantiator with a Guice Injector to configure TransactionOutbox. ```java Injector injector = Guice.createInjector(...); TransactionOutbox outbox = TransactionOutbox.builder() .instantiator(new GuiceInstantiator(injector)) .build(); ``` -------------------------------- ### Manual Spring Bean Configuration Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/configuration.md Manually configure TransactionManager, Instantiator, and TransactionOutbox beans in a Spring application. This provides explicit control over the setup. ```java @Configuration public class TransactionOutboxConfig { @Bean public TransactionManager transactionManager( PlatformTransactionManager ptm, DataSource dataSource) { return new SpringTransactionManager(ptm, dataSource); } @Bean public Instantiator instantiator(ApplicationContext context) { return new SpringInstantiator(context); } @Bean public TransactionOutbox transactionOutbox( TransactionManager tm, Instantiator inst) { return TransactionOutbox.builder() .transactionManager(tm) .instantiator(inst) .persistor(Persistor.forDialect(Dialect.POSTGRESQL_9)) .build(); } } ``` -------------------------------- ### Get Custom Session State Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/invocation.md Retrieves custom session state from TransactionOutboxListener.extractSession(). This state can include authentication tokens, tenant IDs, and other application-specific data. ```java Map getSession() ``` -------------------------------- ### Basic Spring Configuration for Transaction Outbox Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/spring-integration.md Provides the necessary Spring beans for Transaction Outbox, including transaction management, instantiator, and the outbox itself with a PostgreSQL persistor and a production listener. ```java @Configuration public class OutboxConfig { @Bean public TransactionManager transactionManager( PlatformTransactionManager platformTxManager, DataSource dataSource) { return new SpringTransactionManager(platformTxManager, dataSource); } @Bean public Instantiator instantiator(ApplicationContext context) { return new SpringInstantiator(context); } @Bean public TransactionOutbox transactionOutbox( TransactionManager txManager, Instantiator instantiator) { return TransactionOutbox.builder() .transactionManager(txManager) .instantiator(instantiator) .persistor(Persistor.forDialect(Dialect.POSTGRESQL_9)) .listener(new ProductionListener()) .build(); } } ``` -------------------------------- ### Custom Instantiator for Stable Names Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md An example of a custom instantiator that uses stable names to decouple class names from refactoring, mapping simple names to stable identifiers. ```java Instantiator stableNames = Instantiator.using(clazz -> { String stableName = switch (clazz.getSimpleName()) { case "UserServiceV2" -> "user-service"; // Stable name case "EmailServiceV3" -> "email-service"; default -> clazz.getName(); }; // Look up by stable name return serviceRegistry.get(stableName); }); ``` -------------------------------- ### inTransactionThrows Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-manager.md Starts a new transaction and executes work that may throw a checked exception. This method is suitable for operations that do not need to return a value but might encounter exceptions. ```APIDOC ## inTransactionThrows(ThrowingTransactionalWork work) ### Description Starts a new transaction and executes work that may throw a checked exception. This method is suitable for operations that do not need to return a value but might encounter exceptions. ### Method ```java default void inTransactionThrows(ThrowingTransactionalWork work) throws E ``` ### Parameters #### Path Parameters * None #### Query Parameters * None #### Request Body * **work** (ThrowingTransactionalWork) - Required - Description: Work that may throw checked exceptions ### Request Example ```java transactionManager.inTransactionThrows(tx -> { someMethodThatThrowsCheckedException(); }); ``` ### Response #### Success Response * None (void return type) #### Response Example * None ``` -------------------------------- ### Defer TransactionOutbox Initialization Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/configuration.md Configure the Transaction Outbox to defer its initialization until explicitly called. This is useful for frameworks that manage their own startup sequences. ```java // Defer initialization to structured startup TransactionOutbox.builder() .initializeImmediately(false) .build(); // Later, during startup sequence outbox.initialize(); ``` -------------------------------- ### Unblock with Parameter Context Manager Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/errors.md Illustrates the correct usage of `outbox.unblock()` with a parameter context manager, requiring the explicit passing of the `transactionContext`. ```java outbox.unblock(entryId, transactionContext); // OK ``` -------------------------------- ### Schedule with Additional Parameterization Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-outbox.md Use this to schedule requests with advanced options like unique request IDs, topics for ordered processing, or delays. This returns a builder for fluent configuration. ```java ParameterizedScheduleBuilder with() ``` ```java outbox.with() .uniqueRequestId("request-123") .schedule(Service.class) .process("data"); ``` -------------------------------- ### Configure DefaultInvocationSerializer with Custom Types Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/invocation.md Configures a DefaultInvocationSerializer to include custom serializable types like enums and custom classes. This setup is used with a DefaultPersistor for database persistence. ```java DefaultInvocationSerializer serializer = DefaultInvocationSerializer.builder() .serializableTypes(Set.of( MyEnumType.class, MyCustomType.class, MyOtherType.class )) .build(); DefaultPersistor persistor = DefaultPersistor.builder() .dialect(Dialect.POSTGRESQL_9) .serializer(serializer) .build(); ``` -------------------------------- ### Create Reflection Instantiator Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/instantiator.md Creates an instantiator that uses reflection to create instances with a no-args constructor. All classes must have a public no-args constructor and be on the application classpath at execution time. ```java static Instantiator usingReflection() ``` ```java TransactionOutbox outbox = TransactionOutbox.builder() .instantiator(Instantiator.usingReflection()) .build(); ``` -------------------------------- ### Usage Example - Extracting Request Tracing Session Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/listener.md Implement extractSession to capture and serialize OpenTelemetry trace context (traceId, spanId) and user context (userId) for request tracing. ```java @Override public Map extractSession() { Map session = new HashMap<>(); // Extract OTEL trace context String traceId = Span.current().getSpanContext().getTraceId(); String spanId = Span.current().getSpanContext().getSpanId(); session.put("traceId", traceId); session.put("spanId", spanId); // Extract user context String userId = SecurityContextHolder.getContext() .getAuthentication().getName(); session.put("userId", userId); return session; } ``` -------------------------------- ### inTransactionReturnsThrows Source: https://github.com/gruelbox/transaction-outbox/blob/master/_autodocs/api-reference/transaction-manager.md Starts a new transaction, executes work that may throw a checked exception, and returns a value. This is useful for transactional operations that produce a result and can throw checked exceptions. ```APIDOC ## inTransactionReturnsThrows(ThrowingTransactionalSupplier work) ### Description Starts a new transaction, executes work that may throw a checked exception, and returns a value. This is useful for transactional operations that produce a result and can throw checked exceptions. ### Method ```java T inTransactionReturnsThrows(ThrowingTransactionalSupplier work) throws E ``` ### Parameters #### Path Parameters * None #### Query Parameters * None #### Request Body * **work** (ThrowingTransactionalSupplier) - Required - Description: Work with return value and checked exceptions ### Request Example ```java String result = transactionManager.inTransactionReturnsThrows(tx -> { doSomethingThatThrows(); return "success"; }); ``` ### Response #### Success Response * **T** (Type T) - Description: The value returned by the work #### Response Example ```json { "example": "success" } ``` ```